Skip to content

Commit b688172

Browse files
committed
fix: bulk edit, does not clobber taxAmount when the transaction taxCode is unknown to the policy
1 parent a843b59 commit b688172

2 files changed

Lines changed: 72 additions & 4 deletions

File tree

src/libs/actions/IOU/BulkEdit.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,16 @@ function updateMultipleMoneyRequests({
170170
}
171171
// When bulk-editing amount on a taxed expense without also changing taxCode, recompute
172172
// taxAmount from the transaction's existing taxCode so offline optimistic data and the
173-
// queued payload stay in sync with the new amount
173+
// queued payload stay in sync with the new amount. Skip when the rate can't be resolved
174+
// (e.g. cross-policy bulk edit where the transaction's own policy is missing from cache)
175+
// to avoid silently overwriting a non-zero taxAmount with 0.
174176
if (changes.amount !== undefined && !changes.taxCode && transaction.taxCode && supportsExpenseFields && canEditField(CONST.EDIT_REQUEST_FIELD.TAX_RATE)) {
175177
const taxValue = getTaxValue(transactionPolicy, transaction, transaction.taxCode);
176-
const decimals = getCurrencyDecimals(getCurrency(transaction));
177-
const taxAmount = calculateTaxAmount(taxValue, Math.abs(changes.amount), decimals);
178-
transactionChanges.taxAmount = convertToBackendAmount(taxAmount);
178+
if (taxValue) {
179+
const decimals = getCurrencyDecimals(getCurrency(transaction));
180+
const taxAmount = calculateTaxAmount(taxValue, Math.abs(changes.amount), decimals);
181+
transactionChanges.taxAmount = convertToBackendAmount(taxAmount);
182+
}
179183
}
180184
if (changes.currency && canEditField(CONST.EDIT_REQUEST_FIELD.CURRENCY)) {
181185
transactionChanges.currency = changes.currency;

tests/actions/IOUTest/BulkEditTest.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,70 @@ describe('actions/IOU/BulkEdit', () => {
13691369
writeSpy.mockRestore();
13701370
canEditFieldSpy.mockRestore();
13711371
});
1372+
1373+
it('does not clobber taxAmount when the transaction taxCode is unknown to the policy', () => {
1374+
// Cross-policy bulk edit / stale cache: the transaction has a taxCode that doesn't
1375+
// exist in the resolved policy's taxRates. getTaxValue returns undefined and
1376+
// calculateTaxAmount short-circuits to 0 — we must not write that 0 over a non-zero
1377+
// existing taxAmount.
1378+
const transactionID = 'transaction-unresolvable-tax';
1379+
const iouReportID = 'iou-unresolvable-tax';
1380+
const policy: Policy = {
1381+
...createRandomPolicy(1, CONST.POLICY.TYPE.TEAM),
1382+
taxRates: CONST.DEFAULT_TAX,
1383+
};
1384+
1385+
const iouReport: Report = {
1386+
...createRandomReport(2, undefined),
1387+
reportID: iouReportID,
1388+
policyID: policy.id,
1389+
type: CONST.REPORT.TYPE.EXPENSE,
1390+
};
1391+
1392+
const reports = {
1393+
[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`]: iouReport,
1394+
};
1395+
1396+
const transaction: Transaction = {
1397+
...createRandomTransaction(1),
1398+
transactionID,
1399+
reportID: iouReportID,
1400+
amount: -1000,
1401+
currency: CONST.CURRENCY.USD,
1402+
taxCode: 'id_TAX_RATE_NOT_IN_POLICY',
1403+
taxAmount: -48,
1404+
};
1405+
const transactions = {
1406+
[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]: transaction,
1407+
};
1408+
1409+
const canEditFieldSpy = jest.spyOn(require('@libs/ReportUtils'), 'canEditFieldOfMoneyRequest').mockReturnValue(true);
1410+
// eslint-disable-next-line rulesdir/no-multiple-api-calls
1411+
const writeSpy = jest.spyOn(API, 'write').mockImplementation(jest.fn());
1412+
1413+
updateMultipleMoneyRequests({
1414+
transactionIDs: [transactionID],
1415+
changes: {amount: -2000},
1416+
policy,
1417+
reports,
1418+
transactions,
1419+
reportActions: {},
1420+
policyCategories: undefined,
1421+
policyTags: {},
1422+
hash: undefined,
1423+
introSelected: undefined,
1424+
betas: undefined,
1425+
});
1426+
1427+
const params = writeSpy.mock.calls.at(0)?.[1] as {updates: string};
1428+
const updates = JSON.parse(params.updates) as {amount: number; taxAmount?: number};
1429+
expect(updates.amount).toBe(-2000);
1430+
// No taxAmount should be queued — we couldn't resolve a rate to recompute from.
1431+
expect(updates.taxAmount).toBeUndefined();
1432+
1433+
writeSpy.mockRestore();
1434+
canEditFieldSpy.mockRestore();
1435+
});
13721436
});
13731437

13741438
describe('bulk edit draft transaction', () => {

0 commit comments

Comments
 (0)