From a13d9e2dd9d0b1e4cc8f4cf488b2274b8ea05264 Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 11 May 2026 18:52:32 +0700 Subject: [PATCH 01/17] fix: refactor getReportOrDraftReport usage --- src/hooks/useSearchBulkActions.ts | 13 +- src/hooks/useSelectedTransactionsActions.ts | 2 +- src/libs/ReportUtils.ts | 30 ++++- .../MergeTransactionsListContent.tsx | 3 + .../step/IOURequestStepConfirmation.tsx | 3 +- .../SubmitExpenseOrchestrator.tsx | 7 +- tests/unit/ReportUtilsTest.ts | 121 ++++++++++++++++++ 7 files changed, 166 insertions(+), 13 deletions(-) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index 82cd43bd6319..d8cacd4a5658 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -162,7 +162,9 @@ function shouldShowBulkDuplicateOption({ } const reportID = selectedTransactions[id]?.reportID; - const submitterReport = reportID ? getReportOrDraftReport(reportID, searchReports) : undefined; + const submitterReport = reportID + ? getReportOrDraftReport(reportID, searchReports, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]) + : undefined; if (submitterReport && !isCurrentUserSubmitter(submitterReport)) { return false; } @@ -845,10 +847,10 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { return ( selectedTransactionReportIDs.length > 0 && selectedTransactionReportIDs.every((id) => { - return isCurrentUserSubmitter(getReportOrDraftReport(id, reports)); + return isCurrentUserSubmitter(getReportOrDraftReport(id, reports, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`])); }) ); - }, [selectedTransactionReportIDs, currentUserPersonalDetails?.accountID, currentSearchResults?.data]); + }, [selectedTransactionReportIDs, currentUserPersonalDetails?.accountID, currentSearchResults?.data, allReports]); const duplicateHandlerRef = useRef<() => void>(() => {}); const setDuplicateHandler = useCallback((handler: () => void) => { @@ -1376,7 +1378,10 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { if (!transactionEntry) { continue; } - const ownerAccountID = transactionEntry.ownerAccountID ?? getReportOrDraftReport(transactionEntry.reportID)?.ownerAccountID; + const ownerAccountID = + transactionEntry.ownerAccountID ?? + getReportOrDraftReport(transactionEntry.reportID, undefined, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionEntry.reportID}`]) + ?.ownerAccountID; if (typeof ownerAccountID === 'number') { ownerAccountIDs.add(ownerAccountID); if (ownerAccountIDs.size > 1) { diff --git a/src/hooks/useSelectedTransactionsActions.ts b/src/hooks/useSelectedTransactionsActions.ts index fefb1ed701a4..f86d5e3c73c6 100644 --- a/src/hooks/useSelectedTransactionsActions.ts +++ b/src/hooks/useSelectedTransactionsActions.ts @@ -140,7 +140,7 @@ function useSelectedTransactionsActions({ continue; } - const parentReport = getReportOrDraftReport(reportID); + const parentReport = getReportOrDraftReport(reportID, undefined, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]); const ownerAccountID = parentReport?.ownerAccountID; if (typeof ownerAccountID === 'number') { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1480bf4a695c..24461f196f65 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -979,6 +979,10 @@ type GetReportNameParams = { // TODO: Make this required when https://github.com/Expensify/App/issues/66411 is done /** Used to identify the Concierge chat so its name can be set to the Concierge display name */ conciergeReportID?: string; + /** The transaction linked to the report action */ + transaction?: OnyxEntry; + /** The report that the transaction belongs to */ + transactionReport?: OnyxEntry; }; type GetReportStatusParams = { @@ -5255,13 +5259,17 @@ function shouldShowRBRForMissingSmartscanFields(iouReport: OnyxEntry, io function getTransactionReportName({ translate, reportAction, + transaction: transactionParam, transactions, reports, + transactionReport, }: { translate: LocalizedTranslate; reportAction: OnyxEntry; + transaction?: OnyxEntry; transactions?: Transaction[]; reports?: Report[]; + transactionReport?: OnyxEntry; }): string { if (reportAction && isReversedTransaction(reportAction)) { return translate('parentReportAction.reversedTransaction'); @@ -5271,7 +5279,7 @@ function getTransactionReportName({ return translate('parentReportAction.deletedExpense'); } - const transaction = reportAction ? getLinkedTransaction(reportAction, transactions) : transactions?.at(0); + const transaction = transactionParam ?? (reportAction ? getLinkedTransaction(reportAction, transactions) : transactions?.at(0)); if (isEmptyObject(transaction)) { // Transaction data might be empty on app's first load, if so we fallback to Expense/Track Expense @@ -5282,7 +5290,7 @@ function getTransactionReportName({ return translate('iou.receiptScanning', {count: 1}); } - const report = getReportOrDraftReport(transaction?.reportID, reports); + const report = getReportOrDraftReport(transaction?.reportID, reports, undefined, undefined, transactionReport); if (hasMissingSmartscanFieldsTransactionUtils(transaction, report)) { return translate('iou.receiptMissingDetails'); } @@ -5735,8 +5743,20 @@ function parseReportActionHtmlToText(reportAction: OnyxEntry, repo * @deprecated Moved to src/libs/ReportNameUtils.ts. */ function getReportName(reportNameInformation: GetReportNameParams): string { - const {report, policy, parentReportActionParam, personalDetails, invoiceReceiverPolicy, reportAttributes, transactions, isReportArchived, reports, conciergeReportID} = - reportNameInformation; + const { + report, + policy, + parentReportActionParam, + personalDetails, + invoiceReceiverPolicy, + reportAttributes, + transactions, + isReportArchived, + reports, + conciergeReportID, + transaction: transactionParam, + transactionReport, + } = reportNameInformation; // Check if we can use report name in derived values - only when we have report but no other params const canUseDerivedValue = report && policy === undefined && parentReportActionParam === undefined && personalDetails === undefined && invoiceReceiverPolicy === undefined && isReportArchived === undefined; @@ -5788,7 +5808,7 @@ function getReportName(reportNameInformation: GetReportNameParams): string { if (isChatThread(report)) { if (!isEmptyObject(parentReportAction) && isTransactionThread(parentReportAction)) { - formattedName = getTransactionReportName({translate: translateLocal, reportAction: parentReportAction, transactions, reports}); + formattedName = getTransactionReportName({translate: translateLocal, reportAction: parentReportAction, transaction: transactionParam, transactions, reports, transactionReport}); if (isArchivedNonExpense) { formattedName = generateArchivedReportName(formattedName); diff --git a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx index ef2d7fafaea8..898902380e99 100644 --- a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx +++ b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx @@ -51,6 +51,7 @@ function MergeTransactionsListContent({transactionID, mergeTransaction}: MergeTr const {targetTransaction, sourceTransaction, targetTransactionReport, sourceTransactionReport, targetTransactionPolicy, sourceTransactionPolicy} = useMergeTransactions({ mergeTransaction, }); + const [targetTransactionReportByID] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${targetTransaction?.reportID}`); useEffect(() => { // If the eligible transactions are already loaded, don't fetch them again @@ -130,8 +131,10 @@ function MergeTransactionsListContent({transactionID, mergeTransaction}: MergeTr ? getTransactionReportName({ translate, reportAction: undefined, + transaction: targetTransaction, transactions: [targetTransaction], reports: targetTransactionReport ? [targetTransactionReport] : [], + transactionReport: targetTransactionReportByID, }) : ''; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 999132f92873..3586c0fad4e8 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -288,6 +288,7 @@ function IOURequestStepConfirmation({ const hasPreInsertFired = useRef(false); const isTransactionReady = !!transaction; const destinationReportID = backToReport ?? report?.reportID; + const [destinationReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${destinationReportID}`); useEffect(() => { if (hasPreInsertFired.current || !isTransactionReady || !getIsNarrowLayout()) { @@ -319,7 +320,7 @@ function IOURequestStepConfirmation({ const hasValidDestination = !!destinationReportID && Navigation.getTopmostReportId() !== destinationReportID; // The report must be in Onyx so the pre-inserted screen can render immediately. - const isDestinationReportLoaded = !!destinationReportID && !!getReportOrDraftReport(destinationReportID)?.reportID; + const isDestinationReportLoaded = !!destinationReportID && !!getReportOrDraftReport(destinationReportID, undefined, undefined, undefined, destinationReport)?.reportID; const shouldPreInsertReport = canUseReportPreInsert && isOutsideRHP && hasValidDestination && isDestinationReportLoaded; diff --git a/src/pages/iou/request/step/confirmation/SubmitExpenseOrchestrator.tsx b/src/pages/iou/request/step/confirmation/SubmitExpenseOrchestrator.tsx index 52c89ee672fc..3275d3f38772 100644 --- a/src/pages/iou/request/step/confirmation/SubmitExpenseOrchestrator.tsx +++ b/src/pages/iou/request/step/confirmation/SubmitExpenseOrchestrator.tsx @@ -1,5 +1,6 @@ import React, {useEffect, useRef, useState} from 'react'; import LocationPermissionModal from '@components/LocationPermissionModal'; +import useOnyx from '@hooks/useOnyx'; import DateUtils from '@libs/DateUtils'; import {cancelDeferredWrite, flushDeferredWrite, reserveDeferredWriteChannel} from '@libs/deferredLayoutWrite'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; @@ -18,6 +19,7 @@ import {updateLastLocationPermissionPrompt} from '@userActions/IOU'; import type {IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Participant} from '@src/types/onyx/IOU'; import type {Receipt} from '@src/types/onyx/Transaction'; @@ -122,6 +124,7 @@ function SubmitExpenseOrchestrator({ isFromFloatingActionButtonOnTransaction, children, }: SubmitExpenseOrchestratorProps) { + const [destinationReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${destinationReportID}`); const [isConfirming, setIsConfirming] = useState(false); const [selectedParticipantList, setSelectedParticipantList] = useState([]); const [startLocationPermissionFlow, setStartLocationPermissionFlow] = useState(false); @@ -180,7 +183,7 @@ function SubmitExpenseOrchestrator({ isReportInRHP: isReportOpenInRHP(rootState), isReportTopmostSplit: isReportTopmostSplitNavigator(), isSearchTopmostFullScreen: isSearchTopmostFullScreenRoute(), - isDestinationReportLoaded: !!destinationReportID && !!getReportOrDraftReport(destinationReportID)?.reportID, + isDestinationReportLoaded: !!destinationReportID && !!getReportOrDraftReport(destinationReportID, undefined, undefined, undefined, destinationReport)?.reportID, }; }; @@ -286,7 +289,7 @@ function SubmitExpenseOrchestrator({ setFastPath(CONST.TELEMETRY.FAST_PATH_HANDLER.REPORT_IN_RHP_DISMISS, CONST.TELEMETRY.SUBMIT_OPTIMIZATION.DISMISS_FIRST); const rootState = navigationRef.getRootState(); - const report = destinationReportID ? getReportOrDraftReport(destinationReportID) : undefined; + const report = destinationReportID ? getReportOrDraftReport(destinationReportID, undefined, undefined, undefined, destinationReport) : undefined; const isDestinationEmpty = !!report && isMoneyRequestReport(report) && !report.transactionCount; if (isDestinationEmpty) { reserveDeferredWriteChannel(CONST.DEFERRED_LAYOUT_WRITE_KEYS.DISMISS_MODAL, {destinationReportID}); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 6478f908203a..5dc0f8adcfb0 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -112,6 +112,7 @@ import { getReportSubtitlePrefix, getTaskAssigneeChatOnyxData, getTransactionDetails, + getTransactionReportName, getTransactionSortValue, getUnreportedTransactionMessage, getViolatingReportIDForRBRInLHN, @@ -12205,6 +12206,125 @@ describe('ReportUtils', () => { }); }); + describe('getTransactionReportName', () => { + const mockReportID = '100'; + const mockTransactionReport: Report = { + ...createRandomReport(100, undefined), + reportName: 'Transaction Report', + type: CONST.REPORT.TYPE.EXPENSE, + }; + + beforeEach(async () => { + await Onyx.clear(); + }); + + test('uses transactionReport param instead of Onyx state when provided', async () => { + const onyxReport: Report = { + ...createRandomReport(100, undefined), + reportName: 'Onyx Report', + type: CONST.REPORT.TYPE.EXPENSE, + }; + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, onyxReport); + + const transaction: Transaction = { + ...createRandomTransaction(1), + reportID: mockReportID, + merchant: 'Coffee Shop', + amount: -1000, + currency: CONST.CURRENCY.USD, + }; + + const result = getTransactionReportName({ + translate: translateLocal, + reportAction: undefined, + transactions: [transaction], + transactionReport: mockTransactionReport, + }); + + expect(result).toBeDefined(); + expect(typeof result).toBe('string'); + }); + + test('falls back to Onyx state when transactionReport is not provided', async () => { + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, mockTransactionReport); + + const transaction: Transaction = { + ...createRandomTransaction(1), + reportID: mockReportID, + merchant: 'Coffee Shop', + amount: -1000, + currency: CONST.CURRENCY.USD, + }; + + const result = getTransactionReportName({ + translate: translateLocal, + reportAction: undefined, + transactions: [transaction], + }); + + expect(result).toBeDefined(); + expect(typeof result).toBe('string'); + }); + + test('returns reversed transaction message when action is reversed', () => { + const reportAction = { + ...createRandomReportAction(1), + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + originalMessage: { + type: CONST.IOU.REPORT_ACTION_TYPE.PAY, + IOUTransactionID: '1', + amount: 0, + currency: CONST.CURRENCY.USD, + }, + } as unknown as ReportAction; + + jest.spyOn(require('@libs/ReportActionsUtils'), 'isReversedTransaction').mockReturnValueOnce(true); + + const result = getTransactionReportName({ + translate: translateLocal, + reportAction, + }); + + expect(result).toBe(translateLocal('parentReportAction.reversedTransaction')); + }); + + test('returns deleted expense message when action is deleted', () => { + const reportAction = { + ...createRandomReportAction(1), + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + } as unknown as ReportAction; + + jest.spyOn(require('@libs/ReportActionsUtils'), 'isDeletedAction').mockReturnValueOnce(true); + + const result = getTransactionReportName({ + translate: translateLocal, + reportAction, + }); + + expect(result).toBe(translateLocal('parentReportAction.deletedExpense')); + }); + + test('uses reports array as searchReports when transactionReport is not provided', () => { + const transaction: Transaction = { + ...createRandomTransaction(1), + reportID: mockReportID, + merchant: 'Coffee Shop', + amount: -1000, + currency: CONST.CURRENCY.USD, + }; + + const result = getTransactionReportName({ + translate: translateLocal, + reportAction: undefined, + transactions: [transaction], + reports: [mockTransactionReport], + }); + + expect(result).toBeDefined(); + expect(typeof result).toBe('string'); + }); + }); + describe('buildOptimisticExpenseReport', () => { beforeEach(Onyx.clear); @@ -15802,6 +15922,7 @@ describe('ReportUtils', () => { // Cleanup await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${onyxPolicy.id}`, null); }); + }); describe('getBillableAndTaxTotal', () => { From 2da8f5f8e3a0031f27eba5a6439e8dbffd374926 Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 18 May 2026 16:23:44 +0700 Subject: [PATCH 02/17] fix: prettier --- src/hooks/useSearchBulkActions.ts | 7 ++----- tests/unit/ReportUtilsTest.ts | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/hooks/useSearchBulkActions.ts b/src/hooks/useSearchBulkActions.ts index ba17384a6646..7088dbce6856 100644 --- a/src/hooks/useSearchBulkActions.ts +++ b/src/hooks/useSearchBulkActions.ts @@ -170,9 +170,7 @@ function shouldShowBulkDuplicateOption({ } const reportID = selectedTransactions[id]?.reportID; - const submitterReport = reportID - ? getReportOrDraftReport(reportID, searchReports, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]) - : undefined; + const submitterReport = reportID ? getReportOrDraftReport(reportID, searchReports, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]) : undefined; if (submitterReport && !isCurrentUserSubmitter(submitterReport)) { return false; } @@ -1412,8 +1410,7 @@ function useSearchBulkActions({queryJSON}: UseSearchBulkActionsParams) { } const ownerAccountID = transactionEntry.ownerAccountID ?? - getReportOrDraftReport(transactionEntry.reportID, undefined, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionEntry.reportID}`]) - ?.ownerAccountID; + getReportOrDraftReport(transactionEntry.reportID, undefined, undefined, undefined, allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionEntry.reportID}`])?.ownerAccountID; if (typeof ownerAccountID === 'number') { ownerAccountIDs.add(ownerAccountID); if (ownerAccountIDs.size > 1) { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 5dc0f8adcfb0..cd2cc462c13b 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -15922,7 +15922,6 @@ describe('ReportUtils', () => { // Cleanup await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${onyxPolicy.id}`, null); }); - }); describe('getBillableAndTaxTotal', () => { From f0a00b2ccba2426d7134549c936f635068120143 Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 18 May 2026 16:45:57 +0700 Subject: [PATCH 03/17] fix: use targetTransactionReport directly --- src/pages/TransactionMerge/MergeTransactionsListContent.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx index aadc2fafe960..58e90b7d66f6 100644 --- a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx +++ b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx @@ -51,7 +51,6 @@ function MergeTransactionsListContent({transactionID, mergeTransaction}: MergeTr const {targetTransaction, sourceTransaction, targetTransactionReport, sourceTransactionReport, targetTransactionPolicy, sourceTransactionPolicy} = useMergeTransactions({ mergeTransaction, }); - const [targetTransactionReportByID] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${targetTransaction?.reportID}`); useEffect(() => { // If the eligible transactions are already loaded, don't fetch them again @@ -134,7 +133,7 @@ function MergeTransactionsListContent({transactionID, mergeTransaction}: MergeTr transaction: targetTransaction, transactions: [targetTransaction], reports: targetTransactionReport ? [targetTransactionReport] : [], - transactionReport: targetTransactionReportByID, + transactionReport: targetTransactionReport, }) : ''; From 5fcb07c3d3e73c33aa8adfce807c49a0de309dcc Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 19 May 2026 11:21:11 +0700 Subject: [PATCH 04/17] fix: add comment --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ea57d9d313eb..483860caf43a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5260,6 +5260,7 @@ function getTransactionReportName({ }: { translate: LocalizedTranslate; reportAction: OnyxEntry; + // The optional operator will be removed in next PR once all remaining getTransactionReportName function calls are updated to pass the `transaction` parameter. transaction?: OnyxEntry; transactions?: Transaction[]; reports?: Report[]; From 985b593194875b023307a24084a3c5c9c133b36d Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 19 May 2026 11:32:57 +0700 Subject: [PATCH 05/17] fix: add comment --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 483860caf43a..e650b70c52a5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5264,6 +5264,7 @@ function getTransactionReportName({ transaction?: OnyxEntry; transactions?: Transaction[]; reports?: Report[]; + // The optional operator will be removed in next PR once all remaining getTransactionReportName function calls are updated to pass the `transactionReport` parameter. transactionReport?: OnyxEntry; }): string { if (reportAction && isReversedTransaction(reportAction)) { From 7b1057c3646575feb2efb68421554c18b75b89e3 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 19 May 2026 11:36:36 +0700 Subject: [PATCH 06/17] fix: conflicts --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 493c146a72c7..e48a63dd0ba2 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 493c146a72c74edbaf66450446c5f2e1efe5e02a +Subproject commit e48a63dd0ba213bb646fc751303428906e8e51ea From c734a15c857c59f08a729b5e327312f7ee343a18 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 26 May 2026 16:08:26 +0700 Subject: [PATCH 07/17] fix: merge main --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 75cbb1baeaab..d1139c3b022d 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 75cbb1baeaabbd7d4da6eaa92444f005471632d1 +Subproject commit d1139c3b022dedc336ab9f5f1bdd3bc0fb535329 From 7de4a901b6630a5c1b1e1b7cd3da2ecdd8c578af Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 26 May 2026 16:08:49 +0700 Subject: [PATCH 08/17] fix: conflicts --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index d1139c3b022d..75cbb1baeaab 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit d1139c3b022dedc336ab9f5f1bdd3bc0fb535329 +Subproject commit 75cbb1baeaabbd7d4da6eaa92444f005471632d1 From 7e9c970385e67ebb6cd9d6ad489053694866c21e Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 2 Jun 2026 05:20:45 +0700 Subject: [PATCH 09/17] fix: revert the refactor related to getReportName --- src/libs/ReportNameUtils.ts | 2 ++ src/libs/ReportUtils.ts | 35 ++++++------------- .../MergeTransactionsListContent.tsx | 5 ++- tests/unit/ReportUtilsTest.ts | 19 +++------- 4 files changed, 20 insertions(+), 41 deletions(-) diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index e533b73e3099..b0a178ab0212 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -820,10 +820,12 @@ function computeChatThreadReportName( if (transactions) { linkedTransactions = linkedTransaction ? [linkedTransaction] : []; } + const linkedTransactionReport = linkedTransaction?.reportID ? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${linkedTransaction.reportID}`] : undefined; let formattedName = getTransactionReportName({ translate, reportAction: parentReportAction, transactions: linkedTransactions, + report: linkedTransactionReport, }); if (isArchivedNonExpense) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a3f9a43ab4a3..9e38bcf2b884 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5319,19 +5319,14 @@ function shouldShowRBRForMissingSmartscanFields(iouReport: OnyxEntry, io function getTransactionReportName({ translate, reportAction, - transaction: transactionParam, transactions, - reports, - transactionReport, + report, }: { translate: LocalizedTranslate; reportAction: OnyxEntry; - // The optional operator will be removed in next PR once all remaining getTransactionReportName function calls are updated to pass the `transaction` parameter. transaction?: OnyxEntry; transactions?: Transaction[]; - reports?: Report[]; - // The optional operator will be removed in next PR once all remaining getTransactionReportName function calls are updated to pass the `transactionReport` parameter. - transactionReport?: OnyxEntry; + report: OnyxEntry | undefined; }): string { if (reportAction && isReversedTransaction(reportAction)) { return translate('parentReportAction.reversedTransaction'); @@ -5341,7 +5336,7 @@ function getTransactionReportName({ return translate('parentReportAction.deletedExpense'); } - const transaction = transactionParam ?? (reportAction ? getLinkedTransaction(reportAction, transactions) : transactions?.at(0)); + const transaction = reportAction ? getLinkedTransaction(reportAction, transactions) : transactions?.at(0); if (isEmptyObject(transaction)) { // Transaction data might be empty on app's first load, if so we fallback to Expense/Track Expense @@ -5352,7 +5347,6 @@ function getTransactionReportName({ return translate('iou.receiptScanning', {count: 1}); } - const report = getReportOrDraftReport(transaction?.reportID, reports, undefined, undefined, transactionReport); if (hasMissingSmartscanFieldsTransactionUtils(transaction, report)) { return translate('iou.receiptMissingDetails'); } @@ -5804,20 +5798,8 @@ function parseReportActionHtmlToText(reportAction: OnyxEntry, repo * @deprecated Moved to src/libs/ReportNameUtils.ts. */ function getReportName(reportNameInformation: GetReportNameParams): string { - const { - report, - policy, - parentReportActionParam, - personalDetails, - invoiceReceiverPolicy, - reportAttributes, - transactions, - isReportArchived, - reports, - conciergeReportID, - transaction: transactionParam, - transactionReport, - } = reportNameInformation; + const {report, policy, parentReportActionParam, personalDetails, invoiceReceiverPolicy, reportAttributes, transactions, isReportArchived, reports, conciergeReportID} = + reportNameInformation; // Check if we can use report name in derived values - only when we have report but no other params const canUseDerivedValue = report && policy === undefined && parentReportActionParam === undefined && personalDetails === undefined && invoiceReceiverPolicy === undefined && isReportArchived === undefined; @@ -5869,7 +5851,12 @@ function getReportName(reportNameInformation: GetReportNameParams): string { if (isChatThread(report)) { if (!isEmptyObject(parentReportAction) && isTransactionThread(parentReportAction)) { - formattedName = getTransactionReportName({translate: translateLocal, reportAction: parentReportAction, transaction: transactionParam, transactions, reports, transactionReport}); + formattedName = getTransactionReportName({ + translate: translateLocal, + reportAction: parentReportAction, + transactions, + report: getReportOrDraftReport(getLinkedTransaction(parentReportAction, transactions)?.reportID, reports), + }); if (isArchivedNonExpense) { formattedName = generateArchivedReportName(formattedName); diff --git a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx index 58e90b7d66f6..44411e147aa6 100644 --- a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx +++ b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx @@ -17,7 +17,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getTransactionsForMerging, setupMergeTransactionData, setupMergeTransactionDataAndNavigate} from '@libs/actions/MergeTransaction'; import {fillMissingReceiptSource} from '@libs/MergeTransactionUtils'; -import {getTransactionReportName, isIOUReport} from '@libs/ReportUtils'; +import {getReportOrDraftReport, getTransactionReportName, isIOUReport} from '@libs/ReportUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import tokenizedSearch from '@libs/tokenizedSearch'; import {getAmount, getCreated, getCurrency, getDescription, getMerchant, isExpenseUnreported} from '@libs/TransactionUtils'; @@ -132,8 +132,7 @@ function MergeTransactionsListContent({transactionID, mergeTransaction}: MergeTr reportAction: undefined, transaction: targetTransaction, transactions: [targetTransaction], - reports: targetTransactionReport ? [targetTransactionReport] : [], - transactionReport: targetTransactionReport, + report: getReportOrDraftReport(targetTransaction?.reportID, targetTransactionReport ? [targetTransactionReport] : [], undefined, undefined, targetTransactionReport), }) : ''; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 37b221efe21f..0ebcedf168b6 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -12694,14 +12694,7 @@ describe('ReportUtils', () => { await Onyx.clear(); }); - test('uses transactionReport param instead of Onyx state when provided', async () => { - const onyxReport: Report = { - ...createRandomReport(100, undefined), - reportName: 'Onyx Report', - type: CONST.REPORT.TYPE.EXPENSE, - }; - await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, onyxReport); - + test('uses report param when provided', async () => { const transaction: Transaction = { ...createRandomTransaction(1), reportID: mockReportID, @@ -12714,16 +12707,14 @@ describe('ReportUtils', () => { translate: translateLocal, reportAction: undefined, transactions: [transaction], - transactionReport: mockTransactionReport, + report: mockTransactionReport, }); expect(result).toBeDefined(); expect(typeof result).toBe('string'); }); - test('falls back to Onyx state when transactionReport is not provided', async () => { - await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${mockReportID}`, mockTransactionReport); - + test('handles undefined report param', () => { const transaction: Transaction = { ...createRandomTransaction(1), reportID: mockReportID, @@ -12780,7 +12771,7 @@ describe('ReportUtils', () => { expect(result).toBe(translateLocal('parentReportAction.deletedExpense')); }); - test('uses reports array as searchReports when transactionReport is not provided', () => { + test('uses report param from caller', () => { const transaction: Transaction = { ...createRandomTransaction(1), reportID: mockReportID, @@ -12793,7 +12784,7 @@ describe('ReportUtils', () => { translate: translateLocal, reportAction: undefined, transactions: [transaction], - reports: [mockTransactionReport], + report: mockTransactionReport, }); expect(result).toBeDefined(); From c89640c3a6e891702578005a0627d0ee06fd4338 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 2 Jun 2026 05:23:41 +0700 Subject: [PATCH 10/17] fix: revert the refactor related to getReportName --- src/libs/ReportUtils.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9e38bcf2b884..8ca607463369 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -984,10 +984,6 @@ type GetReportNameParams = { // TODO: Make this required when https://github.com/Expensify/App/issues/66411 is done /** Used to identify the Concierge chat so its name can be set to the Concierge display name */ conciergeReportID?: string; - /** The transaction linked to the report action */ - transaction?: OnyxEntry; - /** The report that the transaction belongs to */ - transactionReport?: OnyxEntry; }; type GetReportStatusParams = { From a20fc25fd8e65f0da001b3cf90cbbb222e69ebf4 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 2 Jun 2026 05:24:32 +0700 Subject: [PATCH 11/17] fix: remove transaction?: OnyxEntry --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8ca607463369..cb7bac8365d6 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5320,7 +5320,6 @@ function getTransactionReportName({ }: { translate: LocalizedTranslate; reportAction: OnyxEntry; - transaction?: OnyxEntry; transactions?: Transaction[]; report: OnyxEntry | undefined; }): string { From e09f1ae95099ae8ac40255cf5f1280cf29d5d1a2 Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 2 Jun 2026 05:51:37 +0700 Subject: [PATCH 12/17] fix: test --- src/libs/ReportUtils.ts | 2 +- tests/unit/ReportUtilsTest.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cb7bac8365d6..0ba346eedd7d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5321,7 +5321,7 @@ function getTransactionReportName({ translate: LocalizedTranslate; reportAction: OnyxEntry; transactions?: Transaction[]; - report: OnyxEntry | undefined; + report: OnyxEntry; }): string { if (reportAction && isReversedTransaction(reportAction)) { return translate('parentReportAction.reversedTransaction'); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 0ebcedf168b6..611f63bef9aa 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -12714,7 +12714,7 @@ describe('ReportUtils', () => { expect(typeof result).toBe('string'); }); - test('handles undefined report param', () => { + test('returns report name with report param', () => { const transaction: Transaction = { ...createRandomTransaction(1), reportID: mockReportID, @@ -12727,6 +12727,7 @@ describe('ReportUtils', () => { translate: translateLocal, reportAction: undefined, transactions: [transaction], + report: mockTransactionReport, }); expect(result).toBeDefined(); @@ -12750,6 +12751,7 @@ describe('ReportUtils', () => { const result = getTransactionReportName({ translate: translateLocal, reportAction, + report: mockTransactionReport, }); expect(result).toBe(translateLocal('parentReportAction.reversedTransaction')); @@ -12766,6 +12768,7 @@ describe('ReportUtils', () => { const result = getTransactionReportName({ translate: translateLocal, reportAction, + report: mockTransactionReport, }); expect(result).toBe(translateLocal('parentReportAction.deletedExpense')); From 34818a868de5d93128f8d5f4b364ae077a80aaae Mon Sep 17 00:00:00 2001 From: truph01 Date: Tue, 2 Jun 2026 05:54:27 +0700 Subject: [PATCH 13/17] fix: remove redundant param --- src/pages/TransactionMerge/MergeTransactionsListContent.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx index 44411e147aa6..5228c5522009 100644 --- a/src/pages/TransactionMerge/MergeTransactionsListContent.tsx +++ b/src/pages/TransactionMerge/MergeTransactionsListContent.tsx @@ -130,7 +130,6 @@ function MergeTransactionsListContent({transactionID, mergeTransaction}: MergeTr ? getTransactionReportName({ translate, reportAction: undefined, - transaction: targetTransaction, transactions: [targetTransaction], report: getReportOrDraftReport(targetTransaction?.reportID, targetTransactionReport ? [targetTransactionReport] : [], undefined, undefined, targetTransactionReport), }) From b1d4a593b0706ff6e74aee4a0b042e8d607e8563 Mon Sep 17 00:00:00 2001 From: truph01 Date: Thu, 4 Jun 2026 13:59:18 +0700 Subject: [PATCH 14/17] fix: conflicts --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index e140494a35da..50ba47653d86 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit e140494a35da9c8ee49a3dd96db69194a111940d +Subproject commit 50ba47653d86af9c03475dcec7446008e1b8d8c0 From 594bf74f1a4c3d7969d68e8b2fe0e438398f9351 Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 8 Jun 2026 14:55:05 +0700 Subject: [PATCH 15/17] fix: conflicts --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 4d18f39aeda3..51d784194fc4 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 4d18f39aeda35321fefa4297e903ca0a85a43358 +Subproject commit 51d784194fc4d515be523c8d345cd9d88a1e7a29 From 93181c0b023cac817139ea31f6eaadb361818e2c Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 10 Jun 2026 16:07:59 +0700 Subject: [PATCH 16/17] fix: conflicts --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 79c0e204a261..a45e4eb672dd 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 79c0e204a261700610c066a04b700bd7fe8876ec +Subproject commit a45e4eb672dd52e0a860795943458d71b3345ebc From afaea685acfdaccf466d123db8e73ea1589d736e Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 10 Jun 2026 18:03:07 +0700 Subject: [PATCH 17/17] fix: lint --- tests/unit/ReportUtilsTest.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 6dac8c5dc1e6..68e70c5214b7 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -12754,16 +12754,7 @@ describe('ReportUtils', () => { }); test('returns reversed transaction message when action is reversed', () => { - const reportAction = { - ...createRandomReportAction(1), - actionName: CONST.REPORT.ACTIONS.TYPE.IOU, - originalMessage: { - type: CONST.IOU.REPORT_ACTION_TYPE.PAY, - IOUTransactionID: '1', - amount: 0, - currency: CONST.CURRENCY.USD, - }, - } as unknown as ReportAction; + const reportAction = createRandomReportAction(1); jest.spyOn(require('@libs/ReportActionsUtils'), 'isReversedTransaction').mockReturnValueOnce(true); @@ -12777,10 +12768,7 @@ describe('ReportUtils', () => { }); test('returns deleted expense message when action is deleted', () => { - const reportAction = { - ...createRandomReportAction(1), - actionName: CONST.REPORT.ACTIONS.TYPE.IOU, - } as unknown as ReportAction; + const reportAction = createRandomReportAction(1); jest.spyOn(require('@libs/ReportActionsUtils'), 'isDeletedAction').mockReturnValueOnce(true);