From 7f63228d704d89756c1baa7351a0bd6a674293e1 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 18 Sep 2025 15:00:32 +0200 Subject: [PATCH 01/16] fix: tax amount error --- .../request/step/IOURequestStepTaxAmountPage.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index b0b3e09819106..995c861a7cc8f 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -49,8 +49,13 @@ function IOURequestStepTaxAmountPage({ report, }: IOURequestStepTaxAmountPageProps) { const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); - const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report?.policyID}`, {canBeMissing: true}); - const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${report?.policyID}`, {canBeMissing: true}); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); + const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); + const taxPolicy = report?.policyID === CONST.POLICY.ID_FAKE ? activePolicy : policy; + const taxPolicyID = report?.policyID === CONST.POLICY.ID_FAKE ? activePolicyID : report?.policyID; + + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${taxPolicyID}`, {canBeMissing: true}); + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${taxPolicyID}`, {canBeMissing: true}); const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, {canBeMissing: true}); const {translate} = useLocalize(); const textInput = useRef(null); @@ -109,7 +114,7 @@ function IOURequestStepTaxAmountPage({ navigateBack(); return; } - updateMoneyRequestTaxAmount(transactionID, report?.reportID, taxAmountInSmallestCurrencyUnits, policy, policyTags, policyCategories); + updateMoneyRequestTaxAmount(transactionID, report?.reportID, taxAmountInSmallestCurrencyUnits, taxPolicy, policyTags, policyCategories); navigateBack(); return; } @@ -151,7 +156,7 @@ function IOURequestStepTaxAmountPage({ isEditing={!!(backTo || isEditing)} currency={currency} amount={Math.abs(transactionDetails?.taxAmount ?? 0)} - taxAmount={getTaxAmount(currentTransaction, policy, currency, !!(backTo || isEditing))} + taxAmount={getTaxAmount(currentTransaction, taxPolicy, currency, !!(backTo || isEditing))} ref={(e) => { textInput.current = e; }} From 0dac2f7f8b73c86a5c2df2549e289f163b8e490c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 18 Sep 2025 17:07:53 +0200 Subject: [PATCH 02/16] fix: minor fix --- src/libs/actions/Transaction.ts | 1 + .../iou/request/step/IOURequestStepTaxAmountPage.tsx | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index daa1fedd0f3a7..9ebab3748dc29 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -343,6 +343,7 @@ function getRoute(transactionID: string, waypoints: WaypointCollection, routeTyp API.read(command, parameters, getOnyxDataForRouteRequest(transactionID, routeType)); } + /** * Updates all waypoints stored in the transaction specified by the provided transactionID. * diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 995c861a7cc8f..8aad114fcea96 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -48,11 +48,14 @@ function IOURequestStepTaxAmountPage({ transaction, report, }: IOURequestStepTaxAmountPageProps) { - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); - const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, {canBeMissing: true}); - const taxPolicy = report?.policyID === CONST.POLICY.ID_FAKE ? activePolicy : policy; - const taxPolicyID = report?.policyID === CONST.POLICY.ID_FAKE ? activePolicyID : report?.policyID; + const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, { + canBeMissing: true, + selector: (policy) => (policy?.type !== CONST.POLICY.TYPE.PERSONAL ? policy : undefined), + }); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); + const taxPolicy = report?.policyID === CONST.POLICY.ID_FAKE && activePolicy ? activePolicy : policy; + const taxPolicyID = report?.policyID === CONST.POLICY.ID_FAKE && activePolicy ? activePolicyID : report?.policyID; const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${taxPolicyID}`, {canBeMissing: true}); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${taxPolicyID}`, {canBeMissing: true}); From 186701efafe419077b53044dfed2564167eeec7d Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 18 Sep 2025 18:09:29 +0200 Subject: [PATCH 03/16] fix: report action modified expense message --- src/libs/actions/IOU.ts | 1 + src/pages/home/report/ReportActionItem.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index bbe59f0cff1df..c87836d1d4cab 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -10610,6 +10610,7 @@ function completePaymentOnboarding(paymentSelected: ValueOf, paymentPolicyID?: string, full = true) { if (chatReport.policyID && shouldRestrictUserBillableActions(chatReport.policyID)) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(chatReport.policyID)); diff --git a/src/pages/home/report/ReportActionItem.tsx b/src/pages/home/report/ReportActionItem.tsx index a96c80ab3334f..39537a5d0fc63 100644 --- a/src/pages/home/report/ReportActionItem.tsx +++ b/src/pages/home/report/ReportActionItem.tsx @@ -17,6 +17,7 @@ import { isChatThread, isClosedExpenseReportWithNoExpenses, isCurrentUserTheOnlyParticipant, + isSelfDM, } from '@libs/ReportUtils'; import { deleteReportActionDraft, @@ -98,6 +99,7 @@ function ReportActionItem({ const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getIOUReportIDFromReportActionPreview(action)}`]; const movedFromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(action, CONST.REPORT.MOVE_TYPE.FROM)}`]; const movedToReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(action, CONST.REPORT.MOVE_TYPE.TO)}`]; + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); // The app would crash due to subscribing to the entire report collection if parentReportID is an empty string. So we should have a fallback ID here. @@ -149,7 +151,7 @@ function ReportActionItem({ )} modifiedExpenseMessage={getForReportAction({ reportAction: action, - policyID: report?.policyID, + policyID: isSelfDM(parentReport) ? activePolicyID : report?.policyID, movedFromReport, movedToReport, })} From b6eec489bc80de93d06c981f1e9fb2cc8d87546d Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Thu, 18 Sep 2025 18:38:34 +0200 Subject: [PATCH 04/16] fix: show proper message on tax change --- src/libs/ModifiedExpenseMessage.ts | 2 +- src/libs/actions/IOU.ts | 1 + .../request/step/IOURequestStepTaxAmountPage.tsx | 14 +++++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index a8412fe5ea5f9..c892bd2ec65a7 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -308,7 +308,7 @@ function getForReportAction({ buildMessageFragmentForValue(taxAmount, oldTaxAmount, translateLocal('iou.taxAmount'), false, setFragments, removalFragments, changeFragments); } - const hasModifiedTaxRate = isReportActionOriginalMessageAnObject && 'oldTaxRate' in reportActionOriginalMessage && 'taxRate' in reportActionOriginalMessage; + const hasModifiedTaxRate = isReportActionOriginalMessageAnObject && ('oldTaxRate' in reportActionOriginalMessage || 'taxRate' in reportActionOriginalMessage); if (hasModifiedTaxRate) { buildMessageFragmentForValue( reportActionOriginalMessage?.taxRate ?? '', diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c87836d1d4cab..4be2e479c5ea6 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4473,6 +4473,7 @@ function getUpdateMoneyRequestParams( if ( policy && isPaidGroupPolicy(policy) && + !isSelfDM(iouReport) && !isInvoice && updatedTransaction && (hasModifiedTag || diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 8aad114fcea96..8f8d507f0683e 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -8,7 +8,14 @@ import {setDraftSplitTransaction, setMoneyRequestCurrency, setMoneyRequestPartic import {convertToBackendAmount, isValidCurrencyCode} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getTransactionDetails} from '@libs/ReportUtils'; -import {calculateTaxAmount, getAmount, getDefaultTaxCode, getTaxValue, getTaxAmount as getTransactionTaxAmount} from '@libs/TransactionUtils'; +import { + calculateTaxAmount, + getAmount, + getDefaultTaxCode, + getTaxValue, + getTaxAmount as getTransactionTaxAmount, + isExpenseUnreported as isExpenseUnreportedTransactionUtils, +} from '@libs/TransactionUtils'; import type {CurrentMoney} from '@pages/iou/MoneyRequestAmountForm'; import MoneyRequestAmountForm from '@pages/iou/MoneyRequestAmountForm'; import CONST from '@src/CONST'; @@ -54,8 +61,9 @@ function IOURequestStepTaxAmountPage({ selector: (policy) => (policy?.type !== CONST.POLICY.TYPE.PERSONAL ? policy : undefined), }); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, {canBeMissing: true}); - const taxPolicy = report?.policyID === CONST.POLICY.ID_FAKE && activePolicy ? activePolicy : policy; - const taxPolicyID = report?.policyID === CONST.POLICY.ID_FAKE && activePolicy ? activePolicyID : report?.policyID; + const isExpenseUnreported = isExpenseUnreportedTransactionUtils(transaction); + const taxPolicy = isExpenseUnreported ? activePolicy : policy; + const taxPolicyID = isExpenseUnreported ? activePolicyID : report?.policyID; const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${taxPolicyID}`, {canBeMissing: true}); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${taxPolicyID}`, {canBeMissing: true}); From f4048ff3d71f396af7f646709b92b525e62f2987 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Mon, 22 Sep 2025 11:41:48 +0200 Subject: [PATCH 05/16] fix: navigation issue --- src/pages/iou/request/step/IOURequestStepUpgrade.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx index ba9a1fe8a2ed3..21f7c702db2f9 100644 --- a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx +++ b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx @@ -74,6 +74,9 @@ function IOURequestStepUpgrade({ } Navigation.goBack(); + // If we're submitting the expense to the workspace, we don't need the backTo param + const backTo = action === CONST.IOU.ACTION.CATEGORIZE ? '' : ROUTES.REPORT_WITH_ID.getRoute(reportID); + switch (upgradePath) { case CONST.UPGRADE_PATHS.DISTANCE_RATES: { if (!policyID || !reportID) { @@ -87,7 +90,7 @@ function IOURequestStepUpgrade({ break; } case CONST.UPGRADE_PATHS.CATEGORIES: - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID, ROUTES.REPORT_WITH_ID.getRoute(reportID))); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID, backTo)); break; case CONST.UPGRADE_PATHS.REPORTS: Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_REPORT.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID)); From 626e2d083e340cd9db40303c28c52f484b4e4777 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 09:12:30 +0200 Subject: [PATCH 06/16] fix: remove the flag --- src/libs/Permissions.ts | 8 -------- src/libs/ReportUtils.ts | 33 ++++++++---------------------- src/libs/TransactionUtils/index.ts | 6 ------ src/pages/Search/SearchPage.tsx | 6 +----- 4 files changed, 10 insertions(+), 43 deletions(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index bff77bcb9538c..434b8647e9739 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -15,13 +15,6 @@ function canUseLinkPreviews(): boolean { return false; } -/** - * Temporary check for Unreported Expense Project - change to true for testing - */ -function canUseUnreportedExpense(): boolean { - return false; -} - function isBetaEnabled(beta: Beta, betas: OnyxEntry, betaConfiguration?: OnyxEntry): boolean { const hasAllBetasEnabled = canUseAllBetas(betas); const isFeatureEnabled = !!betas?.includes(beta); @@ -38,5 +31,4 @@ function isBetaEnabled(beta: Beta, betas: OnyxEntry, betaConfiguration?: export default { canUseLinkPreviews, isBetaEnabled, - canUseUnreportedExpense, }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cad7d1244ef0a..97adcb89ef35a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4342,8 +4342,7 @@ function canEditFieldOfMoneyRequest( // Unreported transaction from OldDot can have the reportID as an empty string const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; - // TODO: remove permission check after the Unreported Expense project is complete - if (isUnreportedExpense && Permissions.canUseUnreportedExpense()) { + if (isUnreportedExpense) { return true; } @@ -4357,9 +4356,7 @@ function canEditFieldOfMoneyRequest( } const isOwner = moneyRequestReport?.ownerAccountID === currentUserAccountID; - // TODO: uncomment after the Unreported Expense project is complete - // if (isInvoiceReport(moneyRequestReport)) { - if (isInvoiceReport(moneyRequestReport) && !isUnreportedExpense) { + if (isInvoiceReport(moneyRequestReport)) { return ( getOutstandingReportsForUser( moneyRequestReport?.policyID, @@ -4372,28 +4369,16 @@ function canEditFieldOfMoneyRequest( // If the report is Open, then only submitters, admins can move expenses const isOpen = isOpenExpenseReport(moneyRequestReport); - // TODO: uncomment after the Unreported Expense project is complete - // if (isOpen && !isSubmitter && !isAdmin) { - if (!isUnreportedExpense && isOpen && !isSubmitter && !isAdmin) { + if (isOpen && !isSubmitter && !isAdmin) { return false; } - // TODO: uncomment after the Unreported Expense project is complete - // return ( - // Object.values(allPolicies ?? {}).flatMap((currentPolicy) => - // getOutstandingReportsForUser(currentPolicy?.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), - // ).length > 1 || - // (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) - // ); - - return isUnreportedExpense - ? Object.values(allPolicies ?? {}).flatMap((currentPolicy) => - getOutstandingReportsForUser(currentPolicy?.id, currentUserAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), - ).length > 0 - : Object.values(allPolicies ?? {}).flatMap((currentPolicy) => - getOutstandingReportsForUser(currentPolicy?.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), - ).length > 1 || - (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)); + return ( + Object.values(allPolicies ?? {}).flatMap((currentPolicy) => + getOutstandingReportsForUser(currentPolicy?.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), + ).length > 1 || + (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) + ); } const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 2b0d7c5a9b985..64d7e15733df8 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -18,7 +18,6 @@ import {toLocaleDigit} from '@libs/LocaleDigitUtils'; import {translateLocal} from '@libs/Localize'; import Log from '@libs/Log'; import {rand64, roundToTwoDecimalPlaces} from '@libs/NumberUtils'; -import Permissions from '@libs/Permissions'; import {getPersonalDetailsByIDs} from '@libs/PersonalDetailsUtils'; import { getCommaSeparatedTagNameWithSanitizedColons, @@ -1952,12 +1951,7 @@ function createUnreportedExpenseSections(transactions: Array selectedTransactions[id].canChangeReport) && !!activePolicy && activePolicy?.type !== CONST.POLICY.TYPE.PERSONAL - : selectedTransactionsKeys.every((id) => selectedTransactions[id].canChangeReport); + const canAllTransactionsBeMoved = selectedTransactionsKeys.every((id) => selectedTransactions[id].canChangeReport) && !!activePolicy && activePolicy?.type !== CONST.POLICY.TYPE.PERSONAL if (canAllTransactionsBeMoved) { options.push({ From 3c2700f7418a1cdf0b4ae7a6ee6647dc8c513360 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 09:44:34 +0200 Subject: [PATCH 07/16] fix: prettier --- src/pages/Search/SearchPage.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 48c795f994df9..1083d300da015 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -390,7 +390,8 @@ function SearchPage({route}: SearchPageProps) { }); } - const canAllTransactionsBeMoved = selectedTransactionsKeys.every((id) => selectedTransactions[id].canChangeReport) && !!activePolicy && activePolicy?.type !== CONST.POLICY.TYPE.PERSONAL + const canAllTransactionsBeMoved = + selectedTransactionsKeys.every((id) => selectedTransactions[id].canChangeReport) && !!activePolicy && activePolicy?.type !== CONST.POLICY.TYPE.PERSONAL; if (canAllTransactionsBeMoved) { options.push({ From 62b056af4ad5416192ccc971c6356a3e19fadd1d Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 5 Sep 2025 15:30:39 +0200 Subject: [PATCH 08/16] feat: add translation for reports --- src/languages/de.ts | 5 +++++ src/languages/en.ts | 5 +++++ src/languages/es.ts | 5 +++++ src/languages/fr.ts | 5 +++++ src/languages/it.ts | 5 +++++ src/languages/ja.ts | 5 +++++ src/languages/nl.ts | 5 +++++ src/languages/pl.ts | 5 +++++ src/languages/pt-BR.ts | 5 +++++ src/languages/zh-hans.ts | 5 +++++ 10 files changed, 50 insertions(+) diff --git a/src/languages/de.ts b/src/languages/de.ts index 19a80d5d203fb..ba07468fa3971 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -5542,6 +5542,11 @@ const translations = { 'Expensify Travel ist eine neue Plattform für die Buchung und Verwaltung von Geschäftsreisen, die es Mitgliedern ermöglicht, Unterkünfte, Flüge, Transportmittel und mehr zu buchen.', onlyAvailableOnPlan: 'Reisen ist im Collect-Plan verfügbar, beginnend bei', }, + reports: { + title: 'Berichte', + description: 'Erstellen Sie organisierte Spesenabrechnungen, um Ihre Geschäftsausgaben zu verfolgen, zur Genehmigung einzureichen und Ihren Erstattungsprozess zu optimieren.', + onlyAvailableOnPlan: 'Berichte sind im Collect-Plan verfügbar, beginnend bei ', + }, multiLevelTags: { title: 'Mehrstufige Tags', description: diff --git a/src/languages/en.ts b/src/languages/en.ts index 31d8516a52061..dfd29a186a894 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5511,6 +5511,11 @@ const translations = { description: 'Expensify Travel is a new corporate travel booking and management platform that allows members to book accommodations, flights, transportation, and more.', onlyAvailableOnPlan: 'Travel is available on the Collect plan, starting at ', }, + reports: { + title: 'Reports', + description: 'Create organized expense reports to track your business spending, submit for approvals, and streamline your reimbursement process.', + onlyAvailableOnPlan: 'Reports are available on the Collect plan, starting at ', + }, multiLevelTags: { title: 'Multi-level tags', description: diff --git a/src/languages/es.ts b/src/languages/es.ts index ea7802e188c70..3ab442faf1006 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5545,6 +5545,11 @@ const translations = { 'Expensify Travel es una nueva plataforma corporativa de reserva y gestión de viajes que permite a los miembros reservar alojamientos, vuelos, transporte y mucho más.', onlyAvailableOnPlan: 'Los viajes están disponibles en el plan Recopilar, a partir de ', }, + reports: { + title: 'Informes', + description: 'Crea informes de gastos organizados para hacer seguimiento de tus gastos comerciales, enviarlos para aprobación y optimizar tu proceso de reembolso.', + onlyAvailableOnPlan: 'Los informes están disponibles en el plan Recopilar, a partir de ', + }, multiLevelTags: { title: 'Etiquetas multinivel', description: diff --git a/src/languages/fr.ts b/src/languages/fr.ts index a4c2fe856389a..0576d8f2a84fa 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -5552,6 +5552,11 @@ const translations = { "Expensify Travel est une nouvelle plateforme de réservation et de gestion de voyages d'affaires qui permet aux membres de réserver des hébergements, des vols, des transports, et plus encore.", onlyAvailableOnPlan: 'Le voyage est disponible sur le plan Collect, à partir de', }, + reports: { + title: 'Rapports', + description: 'Créez des rapports de dépenses organisés pour suivre vos dépenses professionnelles, les soumettre pour approbation et rationaliser votre processus de remboursement.', + onlyAvailableOnPlan: 'Les rapports sont disponibles sur le plan Collect, à partir de ', + }, multiLevelTags: { title: 'Tags multi-niveaux', description: diff --git a/src/languages/it.ts b/src/languages/it.ts index 2e2d5c12fba2b..643c46b2aa97e 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -5550,6 +5550,11 @@ const translations = { 'Expensify Travel è una nuova piattaforma aziendale per la prenotazione e la gestione dei viaggi che consente ai membri di prenotare alloggi, voli, trasporti e altro.', onlyAvailableOnPlan: 'Il viaggio è disponibile nel piano Collect, a partire da', }, + reports: { + title: 'Report', + description: 'Crea report spese organizzati per tenere traccia delle tue spese aziendali, inviarli per approvazione e semplificare il processo di rimborso.', + onlyAvailableOnPlan: 'I report sono disponibili nel piano Collect, a partire da ', + }, multiLevelTags: { title: 'Tag multi-livello', description: diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 983695e7acd81..ca3555e413953 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -5504,6 +5504,11 @@ const translations = { description: 'Expensify Travelは、メンバーが宿泊施設、フライト、交通機関などを予約できる新しい法人向け旅行予約および管理プラットフォームです。', onlyAvailableOnPlan: '旅行は、Collectプランで利用可能です。料金は', }, + reports: { + title: 'レポート', + description: '組織化された経費レポートを作成して、ビジネス支出を追跡し、承認のために提出し、払い戻しプロセスを合理化します。', + onlyAvailableOnPlan: 'レポートは、Collectプランで利用可能です。料金は ', + }, multiLevelTags: { title: 'マルチレベルタグ', description: diff --git a/src/languages/nl.ts b/src/languages/nl.ts index c5c0b6229637c..0993520019f0d 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -5542,6 +5542,11 @@ const translations = { description: 'Expensify Travel is een nieuw platform voor het boeken en beheren van zakelijke reizen waarmee leden accommodaties, vluchten, vervoer en meer kunnen boeken.', onlyAvailableOnPlan: 'Reizen is beschikbaar op het Collect-plan, beginnend bij', }, + reports: { + title: 'Rapporten', + description: 'Maak georganiseerde onkostenrapporten om uw zakelijke uitgaven bij te houden, in te dienen voor goedkeuring en uw vergoedingsproces te stroomlijnen.', + onlyAvailableOnPlan: 'Rapporten zijn beschikbaar op het Collect-plan, beginnend bij ', + }, multiLevelTags: { title: 'Meerniveautags', description: diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 3d786e6f3d11e..3d96b8f11f963 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -5530,6 +5530,11 @@ const translations = { 'Expensify Travel to nowa platforma do rezerwacji i zarządzania podróżami służbowymi, która umożliwia członkom rezerwację zakwaterowania, lotów, transportu i nie tylko.', onlyAvailableOnPlan: 'Podróże są dostępne w planie Collect, zaczynając od', }, + reports: { + title: 'Raporty', + description: 'Twórz uporządkowane raporty wydatków, aby śledzić swoje wydatki biznesowe, przesyłać je do zatwierdzenia i usprawniać proces zwrotu kosztów.', + onlyAvailableOnPlan: 'Raporty są dostępne w planie Collect, zaczynając od ', + }, multiLevelTags: { title: 'Wielopoziomowe tagi', description: diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 59ffec90cf86a..db7feecf78910 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -5541,6 +5541,11 @@ const translations = { description: 'Expensify Travel é uma nova plataforma de reserva e gestão de viagens corporativas que permite aos membros reservar acomodações, voos, transporte e mais.', onlyAvailableOnPlan: 'Viagens estão disponíveis no plano Collect, a partir de', }, + reports: { + title: 'Relatórios', + description: 'Crie relatórios de despesas organizados para acompanhar seus gastos empresariais, enviá-los para aprovação e otimizar seu processo de reembolso.', + onlyAvailableOnPlan: 'Os relatórios estão disponíveis no plano Collect, a partir de ', + }, multiLevelTags: { title: 'Tags multiníveis', description: diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index e0a154e64e3d9..69e68d2eb8b4a 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -5442,6 +5442,11 @@ const translations = { description: 'Expensify Travel 是一个新的企业差旅预订和管理平台,允许会员预订住宿、航班、交通等。', onlyAvailableOnPlan: '旅行功能在 Collect 计划中提供,起价为', }, + reports: { + title: '报告', + description: '创建有序的费用报告来跟踪您的商业开支,提交审批,并简化您的报销流程。', + onlyAvailableOnPlan: '报告功能在 Collect 计划中提供,起价为 ', + }, multiLevelTags: { title: '多级标签', description: '多级标签帮助您更精确地跟踪费用。为每个项目分配多个标签,例如部门、客户或成本中心,以捕获每笔费用的完整上下文。这使得更详细的报告、审批流程和会计导出成为可能。', From 944fc70fe985ccf037d76dfac87c571c9da92ac2 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 5 Sep 2025 16:07:34 +0200 Subject: [PATCH 09/16] feat: add consts, enable editing report field --- src/CONST/index.ts | 8 ++++++++ src/components/ReportActionItem/MoneyRequestView.tsx | 9 ++++++++- src/libs/ReportUtils.ts | 6 ++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index e90e84e1ab075..0183436f42a3c 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -6960,6 +6960,14 @@ const CONST = { description: 'workspace.upgrade.travel.description' as const, icon: 'Luggage', }, + reports: { + id: 'reports' as const, + alias: 'reports', + name: 'Reports', + title: 'workspace.upgrade.reports.title' as const, + description: 'workspace.upgrade.reports.description' as const, + icon: 'ReportReceipt', + }, distanceRates: { id: 'distanceRates' as const, alias: 'distance-rates', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 24639427b7aad..a4742c635f8bd 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -841,7 +841,7 @@ function MoneyRequestView({ + return Object.values(allPolicies ?? {}).flatMap((currentPolicy) => getOutstandingReportsForUser(currentPolicy?.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), ).length > 1 || - (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) - ); + (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)); } const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; From 7bf618c1045b44eab0418e5bf4ecaae9d21d9517 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 14:37:55 +0200 Subject: [PATCH 10/16] fix: restore changes from bulk upgrade --- .../ReportActionItem/MoneyRequestView.tsx | 11 ++++- .../Search/SearchTransactionsChangeReport.tsx | 23 +++++++++ .../step/IOURequestEditReportCommon.tsx | 48 +++++++++++++++---- .../iou/request/step/IOURequestStepReport.tsx | 10 ++++ .../request/step/IOURequestStepUpgrade.tsx | 1 + src/pages/workspace/upgrade/UpgradeIntro.tsx | 6 ++- 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index a4742c635f8bd..04887e9fe3457 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -851,8 +851,15 @@ function MoneyRequestView({ } if (!policy) { - // TODO: fix it later - Navigation.navigate(ROUTES.MONEY_REQUEST_UPGRADE.getRoute()); + Navigation.navigate( + ROUTES.MONEY_REQUEST_UPGRADE.getRoute({ + iouType, + action: CONST.IOU.ACTION.EDIT, + transactionID: transaction?.transactionID, + reportID: report.reportID, + upgradePath: CONST.UPGRADE_PATHS.REPORTS, + }), + ); return; } diff --git a/src/pages/Search/SearchTransactionsChangeReport.tsx b/src/pages/Search/SearchTransactionsChangeReport.tsx index 9591597097561..965a08464a844 100644 --- a/src/pages/Search/SearchTransactionsChangeReport.tsx +++ b/src/pages/Search/SearchTransactionsChangeReport.tsx @@ -3,7 +3,9 @@ import {InteractionManager} from 'react-native'; import {useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; +import {createNewReport} from '@libs/actions/Report'; import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; @@ -25,6 +27,7 @@ function SearchTransactionsChangeReport() { const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, allBetas); const session = useSession(); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const firstTransactionKey = selectedTransactionsKeys.at(0); const firstTransactionReportID = firstTransactionKey ? selectedTransactions[firstTransactionKey]?.reportID : undefined; @@ -33,6 +36,25 @@ function SearchTransactionsChangeReport() { ? firstTransactionReportID : undefined; + // Get the policy ID from the first transaction + const activePolicyID = firstTransactionKey ? selectedTransactions[firstTransactionKey]?.policyID : undefined; + + const createReport = () => { + const createdReportID = createNewReport(currentUserPersonalDetails, activePolicyID); + const reportNextStep = allReportNextSteps?.[`${ONYXKEYS.COLLECTION.NEXT_STEP}${createdReportID}`]; + changeTransactionsReport( + selectedTransactionsKeys, + createdReportID, + isASAPSubmitBetaEnabled, + session?.accountID ?? CONST.DEFAULT_NUMBER_ID, + session?.email ?? '', + activePolicyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`] : undefined, + reportNextStep, + ); + clearSelectedTransactions(); + Navigation.goBack(); + }; + const selectReport = (item: TransactionGroupListItem) => { if (selectedTransactionsKeys.length === 0) { return; @@ -71,6 +93,7 @@ function SearchTransactionsChangeReport() { selectedReportID={selectedReportID} selectReport={selectReport} removeFromReport={removeFromReport} + createReport={createReport} isEditing /> ); diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 01b207c0a9a55..4affce0c7ee24 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -38,6 +38,7 @@ type Props = { isEditing?: boolean; isUnreported?: boolean; shouldShowNotFoundPage?: boolean; + createReport?: () => void; }; const policyIdSelector = (policy: OnyxEntry) => policy?.id; @@ -54,6 +55,7 @@ function IOURequestEditReportCommon({ isEditing = false, isUnreported, shouldShowNotFoundPage: shouldShowNotFoundPageFromProps, + createReport, }: Props) { const {translate, localeCompare} = useLocalize(); const {options} = useOptionsList(); @@ -62,6 +64,11 @@ function IOURequestEditReportCommon({ const [selectedReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`, {canBeMissing: true}); const reportOwnerAccountID = useMemo(() => selectedReport?.ownerAccountID ?? currentUserPersonalDetails.accountID, [selectedReport, currentUserPersonalDetails.accountID]); const reportPolicy = usePolicy(selectedReport?.policyID); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); + const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`, { + canBeMissing: true, + selector: (policy) => (policy?.type !== CONST.POLICY.TYPE.PERSONAL ? policy : undefined), + }); const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true}); const [allPoliciesID] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policiesSelector, canBeMissing: false}); @@ -145,8 +152,27 @@ function IOURequestEditReportCommon({ const headerMessage = useMemo(() => (searchValue && !reportOptions.length ? translate('common.noResultsFound') : ''), [searchValue, reportOptions, translate]); + const createReportOption = useMemo(() => { + if (!createReport) { + return undefined; + } + + return ( + + ); + }, [createReport, translate, activePolicy]); + // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo(() => { + if (createReport) { + return false; + } + if (expenseReports.length === 0 || shouldShowNotFoundPageFromProps) { return true; } @@ -160,7 +186,7 @@ function IOURequestEditReportCommon({ const isSubmitter = isReportOwner(selectedReport); // If the report is Open, then only submitters, admins can move expenses return isOpen && !isAdmin && !isSubmitter; - }, [selectedReport, reportPolicy, expenseReports.length, shouldShowNotFoundPageFromProps]); + }, [createReport, selectedReport, reportPolicy, expenseReports.length, shouldShowNotFoundPageFromProps]); return ( - ) : undefined + <> + {shouldShowRemoveFromReport && ( + + )} + {createReportOption} + } + listEmptyContent={createReportOption} /> ); diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index b5e331da9af59..afa9219645416 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -3,8 +3,10 @@ import {InteractionManager} from 'react-native'; import {useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; +import {createNewReport} from '@libs/actions/Report'; import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; @@ -38,6 +40,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const selectedReportID = shouldUseTransactionReport ? transactionReport?.reportID : outstandingReportID; const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); const {removeTransaction} = useSearchContext(); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); const reportOrDraftReport = getReportOrDraftReport(reportIDFromRoute); const isEditing = action === CONST.IOU.ACTION.EDIT; const isCreateReport = action === CONST.IOU.ACTION.CREATE; @@ -45,6 +48,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const [allBetas] = useOnyx(ONYXKEYS.BETAS, {canBeMissing: true}); const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, allBetas); const session = useSession(); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const handleGoBack = () => { if (isEditing) { @@ -158,6 +162,11 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useShowNotFoundPageInIOUStep(action, iouType, reportActionID, reportOrDraftReport, transaction); + const createReport = () => { + const createdReportID = createNewReport(currentUserPersonalDetails, activePolicyID); + handleRegularReportSelection({value: createdReportID}); + }; + return ( ); } diff --git a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx index 079a0fd5954cf..c0dfb6edc8a7b 100644 --- a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx +++ b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx @@ -171,6 +171,7 @@ function IOURequestStepUpgrade({ buttonDisabled={isOffline} loading={false} isCategorizing={isCategorizing} + isReporting={isReporting} isDistanceRateUpgrade={isDistanceRateUpgrade} /> )} diff --git a/src/pages/workspace/upgrade/UpgradeIntro.tsx b/src/pages/workspace/upgrade/UpgradeIntro.tsx index 9a90ab3c26332..6cdb53763cf8a 100644 --- a/src/pages/workspace/upgrade/UpgradeIntro.tsx +++ b/src/pages/workspace/upgrade/UpgradeIntro.tsx @@ -30,12 +30,14 @@ type Props = { onUpgrade: () => void; /** Whether is categorizing the expense */ isCategorizing?: boolean; + /** Whether is adding an unreported expense to a report */ + isReporting?: boolean; isDistanceRateUpgrade?: boolean; policyID?: string; backTo?: Route; }; -function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizing, isDistanceRateUpgrade, policyID, backTo}: Props) { +function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizing, isDistanceRateUpgrade, isReporting, policyID, backTo}: Props) { const styles = useThemeStyles(); const {isExtraSmallScreenWidth} = useResponsiveLayout(); const {translate} = useLocalize(); @@ -68,7 +70,7 @@ function UpgradeIntro({feature, onUpgrade, buttonDisabled, loading, isCategorizi * The "isCategorizing" flag is set to true when the user accesses the "Categorize" option in the Self-DM whisper. * In such scenarios, a separate Categories upgrade UI is displayed. */ - if (!feature || (!isCategorizing && !isDistanceRateUpgrade && !policyID)) { + if (!feature || (!isCategorizing && !isDistanceRateUpgrade && !isReporting && !policyID)) { return ( Date: Wed, 24 Sep 2025 15:12:19 +0200 Subject: [PATCH 11/16] fix: add missing create report action --- .../iou/request/step/IOURequestEditReport.tsx | 20 +++++++++++++++++++ .../step/IOURequestEditReportCommon.tsx | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestEditReport.tsx b/src/pages/iou/request/step/IOURequestEditReport.tsx index 5fc36a082970c..3dfb8271d7d82 100644 --- a/src/pages/iou/request/step/IOURequestEditReport.tsx +++ b/src/pages/iou/request/step/IOURequestEditReport.tsx @@ -2,11 +2,13 @@ import React from 'react'; import {useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; +import {createNewReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -32,6 +34,8 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, allBetas); const session = useSession(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); const selectReport = (item: TransactionGroupListItem) => { if (selectedTransactionIDs.length === 0 || item.value === reportID) { @@ -65,6 +69,21 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { Navigation.dismissModal(); }; + const createReport = () => { + const createdReportID = createNewReport(currentUserPersonalDetails, selectedReport?.policyID ?? activePolicyID); + changeTransactionsReport( + selectedTransactionIDs, + createdReportID, + isASAPSubmitBetaEnabled, + session?.accountID ?? CONST.DEFAULT_NUMBER_ID, + session?.email ?? '', + allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${selectedReport?.policyID ?? activePolicyID}`], + reportNextStep, + ); + clearSelectedTransactions(); + Navigation.goBack(); + }; + return ( ); } diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 4affce0c7ee24..e5e714b547503 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -161,11 +161,11 @@ function IOURequestEditReportCommon({ ); - }, [createReport, translate, activePolicy]); + }, [createReport, translate, activePolicy, reportPolicy]); // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo(() => { From 2cc484970cb869964f3a69d90508b81fc324b572 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 15:53:17 +0200 Subject: [PATCH 12/16] fix: revert commit --- .../iou/request/step/IOURequestEditReport.tsx | 20 ------------------- .../step/IOURequestEditReportCommon.tsx | 4 ++-- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestEditReport.tsx b/src/pages/iou/request/step/IOURequestEditReport.tsx index 3dfb8271d7d82..5fc36a082970c 100644 --- a/src/pages/iou/request/step/IOURequestEditReport.tsx +++ b/src/pages/iou/request/step/IOURequestEditReport.tsx @@ -2,13 +2,11 @@ import React from 'react'; import {useSession} from '@components/OnyxListItemProvider'; import {useSearchContext} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnyx from '@hooks/useOnyx'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; -import {createNewReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -34,8 +32,6 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { const isASAPSubmitBetaEnabled = Permissions.isBetaEnabled(CONST.BETAS.ASAP_SUBMIT, allBetas); const session = useSession(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true}); - const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID, {canBeMissing: true}); const selectReport = (item: TransactionGroupListItem) => { if (selectedTransactionIDs.length === 0 || item.value === reportID) { @@ -69,21 +65,6 @@ function IOURequestEditReport({route}: IOURequestEditReportProps) { Navigation.dismissModal(); }; - const createReport = () => { - const createdReportID = createNewReport(currentUserPersonalDetails, selectedReport?.policyID ?? activePolicyID); - changeTransactionsReport( - selectedTransactionIDs, - createdReportID, - isASAPSubmitBetaEnabled, - session?.accountID ?? CONST.DEFAULT_NUMBER_ID, - session?.email ?? '', - allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${selectedReport?.policyID ?? activePolicyID}`], - reportNextStep, - ); - clearSelectedTransactions(); - Navigation.goBack(); - }; - return ( ); } diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index e5e714b547503..4affce0c7ee24 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -161,11 +161,11 @@ function IOURequestEditReportCommon({ ); - }, [createReport, translate, activePolicy, reportPolicy]); + }, [createReport, translate, activePolicy]); // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo(() => { From 5be3fcf66e704cab5b000497d6e63d1e4207193c Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 15:59:49 +0200 Subject: [PATCH 13/16] fix: only show create report button for unreported epenses --- src/pages/iou/request/step/IOURequestEditReportCommon.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 4affce0c7ee24..79e8feb7aef11 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -153,7 +153,7 @@ function IOURequestEditReportCommon({ const headerMessage = useMemo(() => (searchValue && !reportOptions.length ? translate('common.noResultsFound') : ''), [searchValue, reportOptions, translate]); const createReportOption = useMemo(() => { - if (!createReport) { + if (!createReport || !isUnreported) { return undefined; } @@ -165,7 +165,7 @@ function IOURequestEditReportCommon({ icon={Expensicons.DocumentPlus} /> ); - }, [createReport, translate, activePolicy]); + }, [createReport, isUnreported, translate, activePolicy?.name]); // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo(() => { From 7ad25cc3d1aab44be9010a987ae7d11cdff8862d Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 16:02:33 +0200 Subject: [PATCH 14/16] fix: minor fix --- src/pages/iou/request/step/IOURequestStepUpgrade.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx index c0dfb6edc8a7b..21f7c702db2f9 100644 --- a/src/pages/iou/request/step/IOURequestStepUpgrade.tsx +++ b/src/pages/iou/request/step/IOURequestStepUpgrade.tsx @@ -92,6 +92,9 @@ function IOURequestStepUpgrade({ case CONST.UPGRADE_PATHS.CATEGORIES: Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID, backTo)); break; + case CONST.UPGRADE_PATHS.REPORTS: + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_REPORT.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID)); + break; default: } }, [action, reportID, shouldSubmitExpense, transactionID, upgradePath]); From 51b53140721f7e0008530445972770e27e7216a7 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 16:07:17 +0200 Subject: [PATCH 15/16] fix: prettier --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- src/languages/de.ts | 3 ++- src/languages/fr.ts | 3 ++- src/libs/ReportUtils.ts | 6 ++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 04887e9fe3457..c55c4d89644b6 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -841,7 +841,7 @@ function MoneyRequestView({ + return ( + Object.values(allPolicies ?? {}).flatMap((currentPolicy) => getOutstandingReportsForUser(currentPolicy?.id, moneyRequestReport?.ownerAccountID, outstandingReportsByPolicyID?.[currentPolicy?.id ?? CONST.DEFAULT_NUMBER_ID] ?? {}), ).length > 1 || - (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)); + (isOwner && isReportOutstanding(moneyRequestReport, moneyRequestReport.policyID)) + ); } const isUnreportedExpense = !transaction?.reportID || transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; From efaa1c888d8a186e3de3df3a42255ce396e53ed4 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 24 Sep 2025 18:30:33 +0200 Subject: [PATCH 16/16] fix: eslint --- src/pages/Search/SearchPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 1083d300da015..db8408f8db5b6 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -445,6 +445,7 @@ function SearchPage({route}: SearchPageProps) { return options; }, [ + activePolicy, selectedTransactionsKeys, status, hash,