Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 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
c29e2ee
Refactor PutonHold functions
parasharrajat Sep 6, 2026
7de7b10
Refactor function to object type
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
98 changes: 70 additions & 28 deletions src/libs/actions/IOU/Hold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,34 @@ import {getAllReports, getAllTransactions} from '.';
/**
* Put expense on HOLD
*/
function putOnHold(
transactionID: string,
comment: string,
initialReportID: string | undefined,
isOffline: boolean,
currentUserLogin: string,
currentUserAccountID: number,
transactionViolations: OnyxEntry<OnyxTypes.TransactionViolations>,
isTrackIntentUser: boolean | undefined,
delegateAccountID: number | undefined,
ancestors: Ancestor[] = [],
) {
function putOnHold({
transactionID,
comment,
initialReportID,
initialReport,
transactionReport,
isOffline,
currentUserLogin,
currentUserAccountID,
transactionViolations,
isTrackIntentUser,
delegateAccountID,
ancestors = [],
}: {
transactionID: string;
comment: string;
initialReportID: string | undefined;
initialReport: OnyxEntry<OnyxTypes.Report>;
transactionReport: OnyxEntry<OnyxTypes.Report>;
isOffline: boolean;
currentUserLogin: string;
currentUserAccountID: number;
transactionViolations: OnyxEntry<OnyxTypes.TransactionViolations>;
isTrackIntentUser: boolean | undefined;
delegateAccountID: number | undefined;
ancestors?: Ancestor[];
}) {
const allTransactions = getAllTransactions();
const allReports = getAllReports();

const currentTime = DateUtils.getDBTime();
const reportID = initialReportID ?? generateReportID();
Expand All @@ -71,14 +85,14 @@ function putOnHold(
const newViolation = {name: CONST.VIOLATIONS.HOLD, type: CONST.VIOLATION_TYPES.VIOLATION, showInReview: true};
const updatedViolations = [...(transactionViolations ?? []), newViolation];
const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID}`];
const iouReport = transactionReport;
const iouAction = getIOUActionForReportID(transaction?.reportID, transactionID);
let transactionThreadReport: OnyxTypes.Report;

// If there is no existing transaction thread report, we should create one
// This way we ensure every held request has a dedicated thread for comments
if (initialReportID) {
transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${initialReportID}`] ?? ({} as OnyxTypes.Report);
transactionThreadReport = initialReport ?? ({} as OnyxTypes.Report);
} else {
const moneyRequestReport = getReportOrDraftReport(transaction?.reportID);
transactionThreadReport = buildTransactionThread(iouAction, moneyRequestReport, currentUserAccountID, undefined, reportID);
Expand Down Expand Up @@ -341,22 +355,50 @@ function putOnHold(
Navigation.setNavigationActionToMicrotaskQueue(() => notifyNewAction(currentReportID, undefined, true));
}

function putTransactionsOnHold(
transactionsID: string[],
comment: string,
reportID: string,
isOffline: boolean,
currentUserLogin: string,
currentUserAccountID: number,
allTransactionViolations: OnyxCollection<OnyxTypes.TransactionViolations>,
isTrackIntentUser: boolean | undefined,
delegateAccountID: number | undefined,
ancestors: Ancestor[] = [],
) {
function putTransactionsOnHold({
transactionsID,
allReports,
transactionReports,
comment,
reportID,
isOffline,
currentUserLogin,
currentUserAccountID,
allTransactionViolations,
isTrackIntentUser,
delegateAccountID,
ancestors = [],
}: {
transactionsID: string[];
allReports: OnyxCollection<OnyxTypes.Report>;
transactionReports: Record<string, OnyxEntry<OnyxTypes.Report>>;
comment: string;
reportID: string;
isOffline: boolean;
currentUserLogin: string;
currentUserAccountID: number;
allTransactionViolations: OnyxCollection<OnyxTypes.TransactionViolations>;
isTrackIntentUser: boolean | undefined;
delegateAccountID: number | undefined;
ancestors?: Ancestor[];
}) {
for (const transactionID of transactionsID) {
const {childReportID} = getIOUActionForReportID(reportID, transactionID) ?? {};
const transactionViolations = allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`];
putOnHold(transactionID, comment, childReportID, isOffline, currentUserLogin, currentUserAccountID, transactionViolations, isTrackIntentUser, delegateAccountID, ancestors);
putOnHold({
transactionID,
comment,
initialReportID: childReportID,
initialReport: allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${childReportID}`],
transactionReport: transactionReports[transactionID],
isOffline,
currentUserLogin,
currentUserAccountID,
transactionViolations,
isTrackIntentUser,
delegateAccountID,
ancestors,
});
}
}

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
Loading
Loading