diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index c3b3e386fead3..cb97d3df399e5 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -272,6 +272,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); @@ -462,6 +463,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 4bc3e042d7800..9c741519b3024 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -291,6 +291,7 @@ import { getCardID, getCardName, getCategory, + getConvertedAmount, getCurrency, getDescription, getFormattedCreated, @@ -790,6 +791,7 @@ type TransactionDetails = { distance?: number; odometerStart?: number; odometerEnd?: number; + convertedAmount: number; }; type OptimisticIOUReport = Pick< @@ -4476,6 +4478,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 174ac9f465162..d7a040d75ac42 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -906,6 +906,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). @@ -2629,6 +2654,7 @@ export { shouldReuseInitialTransaction, getOriginalAmountForDisplay, getOriginalCurrencyForDisplay, + getConvertedAmount, shouldShowExpenseBreakdown, isTimeRequest, }; diff --git a/tests/unit/ReportSecondaryActionUtilsTest.ts b/tests/unit/ReportSecondaryActionUtilsTest.ts index 0bc1dd2ba3efe..c4f900f963b33 100644 --- a/tests/unit/ReportSecondaryActionUtilsTest.ts +++ b/tests/unit/ReportSecondaryActionUtilsTest.ts @@ -2636,6 +2636,7 @@ describe('getSecondaryTransactionThreadActions', () => { originalCurrency: 'USD', postedDate: '2025-01-01', cardID: 1, + convertedAmount: -100, }); jest.spyOn(ReportUtils, 'isMoneyRequestReportEligibleForMerge').mockReturnValue(true); @@ -2688,6 +2689,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 845fc55798e19..3067e1b00fdd6 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -1552,4 +1552,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); + }); + }); });