Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
84044b5
Refactor getCleanUpTransactionThreadReportOnyxData to accept optional…
parasharrajat Aug 7, 2026
64eedc5
Pass transactionThread to getCleanUpTransactionThreadReportOnyxData
parasharrajat Aug 7, 2026
d5e5682
Pass reports to getCleanUpTransactionThreadReportOnyxData in duplicat…
parasharrajat Aug 7, 2026
b284170
refactor pass chatReport to cleanup transaction thread data builder
parasharrajat Aug 7, 2026
5b558d5
Pass source transaction thread report via hooks to cleanup transactio…
parasharrajat Aug 7, 2026
c2d565e
Source transaction thread and duplicate merge reports via hooks inste…
parasharrajat Aug 8, 2026
030f959
Source transaction thread and duplicate merge reports via hooks, remo…
parasharrajat Aug 10, 2026
69abde3
Thread reports via hooks to getCleanUpTransactionThreadReportOnyxData…
parasharrajat Aug 10, 2026
ffe3635
Merge branch 'main' of github.com:Expensify/App into refactor-delete-…
parasharrajat Sep 1, 2026
967176f
changes
parasharrajat Sep 1, 2026
74feda4
Remove unused
parasharrajat Sep 2, 2026
2b300cb
refactor params
parasharrajat Sep 2, 2026
d7cf7ce
fixes
parasharrajat Sep 2, 2026
484f91a
More cleanup
parasharrajat Sep 2, 2026
e3f63b2
fix tests
parasharrajat Sep 2, 2026
edd864e
Merge branch 'main' of github.com:Expensify/App into refactor-delete-…
parasharrajat Sep 2, 2026
f0053f6
Merge branch 'main' of github.com:Expensify/App into refactor-delete-…
parasharrajat Sep 6, 2026
051022b
fixes
parasharrajat Sep 6, 2026
9416703
fix
parasharrajat Sep 6, 2026
19c5766
fix tests
parasharrajat Sep 6, 2026
ac766ba
fix tests
parasharrajat Sep 6, 2026
d8f7a93
fix tests
parasharrajat Sep 6, 2026
718e0f4
fix tests
parasharrajat Sep 6, 2026
a966dcf
Refactor deleteMoneyRequest function to pass param
parasharrajat Sep 6, 2026
c43a813
pass iou and chat report to mergeTransactionRequest for source action
parasharrajat Sep 6, 2026
87a6c7f
fix type
parasharrajat Sep 6, 2026
74f0559
Pass iOUReport and ChatReport to `getCleanUpTransactionThreadReportOn…
parasharrajat Sep 6, 2026
75c7f50
remove unused
parasharrajat Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/libs/actions/IOU/DeleteMoneyRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate}
import cloneDeep from 'lodash/cloneDeep';
import Onyx from 'react-native-onyx';

import {getAllReportActionsFromIOU, getAllReportNameValuePairs, getAllReports, getAllTransactions, getAllTransactionViolations} from '.';
import {getAllReportActionsFromIOU, getAllReportNameValuePairs, getAllTransactions, getAllTransactionViolations} from '.';
import {getReportPreviewReportAction, maybeUpdateReportNameForFormulaTitle} from './MoneyRequestBuilder';

type PrepareToCleanUpMoneyRequestResult = {
Expand Down Expand Up @@ -620,18 +620,31 @@ function getCleanUpTransactionThreadReportOnyxData({
updatedReportPreviewAction,
shouldAddUpdatedReportPreviewActionToOnyxData = true,
currentUserAccountID,
transactionThread: transactionThreadParam,
iouReport,
chatReport,
transactionThreadReportActionsParam,
}: {
transactionThreadID?: string;
shouldDeleteTransactionThread: boolean;
reportAction?: ReportAction;
isChatIOUReportArchived?: boolean;
updatedReportPreviewAction?: ReportAction;
shouldAddUpdatedReportPreviewActionToOnyxData?: boolean;
currentUserAccountID: number;
transactionThreadReportActionsParam: OnyxEntry<OnyxTypes.ReportActions>;
}) {
const allReports = getAllReports();
transactionThread?: OnyxEntry<OnyxTypes.Report>;
transactionThreadReportActionsParam?: OnyxEntry<OnyxTypes.ReportActions>;
} & (
| {
reportAction: ReportAction;
iouReport: OnyxEntry<OnyxTypes.Report>;
chatReport: OnyxEntry<OnyxTypes.Report>;
}
| {
reportAction?: undefined;
iouReport?: OnyxEntry<OnyxTypes.Report>;
chatReport?: OnyxEntry<OnyxTypes.Report>;
}
)) {
const allReportNameValuePairs = getAllReportNameValuePairs();

const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT | typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS>> = [];
Expand All @@ -642,7 +655,7 @@ function getCleanUpTransactionThreadReportOnyxData({
let transactionThread = null;
let transactionThreadReportActions = null;
if (transactionThreadID) {
transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`] ?? null;
transactionThread = transactionThreadParam ?? null;
transactionThreadReportActions = transactionThreadReportActionsParam ?? null;
}

Expand Down Expand Up @@ -692,8 +705,6 @@ function getCleanUpTransactionThreadReportOnyxData({

// Update the child comment visible count for reportPreviewAction.
const iouReportID = isMoneyRequestAction(reportAction) ? reportAction?.reportID : undefined;
const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`];
const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`];
const originalReportPreviewAction = getReportPreviewReportAction(chatReport?.reportID, iouReport?.reportID) ?? undefined;
let reportPreviewAction = updatedReportPreviewAction ?? originalReportPreviewAction;
if (
Expand Down Expand Up @@ -943,6 +954,9 @@ function deleteMoneyRequest({
reportAction,
isChatIOUReportArchived,
currentUserAccountID,
transactionThread: transactionThreadReport,
iouReport,
chatReport,
transactionThreadReportActionsParam: transactionThreadReportActions,
});
optimisticData.push(...cleanUpTransactionThreadReportOnyxData.optimisticData);
Expand Down
16 changes: 12 additions & 4 deletions src/libs/actions/IOU/Duplicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import type {PerDiemExpenseInformation} from './PerDiem';
import type {CreateDistanceRequestInformation} from './Split';
import type {CreateTrackExpenseParams} from './TrackExpense';

import {getAllReports, getAllTransactions, getCurrentUserAccountIDFromSession} from '.';
import {getAllTransactions, getCurrentUserAccountIDFromSession} from '.';
import {getCleanUpTransactionThreadReportOnyxData} from './DeleteMoneyRequest';
import {getMoneyRequestParticipantsFromReport} from './MoneyRequest';
import {submitPerDiemExpense} from './PerDiem';
Expand Down Expand Up @@ -179,6 +179,7 @@ type MergeDuplicatesFuncParams = MergeDuplicatesParams & {
taxValue?: string;
allTransactionViolations: OnyxCollection<OnyxTypes.TransactionViolations>;
allReportActionsList: OnyxCollection<OnyxTypes.ReportActions>;
allReportsList: OnyxCollection<OnyxTypes.Report>;
};

/** Merge several transactions into one by updating the fields of the one we want to keep and deleting the rest */
Expand All @@ -190,11 +191,11 @@ function mergeDuplicates({
taxValue,
allTransactionViolations,
allReportActionsList,
allReportsList,
...params
}: MergeDuplicatesFuncParams) {
const allParams: MergeDuplicatesParams = {...params};
const allTransactions = getAllTransactions();
const allReports = getAllReports();
const originalSelectedTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${params.transactionID}`];

const optimisticTransactionData = buildOptimisticTransactionData({
Expand Down Expand Up @@ -246,7 +247,7 @@ function mergeDuplicates({
};
});

const expenseReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${params.reportID}`];
const expenseReport = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${params.reportID}`];

// Group each discarded duplicate's IOU action and amount by its own source report so the
// soft-delete MERGE and total decrement target the correct keys when duplicates span reports.
Expand All @@ -273,7 +274,7 @@ function mergeDuplicates({
const cleanUpTransactionThreadReportsSuccessData = [];
const cleanUpTransactionThreadReportsFailureData = [];
for (const [sourceReportID, {amount, reimbursableAmount, actions}] of sources) {
const sourceReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${sourceReportID}`];
const sourceReport = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${sourceReportID}`];
const sourceReimbursableTotal = getReimbursableTotal(sourceReport);
expenseReportOptimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
Expand Down Expand Up @@ -301,13 +302,20 @@ function mergeDuplicates({
let updatedReportPreviewAction;
for (const [index, iouAction] of actions.entries()) {
const transactionThreadID = iouAction.childReportID;
const transactionThread = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`];
const iouReportID = isMoneyRequestAction(iouAction) ? iouAction?.reportID : undefined;
const iouReport = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`];
const chatReport = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`];
const cleanUp = getCleanUpTransactionThreadReportOnyxData({
transactionThreadID,
shouldDeleteTransactionThread: !!transactionThreadID,
reportAction: iouAction,
updatedReportPreviewAction,
shouldAddUpdatedReportPreviewActionToOnyxData: index === actions.length - 1,
currentUserAccountID,
transactionThread,
iouReport,
chatReport,
transactionThreadReportActionsParam: allReportActionsList?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadID}`],
});
cleanUpTransactionThreadReportsOptimisticData.push(...cleanUp.optimisticData);
Expand Down
8 changes: 8 additions & 0 deletions src/libs/actions/IOU/SplitTransactionUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1419,12 +1419,15 @@ function updateSplitTransactions({

// getDeleteTrackExpenseInformation only handles deleting the transaction report thread, so we need to update the report preview action here
if (originalReportPreviewAction) {
const currentActionIOUReport = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${currentReportAction?.reportID}`];
const cleanUpTransactionThreadReportOnyxData = getCleanUpTransactionThreadReportOnyxData({
shouldDeleteTransactionThread: false,
reportAction: currentReportAction,
updatedReportPreviewAction: (updatedReportPreviewAction ?? originalReportPreviewAction) as OnyxTypes.ReportAction,
shouldAddUpdatedReportPreviewActionToOnyxData: false,
currentUserAccountID: currentUserPersonalDetails.accountID,
iouReport: currentActionIOUReport,
chatReport: allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${currentActionIOUReport?.chatReportID}`],
// shouldDeleteTransactionThread is false, so the transaction-thread report actions are never read here.
transactionThreadReportActionsParam: undefined,
});
Expand Down Expand Up @@ -1670,13 +1673,18 @@ function updateSplitTransactions({
},
}),
};
const iouActionIOUReportID = isMoneyRequestAction(iouActionToCleanUp) ? iouActionToCleanUp.reportID : undefined;
const iouActionIOUReport = allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${iouActionIOUReportID}`];

const {optimisticData, successData, failureData} = getCleanUpTransactionThreadReportOnyxData({
transactionThreadID: iouActionToCleanUp.childReportID,
shouldDeleteTransactionThread: true,
reportAction: iouActionToCleanUp,
updatedReportPreviewAction: updatedReportPreviewAction as OnyxTypes.ReportAction,
currentUserAccountID: currentUserPersonalDetails.accountID,
transactionThread: allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${iouActionToCleanUp.childReportID}`],
iouReport: iouActionIOUReport,
chatReport: allReportsList?.[`${ONYXKEYS.COLLECTION.REPORT}${iouActionIOUReport?.chatReportID}`],
transactionThreadReportActionsParam: allReportActionsList?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouActionToCleanUp.childReportID}`],
});

Expand Down
3 changes: 3 additions & 0 deletions src/libs/actions/IOU/TrackExpense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ type GetDeleteTrackExpenseInformationParams = {
actionableWhisperReportActionID?: string;
resolution?: string;
shouldRemoveIOUTransaction?: boolean;
transactionThread?: OnyxEntry<OnyxTypes.Report>;
};

function getDeleteTrackExpenseInformation({
Expand All @@ -679,6 +680,7 @@ function getDeleteTrackExpenseInformation({
actionableWhisperReportActionID = '',
resolution = '',
shouldRemoveIOUTransaction = true,
transactionThread,
}: GetDeleteTrackExpenseInformationParams) {
// STEP 1: Get all collections we're updating
const transaction = getAllTransactions()?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
Expand Down Expand Up @@ -753,6 +755,7 @@ function getDeleteTrackExpenseInformation({
const cleanUpTransactionThreadReportOnyxData = getCleanUpTransactionThreadReportOnyxData({
transactionThreadID,
shouldDeleteTransactionThread,
transactionThread,
currentUserAccountID,
transactionThreadReportActionsParam: transactionThreadReportActions,
});
Expand Down
10 changes: 10 additions & 0 deletions src/libs/actions/MergeTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,9 @@ type MergeTransactionRequestParams = {
sourceIOUAction: OnyxEntry<ReportAction>;
getCurrencyDecimals: CurrencyListActionsContextType['getCurrencyDecimals'];
getCurrencySymbol: CurrencyListActionsContextType['getCurrencySymbol'];
sourceIOUActionThreadReport: OnyxEntry<Report>;
sourceActionIOUReport: OnyxEntry<Report>;
sourceActionChatReport: OnyxEntry<Report>;
};
/**
* Merges two transactions by updating the target transaction with selected fields and deleting the source transaction.
Expand Down Expand Up @@ -454,6 +457,9 @@ function mergeTransactionRequest({
sourceIOUAction,
getCurrencyDecimals,
getCurrencySymbol,
sourceIOUActionThreadReport,
sourceActionIOUReport,
sourceActionChatReport,
}: MergeTransactionRequestParams) {
// For both unreported expenses and expense reports, negate the display amount when storing
// This preserves the user's chosen sign while following the storage convention
Expand Down Expand Up @@ -625,6 +631,9 @@ function mergeTransactionRequest({
shouldDeleteTransactionThread,
reportAction: sourceIOUAction,
currentUserAccountID: currentUserAccountIDParam,
iouReport: sourceActionIOUReport,
chatReport: sourceActionChatReport,
transactionThread: sourceIOUActionThreadReport,
transactionThreadReportActionsParam: sourceTransactionThreadReportActions,
});
optimisticSourceReportActionData.push(...cleanUpSourceTransactionThreadReportOnyxData.optimisticData);
Expand Down Expand Up @@ -656,6 +665,7 @@ function mergeTransactionRequest({
actionableWhisperReportActionID,
resolution: CONST.REPORT.ACTIONABLE_TRACK_EXPENSE_WHISPER_RESOLUTION.NOTHING,
shouldRemoveIOUTransaction: false,
transactionThread: sourceIOUActionThreadReport,
});

sourceTransactionOptimisticData.push(...optimisticData);
Expand Down
13 changes: 11 additions & 2 deletions src/pages/TransactionDuplicate/DynamicConfirmationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ function DynamicConfirmationPage() {
const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newTransaction?.reportID}`);
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newTransaction?.reportID}`);
const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS);
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const reportAction = Object.values(reportActions ?? {}).find(
(action) => ReportActionsUtils.isMoneyRequestAction(action) && ReportActionsUtils.getOriginalMessage(action)?.IOUTransactionID === reviewDuplicates?.transactionID,
);
Expand Down Expand Up @@ -111,7 +112,15 @@ function DynamicConfirmationPage() {
// Suppress the NotFound guard for the discarded thread the server tears down on merge.
const keptReportRoute = ROUTES.REPORT_WITH_ID.getRoute(mergeParams.reportID);
setDeleteTransactionNavigateBackUrl(keptReportRoute);
mergeDuplicates({...mergeParams, ...taxData, currentUserAccountID, currentUserLogin: currentUserLogin ?? '', allTransactionViolations, allReportActionsList: allReportActions});
mergeDuplicates({
...mergeParams,
...taxData,
currentUserAccountID,
currentUserLogin: currentUserLogin ?? '',
allTransactionViolations,
allReportActionsList: allReportActions,
allReportsList: allReports,
});
if (isSuperWideRHPDisplayed) {
Navigation.dismissToSuperWideRHP();
return;
Expand All @@ -125,7 +134,7 @@ function DynamicConfirmationPage() {
Navigation.dismissModal({
afterTransition: () => Navigation.navigate(keptReportRoute, {forceReplace: true}),
});
}, [childReportID, transactionsMergeParams, taxData, currentUserAccountID, currentUserLogin, isSuperWideRHPDisplayed, allTransactionViolations, allReportActions]);
}, [childReportID, transactionsMergeParams, taxData, currentUserAccountID, currentUserLogin, isSuperWideRHPDisplayed, allTransactionViolations, allReportActions, allReports]);

const handleResolveDuplicates = useCallback(() => {
resolveDuplicates({...transactionsMergeParams, ...taxData, transactionThreadReportIDMap, allTransactionViolations, allReportActionsList: allReportActions, delegateAccountID});
Expand Down
13 changes: 11 additions & 2 deletions src/pages/TransactionMerge/DynamicConfirmationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTop
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {MergeTransactionNavigatorParamList} from '@libs/Navigation/types';
import {getFilteredReportActionsForReportView, getIOUActionForTransactionID} from '@libs/ReportActionsUtils';
import {getFilteredReportActionsForReportView, getIOUActionForTransactionID, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {findSelfDMReportID} from '@libs/ReportUtils';

import CONST from '@src/CONST';
Expand Down Expand Up @@ -91,8 +91,14 @@ function DynamicConfirmationPage({route}: DynamicConfirmationPageProps) {

const [sourceReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(sourceTransaction?.reportID)}`);
const sourceIOUAction = sourceTransaction ? getIOUActionForTransactionID(Object.values(sourceReportActions ?? {}), sourceTransaction.transactionID) : undefined;
const selfDMSourceIOUAction =
selfDMReport?.reportID && sourceTransaction ? getIOUActionForTransactionID(Object.values(selfDMReportActions ?? {}), sourceTransaction.transactionID) : undefined;
const [sourceTransactionThreadReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(sourceIOUAction?.childReportID)}`);

const sourceThreadReportID = sourceIOUAction?.childReportID ?? selfDMSourceIOUAction?.childReportID;
const [sourceIOUActionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(sourceThreadReportID)}`);
const sourceIOUReportID = isMoneyRequestAction(sourceIOUAction) ? sourceIOUAction?.reportID : undefined;
const [sourceActionIOUReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(sourceIOUReportID)}`);
const [sourceActionChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(sourceActionIOUReport?.chatReportID)}`);
// Build the merged transaction data for display
const mergedTransactionData = buildMergedTransactionData(targetTransaction, mergeTransaction);

Expand Down Expand Up @@ -134,6 +140,9 @@ function DynamicConfirmationPage({route}: DynamicConfirmationPageProps) {
reportPolicyTags,
sourceTransactionThreadReportActions,
sourceIOUAction,
sourceIOUActionThreadReport,
sourceActionIOUReport,
sourceActionChatReport,
});

const reportIDToDismiss = reportID !== CONST.REPORT.UNREPORTED_REPORT_ID ? reportID : undefined;
Expand Down
Loading
Loading