diff --git a/src/components/ReportActionItem/MovedTransactionAction.tsx b/src/components/ReportActionItem/MovedTransactionAction.tsx index c8fa0f60446e7..7f55834aa67dc 100644 --- a/src/components/ReportActionItem/MovedTransactionAction.tsx +++ b/src/components/ReportActionItem/MovedTransactionAction.tsx @@ -34,6 +34,7 @@ function MovedTransactionAction({action, emptyHTML, childReport, originalReport} const [toReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${toReportID}`); const [fromReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isPendingDelete = fromReport?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; // When the transaction is moved from personal space (unreported), fromReportID will be "0" which doesn't exist in allReports @@ -46,7 +47,7 @@ function MovedTransactionAction({action, emptyHTML, childReport, originalReport} return emptyHTML; } - const message = getMovedTransactionMessage(translate, action); + const message = getMovedTransactionMessage(translate, action, conciergeReportID); if (hasReasoning(action)) { return ( diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 58c7af67f25ba..cca9fd509fdaa 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -422,8 +422,7 @@ function computeReportNameBasedOnReportAction( reportPolicy: Policy | undefined, parentReport: Report | undefined, personalDetailsList: OnyxEntry, - // TODO: Make this required when https://github.com/Expensify/App/issues/66411 is done - conciergeReportID?: string, + conciergeReportID: string | undefined, ): string | undefined { if (!parentReportAction) { return undefined; @@ -533,7 +532,7 @@ function computeReportNameBasedOnReportAction( } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - return Parser.htmlToText(getMovedTransactionMessage(translate, parentReportAction)); + return Parser.htmlToText(getMovedTransactionMessage(translate, parentReportAction, conciergeReportID)); } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_MAX_EXPENSE_AMOUNT)) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fa4eb376dbc63..ea25c2d35d1f4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6951,8 +6951,7 @@ function getDeletedTransactionMessage(translate: LocalizedTranslate, action: Rep return message; } -// TODO: conciergeReportID will be required eventually. Refactor issue: https://github.com/Expensify/App/issues/66411 -function getMovedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, conciergeReportID?: string) { +function getMovedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, conciergeReportID: string | undefined) { const movedTransactionOriginalMessage = getOriginalMessage(action) ?? {}; const {toReportID, fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 01c351f1a673b..fe3de00aec9a3 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1129,7 +1129,7 @@ function getOptionData({ } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_OWNERSHIP) { result.alternateText = Parser.htmlToText(getUpdatedOwnershipMessage(translate, lastAction, policy)); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - result.alternateText = Parser.htmlToText(getMovedTransactionMessage(translate, lastAction)); + result.alternateText = Parser.htmlToText(getMovedTransactionMessage(translate, lastAction, conciergeReportID)); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.SETTLEMENT_ACCOUNT_LOCKED)) { result.alternateText = Parser.htmlToText(getSettlementAccountLockedMessage(translate, lastAction)); } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && lastActorDisplayName && lastMessageTextFromReport) { diff --git a/src/libs/actions/IOU/MoneyRequest.ts b/src/libs/actions/IOU/MoneyRequest.ts index 551560403c011..09ba9ed21a331 100644 --- a/src/libs/actions/IOU/MoneyRequest.ts +++ b/src/libs/actions/IOU/MoneyRequest.ts @@ -176,6 +176,7 @@ type MoneyRequestStepDistanceNavigationParams = { amountOwed: OnyxEntry; userBillingGracePeriodEnds: OnyxCollection; ownerBillingGracePeriodEnd?: OnyxEntry; + conciergeReportID: string | undefined; }; function createTransaction({ @@ -297,6 +298,7 @@ function getMoneyRequestParticipantOptions( report: OnyxEntry, policy: OnyxEntry, personalDetails: OnyxEntry, + conciergeReportID: string | undefined, privateIsArchived?: boolean, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ): Array { @@ -305,8 +307,7 @@ function getMoneyRequestParticipantOptions( const participantAccountID = participant?.accountID ?? CONST.DEFAULT_NUMBER_ID; return participantAccountID ? getParticipantsOption(participant, personalDetails) - : // TODO: We'll pass the conciergeReportID in the next PR. Refactor issue: https://github.com/Expensify/App/issues/66411 - getReportOption(participant, privateIsArchived, policy, personalDetails, undefined, reportAttributesDerived); + : getReportOption(participant, privateIsArchived, policy, personalDetails, conciergeReportID, reportAttributesDerived); }); } @@ -606,6 +607,7 @@ function handleMoneyRequestStepDistanceNavigation({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }: MoneyRequestStepDistanceNavigationParams) { const isManualDistance = manualDistance !== undefined; const isOdometerDistance = odometerDistance !== undefined; @@ -628,7 +630,7 @@ function handleMoneyRequestStepDistanceNavigation({ // to the confirm step. // If the user started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. if (report?.reportID && !isArchivedExpenseReport && iouType !== CONST.IOU.TYPE.CREATE) { - const participants = getMoneyRequestParticipantOptions(currentUserAccountID, report, policy, personalDetails, privateIsArchived, reportAttributesDerived); + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, report, policy, personalDetails, conciergeReportID, privateIsArchived, reportAttributesDerived); setDistanceRequestData?.(participants); if (shouldSkipConfirmation) { diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index 3753fa96f4293..5117668139b01 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -1112,7 +1112,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.TAKE_CONTROL) || isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REROUTE)) { setClipboardMessage(getChangedApproverActionMessage(translate, reportAction)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - setClipboardMessage(getMovedTransactionMessage(translate, reportAction)); + setClipboardMessage(getMovedTransactionMessage(translate, reportAction, conciergeReportID)); } else if (isMovedAction(reportAction)) { setClipboardMessage(getMovedActionMessage(translate, reportAction, originalReport)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_CARD_FRAUD_ALERT)) { diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index bfc28575af7ba..a2be8424bc4b2 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -109,6 +109,7 @@ function IOURequestStepDistance({ const [betas] = useOnyx(ONYXKEYS.BETAS); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isEditing = action === CONST.IOU.ACTION.EDIT; const isEditingSplit = (iouType === CONST.IOU.TYPE.SPLIT || iouType === CONST.IOU.TYPE.SPLIT_EXPENSE) && isEditing; @@ -343,6 +344,7 @@ function IOURequestStepDistance({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }, [ iouType, @@ -380,6 +382,7 @@ function IOURequestStepDistance({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, ]); const getError = () => { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx b/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx index 2a43113250515..9f8eec56b561f 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceGPS/index.native.tsx @@ -64,6 +64,7 @@ function IOURequestStepDistanceGPS({ const {policyForMovingExpenses} = usePolicyForMovingExpenses(); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isEditing = action === CONST.IOU.ACTION.EDIT; const isCreatingNewRequest = !isEditing; // eslint-disable-next-line rulesdir/no-negated-variables @@ -135,6 +136,7 @@ function IOURequestStepDistanceGPS({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }; diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index c699e60a0fe0f..4cf70800125e6 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -100,6 +100,7 @@ function IOURequestStepDistanceManual({ const [betas] = useOnyx(ONYXKEYS.BETAS); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`); @@ -252,6 +253,7 @@ function IOURequestStepDistanceManual({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }, [ @@ -298,6 +300,7 @@ function IOURequestStepDistanceManual({ isSelfTourViewed, amountOwed, ownerBillingGracePeriodEnd, + conciergeReportID, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index caa7b3e1ee502..a605b9fc83800 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -106,6 +106,7 @@ function IOURequestStepDistanceMap({ const [policyRecentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isEditing = action === CONST.IOU.ACTION.EDIT; const isEditingSplit = (iouType === CONST.IOU.TYPE.SPLIT || iouType === CONST.IOU.TYPE.SPLIT_EXPENSE) && isEditing; const currentTransaction = isEditingSplit && !isEmpty(splitDraftTransaction) ? splitDraftTransaction : transaction; @@ -337,6 +338,7 @@ function IOURequestStepDistanceMap({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }, [ iouType, @@ -374,6 +376,7 @@ function IOURequestStepDistanceMap({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, ]); const getError = () => { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index ea7e113285d7e..478bbefac9808 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -124,6 +124,7 @@ function IOURequestStepDistanceOdometer({ const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [selectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.DISTANCE_REQUEST_TYPE}`); const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult); // isEditing: we're changing an already existing odometer expense; isEditingConfirmation: we navigated here by pressing 'Distance' field from the confirmation step during the creation of a new odometer expense to adjust the input before submitting @@ -504,6 +505,7 @@ function IOURequestStepDistanceOdometer({ amountOwed, userBillingGracePeriodEnds, ownerBillingGracePeriodEnd, + conciergeReportID, }); }; diff --git a/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts b/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts index 229bb65f2d7a1..f2af7579fbe20 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts +++ b/src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts @@ -100,10 +100,11 @@ function useReceiptScan({ }, [isMultiScanEnabled]); const [recentWaypoints] = useOnyx(ONYXKEYS.NVP_RECENT_WAYPOINTS); + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const participants = useMemo( - () => getMoneyRequestParticipantOptions(currentUserPersonalDetails.accountID, report, policy, personalDetails, isArchived, reportAttributesDerived), - [currentUserPersonalDetails.accountID, report, policy, personalDetails, isArchived, reportAttributesDerived], + () => getMoneyRequestParticipantOptions(currentUserPersonalDetails.accountID, report, policy, personalDetails, conciergeReportID, isArchived, reportAttributesDerived), + [currentUserPersonalDetails.accountID, report, policy, personalDetails, conciergeReportID, isArchived, reportAttributesDerived], ); const participantsPolicyTags = useParticipantsPolicyTags(participants); diff --git a/tests/actions/IOU/MoneyRequestTest.ts b/tests/actions/IOU/MoneyRequestTest.ts index dbd0df2d1d384..9e266f83e665e 100644 --- a/tests/actions/IOU/MoneyRequestTest.ts +++ b/tests/actions/IOU/MoneyRequestTest.ts @@ -590,7 +590,14 @@ describe('MoneyRequest', () => { }, }); baseParams.recentWaypoints = (await getOnyxValue(ONYXKEYS.NVP_RECENT_WAYPOINTS)) ?? []; - baseParams.participants = getMoneyRequestParticipantOptions(baseParams.currentUserAccountID, baseParams.report, baseParams.policy, baseParams.personalDetails, undefined, {}); + baseParams.participants = getMoneyRequestParticipantOptions( + baseParams.currentUserAccountID, + baseParams.report, + baseParams.policy, + baseParams.personalDetails, + undefined, + undefined, + ); await getOnyxData({ key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`, waitForCollectionCallback: true, @@ -709,7 +716,7 @@ describe('MoneyRequest', () => { ...fakeReport, chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, }; - baseParams.participants = getMoneyRequestParticipantOptions(baseParams.currentUserAccountID, report, baseParams.policy, baseParams.personalDetails, undefined, {}); + baseParams.participants = getMoneyRequestParticipantOptions(baseParams.currentUserAccountID, report, baseParams.policy, baseParams.personalDetails, undefined, undefined); await getOnyxData({ key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`, @@ -1081,6 +1088,7 @@ describe('MoneyRequest', () => { amountOwed: 0, draftTransactionIDs: undefined, userBillingGracePeriodEnds: undefined, + conciergeReportID: undefined, }; const splitShares: SplitShares = { [firstSplitParticipantID]: { @@ -1505,6 +1513,90 @@ describe('MoneyRequest', () => { expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(CONST.IOU.TYPE.CREATE, baseParams.transactionID, baseParams.reportID)); }); + + it('should pass conciergeReportID through to getMoneyRequestParticipantOptions when report exists', async () => { + const conciergeReportID = 'concierge789'; + handleMoneyRequestStepDistanceNavigation({ + ...baseParams, + iouType: CONST.IOU.TYPE.SUBMIT, + shouldSkipConfirmation: false, + isArchivedExpenseReport: false, + draftTransactionIDs: [baseParams.transactionID], + conciergeReportID, + }); + + // When report exists and iouType is not CREATE, the function calls getMoneyRequestParticipantOptions + // with conciergeReportID, sets distance request data, and then navigates to confirmation page + await waitForBatchedUpdates(); + expect(baseParams.setDistanceRequestData).toHaveBeenCalled(); + }); + + it('should set distance request data when conciergeReportID is undefined', async () => { + handleMoneyRequestStepDistanceNavigation({ + ...baseParams, + iouType: CONST.IOU.TYPE.SUBMIT, + shouldSkipConfirmation: false, + isArchivedExpenseReport: false, + draftTransactionIDs: [baseParams.transactionID], + conciergeReportID: undefined, + }); + + await waitForBatchedUpdates(); + expect(baseParams.setDistanceRequestData).toHaveBeenCalled(); + }); + }); + + describe('getMoneyRequestParticipantOptions', () => { + const fakeReport = createRandomReport(1, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT); + const fakePolicy = createRandomPolicy(1, CONST.POLICY.TYPE.TEAM); + + beforeEach(async () => { + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`, { + ...fakeReport, + participants: { + [TEST_USER_ACCOUNT_ID]: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + role: CONST.REPORT.ROLE.MEMBER, + }, + }, + }); + }); + + afterEach(async () => { + await Onyx.clear(); + }); + + it('should return participants when conciergeReportID is undefined', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, undefined); + expect(Array.isArray(participants)).toBe(true); + }); + + it('should return participants when conciergeReportID is provided', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, 'concierge123'); + expect(Array.isArray(participants)).toBe(true); + }); + + it('should pass conciergeReportID through to getReportOption for policy expense chat participants', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, 'concierge456'); + // For policy expense chat, participants have accountID 0 and go through getReportOption + // which uses conciergeReportID for identifying concierge chat + expect(Array.isArray(participants)).toBe(true); + expect(participants.length).toBeGreaterThan(0); + }); + + it('should return participants with privateIsArchived passed through', () => { + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, fakeReport, fakePolicy, {}, undefined, true); + expect(Array.isArray(participants)).toBe(true); + }); + + it('should return participants for report with no chat participants (DM-like)', () => { + const dmReport = { + ...createRandomReport(2, undefined), + participants: {}, + }; + const participants = getMoneyRequestParticipantOptions(currentUserAccountID, dmReport, fakePolicy, {}, undefined); + expect(Array.isArray(participants)).toBe(true); + }); }); describe('shouldUseDefaultExpensePolicy', () => { diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 79a7807d25064..e9d066817d968 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -4088,7 +4088,7 @@ describe('OptionsListUtils', () => { currentUserLogin: CURRENT_USER_EMAIL, }); - expect(lastMessage).toBe(Parser.htmlToText(getMovedTransactionMessage(translateLocal, movedTransactionAction))); + expect(lastMessage).toBe(Parser.htmlToText(getMovedTransactionMessage(translateLocal, movedTransactionAction, undefined))); }); describe('SUBMITTED action', () => { it('should return automatic submitted message if submitted via harvesting', async () => { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 061ada5226b55..39dc8a92efcb7 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -80,6 +80,7 @@ import { getChatRoomSubtitle, getChildReportNotificationPreference, getDefaultWorkspaceAvatar, + getDeletedTransactionMessage, getDisplayNameForParticipant, getDisplayNamesWithTooltips, getHarvestOriginalReportID, @@ -88,6 +89,8 @@ import { getIOUReportActionDisplayMessage, getLinkedIOUTransaction, getMostRecentlyVisitedReport, + getMovedActionMessage, + getMovedTransactionMessage, getOriginalReportID, getOutstandingChildRequest, getParentNavigationSubtitle, @@ -15901,6 +15904,132 @@ describe('ReportUtils', () => { }); }); + describe('getDeletedTransactionMessage', () => { + it('should return a formatted deleted transaction message', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION, + originalMessage: { + amount: -5000, + currency: 'USD', + merchant: 'Starbucks', + }, + } as unknown as ReportAction; + + const result = getDeletedTransactionMessage(translateLocal, action); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + + it('should handle missing amount and currency gracefully', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION, + originalMessage: {}, + } as unknown as ReportAction; + + const result = getDeletedTransactionMessage(translateLocal, action); + expect(typeof result).toBe('string'); + }); + }); + + describe('getMovedTransactionMessage', () => { + it('should return movedTransactionTo message when fromReportID is undefined', async () => { + const toReport = LHNTestUtils.getFakeReport(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${toReport.reportID}`, toReport); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + originalMessage: { + toReportID: toReport.reportID, + }, + } as unknown as ReportAction; + + const result = getMovedTransactionMessage(translateLocal, action, undefined); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + + it('should return movedTransactionFrom message when fromReportID is defined', async () => { + const fromReport = LHNTestUtils.getFakeReport(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${fromReport.reportID}`, fromReport); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + originalMessage: { + fromReportID: fromReport.reportID, + toReportID: '99999', + }, + } as unknown as ReportAction; + + const result = getMovedTransactionMessage(translateLocal, action, undefined); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + + it('should handle conciergeReportID parameter', async () => { + const toReport = LHNTestUtils.getFakeReport(); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${toReport.reportID}`, toReport); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + originalMessage: { + toReportID: toReport.reportID, + }, + } as unknown as ReportAction; + + const result = getMovedTransactionMessage(translateLocal, action, '12345'); + expect(typeof result).toBe('string'); + }); + }); + + describe('getMovedActionMessage', () => { + it('should return empty string for non-moved action', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, + } as unknown as ReportAction; + + const report = LHNTestUtils.getFakeReport(); + const result = getMovedActionMessage(translateLocal, action, report); + expect(result).toBe(''); + }); + + it('should return empty string when original message is missing', () => { + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, + originalMessage: undefined, + } as unknown as ReportAction; + + const report = LHNTestUtils.getFakeReport(); + const result = getMovedActionMessage(translateLocal, action, report); + expect(result).toBe(''); + }); + + it('should return moved action message with policy name', async () => { + const testPolicy = createRandomPolicy(0); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${testPolicy.id}`, testPolicy); + + const action = { + ...LHNTestUtils.getFakeReportAction(), + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED, + originalMessage: { + toPolicyID: testPolicy.id, + newParentReportID: '11111', + movedReportID: '22222', + }, + } as unknown as ReportAction; + + const report = LHNTestUtils.getFakeReport(); + const result = getMovedActionMessage(translateLocal, action, report); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + }); describe('getReportActionWithSmartscanError', () => { const chatReportID = '100'; const expenseReportID = '200';