Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,10 +699,19 @@ function getUpdatedTransaction({
if (Object.hasOwn(transactionChanges, 'amount') && typeof transactionChanges.amount === 'number') {
updatedTransaction.modifiedAmount = isFromExpenseReport || isUnReportedExpense ? -transactionChanges.amount : transactionChanges.amount;
shouldStopSmartscan = true;
// Clear convertedAmount when amount is manually modified to prevent stale conversion data
// from being used during export (the server will recalculate if needed)
// Use null instead of undefined because Onyx.merge treats undefined as no-op
updatedTransaction.convertedAmount = null;
}
if (Object.hasOwn(transactionChanges, 'currency')) {
updatedTransaction.modifiedCurrency = transactionChanges.currency;
shouldStopSmartscan = true;
// Clear convertedAmount when currency is manually modified to prevent incorrect
// currency conversion being applied during export (fixes issue where manually entered
// amounts in a different currency were being re-converted)
// Use null instead of undefined because Onyx.merge treats undefined as no-op
updatedTransaction.convertedAmount = null;
}

if (Object.hasOwn(transactionChanges, 'merchant')) {
Expand Down
2 changes: 1 addition & 1 deletion src/types/onyx/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ type Transaction = OnyxCommon.OnyxValueWithOfflineFeedback<
accountant?: Accountant;

/** The transaction converted amount in report's currency */
convertedAmount?: number;
convertedAmount?: number | null;

/** The transaction tax amount */
taxAmount?: number;
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/TransactionUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,50 @@ describe('TransactionUtils', () => {
merchant: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
});
});

it('should clear convertedAmount when currency is changed to prevent incorrect export conversion', () => {
// Given: a transaction with a converted amount (simulating a foreign currency transaction)
const transaction = generateTransaction({
amount: -4500, // $45.00 USD
currency: CONST.CURRENCY.USD,
convertedAmount: -6285, // C$62.85 CAD (server-calculated conversion)
});

// When: user manually changes the currency to CAD
const updatedTransaction = TransactionUtils.getUpdatedTransaction({
transaction,
isFromExpenseReport: true,
transactionChanges: {currency: 'CAD'},
});

// Then: convertedAmount should be cleared (set to null, not undefined, because
// Onyx.merge treats undefined as no-op) to prevent stale conversion data
// being incorrectly applied during export
expect(updatedTransaction.modifiedCurrency).toBe('CAD');
expect(updatedTransaction.convertedAmount).toBeNull();
});

it('should clear convertedAmount when amount is manually changed to prevent incorrect export conversion', () => {
// Given: a transaction with a converted amount
const transaction = generateTransaction({
amount: -4500, // $45.00 USD
currency: CONST.CURRENCY.USD,
convertedAmount: -6285, // C$62.85 CAD
});

// When: user manually changes the amount
const updatedTransaction = TransactionUtils.getUpdatedTransaction({
transaction,
isFromExpenseReport: true,
transactionChanges: {amount: 6285}, // C$62.85
});

// Then: convertedAmount should be cleared (set to null, not undefined, because
// Onyx.merge treats undefined as no-op) since the manual amount
// is the final value and shouldn't be re-converted during export
expect(updatedTransaction.modifiedAmount).toBe(-6285);
expect(updatedTransaction.convertedAmount).toBeNull();
});
});

describe('getTransactionType', () => {
Expand Down
Loading