Skip to content

Commit 436f35b

Browse files
authored
Merge pull request #89403 from Expensify/claude-splitDeleteOfflineTotalFix
Pass splitExpensesTotal in useDeleteTransactions to fix offline split deletion total
2 parents 5001fcf + b1f17b8 commit 436f35b

3 files changed

Lines changed: 87 additions & 4 deletions

File tree

src/hooks/useDeleteTransactions.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,8 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac
111111

112112
for (const transactionID of Object.keys(splitTransactionsByOriginalTransactionID)) {
113113
const splitIDs = new Set((splitTransactionsByOriginalTransactionID[transactionID] ?? []).map((transaction) => transaction.transactionID));
114-
const childTransactions = getChildTransactions(allTransactions, allReports, transactionID).filter(
115-
(transaction) => !splitIDs.has(transaction?.transactionID ?? String(CONST.DEFAULT_NUMBER_ID)),
116-
);
114+
const allChildTransactions = getChildTransactions(allTransactions, allReports, transactionID);
115+
const childTransactions = allChildTransactions.filter((transaction) => !splitIDs.has(transaction?.transactionID ?? String(CONST.DEFAULT_NUMBER_ID)));
117116

118117
if (childTransactions.length === 0) {
119118
nonSplitTransactions.push(...splitTransactionsByOriginalTransactionID[transactionID]);
@@ -158,6 +157,10 @@ function useDeleteTransactions({report, reportActions, policy}: UseDeleteTransac
158157
reportID: report?.reportID ?? String(CONST.DEFAULT_NUMBER_ID),
159158
originalTransactionID: transactionID,
160159
splitExpenses: remainingSplitExpenses,
160+
splitExpensesTotal: allChildTransactions.reduce((total, childTransaction) => {
161+
const transactionReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${childTransaction?.reportID}`];
162+
return total + initSplitExpenseItemData(childTransaction, transactionReport).amount;
163+
}, 0),
161164
},
162165
searchContext: {
163166
currentSearchHash,

src/libs/actions/IOU/Split.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ type UpdateSplitTransactionsParams = {
216216
reportID: string;
217217
originalTransactionID: string;
218218
splitExpenses: SplitExpense[];
219-
splitExpensesTotal?: number;
219+
splitExpensesTotal: number | undefined;
220220
};
221221
searchContext?: (Partial<SearchStateContextValue & SearchActionsContextValue> & {activeGroupSearchHashes?: number[]}) | undefined;
222222
policyCategories: OnyxTypes.PolicyCategories | undefined;

tests/actions/IOUTest/SplitTest.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
21992199
{transactionID: splitTransactionID1, amount: amount / 2, created: DateUtils.getDBTime()},
22002200
{transactionID: splitTransactionID2, amount: amount / 2, created: DateUtils.getDBTime()},
22012201
],
2202+
splitExpensesTotal: undefined,
22022203
},
22032204
policyCategories: undefined,
22042205
policy: undefined,
@@ -2259,6 +2260,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
22592260
reportID,
22602261
originalTransactionID: originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID),
22612262
splitExpenses: [{transactionID: splitTransactionID1, amount, created: DateUtils.getDBTime(), reportID: differentReportID}],
2263+
splitExpensesTotal: undefined,
22622264
},
22632265
policyCategories: undefined,
22642266
policy: undefined,
@@ -2423,6 +2425,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
24232425
{transactionID: splitTransactionID1, amount: amount / 2, created: DateUtils.getDBTime()},
24242426
{transactionID: splitTransactionID2, amount: amount / 2, created: DateUtils.getDBTime()},
24252427
],
2428+
splitExpensesTotal: undefined,
24262429
},
24272430
policyCategories: undefined,
24282431
policy: undefined,
@@ -2530,6 +2533,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
25302533
reportID,
25312534
originalTransactionID: originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID),
25322535
splitExpenses: [{transactionID: splitTransactionID1, amount, created: DateUtils.getDBTime()}],
2536+
splitExpensesTotal: undefined,
25332537
},
25342538
policyCategories: undefined,
25352539
policy: undefined,
@@ -2703,6 +2707,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
27032707
reportID: reportID1,
27042708
originalTransactionID: originalTransaction.transactionID,
27052709
splitExpenses: [{transactionID: childTransaction.transactionID, amount: 10000, created: DateUtils.getDBTime()}],
2710+
splitExpensesTotal: undefined,
27062711
},
27072712
searchContext: {
27082713
currentSearchHash: -2,
@@ -2837,6 +2842,7 @@ describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
28372842
reportID: reportID2,
28382843
originalTransactionID: originalTransaction.transactionID,
28392844
splitExpenses: [{transactionID: childTransaction.transactionID, amount: 10000, created: DateUtils.getDBTime()}],
2845+
splitExpensesTotal: undefined,
28402846
},
28412847
searchContext: {
28422848
currentSearchHash: -2,
@@ -3792,6 +3798,7 @@ describe('updateSplitTransactions', () => {
37923798
{transactionID: 'split-1', amount: amount / 2, description: 'Split 1', created: DateUtils.getDBTime()},
37933799
{transactionID: 'split-2', amount: amount / 2, description: 'Split 2', created: DateUtils.getDBTime()},
37943800
],
3801+
splitExpensesTotal: undefined,
37953802
},
37963803
searchContext: {currentSearchHash: -2},
37973804
policyCategories: undefined,
@@ -3923,6 +3930,7 @@ describe('updateSplitTransactions', () => {
39233930
{transactionID: 'offline-split-1', amount: amount / 2, description: 'Offline Split 1', created: DateUtils.getDBTime()},
39243931
{transactionID: 'offline-split-2', amount: amount / 2, description: 'Offline Split 2', created: DateUtils.getDBTime()},
39253932
],
3933+
splitExpensesTotal: undefined,
39263934
},
39273935
searchContext: {currentSearchHash: -2},
39283936
policyCategories: undefined,
@@ -4053,6 +4061,7 @@ describe('updateSplitTransactions', () => {
40534061
{transactionID: 'cat-tag-1', amount: amount / 2, description: 'Split 1', created: DateUtils.getDBTime(), category: testCategory, tags: [testTag]},
40544062
{transactionID: 'cat-tag-2', amount: amount / 2, description: 'Split 2', created: DateUtils.getDBTime(), category: testCategory, tags: [testTag]},
40554063
],
4064+
splitExpensesTotal: undefined,
40564065
},
40574066
searchContext: {currentSearchHash: -2},
40584067
policyCategories: undefined,
@@ -4230,6 +4239,7 @@ describe('updateSplitTransactions', () => {
42304239
description: `Split ${index + 1}`,
42314240
created: DateUtils.getDBTime(),
42324241
})),
4242+
splitExpensesTotal: undefined,
42334243
},
42344244
searchContext: {currentSearchHash: -2},
42354245
policyCategories: undefined,
@@ -4465,6 +4475,7 @@ describe('updateSplitTransactions', () => {
44654475
reportID: expenseReport.reportID,
44664476
originalTransactionID,
44674477
splitExpenses: [{transactionID: splitTransactionID1, reportID: remainingSplitTransaction?.reportID, amount, created: DateUtils.getDBTime()}],
4478+
splitExpensesTotal: undefined,
44684479
},
44694480
searchContext: {currentSearchHash: -2},
44704481
policyCategories: undefined,
@@ -4544,6 +4555,7 @@ describe('updateSplitTransactions', () => {
45444555
{transactionID: splitTransactionID1, amount: amount / 2, created: DateUtils.getDBTime()},
45454556
{transactionID: splitTransactionID2, amount: amount / 2, created: DateUtils.getDBTime()},
45464557
],
4558+
splitExpensesTotal: undefined,
45474559
},
45484560
searchContext: {currentSearchHash: -2},
45494561
policyCategories: undefined,
@@ -4569,6 +4581,74 @@ describe('updateSplitTransactions', () => {
45694581
const updatedReportPreviewAction = getReportPreviewAction(chatReport?.reportID, expenseReport?.reportID);
45704582
expect(updatedReportPreviewAction?.childVisibleActionCount).toEqual(2);
45714583
});
4584+
4585+
it('should preserve report total when deleting a split with correct splitExpensesTotal', async () => {
4586+
// This tests the bug where useDeleteTransactions was not passing splitExpensesTotal,
4587+
// causing the report total to be incorrect after offline split deletion.
4588+
// Without splitExpensesTotal, changesInReportTotal = sum(remaining splits) - 0 = remaining total,
4589+
// which incorrectly subtracts that from the report total again.
4590+
const {expenseReport, originalTransactionID} = await createBaseExpense();
4591+
const reportID = expenseReport?.reportID ?? String(CONST.DEFAULT_NUMBER_ID);
4592+
const txID = originalTransactionID ?? String(CONST.DEFAULT_NUMBER_ID);
4593+
4594+
// Split into 4 parts for clean division (amount=10000, each split = 2500)
4595+
const splitIDs = await splitToN(4, expenseReport, txID);
4596+
4597+
// Capture the report total after the initial split
4598+
const reportAfterSplit = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
4599+
const totalAfterSplit = reportAfterSplit?.total ?? 0;
4600+
4601+
const {allTransactions, allReports, allReportNameValuePairs} = await getCollections();
4602+
const policyTags = await getPolicyTags(reportID);
4603+
const reports = getTransactionAndExpenseReports(reportID);
4604+
4605+
// "Delete" one split by calling updateSplitTransactions with only three remaining splits.
4606+
// Pass splitExpensesTotal = sum of remaining splits (this is what useDeleteTransactions now does).
4607+
const splitAmount = amount / 4;
4608+
const splitExpensesTotal = splitAmount * 3;
4609+
4610+
updateSplitTransactions({
4611+
allTransactionsList: allTransactions,
4612+
allReportsList: allReports,
4613+
allReportNameValuePairsList: allReportNameValuePairs,
4614+
transactionData: {
4615+
reportID,
4616+
originalTransactionID: txID,
4617+
splitExpenses: [
4618+
{transactionID: splitIDs.at(0) ?? '', amount: splitAmount, created: DateUtils.getDBTime()},
4619+
{transactionID: splitIDs.at(1) ?? '', amount: splitAmount, created: DateUtils.getDBTime()},
4620+
{transactionID: splitIDs.at(2) ?? '', amount: splitAmount, created: DateUtils.getDBTime()},
4621+
],
4622+
splitExpensesTotal,
4623+
},
4624+
searchContext: {currentSearchHash: -2},
4625+
policyCategories: undefined,
4626+
policy: undefined,
4627+
policyRecentlyUsedCategories: [],
4628+
iouReport: expenseReport,
4629+
firstIOU: undefined,
4630+
isASAPSubmitBetaEnabled: false,
4631+
currentUserPersonalDetails,
4632+
transactionViolations: {},
4633+
policyRecentlyUsedCurrencies: [],
4634+
quickAction: undefined,
4635+
iouReportNextStep: undefined,
4636+
betas: [CONST.BETAS.ALL],
4637+
policyTags,
4638+
personalDetails: {[RORY_ACCOUNT_ID]: {accountID: RORY_ACCOUNT_ID, login: RORY_EMAIL}},
4639+
transactionReport: reports.transactionReport,
4640+
expenseReport: reports.expenseReport,
4641+
isOffline: false,
4642+
});
4643+
await waitForBatchedUpdates();
4644+
4645+
// With correct splitExpensesTotal: changesInReportTotal = remainingAmount - splitExpensesTotal = 0,
4646+
// so the report total should not change from the post-split value.
4647+
// Without splitExpensesTotal (the bug): changesInReportTotal = remainingAmount - 0 = remainingAmount,
4648+
// which would incorrectly subtract that from the total, resulting in totalAfterSplit + remainingAmount.
4649+
const updatedReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
4650+
expect(updatedReport?.total).toBe(totalAfterSplit);
4651+
});
45724652
});
45734653

45744654
describe('initSplitExpense', () => {

0 commit comments

Comments
 (0)