diff --git a/src/libs/API/parameters/ApproveMoneyRequestParams.ts b/src/libs/API/parameters/ApproveMoneyRequestParams.ts index 59fa0ed0d6440..620c0fbdf8b3a 100644 --- a/src/libs/API/parameters/ApproveMoneyRequestParams.ts +++ b/src/libs/API/parameters/ApproveMoneyRequestParams.ts @@ -12,6 +12,14 @@ type ApproveMoneyRequestParams = { * }> */ optimisticHoldReportExpenseActionIDs?: string; + /** + * Stringified JSON object of type Record with the following structure: + * { + * [oldReportActionID]: optimisticReportActionID, + * } + * where optimisticReportActionID is the optimistic report action ID and oldReportActionID is the old report action ID + */ + optimisticReportActionCopyIDs?: string; /** The optimistic action ID for the report created for unapproved transactions */ optimisticCreatedReportForUnapprovedTransactionsActionID?: string; diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index c07865a07db4b..55db752783173 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -9299,6 +9299,95 @@ function getHoldReportActionsAndTransactions(reportID: string | undefined) { return {holdReportActions, holdTransactions}; } +type OptimisticReportActionCopyIDs = Record; + +/** + * Gets duplicate workflow actions for a partial expense report. + * Used when splitting held expenses into a new partial report to maintain action history. + * + * @param sourceReportID - The ID of the original report to copy actions from + * @param targetReportID - The ID of the new partial expense report to copy actions to + * @returns A tuple of [optimisticData, successData, failureData, duplicatedReportActionIDs] + */ +function getDuplicateActionsForPartialReport( + sourceReportID: string | undefined, + targetReportID: string | undefined, +): [ + Array>, + Array>, + Array>, + OptimisticReportActionCopyIDs, +] { + const optimisticData: Array> = []; + const successData: Array> = []; + const failureData: Array> = []; + const optimisticReportActionCopyIDs: OptimisticReportActionCopyIDs = {}; + + if (!sourceReportID || !targetReportID) { + return [optimisticData, successData, failureData, optimisticReportActionCopyIDs]; + } + + const sourceReportActions = getAllReportActions(sourceReportID); + + // Match the backend's WORKFLOW_ACTIONS list + const workflowActionTypes = [ + CONST.REPORT.ACTIONS.TYPE.SUBMITTED, + CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED, + CONST.REPORT.ACTIONS.TYPE.APPROVED, + CONST.REPORT.ACTIONS.TYPE.UNAPPROVED, + CONST.REPORT.ACTIONS.TYPE.REJECTED, + CONST.REPORT.ACTIONS.TYPE.RETRACTED, + CONST.REPORT.ACTIONS.TYPE.CLOSED, + CONST.REPORT.ACTIONS.TYPE.REOPENED, + CONST.REPORT.ACTIONS.TYPE.FORWARDED, + CONST.REPORT.ACTIONS.TYPE.TAKE_CONTROL, + CONST.REPORT.ACTIONS.TYPE.REROUTE, + ] as const; + + const copiedActions: Record = {}; + const copiedActionsSuccess: OnyxCollection> = {}; + const copiedActionsFailure: Record = {}; + + for (const action of Object.values(sourceReportActions)) { + if (action && (workflowActionTypes as readonly string[]).includes(action.actionName)) { + const newActionID = NumberUtils.rand64(); + copiedActions[newActionID] = { + ...action, + reportActionID: newActionID, + reportID: targetReportID, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }; + copiedActionsSuccess[newActionID] = { + pendingAction: null, + }; + copiedActionsFailure[newActionID] = null; + optimisticReportActionCopyIDs[action.reportActionID] = newActionID; + } + } + + if (Object.keys(copiedActions).length > 0) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetReportID}`, + value: copiedActions, + }); + + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetReportID}`, + value: copiedActionsSuccess, + }); + + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${targetReportID}`, + value: copiedActionsFailure, + }); + } + + return [optimisticData, successData, failureData, optimisticReportActionCopyIDs]; +} + function getReportFromHoldRequestsOnyxData({ chatReport, iouReport, @@ -9318,6 +9407,7 @@ function getReportFromHoldRequestsOnyxData({ optimisticHoldActionID: string; optimisticCreatedReportForUnapprovedTransactionsActionID: string | undefined; optimisticHoldReportExpenseActionIDs: OptimisticHoldReportExpenseActionID[]; + optimisticReportActionCopyIDs: OptimisticReportActionCopyIDs; optimisticData: OnyxUpdate[]; successData: OnyxUpdate[]; failureData: OnyxUpdate[]; @@ -9562,6 +9652,17 @@ function getReportFromHoldRequestsOnyxData({ }, ]; + // Copy submission/approval actions to the new report + const [copiedActionsOptimistic, copiedActionsSuccess, copiedActionsFailure, optimisticReportActionCopyIDs] = getDuplicateActionsForPartialReport( + iouReport?.reportID, + optimisticExpenseReport.reportID, + ); + // Only copy the report action for approval flow + if (isApprovalFlow && !isEmptyObject(optimisticReportActionCopyIDs)) { + optimisticData.push(...copiedActionsOptimistic); + successData.push(...copiedActionsSuccess); + failureData.push(...copiedActionsFailure); + } // add optimistic system message explaining the created report for unapproved transactions if (isApprovalFlow && optimisticCreatedReportForUnapprovedAction) { optimisticData.push({ @@ -9600,6 +9701,7 @@ function getReportFromHoldRequestsOnyxData({ successData, optimisticHoldReportID: optimisticExpenseReport.reportID, optimisticHoldReportExpenseActionIDs, + optimisticReportActionCopyIDs, }; } @@ -10305,6 +10407,7 @@ function approveMoneyRequest( let optimisticHoldReportID; let optimisticHoldActionID; let optimisticHoldReportExpenseActionIDs; + let optimisticReportActionCopyIDs; let optimisticCreatedReportForUnapprovedTransactionsActionID; if (!full && !!chatReport && !!expenseReport) { const originalCreated = getReportOriginalCreationTimestamp(expenseReport); @@ -10324,6 +10427,7 @@ function approveMoneyRequest( optimisticHoldActionID = holdReportOnyxData.optimisticHoldActionID; optimisticCreatedReportForUnapprovedTransactionsActionID = holdReportOnyxData.optimisticCreatedReportForUnapprovedTransactionsActionID; optimisticHoldReportExpenseActionIDs = JSON.stringify(holdReportOnyxData.optimisticHoldReportExpenseActionIDs); + optimisticReportActionCopyIDs = JSON.stringify(holdReportOnyxData.optimisticReportActionCopyIDs); } // Remove duplicates violations if we approve the report @@ -10366,6 +10470,7 @@ function approveMoneyRequest( optimisticHoldReportID, optimisticHoldActionID, optimisticHoldReportExpenseActionIDs, + optimisticReportActionCopyIDs, optimisticCreatedReportForUnapprovedTransactionsActionID, };