From 00ca266e2cfe43c9bd2b4391aa4941801940f109 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Sun, 21 Sep 2025 18:55:02 +0530 Subject: [PATCH 01/12] feat: added optimistic report --- src/libs/actions/IOU.ts | 107 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8e6fe31c6a721..6bc1e2b588772 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -113,7 +113,7 @@ import { isMoneyRequestAction, isReportPreviewAction, } from '@libs/ReportActionsUtils'; -import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptionData, TransactionDetails} from '@libs/ReportUtils'; +import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticNewReport, OptionData, TransactionDetails} from '@libs/ReportUtils'; import { buildOptimisticActionableTrackExpenseWhisper, buildOptimisticAddCommentReportAction, @@ -133,6 +133,7 @@ import { buildOptimisticMarkedAsResolvedReportAction, buildOptimisticModifiedExpenseReportAction, buildOptimisticMoneyRequestEntities, + buildOptimisticMovedReportAction, buildOptimisticMovedTransactionAction, buildOptimisticRejectReportAction, buildOptimisticRejectReportActionComment, @@ -194,6 +195,7 @@ import { isSettled, isTestTransactionReport, isTrackExpenseReport, + populateOptimisticReportFormula, prepareOnboardingOnyxData, shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, updateReportPreview, @@ -12255,6 +12257,109 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st }); } else { rejectedToReportID = generateReportID(); + + // Create optimistic report for the rejected transaction + const optimisticReport: OptimisticNewReport = { + reportID: rejectedToReportID, + policyID: report?.policyID, + type: CONST.REPORT.TYPE.EXPENSE, + ownerAccountID: report?.ownerAccountID ?? 0, + reportName: '', + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + currency: report?.currency ?? CONST.CURRENCY.USD, + total: 0, // We're updating the report total as a common logic below + nonReimbursableTotal: 0, + parentReportID: report?.chatReportID, + lastVisibleActionCreated: DateUtils.getDBTime(), + parentReportActionID: undefined, + participants: report?.participants ?? {}, + managerID: report?.managerID, + pendingFields: {createReport: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + chatReportID: report?.chatReportID, + }; + + // Generate report name using the pattern "Expense Report #reportID" + const reportNamePattern = '{report:type} #{report:id}'; + optimisticReport.reportName = populateOptimisticReportFormula(reportNamePattern, optimisticReport, policy); + + // Create report preview action for the parent report (chat report) + const chatReport = report?.chatReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`] : null; + const reportPreviewAction = buildOptimisticReportPreview( + chatReport, + optimisticReport as OnyxTypes.Report, + '', // No comment for rejected expenses + transaction, // Pass the transaction for receipt info + ); + + // Update optimistic report with parentReportActionID + optimisticReport.parentReportActionID = reportPreviewAction.reportActionID; + + // Update the report total with the transaction amount (expense reports store negative values) + optimisticReport.total = -transactionAmount; + + // Add optimistic data for the new report + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, + value: optimisticReport as OnyxTypes.Report, + }); + + // Add optimistic metadata for the new report + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${rejectedToReportID}`, + value: { + isOptimisticReport: true, + }, + }); + + // Add report actions to the new optimistic report + const newReportActions: Record = {}; + + // Add the "rejected this expense" action + newReportActions[optimisticRejectReportAction.reportActionID] = optimisticRejectReportAction as OnyxTypes.ReportAction; + + // Add the rejection reason comment + newReportActions[optimisticRejectReportActionComment.reportActionID] = optimisticRejectReportActionComment as OnyxTypes.ReportAction; + + // Add a "moved this expense" action to show where it came from + const movedExpenseAction = buildOptimisticMovedReportAction( + report?.policyID ?? '', + report?.policyID ?? '', + report?.chatReportID ?? '', + reportID, + report?.reportName ?? '', + false, // isFromIOU + ) as OnyxTypes.ReportAction; + newReportActions[movedExpenseAction.reportActionID] = movedExpenseAction; + + // Add all report actions to the new report + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, + value: newReportActions, + }); + + // Add report preview action to parent report (chat report) + if (report?.chatReportID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.chatReportID}`, + value: { + [reportPreviewAction.reportActionID]: reportPreviewAction, + }, + }); + + // Update parent report's lastVisibleActionCreated + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`, + value: { + lastVisibleActionCreated: reportPreviewAction.created, + }, + }); + } } optimisticData.push( { From 6348dfcd93aea9ccd1de90fde6bf99e2ac0a157f Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Mon, 22 Sep 2025 01:07:49 +0530 Subject: [PATCH 02/12] refactor: remove type cast --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 6bc1e2b588772..004a61b6c3e00 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12331,7 +12331,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st reportID, report?.reportName ?? '', false, // isFromIOU - ) as OnyxTypes.ReportAction; + ); newReportActions[movedExpenseAction.reportActionID] = movedExpenseAction; // Add all report actions to the new report From 9d210880283eab2e6f9392a63ccee7cc5a89f65e Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Thu, 25 Sep 2025 02:26:35 +1000 Subject: [PATCH 03/12] fix: offline transaction mapping --- src/libs/actions/IOU.ts | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 8e1926a84ba5b..68eaf4072e913 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12065,6 +12065,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const reportAction = getIOUActionForReportID(reportID, transactionID); const childReportID = reportAction?.childReportID; + const reportOwnerLogin = allPersonalDetails[report?.ownerAccountID ?? 0]?.login ?? ''; let movedToReport; let rejectedToReportID; @@ -12290,6 +12291,8 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st optimisticReport as OnyxTypes.Report, '', // No comment for rejected expenses transaction, // Pass the transaction for receipt info + rejectedToReportID, // childReportID + undefined // reportPreviewReportActionID - will be added when backend supports it ); // Update optimistic report with parentReportActionID @@ -12302,7 +12305,12 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, - value: optimisticReport as OnyxTypes.Report, + value: { + ...optimisticReport, + parentReportID: report?.chatReportID, + parentReportActionID: reportPreviewAction.reportActionID, + chatReportID: report?.chatReportID, + } as OnyxTypes.Report, }); // Add optimistic metadata for the new report @@ -12317,6 +12325,25 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st // Add report actions to the new optimistic report const newReportActions: Record = {}; + // Add CREATED action for the new report (matches online response) + const createdAction = buildOptimisticCreatedReportAction( + reportOwnerLogin, + DateUtils.getDBTime() + ); + newReportActions[createdAction.reportActionID] = createdAction; + + // Add IOU action for the moved transaction (matches online response) + const iouAction = buildOptimisticIOUReportAction({ + type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, + amount: -transactionAmount, + currency: report?.currency ?? CONST.CURRENCY.USD, + comment: typeof transaction?.comment === 'string' ? transaction.comment : '', + participants: [{accountID: report?.ownerAccountID ?? 0}, {accountID: 0}], + transactionID, + iouReportID: rejectedToReportID, + }); + newReportActions[iouAction.reportActionID] = iouAction; + // Add the "rejected this expense" action newReportActions[optimisticRejectReportAction.reportActionID] = optimisticRejectReportAction as OnyxTypes.ReportAction; @@ -12341,6 +12368,33 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st value: newReportActions, }); + + // Add personal details for participants (matches online response) + const optimisticPersonalDetails: Record = {}; + Object.keys(optimisticReport.participants ?? {}).forEach(accountID => { + const personalDetail = allPersonalDetails[accountID]; + if (personalDetail) { + optimisticPersonalDetails[accountID] = personalDetail; + } + }); + + if (Object.keys(optimisticPersonalDetails).length > 0) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: optimisticPersonalDetails, + }); + } + + // Add report name value pairs for proper linking (matches online response) + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${rejectedToReportID}`, + value: { + parentReportID: report?.chatReportID, + }, + }); + // Add report preview action to parent report (chat report) if (report?.chatReportID) { optimisticData.push({ From e7565e5d6fec4eb828eca6a91151b8ca54877489 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Wed, 1 Oct 2025 00:39:36 +1000 Subject: [PATCH 04/12] fix:add reportPreviewReportActionID --- src/libs/API/parameters/RejectMoneyRequestParams.ts | 1 + src/libs/actions/IOU.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/API/parameters/RejectMoneyRequestParams.ts b/src/libs/API/parameters/RejectMoneyRequestParams.ts index a49c17bac363e..815c3ef38efe6 100644 --- a/src/libs/API/parameters/RejectMoneyRequestParams.ts +++ b/src/libs/API/parameters/RejectMoneyRequestParams.ts @@ -3,6 +3,7 @@ type RejectMoneyRequestParams = { reportID: string; comment: string; rejectedToReportID?: string; + reportPreviewReportActionID?: string; rejectedActionReportActionID: string; rejectedCommentReportActionID: string; }; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 87bfa210f7c6e..f720401c4910f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12104,6 +12104,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st let movedToReport; let rejectedToReportID; let urlToNavigateBack; + let reportPreviewAction: OnyxTypes.ReportAction | undefined; const hasMultipleExpenses = getReportTransactions(reportID).length > 1; @@ -12320,7 +12321,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st // Create report preview action for the parent report (chat report) const chatReport = report?.chatReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`] : null; - const reportPreviewAction = buildOptimisticReportPreview( + reportPreviewAction = buildOptimisticReportPreview( chatReport, optimisticReport as OnyxTypes.Report, '', // No comment for rejected expenses @@ -12670,6 +12671,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st reportID, comment, rejectedToReportID, + reportPreviewReportActionID: reportPreviewAction?.reportActionID, rejectedActionReportActionID: optimisticRejectReportAction.reportActionID, rejectedCommentReportActionID: optimisticRejectReportActionComment.reportActionID, }; From a83dc24e56ae4c540e6766be87b2a8ca9b1f7f55 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 15 Oct 2025 03:00:58 +0700 Subject: [PATCH 05/12] fix: update missing optimistic data --- src/libs/actions/IOU.ts | 211 ++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 139 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d096f6d7a4bbf..93faa1119b830 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12548,6 +12548,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; const transactionAmount = getAmount(transaction); const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + const policyExpenseChat = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; const isPolicyDelayedSubmissionEnabled = policy ? isDelayedSubmissionEnabled(policy) : false; const isIOU = isIOUReport(report); @@ -12562,6 +12563,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const reportAction = getIOUActionForReportID(reportID, transactionID); const childReportID = reportAction?.childReportID; + const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${childReportID}`]; const reportOwnerLogin = allPersonalDetails[report?.ownerAccountID ?? 0]?.login ?? ''; let movedToReport; @@ -12579,6 +12581,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const baseTimestamp = DateUtils.getDBTime(); const optimisticRejectReportAction = buildOptimisticRejectReportAction(baseTimestamp); const optimisticRejectReportActionComment = buildOptimisticRejectReportActionComment(comment, DateUtils.addMillisecondsFromDateTime(baseTimestamp, 1)); + let movedTransactionAction; // Build successData and failureData to prevent duplication const successData: OnyxUpdate[] = []; @@ -12756,162 +12759,91 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st }); } else { rejectedToReportID = generateReportID(); - - // Create optimistic report for the rejected transaction - const optimisticReport: OptimisticNewReport = { - reportID: rejectedToReportID, - policyID: report?.policyID, - type: CONST.REPORT.TYPE.EXPENSE, - ownerAccountID: report?.ownerAccountID ?? 0, - reportName: '', - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, - statusNum: CONST.REPORT.STATUS_NUM.OPEN, - currency: report?.currency ?? CONST.CURRENCY.USD, - total: 0, // We're updating the report total as a common logic below - nonReimbursableTotal: 0, - parentReportID: report?.chatReportID, - lastVisibleActionCreated: DateUtils.getDBTime(), - parentReportActionID: undefined, - participants: report?.participants ?? {}, - managerID: report?.managerID, - pendingFields: {createReport: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, - chatReportID: report?.chatReportID, - }; - - // Generate report name using the pattern "Expense Report #reportID" - const reportNamePattern = '{report:type} #{report:id}'; - optimisticReport.reportName = populateOptimisticReportFormula(reportNamePattern, optimisticReport, policy); - - // Create report preview action for the parent report (chat report) - const chatReport = report?.chatReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`] : null; - reportPreviewAction = buildOptimisticReportPreview( - chatReport, - optimisticReport as OnyxTypes.Report, - '', // No comment for rejected expenses - transaction, // Pass the transaction for receipt info - rejectedToReportID, // childReportID - undefined // reportPreviewReportActionID - will be added when backend supports it - ); - - // Update optimistic report with parentReportActionID - optimisticReport.parentReportActionID = reportPreviewAction.reportActionID; - - // Update the report total with the transaction amount (expense reports store negative values) - optimisticReport.total = -transactionAmount; - - // Add optimistic data for the new report - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, - value: { - ...optimisticReport, - parentReportID: report?.chatReportID, - parentReportActionID: reportPreviewAction.reportActionID, - chatReportID: report?.chatReportID, - } as OnyxTypes.Report, - }); - // Add optimistic metadata for the new report - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${rejectedToReportID}`, - value: { - isOptimisticReport: true, - }, - }); - - // Add report actions to the new optimistic report - const newReportActions: Record = {}; - - // Add CREATED action for the new report (matches online response) - const createdAction = buildOptimisticCreatedReportAction( - reportOwnerLogin, - DateUtils.getDBTime() + // Create optimistic report for the rejected transaction + const newExpenseReport = buildOptimisticExpenseReport( + report.chatReportID, + report?.policyID, + userAccountID, + transactionAmount, + getCurrency(transaction), + transactionAmount, + undefined, + rejectedToReportID, ); - newReportActions[createdAction.reportActionID] = createdAction; - - // Add IOU action for the moved transaction (matches online response) - const iouAction = buildOptimisticIOUReportAction({ + const [, createdActionForExpenseReport, iouAction, ,] = buildOptimisticMoneyRequestEntities({ + iouReport: newExpenseReport, type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, - amount: -transactionAmount, - currency: report?.currency ?? CONST.CURRENCY.USD, - comment: typeof transaction?.comment === 'string' ? transaction.comment : '', - participants: [{accountID: report?.ownerAccountID ?? 0}, {accountID: 0}], - transactionID, - iouReportID: rejectedToReportID, - }); - newReportActions[iouAction.reportActionID] = iouAction; - - // Add the "rejected this expense" action - newReportActions[optimisticRejectReportAction.reportActionID] = optimisticRejectReportAction as OnyxTypes.ReportAction; - - // Add the rejection reason comment - newReportActions[optimisticRejectReportActionComment.reportActionID] = optimisticRejectReportActionComment as OnyxTypes.ReportAction; - - // Add a "moved this expense" action to show where it came from - const movedExpenseAction = buildOptimisticMovedReportAction( - report?.policyID ?? '', - report?.policyID ?? '', - report?.chatReportID ?? '', - reportID, - report?.reportName ?? '', - false, // isFromIOU - ); - newReportActions[movedExpenseAction.reportActionID] = movedExpenseAction; - - // Add all report actions to the new report - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, - value: newReportActions, - }); - - - // Add personal details for participants (matches online response) - const optimisticPersonalDetails: Record = {}; - Object.keys(optimisticReport.participants ?? {}).forEach(accountID => { - const personalDetail = allPersonalDetails[accountID]; - if (personalDetail) { - optimisticPersonalDetails[accountID] = personalDetail; - } + amount: transactionAmount, + currency: getCurrency(transaction), + comment, + payeeEmail: currentUserEmail, + participants: [{accountID: report?.ownerAccountID ?? 0}], + transactionID: transaction.transactionID, + existingTransactionThreadReportID: childReportID, + shouldGenerateTransactionThreadReport: false, }); - if (Object.keys(optimisticPersonalDetails).length > 0) { - optimisticData.push({ + reportPreviewAction = buildOptimisticReportPreview(policyExpenseChat, newExpenseReport, undefined, transaction, undefined); + movedTransactionAction = buildOptimisticMovedTransactionAction(childReportID, newExpenseReport.reportID); + newExpenseReport.parentReportActionID = reportPreviewAction.reportActionID; + optimisticData.push( + { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: optimisticPersonalDetails, - }); - } - - // Add report name value pairs for proper linking (matches online response) - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${rejectedToReportID}`, - value: { - parentReportID: report?.chatReportID, + key: `${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat?.reportID}`, + value: { + lastVisibleActionCreated: reportPreviewAction.created, + }, }, - }); - - // Add report preview action to parent report (chat report) - if (report?.chatReportID) { - optimisticData.push({ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat?.reportID}`, + value: { + lastVisibleActionCreated: reportPreviewAction.created, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, + value: { + ...newExpenseReport, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${rejectedToReportID}`, + value: { + isOptimisticReport: true, + hasOnceLoadedReportActions: true, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, + value: {[createdActionForExpenseReport.reportActionID]: createdActionForExpenseReport, [iouAction.reportActionID]: iouAction}, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${rejectedToReportID}`, + value: { + parentReportID: report?.chatReportID, + }, + }, + { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.chatReportID}`, value: { [reportPreviewAction.reportActionID]: reportPreviewAction, }, - }); - - // Update parent report's lastVisibleActionCreated - optimisticData.push({ + }, + { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`, value: { lastVisibleActionCreated: reportPreviewAction.created, }, - }); - } + }, + ); } optimisticData.push( { @@ -13016,6 +12948,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st value: { [optimisticRejectReportAction.reportActionID]: optimisticRejectReportAction, [optimisticRejectReportActionComment.reportActionID]: optimisticRejectReportActionComment, + ...(movedTransactionAction ? {[movedTransactionAction.reportActionID]: movedTransactionAction} : {}), }, }); From c1c4b62559a7428a2929d47e4f36f3b1910f753c Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 20 Oct 2025 16:38:53 +0700 Subject: [PATCH 06/12] fix: lint --- src/libs/actions/IOU.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index dbaf8f0b0a647..3fa597dc89c57 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12617,8 +12617,6 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const reportAction = getIOUActionForReportID(reportID, transactionID); const childReportID = reportAction?.childReportID; - const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${childReportID}`]; - const reportOwnerLogin = allPersonalDetails[report?.ownerAccountID ?? 0]?.login ?? ''; let movedToReport; let rejectedToReportID; @@ -12832,7 +12830,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st currency: getCurrency(transaction), comment, payeeEmail: currentUserEmail, - participants: [{accountID: report?.ownerAccountID ?? 0}], + participants: [{accountID: report?.ownerAccountID}], transactionID: transaction.transactionID, existingTransactionThreadReportID: childReportID, shouldGenerateTransactionThreadReport: false, From 6081827bdd8922bfd3e99799bd775499f73afa26 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 22 Oct 2025 23:12:04 +0700 Subject: [PATCH 07/12] fix: update success and failure data --- .../parameters/RejectMoneyRequestParams.ts | 3 + src/libs/actions/IOU.ts | 83 ++++++++++++++++--- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/src/libs/API/parameters/RejectMoneyRequestParams.ts b/src/libs/API/parameters/RejectMoneyRequestParams.ts index 815c3ef38efe6..193b78a479dba 100644 --- a/src/libs/API/parameters/RejectMoneyRequestParams.ts +++ b/src/libs/API/parameters/RejectMoneyRequestParams.ts @@ -6,6 +6,9 @@ type RejectMoneyRequestParams = { reportPreviewReportActionID?: string; rejectedActionReportActionID: string; rejectedCommentReportActionID: string; + createdIOUReportActionID?: string; + expenseMovedReportActionID?: string; + expenseCreatedReportActionID?: string; }; export default RejectMoneyRequestParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c4e6d86d28b2e..60c98c3c2b722 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12671,6 +12671,9 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st let rejectedToReportID; let urlToNavigateBack; let reportPreviewAction: OnyxTypes.ReportAction | undefined; + let createdIOUReportActionID; + let expenseMovedReportActionID; + let expenseCreatedReportActionID; const hasMultipleExpenses = getReportTransactions(reportID).length > 1; @@ -12887,15 +12890,11 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st reportPreviewAction = buildOptimisticReportPreview(policyExpenseChat, newExpenseReport, undefined, transaction, undefined); movedTransactionAction = buildOptimisticMovedTransactionAction(childReportID, newExpenseReport.reportID); + createdIOUReportActionID = iouAction.reportActionID; + expenseMovedReportActionID = movedTransactionAction.reportActionID; + expenseCreatedReportActionID = createdActionForExpenseReport.reportActionID; newExpenseReport.parentReportActionID = reportPreviewAction.reportActionID; optimisticData.push( - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat?.reportID}`, - value: { - lastVisibleActionCreated: reportPreviewAction.created, - }, - }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat?.reportID}`, @@ -12908,6 +12907,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, value: { ...newExpenseReport, + pendingFields: {createReport: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, }, }, { @@ -12919,7 +12919,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st }, }, { - onyxMethod: Onyx.METHOD.SET, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, value: {[createdActionForExpenseReport.reportActionID]: createdActionForExpenseReport, [iouAction.reportActionID]: iouAction}, }, @@ -12932,16 +12932,74 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.chatReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${policyExpenseChat?.reportID}`, value: { [reportPreviewAction.reportActionID]: reportPreviewAction, }, }, + ); + successData.push( + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, + value: { + pendingFields: null, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${rejectedToReportID}`, + value: { + isOptimisticReport: null, + }, + }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, + value: {[createdActionForExpenseReport.reportActionID]: {pendingAction: null}, [iouAction.reportActionID]: {pendingAction: null}}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${policyExpenseChat?.reportID}`, value: { - lastVisibleActionCreated: reportPreviewAction.created, + [reportPreviewAction.reportActionID]: {pendingAction: null}, + }, + }, + ); + + failureData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat?.reportID}`, + value: { + lastVisibleActionCreated: policyExpenseChat?.lastVisibleActionCreated, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${rejectedToReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${rejectedToReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${rejectedToReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${policyExpenseChat?.reportID}`, + value: { + [reportPreviewAction.reportActionID]: null, }, }, ); @@ -13171,6 +13229,9 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st reportPreviewReportActionID: reportPreviewAction?.reportActionID, rejectedActionReportActionID: optimisticRejectReportAction.reportActionID, rejectedCommentReportActionID: optimisticRejectReportActionComment.reportActionID, + createdIOUReportActionID, + expenseMovedReportActionID, + expenseCreatedReportActionID, }; // Make API call From 180d7ccaeffd12012b7dc2abb2820e4d41cc3c93 Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 22 Oct 2025 23:32:32 +0700 Subject: [PATCH 08/12] fix: lint --- src/libs/actions/IOU.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 60c98c3c2b722..1272496e3a198 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -115,7 +115,7 @@ import { isMoneyRequestAction, isReportPreviewAction, } from '@libs/ReportActionsUtils'; -import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticNewReport, OptionData, TransactionDetails} from '@libs/ReportUtils'; +import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptionData, TransactionDetails} from '@libs/ReportUtils'; import { buildOptimisticActionableTrackExpenseWhisper, buildOptimisticAddCommentReportAction, @@ -135,7 +135,6 @@ import { buildOptimisticMarkedAsResolvedReportAction, buildOptimisticModifiedExpenseReportAction, buildOptimisticMoneyRequestEntities, - buildOptimisticMovedReportAction, buildOptimisticMovedTransactionAction, buildOptimisticRejectReportAction, buildOptimisticRejectReportActionComment, @@ -198,7 +197,6 @@ import { isSettled, isTestTransactionReport, isTrackExpenseReport, - populateOptimisticReportFormula, prepareOnboardingOnyxData, shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, updateReportPreview, From e1021fc3a5df7c2fe909aa115cfb3758246dd98a Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 29 Oct 2025 01:00:31 +0700 Subject: [PATCH 09/12] fix Selecting hold throws an error --- src/libs/actions/IOU.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9bae78e7ed157..efa381d914173 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12756,6 +12756,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const reportAction = getIOUActionForReportID(reportID, transactionID); const childReportID = reportAction?.childReportID; + const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${childReportID}`]; let movedToReport; let rejectedToReportID; @@ -13045,6 +13046,14 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st [reportPreviewAction.reportActionID]: reportPreviewAction, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${childReportID}`, + value: { + parentReportActionID: iouAction.reportActionID, + parentReportID: rejectedToReportID, + }, + }, ); successData.push( { @@ -13110,6 +13119,14 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st [reportPreviewAction.reportActionID]: null, }, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${childReportID}`, + value: { + parentReportActionID: transactionThreadReport?.parentReportActionID, + parentReportID: transactionThreadReport?.parentReportID, + }, + }, ); } optimisticData.push( From bf9359dc849b59a8f4f30f24d89a251095f42d2a Mon Sep 17 00:00:00 2001 From: truph01 Date: Wed, 29 Oct 2025 02:03:00 +0700 Subject: [PATCH 10/12] fix: incorrect owner account ID --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index efa381d914173..cb08e3327308b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12977,7 +12977,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st const newExpenseReport = buildOptimisticExpenseReport( report.chatReportID, report?.policyID, - userAccountID, + report?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID, transactionAmount, getCurrency(transaction), transactionAmount, From 2c988392fc125fc6bfd044d08b9e4621c5f072bf Mon Sep 17 00:00:00 2001 From: truph01 Date: Thu, 30 Oct 2025 16:05:54 +0700 Subject: [PATCH 11/12] fix: add iouAction to rejected report optimistically --- src/libs/actions/IOU.ts | 88 +++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9ab88cfd8cfc7..51a96b95edb40 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -78,7 +78,7 @@ import Parser from '@libs/Parser'; import {getCustomUnitID} from '@libs/PerDiemRequestUtils'; import Performance from '@libs/Performance'; import Permissions from '@libs/Permissions'; -import {getAccountIDsByLogins} from '@libs/PersonalDetailsUtils'; +import {getAccountIDsByLogins, getLoginByAccountID} from '@libs/PersonalDetailsUtils'; import {addSMSDomainIfPhoneNumber} from '@libs/PhoneNumber'; import { getCorrectedAutoReportingFrequency, @@ -12947,31 +12947,77 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st if (existingOpenReport) { movedToReport = existingOpenReport; rejectedToReportID = existingOpenReport.reportID; - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${movedToReport?.reportID}`, - value: { - ...movedToReport, - total: (movedToReport?.total ?? 0) - transactionAmount, - }, + + const [, , iouAction, ,] = buildOptimisticMoneyRequestEntities({ + iouReport: movedToReport, + type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, + amount: transactionAmount, + currency: getCurrency(transaction), + comment, + payeeEmail: getLoginByAccountID(report.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID) ?? '', + participants: [{accountID: report?.ownerAccountID}], + transactionID: transaction.transactionID, + existingTransactionThreadReportID: childReportID, + shouldGenerateTransactionThreadReport: false, }); + optimisticData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${movedToReport?.reportID}`, + value: { + ...movedToReport, + total: (movedToReport?.total ?? 0) - transactionAmount, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, + value: {[iouAction.reportActionID]: iouAction}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${childReportID}`, + value: { + parentReportActionID: iouAction.reportActionID, + parentReportID: rejectedToReportID, + }, + }, + ); + // Add success data for existing report update - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${movedToReport?.reportID}`, - value: {pendingFields: {total: null}}, - }); + successData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${movedToReport?.reportID}`, + value: {pendingFields: {total: null}}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${rejectedToReportID}`, + value: {[iouAction.reportActionID]: {pendingAction: null}}, + }, + ); - // Add failure data to revert existing report total - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${movedToReport?.reportID}`, - value: { - total: movedToReport?.total ?? 0, - pendingFields: {total: null}, + failureData.push( + // Add failure data to revert existing report total + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${movedToReport?.reportID}`, + value: { + total: movedToReport?.total ?? 0, + pendingFields: {total: null}, + }, }, - }); + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${childReportID}`, + value: { + parentReportActionID: transactionThreadReport?.parentReportActionID, + parentReportID: transactionThreadReport?.parentReportID, + }, + }, + ); } else { // When no existing open report is found, use the sharedRejectedToReportID // so multiple sequential rejections land in the same destination report From 8a6bb8561fb6ca62e4500d9cf65ae2a6e82006ec Mon Sep 17 00:00:00 2001 From: truph01 Date: Mon, 3 Nov 2025 16:19:21 +0700 Subject: [PATCH 12/12] fix: empty report is shown after reject --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9c9c67439bdf7..d75268e21633e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -13018,6 +13018,7 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st ); } else { // Create optimistic report for the rejected transaction + rejectedToReportID = generateReportID(); const newExpenseReport = buildOptimisticExpenseReport( report.chatReportID, report?.policyID, @@ -13172,7 +13173,6 @@ function rejectMoneyRequest(transactionID: string, reportID: string, comment: st }, }, ); - rejectedToReportID = generateReportID(); } optimisticData.push( {