From 784d942f87885ab3eeb7509b65cf7c3abb16c994 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 14:56:23 +0700 Subject: [PATCH 01/11] fix: refactor moveIOUReportToPolicy and moveIOUReportToPolicyAndInviteSubmitter --- src/components/KYCWall/BaseKYCWall.tsx | 5 +++-- src/hooks/useSearchBulkActions.ts | 4 ++-- src/libs/ReportActionsUtils.ts | 4 ++-- src/libs/ReportUtils.ts | 10 +++++++-- src/libs/actions/Report/index.ts | 6 +++-- src/pages/ReportChangeWorkspacePage.tsx | 6 +++-- tests/unit/ReportActionsUtilsTest.ts | 29 +++++++++++++++++++++++++ tests/unit/ReportUtilsTest.ts | 22 +++++++++++++++++++ 8 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index d4165757b69b..3bcf8836f324 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -62,6 +62,7 @@ function KYCWall({ const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); const [bankAccountList = getEmptyObject()] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); @@ -143,7 +144,7 @@ function KYCWall({ if (iouReport && isIOUReport(iouReport)) { const adminPolicy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policy?.id}`]; if (adminPolicy) { - const inviteResult = moveIOUReportToPolicyAndInviteSubmitter(iouReport, adminPolicy, formatPhoneNumber, filteredReportActions, reportTransactions); + const inviteResult = moveIOUReportToPolicyAndInviteSubmitter(iouReport, adminPolicy, formatPhoneNumber, filteredReportActions, reportTransactions, allReports); if (inviteResult?.policyExpenseChatReportID) { setNavigationActionToMicrotaskQueue(() => { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(inviteResult.policyExpenseChatReportID)); @@ -153,7 +154,7 @@ function KYCWall({ Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({policyID: adminPolicy.id})); }); } else { - const moveResult = moveIOUReportToPolicy(iouReport, adminPolicy, true, reportTransactions); + const moveResult = moveIOUReportToPolicy(iouReport, adminPolicy, true, reportTransactions, allReports); savePreferredPaymentMethod(iouReport.policyID, adminPolicy.id, CONST.LAST_PAYMENT_METHOD.IOU, lastPaymentMethod?.[adminPolicy.id]); if (moveResult?.policyExpenseChatReportID && !moveResult.useTemporaryOptimisticExpenseChatReportID) { diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index dcbe85086717..f7572a8debf2 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -622,9 +622,9 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { ); return; } - const invite = moveIOUReportToPolicyAndInviteSubmitter(itemReport, adminPolicy, formatPhoneNumber, policyExpenseChatReportActions, reportTransactions); + const invite = moveIOUReportToPolicyAndInviteSubmitter(itemReport, adminPolicy, formatPhoneNumber, policyExpenseChatReportActions, reportTransactions, allReports); if (!invite?.policyExpenseChatReportID) { - moveIOUReportToPolicy(itemReport, adminPolicy, false, reportTransactions); + moveIOUReportToPolicy(itemReport, adminPolicy, false, reportTransactions, allReports); } } } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 9b6522a948fc..62370d8455ef 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -2392,7 +2392,7 @@ function getReportActionMessageFragments(translate: LocalizedTranslate, action: * @param currentAccountID * @returns */ -function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccountID: number): boolean { +function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccountID: number, allReports?: OnyxCollection): boolean { if (!reportID) { return false; } @@ -2400,7 +2400,7 @@ function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccou const reportActions = Object.values(getAllReportActions(reportID)); if (reportActions.length === 0) { // In case the reportActions of the report have not been loaded, we will check based on the transactions. - const report = getReportOrDraftReport(reportID); + const report = getReportOrDraftReport(reportID, undefined, undefined, undefined, allReports); return doesReportContainRequestsFromMultipleUsers(report, true); } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fa4eb376dbc6..6e7e92a3a0ca 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1283,9 +1283,15 @@ function getChatType(report: OnyxInputOrEntry | Participant): ValueOf>, fallbackReport?: Report, reportDrafts?: OnyxCollection): OnyxEntry { +function getReportOrDraftReport( + reportID: string | undefined, + searchReports?: Array>, + fallbackReport?: Report, + reportDrafts?: OnyxCollection, + allReports: OnyxCollection = deprecatedAllReports, +): OnyxEntry { const searchReport = searchReports?.find((report) => report?.reportID === reportID); - const onyxReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; return searchReport ?? onyxReport ?? (reportDrafts ?? allReportsDraft)?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport; } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 465db74078a4..6fff1471984d 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -6050,6 +6050,7 @@ function moveIOUReportToPolicy( policy: Policy, isFromSettlementButton?: boolean, reportTransactions: Transaction[] = [], + allReports?: OnyxCollection, ): {policyExpenseChatReportID?: string; useTemporaryOptimisticExpenseChatReportID: boolean} | undefined { // This flow only works for IOU reports if (!policy || !iouReport || !isIOUReportUsingReport(iouReport)) { @@ -6059,7 +6060,7 @@ function moveIOUReportToPolicy( const isReimbursed = isReportManuallyReimbursed(iouReport); // We do not want to create negative amount expenses - if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID) && !isFromSettlementButton) { + if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReports) && !isFromSettlementButton) { return; } @@ -6099,6 +6100,7 @@ function moveIOUReportToPolicyAndInviteSubmitter( formatPhoneNumber: LocaleContextProps['formatPhoneNumber'], reportActions: OnyxCollection, reportTransactions: Transaction[] = [], + allReports?: OnyxCollection, ): {policyExpenseChatReportID?: string} | undefined { if (!policy || !iouReport) { return; @@ -6119,7 +6121,7 @@ function moveIOUReportToPolicyAndInviteSubmitter( const isReimbursed = isReportManuallyReimbursed(iouReport); // We only allow moving IOU report to a policy if it doesn't have requests from multiple users, as we do not want to create negative amount expenses - if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID)) { + if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReports)) { return; } diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index c037d45f332c..213a4c0bd56f 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -55,6 +55,7 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const reportTransactions = useReportTransactions(reportID); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`); const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [reportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${reportID}`); @@ -90,9 +91,9 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro const {backTo} = route.params; Navigation.goBack(backTo); if (isIOUReport(reportID)) { - const invite = moveIOUReportToPolicyAndInviteSubmitter(report, policy, formatPhoneNumber, filteredReportActions, reportTransactions); + const invite = moveIOUReportToPolicyAndInviteSubmitter(report, policy, formatPhoneNumber, filteredReportActions, reportTransactions, allReports); if (!invite?.policyExpenseChatReportID) { - moveIOUReportToPolicy(report, policy, false, reportTransactions); + moveIOUReportToPolicy(report, policy, false, reportTransactions, allReports); } // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -137,6 +138,7 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro formatPhoneNumber, reportTransactions, filteredReportActions, + allReports, isReportLastVisibleArchived, session?.accountID, session?.email, diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 148ad091b59d..88d2214b2e27 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1196,6 +1196,35 @@ describe('ReportActionsUtils', () => { result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID); expect(result).toBe(false); }); + + it('should use explicit allReports parameter when provided and reportActions are unloaded', async () => { + const unloadedActionsReportID = '6'; + const iouReport = { + type: CONST.REPORT.TYPE.IOU, + reportID: unloadedActionsReportID, + }; + const transactionFromCurrentUser = { + ...createRandomTransaction(1), + reportID: unloadedActionsReportID, + amount: -100, + }; + const transactionFromOtherUser = { + ...createRandomTransaction(2), + reportID: unloadedActionsReportID, + amount: 500, + }; + + // Set up transactions + await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromCurrentUser.transactionID}`, transactionFromCurrentUser); + await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromOtherUser.transactionID}`, transactionFromOtherUser); + + // Pass allReports explicitly instead of relying on Onyx state + const explicitAllReports = { + [`${ONYXKEYS.COLLECTION.REPORT}${unloadedActionsReportID}`]: iouReport, + }; + const result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID, explicitAllReports); + expect(result).toBe(true); + }); }); describe('getLastVisibleAction', () => { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 061ada5226b5..27af5d720061 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -11298,6 +11298,28 @@ describe('ReportUtils', () => { const result = getReportOrDraftReport(mockReportID); expect(result).toEqual(mockOnyxReport); }); + + test('returns report from explicit allReports parameter instead of Onyx state', async () => { + const explicitReport: Report = { + ...createRandomReport(mockReportIDIndex, undefined), + reportName: 'Explicit Report', + type: CONST.REPORT.TYPE.CHAT, + }; + const explicitAllReports = { + [`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`]: explicitReport, + }; + // Set a different report in Onyx to verify the explicit param takes precedence + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, mockOnyxReport); + const result = getReportOrDraftReport(mockReportID, undefined, undefined, undefined, explicitAllReports); + expect(result).toEqual(explicitReport); + expect(result).not.toEqual(mockOnyxReport); + }); + + test('returns undefined when explicit allReports does not contain the report', () => { + const explicitAllReports = {}; + const result = getReportOrDraftReport(mockReportID, undefined, undefined, undefined, explicitAllReports); + expect(result).toBeUndefined(); + }); }); describe('buildOptimisticExpenseReport', () => { From 696a6571d9ff4621fea22f871039b3899ba31ec1 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 15:07:14 +0700 Subject: [PATCH 02/11] fix: remove optional operator --- src/components/KYCWall/BaseKYCWall.tsx | 4 ++-- src/hooks/useSearchBulkActions.ts | 4 ++-- src/libs/actions/Report/index.ts | 4 ++-- src/pages/ReportChangeWorkspacePage.tsx | 4 ++-- tests/actions/ReportTest.ts | 14 +++++++++----- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 3bcf8836f324..ea665f56b8f5 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -144,7 +144,7 @@ function KYCWall({ if (iouReport && isIOUReport(iouReport)) { const adminPolicy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policy?.id}`]; if (adminPolicy) { - const inviteResult = moveIOUReportToPolicyAndInviteSubmitter(iouReport, adminPolicy, formatPhoneNumber, filteredReportActions, reportTransactions, allReports); + const inviteResult = moveIOUReportToPolicyAndInviteSubmitter(iouReport, adminPolicy, formatPhoneNumber, filteredReportActions, allReports, reportTransactions); if (inviteResult?.policyExpenseChatReportID) { setNavigationActionToMicrotaskQueue(() => { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(inviteResult.policyExpenseChatReportID)); @@ -154,7 +154,7 @@ function KYCWall({ Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({policyID: adminPolicy.id})); }); } else { - const moveResult = moveIOUReportToPolicy(iouReport, adminPolicy, true, reportTransactions, allReports); + const moveResult = moveIOUReportToPolicy(iouReport, adminPolicy, allReports, true, reportTransactions); savePreferredPaymentMethod(iouReport.policyID, adminPolicy.id, CONST.LAST_PAYMENT_METHOD.IOU, lastPaymentMethod?.[adminPolicy.id]); if (moveResult?.policyExpenseChatReportID && !moveResult.useTemporaryOptimisticExpenseChatReportID) { diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index f7572a8debf2..b27fc04f62e9 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -622,9 +622,9 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { ); return; } - const invite = moveIOUReportToPolicyAndInviteSubmitter(itemReport, adminPolicy, formatPhoneNumber, policyExpenseChatReportActions, reportTransactions, allReports); + const invite = moveIOUReportToPolicyAndInviteSubmitter(itemReport, adminPolicy, formatPhoneNumber, policyExpenseChatReportActions, allReports, reportTransactions); if (!invite?.policyExpenseChatReportID) { - moveIOUReportToPolicy(itemReport, adminPolicy, false, reportTransactions, allReports); + moveIOUReportToPolicy(itemReport, adminPolicy, allReports, false, reportTransactions); } } } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 6fff1471984d..53ddd6fcaee3 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -6048,9 +6048,9 @@ function deleteAppReport({ function moveIOUReportToPolicy( iouReport: OnyxEntry, policy: Policy, + allReports: OnyxCollection, isFromSettlementButton?: boolean, reportTransactions: Transaction[] = [], - allReports?: OnyxCollection, ): {policyExpenseChatReportID?: string; useTemporaryOptimisticExpenseChatReportID: boolean} | undefined { // This flow only works for IOU reports if (!policy || !iouReport || !isIOUReportUsingReport(iouReport)) { @@ -6099,8 +6099,8 @@ function moveIOUReportToPolicyAndInviteSubmitter( policy: Policy, formatPhoneNumber: LocaleContextProps['formatPhoneNumber'], reportActions: OnyxCollection, + allReports: OnyxCollection, reportTransactions: Transaction[] = [], - allReports?: OnyxCollection, ): {policyExpenseChatReportID?: string} | undefined { if (!policy || !iouReport) { return; diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index 213a4c0bd56f..cd7270db0cdf 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -91,9 +91,9 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro const {backTo} = route.params; Navigation.goBack(backTo); if (isIOUReport(reportID)) { - const invite = moveIOUReportToPolicyAndInviteSubmitter(report, policy, formatPhoneNumber, filteredReportActions, reportTransactions, allReports); + const invite = moveIOUReportToPolicyAndInviteSubmitter(report, policy, formatPhoneNumber, filteredReportActions, allReports, reportTransactions); if (!invite?.policyExpenseChatReportID) { - moveIOUReportToPolicy(report, policy, false, reportTransactions, allReports); + moveIOUReportToPolicy(report, policy, allReports, false, reportTransactions); } // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line @typescript-eslint/no-deprecated diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index a74603ef8da4..ad418fd739d2 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -3410,7 +3410,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); // When moving iou to a workspace - Report.moveIOUReportToPolicy(iouReport, policy); + Report.moveIOUReportToPolicy(iouReport, policy, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}); await waitForBatchedUpdates(); // Then MOVED report action should be added to the expense report @@ -3517,7 +3517,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); // When moving iou to a workspace and invite the submitter - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}); await waitForBatchedUpdates(); // Then MOVED report action should be added to the expense report @@ -3584,7 +3584,7 @@ describe('actions/Report', () => { // Call moveIOUReportToPolicyAndInviteSubmitter const formatPhoneNumber = (phoneNumber: string) => phoneNumber; - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, formatPhoneNumber, {}); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, formatPhoneNumber, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}); await waitForBatchedUpdates(); // Simulate network failure @@ -3641,7 +3641,9 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, transaction); // When moving IOU to a workspace with reportTransactions - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, [transaction]); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, [ + transaction, + ]); await waitForBatchedUpdates(); // Then the transaction amounts should be negated optimistically @@ -3686,7 +3688,9 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, transaction); // When moving IOU to a workspace with transactions - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, [transaction]); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, [ + transaction, + ]); await waitForBatchedUpdates(); // Then the report should be converted to an expense report with the new policyID From b03ab1d356ffeb90d03d88f1672d1ddf956f19f0 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 15:19:09 +0700 Subject: [PATCH 03/11] fix: lint --- src/libs/actions/Report/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 53ddd6fcaee3..7ebc1b4c0507 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -6048,7 +6048,7 @@ function deleteAppReport({ function moveIOUReportToPolicy( iouReport: OnyxEntry, policy: Policy, - allReports: OnyxCollection, + allReportsParam: OnyxCollection, isFromSettlementButton?: boolean, reportTransactions: Transaction[] = [], ): {policyExpenseChatReportID?: string; useTemporaryOptimisticExpenseChatReportID: boolean} | undefined { @@ -6060,7 +6060,7 @@ function moveIOUReportToPolicy( const isReimbursed = isReportManuallyReimbursed(iouReport); // We do not want to create negative amount expenses - if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReports) && !isFromSettlementButton) { + if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReportsParam) && !isFromSettlementButton) { return; } @@ -6099,7 +6099,7 @@ function moveIOUReportToPolicyAndInviteSubmitter( policy: Policy, formatPhoneNumber: LocaleContextProps['formatPhoneNumber'], reportActions: OnyxCollection, - allReports: OnyxCollection, + allReportsParam: OnyxCollection, reportTransactions: Transaction[] = [], ): {policyExpenseChatReportID?: string} | undefined { if (!policy || !iouReport) { @@ -6121,7 +6121,7 @@ function moveIOUReportToPolicyAndInviteSubmitter( const isReimbursed = isReportManuallyReimbursed(iouReport); // We only allow moving IOU report to a policy if it doesn't have requests from multiple users, as we do not want to create negative amount expenses - if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReports)) { + if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReportsParam)) { return; } From 0cb2a7c30b522c93cc4c40fcf784cdebfcd2c1d8 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 15:21:06 +0700 Subject: [PATCH 04/11] fix: lint --- src/libs/ReportActionsUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 62370d8455ef..e02b69d2e038 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -2392,7 +2392,7 @@ function getReportActionMessageFragments(translate: LocalizedTranslate, action: * @param currentAccountID * @returns */ -function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccountID: number, allReports?: OnyxCollection): boolean { +function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccountID: number, allReportsParam: OnyxCollection): boolean { if (!reportID) { return false; } @@ -2400,7 +2400,7 @@ function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccou const reportActions = Object.values(getAllReportActions(reportID)); if (reportActions.length === 0) { // In case the reportActions of the report have not been loaded, we will check based on the transactions. - const report = getReportOrDraftReport(reportID, undefined, undefined, undefined, allReports); + const report = getReportOrDraftReport(reportID, undefined, undefined, undefined, allReportsParam); return doesReportContainRequestsFromMultipleUsers(report, true); } From 33ef2b2c93b7402b23c5c40b69d7a655312a8ec9 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 15:25:38 +0700 Subject: [PATCH 05/11] fix: refactor hasRequestFromCurrentAccount --- src/components/AddPaymentMethodMenu.tsx | 3 ++- src/components/MoneyReportHeader.tsx | 4 ++-- src/components/SettlementButton/index.tsx | 3 ++- src/hooks/useBulkPayOptions.ts | 4 +++- tests/unit/ReportActionsUtilsTest.ts | 12 ++++++++---- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.tsx b/src/components/AddPaymentMethodMenu.tsx index b4481d4cabdc..72b307ee0c33 100644 --- a/src/components/AddPaymentMethodMenu.tsx +++ b/src/components/AddPaymentMethodMenu.tsx @@ -61,6 +61,7 @@ function AddPaymentMethodMenu({ const icons = useMemoizedLazyExpensifyIcons(['Building', 'Bank']); const {translate} = useLocalize(); const [restoreFocusType, setRestoreFocusType] = useState(); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [session] = useOnyx(ONYXKEYS.SESSION); const [introSelected, introSelectedStatus] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); @@ -69,7 +70,7 @@ function AddPaymentMethodMenu({ // Users can choose to pay with business bank account in case of Expense reports or in case of P2P IOU report // which then starts a bottom up flow and creates a Collect workspace where the payer is an admin and payee is an employee. const isIOU = isIOUReport(iouReport); - const canUseBusinessBankAccount = isExpenseReport(iouReport) || (isIOU && !hasRequestFromCurrentAccount(iouReport?.reportID, session?.accountID ?? CONST.DEFAULT_NUMBER_ID)); + const canUseBusinessBankAccount = isExpenseReport(iouReport) || (isIOU && !hasRequestFromCurrentAccount(iouReport?.reportID, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, allReports)); const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || isIOU; diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index f436a183b1eb..584767a941d6 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -1018,13 +1018,13 @@ function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButt return []; } - const canUseBusinessBankAccount = moneyRequestReport?.reportID && !hasRequestFromCurrentAccount(moneyRequestReport.reportID, accountID ?? CONST.DEFAULT_NUMBER_ID); + const canUseBusinessBankAccount = moneyRequestReport?.reportID && !hasRequestFromCurrentAccount(moneyRequestReport.reportID, accountID ?? CONST.DEFAULT_NUMBER_ID, allReports); if (!canUseBusinessBankAccount) { return []; } return sortPoliciesByName(activeAdminPolicies, localeCompare); - }, [moneyRequestReport, paymentButtonOptions, activeAdminPolicies, accountID, localeCompare]); + }, [moneyRequestReport, paymentButtonOptions, activeAdminPolicies, accountID, allReports, localeCompare]); const buildPaymentSubMenuItems = useCallback( (onWorkspaceSelected: (workspacePolicy: OnyxTypes.Policy) => void): PopoverMenuItem[] => { diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index ff1d2c115179..61bd80a420ae 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -110,6 +110,7 @@ function SettlementButton({ // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line rulesdir/no-default-id-values + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport?.reportID}`); @@ -274,7 +275,7 @@ function SettlementButton({ }; const canUseWallet = !isExpenseReport && !isInvoiceReport && isCurrencySupportedForGlobalReimbursement(currency as CurrencyType); - const canUseBusinessBankAccount = isExpenseReport || (isIOUReport(iouReport) && reportID && !hasRequestFromCurrentAccount(reportID, accountID ?? CONST.DEFAULT_NUMBER_ID)); + const canUseBusinessBankAccount = isExpenseReport || (isIOUReport(iouReport) && reportID && !hasRequestFromCurrentAccount(reportID, accountID ?? CONST.DEFAULT_NUMBER_ID, allReports)); const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || isIOUReport(iouReport); const isPersonalOnlyOption = canUsePersonalBankAccount && !canUseBusinessBankAccount; diff --git a/src/hooks/useBulkPayOptions.ts b/src/hooks/useBulkPayOptions.ts index 8c505849862f..ae6bfd1db9a1 100644 --- a/src/hooks/useBulkPayOptions.ts +++ b/src/hooks/useBulkPayOptions.ts @@ -65,6 +65,7 @@ function useBulkPayOptions({ const paymentMethods = useSettlementButtonPaymentMethods(hasActivatedWallet, translate); const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`); const {isBetaEnabled} = usePermissions(); @@ -74,7 +75,8 @@ function useBulkPayOptions({ const isExpenseReport = isExpenseReportUtil(selectedReportID); const isInvoiceReport = isInvoiceReportUtil(selectedReportID); const shouldShowPayElsewhereOption = !isInvoiceReport; - const canUseBusinessBankAccount = isExpenseReport || (isIOUReport && selectedReportID && !hasRequestFromCurrentAccount(selectedReportID, accountID ?? CONST.DEFAULT_NUMBER_ID)); + const canUseBusinessBankAccount = + isExpenseReport || (isIOUReport && selectedReportID && !hasRequestFromCurrentAccount(selectedReportID, accountID ?? CONST.DEFAULT_NUMBER_ID, allReports)); const canUsePersonalBankAccount = isIOUReport; const isPersonalOnlyOption = canUsePersonalBankAccount && !canUseBusinessBankAccount; const shouldShowBusinessBankAccountOptions = isExpenseReport && !isPersonalOnlyOption; diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 88d2214b2e27..9a0e0ebb0c6f 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1151,12 +1151,12 @@ describe('ReportActionsUtils', () => { }); it('should return false for a deleted IOU report action', () => { - const result = ReportActionsUtils.hasRequestFromCurrentAccount(deletedIOUReportID, currentUserAccountID); + const result = ReportActionsUtils.hasRequestFromCurrentAccount(deletedIOUReportID, currentUserAccountID, undefined); expect(result).toBe(false); }); it('should return true for an active IOU report action', () => { - const result = ReportActionsUtils.hasRequestFromCurrentAccount(activeIOUReportID, currentUserAccountID); + const result = ReportActionsUtils.hasRequestFromCurrentAccount(activeIOUReportID, currentUserAccountID, undefined); expect(result).toBe(true); }); @@ -1182,8 +1182,12 @@ describe('ReportActionsUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromCurrentUser.transactionID}`, transactionFromCurrentUser); await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromOtherUser.transactionID}`, transactionFromOtherUser); + const allReportsData = { + [`${ONYXKEYS.COLLECTION.REPORT}${unloadedActionsReportID}`]: iouReport, + }; + // Then: should return true - let result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID); + let result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID, allReportsData); expect(result).toBe(true); // When: all transactions from the current user account have been deleted @@ -1193,7 +1197,7 @@ describe('ReportActionsUtils', () => { }); // Then: should return false - result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID); + result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID, allReportsData); expect(result).toBe(false); }); From 6c5dda6831ea2d266166b507a6184003fc423870 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 15:33:33 +0700 Subject: [PATCH 06/11] fix: lint --- src/components/KYCWall/BaseKYCWall.tsx | 1 + src/components/SettlementButton/index.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index ea665f56b8f5..a878c29c7f7a 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -236,6 +236,7 @@ function KYCWall({ isSelfTourViewed, betas, conciergeReportID, + allReports, ], ); diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 61bd80a420ae..b3d512cc017c 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -111,7 +111,7 @@ function SettlementButton({ // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line rulesdir/no-default-id-values const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport?.reportID}`); const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isUserValidatedSelector}); From 6e6cf84d2383292d43abab2e3cc498056db03c93 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 8 Apr 2026 15:58:35 +0700 Subject: [PATCH 07/11] fix: add test --- tests/actions/ReportTest.ts | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index ad418fd739d2..4ae9d90ddcdd 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -3426,6 +3426,79 @@ describe('actions/Report', () => { expect(Object.values(reportActions ?? {}).at(0)?.actionName).toBe(CONST.REPORT.ACTIONS.TYPE.MOVED); }); + it('should return undefined when iouReport is not an IOU report', () => { + const expenseReport: OnyxTypes.Report = { + ...createRandomReport(1, undefined), + type: CONST.REPORT.TYPE.EXPENSE, + }; + const policy: OnyxTypes.Policy = {...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN}; + const result = Report.moveIOUReportToPolicy(expenseReport, policy, {}); + expect(result).toBeUndefined(); + }); + + it('should return undefined when hasRequestFromCurrentAccount is true and isFromSettlementButton is false', async () => { + const currentUserAccountID = 12; + const iouReport: OnyxTypes.Report = { + ...createRandomReport(1, undefined), + type: CONST.REPORT.TYPE.IOU, + managerID: currentUserAccountID, + }; + const policy: OnyxTypes.Policy = {...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN}; + + // Create a non-deleted IOU report action from the current account so hasRequestFromCurrentAccount returns true + const iouReportAction: OnyxTypes.ReportAction = { + reportActionID: '100', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + actorAccountID: currentUserAccountID, + created: '2024-01-01 00:00:00', + message: [{html: '$100.00 expense', text: '$100.00 expense', type: 'COMMENT'}], + } as OnyxTypes.ReportAction; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, {[iouReportAction.reportActionID]: iouReportAction}); + await waitForBatchedUpdates(); + + const result = Report.moveIOUReportToPolicy(iouReport, policy, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, false); + expect(result).toBeUndefined(); + }); + + it('should proceed when hasRequestFromCurrentAccount is true but isFromSettlementButton is true', async () => { + const currentUserAccountID = 12; + const ownerAccountID = 1; + const ownerEmail = 'owner@gmail.com'; + const adminEmail = 'admin@gmail.com'; + const iouReport: OnyxTypes.Report = { + ...createRandomReport(1, undefined), + type: CONST.REPORT.TYPE.IOU, + ownerAccountID, + managerID: currentUserAccountID, + }; + const policy: OnyxTypes.Policy = { + ...createRandomPolicy(1), + role: CONST.POLICY.ROLE.ADMIN, + employeeList: {[adminEmail]: {email: adminEmail, role: CONST.POLICY.ROLE.ADMIN}, [ownerEmail]: {email: ownerEmail, role: CONST.POLICY.ROLE.USER}}, + }; + + // Create a non-deleted IOU report action from the current account so hasRequestFromCurrentAccount returns true + const iouReportAction: OnyxTypes.ReportAction = { + reportActionID: '100', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + actorAccountID: currentUserAccountID, + created: '2024-01-01 00:00:00', + message: [{html: '$100.00 expense', text: '$100.00 expense', type: 'COMMENT'}], + } as OnyxTypes.ReportAction; + + await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {[ownerAccountID]: {login: ownerEmail}}); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, iouReport); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, {[iouReportAction.reportActionID]: iouReportAction}); + await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); + await waitForBatchedUpdates(); + + // When isFromSettlementButton is true, it should proceed despite hasRequestFromCurrentAccount being true + const result = Report.moveIOUReportToPolicy(iouReport, policy, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, true); + expect(result).toBeDefined(); + expect(result?.policyExpenseChatReportID).toBeDefined(); + }); + describe('convertIOUReportToExpenseReport', () => { it('should set reportName to default formula when policy field list is empty', () => { // Given a policy with an empty field list @@ -3707,6 +3780,21 @@ describe('actions/Report', () => { expect(updatedReport?.policyID).toBe(policy.id); expect(updatedReport?.total).toBe(-3000); }); + + it('should return undefined when policy is missing', () => { + const iouReport: OnyxTypes.Report = { + ...createRandomReport(1, undefined), + type: CONST.REPORT.TYPE.IOU, + }; + const result = Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, undefined as unknown as OnyxTypes.Policy, (phone: string) => phone, {}, {}); + expect(result).toBeUndefined(); + }); + + it('should return undefined when iouReport is missing', () => { + const policy: OnyxTypes.Policy = {...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN}; + const result = Report.moveIOUReportToPolicyAndInviteSubmitter(undefined, policy, (phone: string) => phone, {}, {}); + expect(result).toBeUndefined(); + }); }); describe('buildOptimisticChangePolicyData', () => { From c01f0af266c5413d4f45e7464929f2f2b2a5d100 Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 10 Apr 2026 11:04:33 +0700 Subject: [PATCH 08/11] fix: conflicts --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index bfd3c123139e..ad993d6da779 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit bfd3c123139e2d8e2eea05d56defd6f8336a220f +Subproject commit ad993d6da779ec44c282a19bc1480d65221797cd From fc498f008778008be792f46fa3a54f01786c14d6 Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 10 Apr 2026 14:52:59 +0700 Subject: [PATCH 09/11] fix: update getReportOrDraftReport --- src/components/AddPaymentMethodMenu.tsx | 3 +-- src/components/KYCWall/BaseKYCWall.tsx | 6 ++---- src/components/MoneyReportHeader.tsx | 4 ++-- src/components/SettlementButton/index.tsx | 4 +--- src/hooks/useBulkPayOptions.ts | 4 +--- src/hooks/useSearchBulkActions.ts | 4 ++-- src/hooks/useSelectionModeReportActions.ts | 2 +- src/libs/ReportActionsUtils.ts | 7 ++++--- src/libs/ReportUtils.ts | 6 +++--- src/libs/actions/Report/index.ts | 7 ++----- src/pages/ReportChangeWorkspacePage.tsx | 6 ++---- tests/actions/ReportTest.ts | 24 +++++++++------------- tests/unit/ReportActionsUtilsTest.ts | 19 ++++++----------- tests/unit/ReportUtilsTest.ts | 12 ++++------- 14 files changed, 41 insertions(+), 67 deletions(-) diff --git a/src/components/AddPaymentMethodMenu.tsx b/src/components/AddPaymentMethodMenu.tsx index 72b307ee0c33..d43f333fa10c 100644 --- a/src/components/AddPaymentMethodMenu.tsx +++ b/src/components/AddPaymentMethodMenu.tsx @@ -61,7 +61,6 @@ function AddPaymentMethodMenu({ const icons = useMemoizedLazyExpensifyIcons(['Building', 'Bank']); const {translate} = useLocalize(); const [restoreFocusType, setRestoreFocusType] = useState(); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [session] = useOnyx(ONYXKEYS.SESSION); const [introSelected, introSelectedStatus] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); @@ -70,7 +69,7 @@ function AddPaymentMethodMenu({ // Users can choose to pay with business bank account in case of Expense reports or in case of P2P IOU report // which then starts a bottom up flow and creates a Collect workspace where the payer is an admin and payee is an employee. const isIOU = isIOUReport(iouReport); - const canUseBusinessBankAccount = isExpenseReport(iouReport) || (isIOU && !hasRequestFromCurrentAccount(iouReport?.reportID, session?.accountID ?? CONST.DEFAULT_NUMBER_ID, allReports)); + const canUseBusinessBankAccount = isExpenseReport(iouReport) || (isIOU && !hasRequestFromCurrentAccount(iouReport, session?.accountID ?? CONST.DEFAULT_NUMBER_ID)); const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || isIOU; diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 6f84bd2ab53b..264dda7b64db 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -62,7 +62,6 @@ function KYCWall({ const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); const [bankAccountList = getEmptyObject()] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); @@ -145,7 +144,7 @@ function KYCWall({ if (iouReport && isIOUReport(iouReport)) { const adminPolicy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${policy?.id}`]; if (adminPolicy) { - const inviteResult = moveIOUReportToPolicyAndInviteSubmitter(iouReport, adminPolicy, formatPhoneNumber, filteredReportActions, allReports, reportTransactions); + const inviteResult = moveIOUReportToPolicyAndInviteSubmitter(iouReport, adminPolicy, formatPhoneNumber, filteredReportActions, reportTransactions); if (inviteResult?.policyExpenseChatReportID) { setNavigationActionToMicrotaskQueue(() => { Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(inviteResult.policyExpenseChatReportID)); @@ -155,7 +154,7 @@ function KYCWall({ Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute({policyID: adminPolicy.id})); }); } else { - const moveResult = moveIOUReportToPolicy(iouReport, adminPolicy, allReports, true, reportTransactions); + const moveResult = moveIOUReportToPolicy(iouReport, adminPolicy, true, reportTransactions); savePreferredPaymentMethod(iouReport.policyID, adminPolicy.id, CONST.LAST_PAYMENT_METHOD.IOU, lastPaymentMethod?.[adminPolicy.id]); if (moveResult?.policyExpenseChatReportID && !moveResult.useTemporaryOptimisticExpenseChatReportID) { @@ -238,7 +237,6 @@ function KYCWall({ isSelfTourViewed, betas, conciergeReportID, - allReports, localCurrency, ], ); diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 14fff16df701..508bee4f7ddb 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -963,13 +963,13 @@ function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButt return []; } - const canUseBusinessBankAccount = moneyRequestReport?.reportID && !hasRequestFromCurrentAccount(moneyRequestReport.reportID, accountID ?? CONST.DEFAULT_NUMBER_ID, allReports); + const canUseBusinessBankAccount = moneyRequestReport?.reportID && !hasRequestFromCurrentAccount(moneyRequestReport, accountID ?? CONST.DEFAULT_NUMBER_ID); if (!canUseBusinessBankAccount) { return []; } return sortPoliciesByName(activeAdminPolicies, localeCompare); - }, [moneyRequestReport, paymentButtonOptions, activeAdminPolicies, accountID, allReports, localeCompare]); + }, [moneyRequestReport, paymentButtonOptions, activeAdminPolicies, accountID, localeCompare]); const buildPaymentSubMenuItems = useCallback( (onWorkspaceSelected: (workspacePolicy: OnyxTypes.Policy) => void): PopoverMenuItem[] => { diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 25b1a7cbe998..163d3a67ceaf 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -107,10 +107,8 @@ function SettlementButton({ const {isOffline} = useNetwork(); const policy = usePolicy(policyID); const {accountID, email} = useCurrentUserPersonalDetails(); - // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line rulesdir/no-default-id-values - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport?.reportID}`); @@ -279,7 +277,7 @@ function SettlementButton({ }; const canUseWallet = !isExpenseReport && !isInvoiceReport && isCurrencySupportedForGlobalReimbursement(currency as CurrencyType); - const canUseBusinessBankAccount = isExpenseReport || (isIOUReport(iouReport) && reportID && !hasRequestFromCurrentAccount(reportID, accountID ?? CONST.DEFAULT_NUMBER_ID, allReports)); + const canUseBusinessBankAccount = isExpenseReport || (isIOUReport(iouReport) && reportID && !hasRequestFromCurrentAccount(iouReport, accountID ?? CONST.DEFAULT_NUMBER_ID)); const canUsePersonalBankAccount = shouldShowPersonalBankAccountOption || isIOUReport(iouReport); const isPersonalOnlyOption = canUsePersonalBankAccount && !canUseBusinessBankAccount; diff --git a/src/hooks/useBulkPayOptions.ts b/src/hooks/useBulkPayOptions.ts index ae6bfd1db9a1..6ddab11be5b2 100644 --- a/src/hooks/useBulkPayOptions.ts +++ b/src/hooks/useBulkPayOptions.ts @@ -65,7 +65,6 @@ function useBulkPayOptions({ const paymentMethods = useSettlementButtonPaymentMethods(hasActivatedWallet, translate); const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selectedReportID}`); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`); const {isBetaEnabled} = usePermissions(); @@ -75,8 +74,7 @@ function useBulkPayOptions({ const isExpenseReport = isExpenseReportUtil(selectedReportID); const isInvoiceReport = isInvoiceReportUtil(selectedReportID); const shouldShowPayElsewhereOption = !isInvoiceReport; - const canUseBusinessBankAccount = - isExpenseReport || (isIOUReport && selectedReportID && !hasRequestFromCurrentAccount(selectedReportID, accountID ?? CONST.DEFAULT_NUMBER_ID, allReports)); + const canUseBusinessBankAccount = isExpenseReport || (isIOUReport && selectedReportID && !hasRequestFromCurrentAccount(iouReport, accountID ?? CONST.DEFAULT_NUMBER_ID)); const canUsePersonalBankAccount = isIOUReport; const isPersonalOnlyOption = canUsePersonalBankAccount && !canUseBusinessBankAccount; const shouldShowBusinessBankAccountOptions = isExpenseReport && !isPersonalOnlyOption; diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 53e80cc52510..7481b7d6a8a3 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -716,9 +716,9 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { ); return; } - const invite = moveIOUReportToPolicyAndInviteSubmitter(itemReport, adminPolicy, formatPhoneNumber, policyExpenseChatReportActions, allReports, reportTransactions); + const invite = moveIOUReportToPolicyAndInviteSubmitter(itemReport, adminPolicy, formatPhoneNumber, policyExpenseChatReportActions, reportTransactions); if (!invite?.policyExpenseChatReportID) { - moveIOUReportToPolicy(itemReport, adminPolicy, allReports, false, reportTransactions); + moveIOUReportToPolicy(itemReport, adminPolicy, false, reportTransactions); } } } diff --git a/src/hooks/useSelectionModeReportActions.ts b/src/hooks/useSelectionModeReportActions.ts index b24de6afef1d..e7fdde1b7aaa 100644 --- a/src/hooks/useSelectionModeReportActions.ts +++ b/src/hooks/useSelectionModeReportActions.ts @@ -198,7 +198,7 @@ function useSelectionModeReportActions({ return []; } - const canUseBusinessBankAccount = report?.reportID && !hasRequestFromCurrentAccount(report.reportID, currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID); + const canUseBusinessBankAccount = report?.reportID && !hasRequestFromCurrentAccount(report, currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID); if (!canUseBusinessBankAccount) { return []; } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 390cb618246f..2e32e9f521c9 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -2419,7 +2419,8 @@ function getReportActionMessageFragments(translate: LocalizedTranslate, action: * @param currentAccountID * @returns */ -function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccountID: number, allReportsParam: OnyxCollection): boolean { +function hasRequestFromCurrentAccount(report: OnyxEntry, currentAccountID: number): boolean { + const reportID = report?.reportID; if (!reportID) { return false; } @@ -2427,8 +2428,8 @@ function hasRequestFromCurrentAccount(reportID: string | undefined, currentAccou const reportActions = Object.values(getAllReportActions(reportID)); if (reportActions.length === 0) { // In case the reportActions of the report have not been loaded, we will check based on the transactions. - const report = getReportOrDraftReport(reportID, undefined, undefined, undefined, allReportsParam); - return doesReportContainRequestsFromMultipleUsers(report, true); + const resolvedReport = getReportOrDraftReport(reportID, undefined, undefined, undefined, report); + return doesReportContainRequestsFromMultipleUsers(resolvedReport, true); } return reportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && action.actorAccountID === currentAccountID && !isDeletedAction(action)); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 32913baaa471..806bc09e8872 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1288,10 +1288,10 @@ function getReportOrDraftReport( searchReports?: Array>, fallbackReport?: Report, reportDrafts?: OnyxCollection, - allReports: OnyxCollection = deprecatedAllReports, + report?: OnyxEntry, ): OnyxEntry { - const searchReport = searchReports?.find((report) => report?.reportID === reportID); - const onyxReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const searchReport = searchReports?.find((searchItem) => searchItem?.reportID === reportID); + const onyxReport = report ?? deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; return searchReport ?? onyxReport ?? (reportDrafts ?? allReportsDraft)?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`] ?? fallbackReport; } diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index e4f9038a3d65..01f302427c1c 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -6061,7 +6061,6 @@ function deleteAppReport({ function moveIOUReportToPolicy( iouReport: OnyxEntry, policy: Policy, - allReportsParam: OnyxCollection, isFromSettlementButton?: boolean, reportTransactions: Transaction[] = [], ): {policyExpenseChatReportID?: string; useTemporaryOptimisticExpenseChatReportID: boolean} | undefined { @@ -6069,11 +6068,10 @@ function moveIOUReportToPolicy( if (!policy || !iouReport || !isIOUReportUsingReport(iouReport)) { return; } - const reportID = iouReport.reportID; const isReimbursed = isReportManuallyReimbursed(iouReport); // We do not want to create negative amount expenses - if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReportsParam) && !isFromSettlementButton) { + if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(iouReport, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID) && !isFromSettlementButton) { return; } @@ -6112,7 +6110,6 @@ function moveIOUReportToPolicyAndInviteSubmitter( policy: Policy, formatPhoneNumber: LocaleContextProps['formatPhoneNumber'], reportActions: OnyxCollection, - allReportsParam: OnyxCollection, reportTransactions: Transaction[] = [], ): {policyExpenseChatReportID?: string} | undefined { if (!policy || !iouReport) { @@ -6134,7 +6131,7 @@ function moveIOUReportToPolicyAndInviteSubmitter( const isReimbursed = isReportManuallyReimbursed(iouReport); // We only allow moving IOU report to a policy if it doesn't have requests from multiple users, as we do not want to create negative amount expenses - if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(reportID, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID, allReportsParam)) { + if (!isReimbursed && ReportActionsUtils.hasRequestFromCurrentAccount(iouReport, iouReport.managerID ?? CONST.DEFAULT_NUMBER_ID)) { return; } diff --git a/src/pages/ReportChangeWorkspacePage.tsx b/src/pages/ReportChangeWorkspacePage.tsx index cd7270db0cdf..c037d45f332c 100644 --- a/src/pages/ReportChangeWorkspacePage.tsx +++ b/src/pages/ReportChangeWorkspacePage.tsx @@ -55,7 +55,6 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro const {translate, formatPhoneNumber, localeCompare} = useLocalize(); const reportTransactions = useReportTransactions(reportID); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`); const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [reportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${reportID}`); @@ -91,9 +90,9 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro const {backTo} = route.params; Navigation.goBack(backTo); if (isIOUReport(reportID)) { - const invite = moveIOUReportToPolicyAndInviteSubmitter(report, policy, formatPhoneNumber, filteredReportActions, allReports, reportTransactions); + const invite = moveIOUReportToPolicyAndInviteSubmitter(report, policy, formatPhoneNumber, filteredReportActions, reportTransactions); if (!invite?.policyExpenseChatReportID) { - moveIOUReportToPolicy(report, policy, allReports, false, reportTransactions); + moveIOUReportToPolicy(report, policy, false, reportTransactions); } // This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850 // eslint-disable-next-line @typescript-eslint/no-deprecated @@ -138,7 +137,6 @@ function ReportChangeWorkspacePage({report, route}: ReportChangeWorkspacePagePro formatPhoneNumber, reportTransactions, filteredReportActions, - allReports, isReportLastVisibleArchived, session?.accountID, session?.email, diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 4ae9d90ddcdd..8b7ee6c8f784 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -3410,7 +3410,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); // When moving iou to a workspace - Report.moveIOUReportToPolicy(iouReport, policy, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}); + Report.moveIOUReportToPolicy(iouReport, policy); await waitForBatchedUpdates(); // Then MOVED report action should be added to the expense report @@ -3432,7 +3432,7 @@ describe('actions/Report', () => { type: CONST.REPORT.TYPE.EXPENSE, }; const policy: OnyxTypes.Policy = {...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN}; - const result = Report.moveIOUReportToPolicy(expenseReport, policy, {}); + const result = Report.moveIOUReportToPolicy(expenseReport, policy); expect(result).toBeUndefined(); }); @@ -3457,7 +3457,7 @@ describe('actions/Report', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, {[iouReportAction.reportActionID]: iouReportAction}); await waitForBatchedUpdates(); - const result = Report.moveIOUReportToPolicy(iouReport, policy, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, false); + const result = Report.moveIOUReportToPolicy(iouReport, policy, false); expect(result).toBeUndefined(); }); @@ -3494,7 +3494,7 @@ describe('actions/Report', () => { await waitForBatchedUpdates(); // When isFromSettlementButton is true, it should proceed despite hasRequestFromCurrentAccount being true - const result = Report.moveIOUReportToPolicy(iouReport, policy, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, true); + const result = Report.moveIOUReportToPolicy(iouReport, policy, true); expect(result).toBeDefined(); expect(result?.policyExpenseChatReportID).toBeDefined(); }); @@ -3590,7 +3590,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy); // When moving iou to a workspace and invite the submitter - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}); await waitForBatchedUpdates(); // Then MOVED report action should be added to the expense report @@ -3657,7 +3657,7 @@ describe('actions/Report', () => { // Call moveIOUReportToPolicyAndInviteSubmitter const formatPhoneNumber = (phoneNumber: string) => phoneNumber; - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, formatPhoneNumber, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, formatPhoneNumber, {}); await waitForBatchedUpdates(); // Simulate network failure @@ -3714,9 +3714,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, transaction); // When moving IOU to a workspace with reportTransactions - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, [ - transaction, - ]); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, [transaction]); await waitForBatchedUpdates(); // Then the transaction amounts should be negated optimistically @@ -3761,9 +3759,7 @@ describe('actions/Report', () => { await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, transaction); // When moving IOU to a workspace with transactions - Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, {[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport}, [ - transaction, - ]); + Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, policy, (phone: string) => phone, {}, [transaction]); await waitForBatchedUpdates(); // Then the report should be converted to an expense report with the new policyID @@ -3786,13 +3782,13 @@ describe('actions/Report', () => { ...createRandomReport(1, undefined), type: CONST.REPORT.TYPE.IOU, }; - const result = Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, undefined as unknown as OnyxTypes.Policy, (phone: string) => phone, {}, {}); + const result = Report.moveIOUReportToPolicyAndInviteSubmitter(iouReport, undefined as unknown as OnyxTypes.Policy, (phone: string) => phone, {}); expect(result).toBeUndefined(); }); it('should return undefined when iouReport is missing', () => { const policy: OnyxTypes.Policy = {...createRandomPolicy(1), role: CONST.POLICY.ROLE.ADMIN}; - const result = Report.moveIOUReportToPolicyAndInviteSubmitter(undefined, policy, (phone: string) => phone, {}, {}); + const result = Report.moveIOUReportToPolicyAndInviteSubmitter(undefined, policy, (phone: string) => phone, {}); expect(result).toBeUndefined(); }); }); diff --git a/tests/unit/ReportActionsUtilsTest.ts b/tests/unit/ReportActionsUtilsTest.ts index 9a0e0ebb0c6f..93dc2db7b97f 100644 --- a/tests/unit/ReportActionsUtilsTest.ts +++ b/tests/unit/ReportActionsUtilsTest.ts @@ -1151,12 +1151,12 @@ describe('ReportActionsUtils', () => { }); it('should return false for a deleted IOU report action', () => { - const result = ReportActionsUtils.hasRequestFromCurrentAccount(deletedIOUReportID, currentUserAccountID, undefined); + const result = ReportActionsUtils.hasRequestFromCurrentAccount({reportID: deletedIOUReportID} as Report, currentUserAccountID); expect(result).toBe(false); }); it('should return true for an active IOU report action', () => { - const result = ReportActionsUtils.hasRequestFromCurrentAccount(activeIOUReportID, currentUserAccountID, undefined); + const result = ReportActionsUtils.hasRequestFromCurrentAccount({reportID: activeIOUReportID} as Report, currentUserAccountID); expect(result).toBe(true); }); @@ -1182,12 +1182,8 @@ describe('ReportActionsUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromCurrentUser.transactionID}`, transactionFromCurrentUser); await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromOtherUser.transactionID}`, transactionFromOtherUser); - const allReportsData = { - [`${ONYXKEYS.COLLECTION.REPORT}${unloadedActionsReportID}`]: iouReport, - }; - // Then: should return true - let result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID, allReportsData); + let result = ReportActionsUtils.hasRequestFromCurrentAccount(iouReport, currentUserAccountID); expect(result).toBe(true); // When: all transactions from the current user account have been deleted @@ -1197,7 +1193,7 @@ describe('ReportActionsUtils', () => { }); // Then: should return false - result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID, allReportsData); + result = ReportActionsUtils.hasRequestFromCurrentAccount(iouReport, currentUserAccountID); expect(result).toBe(false); }); @@ -1222,11 +1218,8 @@ describe('ReportActionsUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromCurrentUser.transactionID}`, transactionFromCurrentUser); await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionFromOtherUser.transactionID}`, transactionFromOtherUser); - // Pass allReports explicitly instead of relying on Onyx state - const explicitAllReports = { - [`${ONYXKEYS.COLLECTION.REPORT}${unloadedActionsReportID}`]: iouReport, - }; - const result = ReportActionsUtils.hasRequestFromCurrentAccount(unloadedActionsReportID, currentUserAccountID, explicitAllReports); + // Pass report explicitly instead of relying on Onyx state + const result = ReportActionsUtils.hasRequestFromCurrentAccount(iouReport, currentUserAccountID); expect(result).toBe(true); }); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 757ecdb98c85..9c2e5ebee304 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -11302,25 +11302,21 @@ describe('ReportUtils', () => { expect(result).toEqual(mockOnyxReport); }); - test('returns report from explicit allReports parameter instead of Onyx state', async () => { + test('returns explicit report parameter instead of Onyx state', async () => { const explicitReport: Report = { ...createRandomReport(mockReportIDIndex, undefined), reportName: 'Explicit Report', type: CONST.REPORT.TYPE.CHAT, }; - const explicitAllReports = { - [`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`]: explicitReport, - }; // Set a different report in Onyx to verify the explicit param takes precedence await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, mockOnyxReport); - const result = getReportOrDraftReport(mockReportID, undefined, undefined, undefined, explicitAllReports); + const result = getReportOrDraftReport(mockReportID, undefined, undefined, undefined, explicitReport); expect(result).toEqual(explicitReport); expect(result).not.toEqual(mockOnyxReport); }); - test('returns undefined when explicit allReports does not contain the report', () => { - const explicitAllReports = {}; - const result = getReportOrDraftReport(mockReportID, undefined, undefined, undefined, explicitAllReports); + test('returns undefined when explicit report is undefined and no Onyx state', () => { + const result = getReportOrDraftReport(mockReportID, undefined, undefined, undefined, undefined); expect(result).toBeUndefined(); }); }); From 80ff0fc3a40563444dd1fabaac109259af6f7b20 Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 10 Apr 2026 15:11:06 +0700 Subject: [PATCH 10/11] fix: revert change --- src/components/SettlementButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 163d3a67ceaf..ec2b5eb196f8 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -109,7 +109,7 @@ function SettlementButton({ const {accountID, email} = useCurrentUserPersonalDetails(); // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line rulesdir/no-default-id-values - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`); const [conciergeReportID = ''] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport?.reportID}`); const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isUserValidatedSelector}); From a68c85017e2d44e7a3517ab3f60814e2d26acfc2 Mon Sep 17 00:00:00 2001 From: truph01 Date: Fri, 10 Apr 2026 15:13:03 +0700 Subject: [PATCH 11/11] fix: revert change --- src/components/SettlementButton/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index ec2b5eb196f8..19074f67ff2c 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -107,6 +107,7 @@ function SettlementButton({ const {isOffline} = useNetwork(); const policy = usePolicy(policyID); const {accountID, email} = useCurrentUserPersonalDetails(); + // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line rulesdir/no-default-id-values const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`);