From 0e4759ac1e68832eb5c370f6067ee0a3797236b1 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 23 Jun 2025 15:00:57 +0700 Subject: [PATCH 1/4] fix: next step message on unapproved expense after changing WS --- src/libs/actions/Report.ts | 62 ++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1ac9e94007ee..11712cb62f3e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5444,42 +5444,44 @@ function buildOptimisticChangePolicyData(report: Report, policyID: string, repor updatePolicyIdForReportAndThreads(reportID, policyID, reportIDToThreadsReportIDsMap, optimisticData, failureData); // We reopen and reassign the report if the report is open/submitted and the manager is not a member of the new policy. This is to prevent the old manager from seeing a report that they can't action on. + let newStatusNum = report?.statusNum; const isOpenOrSubmitted = isOpenExpenseReport(report) || isProcessingReport(report); const managerLogin = PersonalDetailsUtils.getLoginByAccountID(report.managerID ?? CONST.DEFAULT_NUMBER_ID); if (isOpenOrSubmitted && managerLogin && !isPolicyMember(managerLogin, policyID)) { - optimisticData.push( - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.OPEN, - statusNum: CONST.REPORT.STATUS_NUM.OPEN, - managerID: getNextApproverAccountID(report, true), - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${reportID}`, - value: buildNextStep(report, CONST.REPORT.STATUS_NUM.OPEN), + newStatusNum = CONST.REPORT.STATUS_NUM.OPEN; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + managerID: getNextApproverAccountID(report, true), }, - ); + }); - failureData.push( - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: report.stateNum, - statusNum: report.statusNum, - managerID: report.managerID, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${reportID}`, - value: reportNextStep, + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum: report.stateNum, + statusNum: report.statusNum, + managerID: report.managerID, }, - ); + }); + } + + if (newStatusNum) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${reportID}`, + value: buildNextStep({...report, policyID}, newStatusNum), + }); + + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${reportID}`, + value: reportNextStep, + }); } // 2. If this is a thread, we have to mark the parent report preview action as deleted to properly update the UI From 3ebe39fcf91d776d62401e1b1323f5359dddcff7 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 10 Jul 2025 16:56:53 +0700 Subject: [PATCH 2/4] add test --- src/libs/actions/Report.ts | 1 + tests/actions/ReportTest.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 40a864eee1a6..aa9c643df2f6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5798,6 +5798,7 @@ export { addPolicyReport, broadcastUserIsLeavingRoom, broadcastUserIsTyping, + buildOptimisticChangePolicyData, clearAddRoomMemberError, clearAvatarErrors, clearDeleteTransactionNavigateBackUrl, diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 8a9c196a7ed2..ab8ad7c50508 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -1754,4 +1754,23 @@ describe('actions/Report', () => { expect(Object.keys(selfDMReportActions ?? {}).length).toBe(3); }); }); + + describe('buildOptimisticChangePolicyData', () => { + it('should build the optimistic data next step for the change policy data', async () => { + const report: OnyxTypes.Report = { + ...createRandomReport(1), + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + type: CONST.REPORT.TYPE.EXPENSE, + }; + const policyID = '1'; + const nextStepResult = { + type: 'neutral', + icon: CONST.NEXT_STEP.ICONS.HOURGLASS, + message: [{text: 'Waiting for '}, {text: '', type: 'strong', clickToCopyText: undefined}, {text: ' to '}, {text: 'approve'}, {text: ' %expenses.'}], + }; + const data = Report.buildOptimisticChangePolicyData(report, policyID); + const nextStepData = data?.optimisticData.find((item) => item.key === `${ONYXKEYS.COLLECTION.NEXT_STEP}${report.reportID}`); + expect(nextStepData?.value).toEqual(nextStepResult); + }); + }); }); From be4af290f0f23313efec1f18b24d6544e6eeb155 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 10 Jul 2025 17:17:27 +0700 Subject: [PATCH 3/4] fix ts --- tests/actions/ReportTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index ab8ad7c50508..3245d81a85a5 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -1756,7 +1756,7 @@ describe('actions/Report', () => { }); describe('buildOptimisticChangePolicyData', () => { - it('should build the optimistic data next step for the change policy data', async () => { + it('should build the optimistic data next step for the change policy data', () => { const report: OnyxTypes.Report = { ...createRandomReport(1), statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, From cb68573ee04caba9667502efbf7ba94a8c4d26a9 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 15 Jul 2025 03:34:23 +0700 Subject: [PATCH 4/4] add tests --- tests/actions/ReportTest.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 536ddbcdbeeb..b0c6628d0131 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -8,6 +8,7 @@ import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import {getOnboardingMessages} from '@libs/actions/Welcome/OnboardingFlow'; import {WRITE_COMMANDS} from '@libs/API/types'; import HttpUtils from '@libs/HttpUtils'; +import {buildNextStep} from '@libs/NextStepUtils'; import {getOriginalMessage} from '@libs/ReportActionsUtils'; import CONST from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; @@ -30,6 +31,10 @@ import type {MockFetch} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForNetworkPromises from '../utils/waitForNetworkPromises'; +jest.mock('@libs/NextStepUtils', () => ({ + buildNextStep: jest.fn(), +})); + jest.mock('@libs/ReportUtils', () => { const originalModule = jest.requireActual('@libs/ReportUtils'); return { @@ -1755,6 +1760,19 @@ describe('actions/Report', () => { }); }); + describe('buildOptimisticChangePolicyData', () => { + it('should build the optimistic data next step for the change policy data', () => { + const report: OnyxTypes.Report = { + ...createRandomReport(1), + statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, + type: CONST.REPORT.TYPE.EXPENSE, + }; + const policyID = '1'; + Report.buildOptimisticChangePolicyData(report, policyID); + expect(buildNextStep).toHaveBeenCalledWith(report, CONST.REPORT.STATUS_NUM.SUBMITTED); + }); + }); + describe('searchInServer', () => { it('should return the same result with or without uppercase input.', () => { Report.searchInServer('test');