diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index ea2f74e06358..9d3441b526d3 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -267,6 +267,7 @@ function MoneyRequestView({ tag: transactionTag, originalCurrency: transactionOriginalCurrency, postedDate: transactionPostedDate, + convertedAmount: transactionConvertedAmount, } = getTransactionDetails(transaction, undefined, undefined, allowNegativeAmount, false, currentUserPersonalDetails) ?? {}; const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isDistanceRequest = isDistanceRequestTransactionUtils(transaction); @@ -461,6 +462,11 @@ function MoneyRequestView({ if (isExpenseSplit) { amountDescription += ` ${CONST.DOT_SEPARATOR} ${translate('iou.split')}`; } + if (currency !== moneyRequestReport?.currency && !isManagedCardTransaction && transaction?.reportID !== CONST.REPORT.UNREPORTED_REPORT_ID) { + if (transactionConvertedAmount) { + amountDescription += ` ${CONST.DOT_SEPARATOR} ${translate('common.converted')} ${convertToDisplayString(transactionConvertedAmount, moneyRequestReport?.currency)}`; + } + } if (isFromMergeTransaction) { // Because we lack the necessary data in policy.customUnits to determine the rate in merge flow, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 22b8ec0f97eb..8086c276ad8f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -284,6 +284,7 @@ import { getCardID, getCardName, getCategory, + getConvertedAmount, getCurrency, getDescription, getFormattedCreated, @@ -781,6 +782,7 @@ type TransactionDetails = { postedDate: string; transactionID: string; distance?: number; + convertedAmount: number; }; type OptimisticIOUReport = Pick< @@ -4461,6 +4463,7 @@ function getTransactionDetails( cardName: getCardName(transaction), originalAmount: getOriginalAmount(transaction), originalCurrency: getOriginalCurrency(transaction), + convertedAmount: getConvertedAmount(transaction, isFromExpenseReport, transaction?.reportID === CONST.REPORT.UNREPORTED_REPORT_ID, allowNegativeAmount, disableOppositeConversion), postedDate: getFormattedPostedDate(transaction), transactionID: transaction.transactionID, ...(isManualDistanceRequest && {distance: transaction.comment?.customUnit?.quantity ?? undefined}), diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 369a80cc643f..b449bc5dd130 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -844,6 +844,31 @@ function getOriginalAmount(transaction: Transaction): number { return Math.abs(amount); } +function getConvertedAmount( + transaction: OnyxInputOrEntry, + isFromExpenseReport = false, + isFromTrackedExpense = false, + allowNegative = false, + disableOppositeConversion = false, +): number { + // IOU requests cannot have negative values, but they can be stored as negative values, let's return absolute value + if (!isFromExpenseReport && !isFromTrackedExpense && !allowNegative) { + return Math.abs(transaction?.convertedAmount ?? 0); + } + + if (disableOppositeConversion) { + return transaction?.convertedAmount ?? 0; + } + + // Expense report case: + // The amounts are stored using an opposite sign and negative values can be set, + // we need to return an opposite sign than is saved in the transaction object + const convertedAmount = transaction?.convertedAmount ?? 0; + + // To avoid -0 being shown, lets only change the sign if the value is other than 0. + return convertedAmount ? -convertedAmount : 0; +} + /** * Return the original amount for display/sorting purposes. * For expense reports, returns the negated value of (originalAmount || amount || modifiedAmount). @@ -2572,6 +2597,7 @@ export { shouldReuseInitialTransaction, getOriginalAmountForDisplay, getOriginalCurrencyForDisplay, + getConvertedAmount, shouldShowExpenseBreakdown, }; diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index b5c672c40640..c91f5618a4fc 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -2631,6 +2631,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalCurrency: 'USD', postedDate: '2025-01-01', cardID: 1, + convertedAmount: -100, }); jest.spyOn(ReportUtils, 'isMoneyRequestReportEligibleForMerge').mockReturnValue(true); @@ -2683,6 +2684,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalCurrency: 'USD', postedDate: '2025-01-01', cardID: 1, + convertedAmount: 100, }); jest.spyOn(ReportUtils, 'isMoneyRequestReportEligibleForMerge').mockReturnValue(true); diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index e3b750543dde..61b0db13ee7b 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -1537,4 +1537,27 @@ describe('TransactionUtils', () => { expect(TransactionUtils.shouldShowExpenseBreakdown(transactions)).toBe(true); }); }); + + describe('getConvertedAmount', () => { + it('should return the absolute amount if transaction is not from expense report, tracked expense and allowNegative is false', () => { + const transaction = generateTransaction({ + convertedAmount: -100, + }); + expect(TransactionUtils.getConvertedAmount(transaction)).toBe(100); + }); + + it('should return the opposite sign amount if the transaction is from the expense report and disableOppositeConversion is false', () => { + const transaction = generateTransaction({ + convertedAmount: -100, + }); + expect(TransactionUtils.getConvertedAmount(transaction, true, false, false, false)).toBe(100); + }); + + it('should return the current converted amount if the transaction is from the expense report and disableOppositeConversion is true', () => { + const transaction = generateTransaction({ + convertedAmount: -100, + }); + expect(TransactionUtils.getConvertedAmount(transaction, true, false, false, true)).toBe(-100); + }); + }); });