diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 8b0afe2d24c3..0e76596ba8fa 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -81,7 +81,7 @@ function buildNextStep( const {policyID = '', ownerAccountID = -1, managerID = -1} = report; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy); - const {submitsTo, harvesting, isPreventSelfApprovalEnabled, preventSelfApproval, autoReportingFrequency, autoReportingOffset} = policy; + const {submitsTo, harvesting, preventSelfApproval, autoReportingFrequency, autoReportingOffset} = policy; const isOwner = currentUserAccountID === ownerAccountID; const isManager = currentUserAccountID === managerID; const isSelfApproval = currentUserAccountID === submitsTo; @@ -172,7 +172,7 @@ function buildNextStep( } // Prevented self submitting - if ((isPreventSelfApprovalEnabled ?? preventSelfApproval) && isSelfApproval) { + if (preventSelfApproval && isSelfApproval) { optimisticNextStep.message = [ { text: "Oops! Looks like you're submitting to ", @@ -255,6 +255,20 @@ function buildNextStep( break; } + // Generates an optimistic nextStep once a report has been closed for example in the case of Submit and Close approval flow + case CONST.REPORT.STATUS_NUM.CLOSED: + optimisticNextStep = { + type, + title: 'Finished!', + message: [ + { + text: 'No further action required!', + }, + ], + }; + + break; + // Generates an optimistic nextStep once a report has been approved case CONST.REPORT.STATUS_NUM.APPROVED: // Self review diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ab49305b5f0b..a9e2d6adec1b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4743,8 +4743,8 @@ function submitReport(expenseReport: OnyxTypes.Report) { const parentReport = ReportUtils.getReport(expenseReport.parentReportID); const policy = getPolicy(expenseReport.policyID); const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID; - const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED); const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy); + const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, isSubmitAndClosePolicy ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.SUBMITTED); const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy ? [ @@ -4769,11 +4769,6 @@ function submitReport(expenseReport: OnyxTypes.Report) { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, - value: optimisticNextStep, - }, ] : [ { @@ -4787,6 +4782,12 @@ function submitReport(expenseReport: OnyxTypes.Report) { }, ]; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, + value: optimisticNextStep, + }); + if (parentReport?.reportID) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -4822,24 +4823,22 @@ function submitReport(expenseReport: OnyxTypes.Report) { stateNum: CONST.REPORT.STATE_NUM.OPEN, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, + value: currentNextStep, + }, ]; if (!isSubmitAndClosePolicy) { - failureData.push( - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, - value: { - [optimisticSubmittedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), - }, + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + value: { + [optimisticSubmittedReportAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, - value: currentNextStep, - }, - ); + }); } if (parentReport?.reportID) { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 977a5c4087d0..3a305b55c5dd 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1946,6 +1946,11 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, customUnits, makeMeAdmin, + autoReporting: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, }, }, { @@ -2004,6 +2009,11 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName isPolicyExpenseChatEnabled: true, outputCurrency, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + autoReporting: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, customUnits, areCategoriesEnabled: true, areTagsEnabled: false, @@ -2492,6 +2502,11 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string // Setting the currency to USD as we can only add the VBBA for this policy currency right now outputCurrency: CONST.CURRENCY.USD, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + autoReporting: true, + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + harvesting: { + enabled: true, + }, customUnits, areCategoriesEnabled: true, areTagsEnabled: false, diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index a06dd28f85e3..d4f726dd0d4f 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -317,9 +317,6 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback< enabled: boolean; }; - /** @deprecated Whether the scheduled submit is enabled */ - isPreventSelfApprovalEnabled?: boolean; - /** Whether the self approval or submitting is enabled */ preventSelfApproval?: boolean; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index caae8a055aa9..251f70226000 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -3048,7 +3048,11 @@ describe('actions/IOU', () => { let chatReport: OnyxEntry; return waitForBatchedUpdates() .then(() => { - PolicyActions.createWorkspace(CARLOS_EMAIL, true, "Carlos's Workspace"); + const policyID = PolicyActions.generatePolicyID(); + PolicyActions.createWorkspace(CARLOS_EMAIL, true, "Carlos's Workspace", policyID); + + // Change the approval mode for the policy since default is Submit and Close + PolicyActions.setWorkspaceApprovalMode(policyID, CARLOS_EMAIL, CONST.POLICY.APPROVAL_MODE.BASIC); return waitForBatchedUpdates(); }) .then( @@ -3144,6 +3148,110 @@ describe('actions/IOU', () => { }), ); }); + it('correctly submits a report with Submit and Close approval mode', () => { + const amount = 10000; + const comment = '💸💸💸💸'; + const merchant = 'NASDAQ'; + let expenseReport: OnyxEntry; + let chatReport: OnyxEntry; + return waitForBatchedUpdates() + .then(() => { + PolicyActions.createWorkspace(CARLOS_EMAIL, true, "Carlos's Workspace"); + return waitForBatchedUpdates(); + }) + .then( + () => + new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (allReports) => { + Onyx.disconnect(connectionID); + chatReport = Object.values(allReports ?? {}).find((report) => report?.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT) ?? null; + + resolve(); + }, + }); + }), + ) + .then(() => { + if (chatReport) { + IOU.requestMoney( + chatReport, + amount, + CONST.CURRENCY.USD, + '', + merchant, + RORY_EMAIL, + RORY_ACCOUNT_ID, + {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID, isPolicyExpenseChat: true, reportID: chatReport.reportID}, + comment, + {}, + ); + } + return waitForBatchedUpdates(); + }) + .then( + () => + new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (allReports) => { + Onyx.disconnect(connectionID); + expenseReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.EXPENSE) ?? null; + Onyx.merge(`report_${expenseReport?.reportID}`, { + statusNum: 0, + stateNum: 0, + }); + resolve(); + }, + }); + }), + ) + .then( + () => + new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (allReports) => { + Onyx.disconnect(connectionID); + expenseReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.EXPENSE) ?? null; + + // Verify report is a draft + expect(expenseReport?.stateNum).toBe(0); + expect(expenseReport?.statusNum).toBe(0); + resolve(); + }, + }); + }), + ) + .then(() => { + if (expenseReport) { + IOU.submitReport(expenseReport); + } + return waitForBatchedUpdates(); + }) + .then( + () => + new Promise((resolve) => { + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (allReports) => { + Onyx.disconnect(connectionID); + expenseReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.EXPENSE) ?? null; + + // Report is closed since the default policy settings is Submit and Close + expect(expenseReport?.stateNum).toBe(2); + expect(expenseReport?.statusNum).toBe(2); + resolve(); + }, + }); + }), + ); + }); it('correctly implements error handling', () => { const amount = 10000; const comment = '💸💸💸💸'; diff --git a/tests/unit/NextStepUtilsTest.ts b/tests/unit/NextStepUtilsTest.ts index 200a8f52349e..072a06748da9 100644 --- a/tests/unit/NextStepUtilsTest.ts +++ b/tests/unit/NextStepUtilsTest.ts @@ -442,6 +442,24 @@ describe('libs/NextStepUtils', () => { expect(result).toMatchObject(optimisticNextStep); }); + + test('submit and close approval mode', () => { + report.ownerAccountID = strangeAccountID; + optimisticNextStep.title = 'Finished!'; + optimisticNextStep.message = [ + { + text: 'No further action required!', + }, + ]; + + return Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL, + }).then(() => { + const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.CLOSED); + + expect(result).toMatchObject(optimisticNextStep); + }); + }); }); describe('it generates an optimistic nextStep once a report has been approved', () => { @@ -553,13 +571,5 @@ describe('libs/NextStepUtils', () => { expect(result).toMatchObject(optimisticNextStep); }); }); - - describe('it generates a nullable optimistic nextStep', () => { - test('closed status', () => { - const result = NextStepUtils.buildNextStep(report, CONST.REPORT.STATUS_NUM.CLOSED); - - expect(result).toBeNull(); - }); - }); }); });