From 0a7a4f6c8e4e2029f18a2009467833ba7d01f505 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Sat, 21 Mar 2026 00:03:34 +0700 Subject: [PATCH 1/2] refactor approveMoneyRequest, cancelPayment and assignCardToEmployee to use email/accountID from useOnyx --- src/libs/ReportUtils.ts | 12 +- src/libs/actions/CompanyCards.ts | 10 +- src/libs/actions/IOU/index.ts | 3 +- .../assignCard/ConfirmationStep.tsx | 6 +- tests/unit/ReportUtilsTest.ts | 115 ++++++++++++++++++ 5 files changed, 136 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 174bc0d37b69..0520a9527518 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4168,10 +4168,10 @@ function buildOptimisticChangeFieldAction(reportField: PolicyReportField, previo * Builds an optimistic REIMBURSEMENT_DEQUEUED report action with a randomly generated reportActionID. * */ -function buildOptimisticCancelPaymentReportAction(expenseReportID: string, amount: number, currency: string): OptimisticCancelPaymentReportAction { +function buildOptimisticCancelPaymentReportAction(expenseReportID: string, amount: number, currency: string, currentUserAccountID: number): OptimisticCancelPaymentReportAction { return { actionName: CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENT_DEQUEUED, - actorAccountID: deprecatedCurrentUserAccountID, + actorAccountID: currentUserAccountID, message: [ { cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN, @@ -7266,7 +7266,7 @@ function buildOptimisticIOUReportAction(params: BuildOptimisticIOUReportActionPa /** * Builds an optimistic APPROVED report action with a randomly generated reportActionID. */ -function buildOptimisticApprovedReportAction(amount: number, currency: string, expenseReportID: string): OptimisticApprovedReportAction { +function buildOptimisticApprovedReportAction(amount: number, currency: string, expenseReportID: string, currentUserAccountID: number): OptimisticApprovedReportAction { const originalMessage = { amount, currency, @@ -7276,7 +7276,7 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e return { actionName: CONST.REPORT.ACTIONS.TYPE.APPROVED, - actorAccountID: deprecatedCurrentUserAccountID, + actorAccountID: currentUserAccountID, automatic: false, avatar: getCurrentUserAvatar(), isAttachmentOnly: false, @@ -8300,10 +8300,10 @@ function buildOptimisticEditedTaskFieldReportAction({title, description}: Task): }; } -function buildOptimisticCardAssignedReportAction(assigneeAccountID: number): OptimisticCardAssignedReportAction { +function buildOptimisticCardAssignedReportAction(assigneeAccountID: number, currentUserAccountID: number): OptimisticCardAssignedReportAction { return { actionName: CONST.REPORT.ACTIONS.TYPE.CARD_ASSIGNED, - actorAccountID: deprecatedCurrentUserAccountID, + actorAccountID: currentUserAccountID, avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), originalMessage: {assigneeAccountID, cardID: -1}, diff --git a/src/libs/actions/CompanyCards.ts b/src/libs/actions/CompanyCards.ts index 4439218ab5ef..c10f3cd381c3 100644 --- a/src/libs/actions/CompanyCards.ts +++ b/src/libs/actions/CompanyCards.ts @@ -317,13 +317,19 @@ function deleteWorkspaceCompanyCardFeed( API.write(WRITE_COMMANDS.DELETE_COMPANY_CARD_FEED, parameters, {optimisticData, successData, failureData}); } -function assignWorkspaceCompanyCard(policy: OnyxEntry, domainOrWorkspaceAccountID: number, translate: LocaleContextProps['translate'], data: Partial) { +function assignWorkspaceCompanyCard( + policy: OnyxEntry, + domainOrWorkspaceAccountID: number, + translate: LocaleContextProps['translate'], + data: Partial, + currentUserAccountID: number, +) { if (!policy?.id) { return; } const {bankName, email = '', encryptedCardNumber = '', startDate = '', customCardName = ''} = data; const assigneeDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email); - const optimisticCardAssignedReportAction = ReportUtils.buildOptimisticCardAssignedReportAction(assigneeDetails?.accountID ?? CONST.DEFAULT_NUMBER_ID); + const optimisticCardAssignedReportAction = ReportUtils.buildOptimisticCardAssignedReportAction(assigneeDetails?.accountID ?? CONST.DEFAULT_NUMBER_ID, currentUserAccountID); const parameters: AssignCompanyCardParams = { domainAccountID: domainOrWorkspaceAccountID, diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index d6a63ac7636b..314d5b5a0acd 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -9921,7 +9921,7 @@ function approveMoneyRequest(params: ApproveMoneyRequestFunctionParams) { if (hasHeldExpenses && !full && !!expenseReport.unheldTotal) { total = expenseReport.unheldTotal; } - const optimisticApprovedReportAction = buildOptimisticApprovedReportAction(total, expenseReport.currency ?? '', expenseReport.reportID); + const optimisticApprovedReportAction = buildOptimisticApprovedReportAction(total, expenseReport.currency ?? '', expenseReport.reportID, currentUserAccountIDParam); const isDEWPolicy = hasDynamicExternalWorkflow(policy); const shouldAddOptimisticApproveAction = !isDEWPolicy || isOffline(); @@ -11079,6 +11079,7 @@ function cancelPayment( expenseReport.reportID, -((expenseReport.total ?? 0) - (expenseReport?.nonReimbursableTotal ?? 0)), expenseReport.currency ?? '', + currentUserAccountIDParam, ); const approvalMode = policy?.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC; diff --git a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx index e9c71c8d091e..1fc637b484dd 100644 --- a/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/ConfirmationStep.tsx @@ -10,6 +10,7 @@ import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useCardFeeds from '@hooks/useCardFeeds'; import {useCurrencyListState} from '@hooks/useCurrencyList'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -69,6 +70,9 @@ function ConfirmationStep({route}: ConfirmationStepProps) { const cardholderEmail = Str.removeSMSDomain(cardToAssign?.email ?? ''); const cardholderAccountID = cardholder?.accountID; + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const currentUserAccountID = currentUserPersonalDetails.accountID; + useEffect(() => { if (!assignCard?.isAssignmentFinished) { return; @@ -112,7 +116,7 @@ function ConfirmationStep({route}: ConfirmationStepProps) { return; } - assignWorkspaceCompanyCard(policy, domainOrWorkspaceAccountID, translate, {...cardToAssign, cardholder, bankName}); + assignWorkspaceCompanyCard(policy, domainOrWorkspaceAccountID, translate, {...cardToAssign, cardholder, bankName}, currentUserAccountID); }; const editStep = (step: string) => { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index bdd2593d62f7..8d7de6a21c07 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -32,6 +32,9 @@ import {getOriginalMessage, getReportAction, isWhisperAction} from '@libs/Report import {buildReportNameFromParticipantNames, computeReportName as computeReportNameOriginal, getGroupChatName, getPolicyExpenseChatName, getReportName} from '@libs/ReportNameUtils'; import type {OptionData} from '@libs/ReportUtils'; import { + buildOptimisticApprovedReportAction, + buildOptimisticCancelPaymentReportAction, + buildOptimisticCardAssignedReportAction, buildOptimisticChatReport, buildOptimisticCreatedReportAction, buildOptimisticCreatedReportForUnapprovedAction, @@ -14881,4 +14884,116 @@ describe('ReportUtils', () => { expect(details?.taxValue).toBe('10%'); }); }); + + describe('buildOptimisticApprovedReportAction', () => { + it('should set actorAccountID to the provided currentUserAccountID', () => { + const customAccountID = 99; + const action = buildOptimisticApprovedReportAction(500, 'USD', 'expenseReport1', customAccountID); + + expect(action.actorAccountID).toBe(customAccountID); + }); + + it('should set actionName to APPROVED', () => { + const action = buildOptimisticApprovedReportAction(500, 'USD', 'expenseReport1', currentUserAccountID); + + expect(action.actionName).toBe(CONST.REPORT.ACTIONS.TYPE.APPROVED); + }); + + it('should set originalMessage with the provided amount, currency, and expenseReportID', () => { + const amount = 1200; + const currency = 'EUR'; + const expenseReportID = 'report42'; + const action = buildOptimisticApprovedReportAction(amount, currency, expenseReportID, currentUserAccountID); + + expect(action.originalMessage).toMatchObject({amount, currency, expenseReportID}); + }); + + it('should set pendingAction to ADD', () => { + const action = buildOptimisticApprovedReportAction(500, 'USD', 'expenseReport1', currentUserAccountID); + + expect(action.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + }); + + it('should generate a non-empty reportActionID', () => { + const action = buildOptimisticApprovedReportAction(500, 'USD', 'expenseReport1', currentUserAccountID); + + expect(action.reportActionID).toBeTruthy(); + }); + }); + + describe('buildOptimisticCancelPaymentReportAction', () => { + it('should set actorAccountID to the provided currentUserAccountID', () => { + const customAccountID = 77; + const action = buildOptimisticCancelPaymentReportAction('expenseReport2', 300, 'USD', customAccountID); + + expect(action.actorAccountID).toBe(customAccountID); + }); + + it('should set actionName to REIMBURSEMENT_DEQUEUED', () => { + const action = buildOptimisticCancelPaymentReportAction('expenseReport2', 300, 'USD', currentUserAccountID); + + expect(action.actionName).toBe(CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENT_DEQUEUED); + }); + + it('should set originalMessage with the provided expenseReportID, amount, currency, and cancellationReason', () => { + const expenseReportID = 'report55'; + const amount = 750; + const currency = 'GBP'; + const action = buildOptimisticCancelPaymentReportAction(expenseReportID, amount, currency, currentUserAccountID); + + expect(action.originalMessage).toMatchObject({ + expenseReportID, + amount, + currency, + cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN, + }); + }); + + it('should set pendingAction to ADD', () => { + const action = buildOptimisticCancelPaymentReportAction('expenseReport2', 300, 'USD', currentUserAccountID); + + expect(action.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + }); + + it('should generate a non-empty reportActionID', () => { + const action = buildOptimisticCancelPaymentReportAction('expenseReport2', 300, 'USD', currentUserAccountID); + + expect(action.reportActionID).toBeTruthy(); + }); + }); + + describe('buildOptimisticCardAssignedReportAction', () => { + it('should set actorAccountID to the provided currentUserAccountID', () => { + const assigneeAccountID = 10; + const customAccountID = 88; + const action = buildOptimisticCardAssignedReportAction(assigneeAccountID, customAccountID); + + expect(action.actorAccountID).toBe(customAccountID); + }); + + it('should set actionName to CARD_ASSIGNED', () => { + const action = buildOptimisticCardAssignedReportAction(10, currentUserAccountID); + + expect(action.actionName).toBe(CONST.REPORT.ACTIONS.TYPE.CARD_ASSIGNED); + }); + + it('should set originalMessage with the provided assigneeAccountID', () => { + const assigneeAccountID = 42; + const action = buildOptimisticCardAssignedReportAction(assigneeAccountID, currentUserAccountID); + + expect(action.originalMessage).toMatchObject({assigneeAccountID}); + }); + + it('should set pendingAction to ADD', () => { + const action = buildOptimisticCardAssignedReportAction(10, currentUserAccountID); + + expect(action.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + }); + + it('should generate a non-empty reportActionID', () => { + const action = buildOptimisticCardAssignedReportAction(10, currentUserAccountID); + + expect(action.reportActionID).toBeTruthy(); + }); + }); }); From 8fe48aacac7f1d221b73b09a9b4c0618eb099c4e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Sat, 21 Mar 2026 00:12:02 +0700 Subject: [PATCH 2/2] lint fix --- tests/unit/ReportUtilsTest.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 8d7de6a21c07..74a1026f016b 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -14905,7 +14905,7 @@ describe('ReportUtils', () => { const expenseReportID = 'report42'; const action = buildOptimisticApprovedReportAction(amount, currency, expenseReportID, currentUserAccountID); - expect(action.originalMessage).toMatchObject({amount, currency, expenseReportID}); + expect(getOriginalMessage(action as ReportAction)).toMatchObject({amount, currency, expenseReportID}); }); it('should set pendingAction to ADD', () => { @@ -14941,7 +14941,7 @@ describe('ReportUtils', () => { const currency = 'GBP'; const action = buildOptimisticCancelPaymentReportAction(expenseReportID, amount, currency, currentUserAccountID); - expect(action.originalMessage).toMatchObject({ + expect(getOriginalMessage(action as ReportAction)).toMatchObject({ expenseReportID, amount, currency, @@ -14981,7 +14981,7 @@ describe('ReportUtils', () => { const assigneeAccountID = 42; const action = buildOptimisticCardAssignedReportAction(assigneeAccountID, currentUserAccountID); - expect(action.originalMessage).toMatchObject({assigneeAccountID}); + expect(getOriginalMessage(action as ReportAction)).toMatchObject({assigneeAccountID}); }); it('should set pendingAction to ADD', () => {