Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
24e6657
Compute effective columns for report details when no custom columns a…
mukhrr Apr 1, 2026
3e39290
Conditionally show comments column in report details based on whether…
mukhrr Apr 1, 2026
25a25fd
Wait for transactions to load before rendering ColumnsSettingsList to…
mukhrr Apr 1, 2026
4626386
Update tests to use shouldShowCommentsColumn parameter instead of inc…
mukhrr Apr 1, 2026
fb8e52a
Merge remote-tracking branch 'origin' into fix/custom-report-columns-…
mukhrr Apr 2, 2026
e58c116
Revert "Remove unused Columns button and related imports from MoneyRe…
mukhrr Apr 2, 2026
a75dbc8
Pass report currency to resolveTransactionCardFields for proper amoun…
mukhrr Apr 2, 2026
8df328e
Use modifiedAmount/modifiedCurrency when determining if currency conv…
mukhrr Apr 2, 2026
e5b7d57
Use groupAmount variable and check for non-zero value instead of trut…
mukhrr Apr 2, 2026
1314b3c
Store current display amount/currency in originalAmount/originalCurre…
mukhrr Apr 9, 2026
b52ecb8
Remove report currency conversion logic from resolveTransactionCardFi…
mukhrr Apr 10, 2026
529fba0
Split currency conversion logic in TotalCell to handle convertedAmoun…
mukhrr Apr 11, 2026
ff8c002
Consolidate tax column visibility logic to show both TAX_RATE and TAX…
mukhrr Apr 13, 2026
07d9e79
Compute effective columns from getColumnsToShow when no custom column…
mukhrr Apr 14, 2026
1267c72
Add TOTAL column to transaction views and update related logic
mukhrr Apr 15, 2026
79db6c6
Enhance TransactionUtils tests for exchange rate handling
mukhrr Apr 15, 2026
f78015c
Merge remote-tracking branch 'origin' into fix/custom-report-columns-…
mukhrr Apr 16, 2026
1146baf
Merge remote-tracking branch 'origin' into fix/custom-report-columns-…
mukhrr Apr 16, 2026
7429e38
Merge remote-tracking branch 'origin' into fix/custom-report-columns-…
mukhrr Apr 16, 2026
dd2a3f5
fixed exchange rate column when default currency is different
mukhrr Apr 16, 2026
e47b148
remove duplicate reportCurrency
mukhrr Apr 16, 2026
4ec4cd7
reverted original amount to search page
mukhrr Apr 17, 2026
9e62df3
Merge remote-tracking branch 'origin' into fix/custom-report-columns-…
mukhrr Apr 17, 2026
6736f01
removed convertedAmount rounds to amount coincidence bug
mukhrr Apr 22, 2026
1829021
made total visible by default
mukhrr Apr 23, 2026
dcf8ca5
Merge branch 'main' into fix/custom-report-columns-bugs
mukhrr Apr 23, 2026
b5d7a95
fixed negative values as Total
mukhrr Apr 24, 2026
4c09ab8
show total as unconverted amount on offline
mukhrr Apr 24, 2026
017a242
Merge remote-tracking branch 'origin/main' into fix/custom-report-col…
mukhrr Apr 25, 2026
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
2 changes: 1 addition & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7661,12 +7661,12 @@ const CONST = {
CATEGORY: this.TABLE_COLUMNS.CATEGORY,
TAG: this.TABLE_COLUMNS.TAG,
EXCHANGE_RATE: this.TABLE_COLUMNS.EXCHANGE_RATE,
ORIGINAL_AMOUNT: this.TABLE_COLUMNS.ORIGINAL_AMOUNT,
REIMBURSABLE: this.TABLE_COLUMNS.REIMBURSABLE,
BILLABLE: this.TABLE_COLUMNS.BILLABLE,
TAX_RATE: this.TABLE_COLUMNS.TAX_RATE,
TAX_AMOUNT: this.TABLE_COLUMNS.TAX_AMOUNT,
AMOUNT: this.TABLE_COLUMNS.TOTAL_AMOUNT,
TOTAL: this.TABLE_COLUMNS.TOTAL,
};
},
get GROUP_CUSTOM_COLUMNS() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {View} from 'react-native';
// ScrollView type is needed for the horizontal scroll ref; the project ScrollView component is used for rendering.
// eslint-disable-next-line no-restricted-imports
import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView as RNScrollView} from 'react-native';
import Button from '@components/Button';
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
import Checkbox from '@components/Checkbox';
import MenuItem from '@components/MenuItem';
Expand All @@ -32,6 +33,7 @@ import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useResponsiveLayoutOnWideRHP from '@hooks/useResponsiveLayoutOnWideRHP';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
Expand Down Expand Up @@ -141,6 +143,7 @@ function MoneyRequestReportTransactionList({
useCopySelectionHelper();
const {convertToDisplayString} = useCurrencyListActions();
const styles = useThemeStyles();
const theme = useTheme();
const StyleUtils = useStyleUtils();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['Location', 'CheckSquare', 'ReceiptPlus', 'Columns', 'Plus']);
const {translate, localeCompare} = useLocalize();
Expand Down Expand Up @@ -295,6 +298,7 @@ function MoneyRequestReportTransactionList({
// Always use default columns for money request report view (don't use user-customized search columns)
const isExpenseReportViewFromIOUReport = isIOUReport(report);
const shouldShowBillableColumn = isBillableEnabledOnPolicy(policy);
const shouldShowCommentsColumn = useMemo(() => Object.values(reportActions ?? {}).some((action) => (action?.childVisibleActionCount ?? 0) > 0), [reportActions]);
const columnsToShow = useMemo(() => {
return getColumnsToShow({
currentAccountID: currentUserDetails?.accountID,
Expand All @@ -303,10 +307,11 @@ function MoneyRequestReportTransactionList({
isExpenseReportView: true,
isExpenseReportViewFromIOUReport,
shouldShowBillableColumn,
shouldShowCommentsColumn,
shouldShowReimbursableColumn: hasNonReimbursableTransactions(transactions),
reportCurrency: report?.currency,
});
}, [currentUserDetails?.accountID, transactions, isExpenseReportViewFromIOUReport, reportDetailsColumns, shouldShowBillableColumn, report?.currency]);
}, [transactions, currentUserDetails?.accountID, isExpenseReportViewFromIOUReport, shouldShowBillableColumn, shouldShowCommentsColumn, reportDetailsColumns, report?.currency]);

const {windowWidth, windowHeight} = useWindowDimensions();
const minTableWidth = getTableMinWidth(columnsToShow);
Expand Down Expand Up @@ -519,6 +524,10 @@ function MoneyRequestReportTransactionList({
[translate],
);

const openColumnsPage = useCallback(() => {
Navigation.navigate(ROUTES.REPORT_SETTINGS_COLUMNS.getRoute(report.reportID));
}, [report.reportID]);

const selectedGroupByItem = useMemo(() => groupByItems.find((item) => item.value === currentGroupBy) ?? groupByItems.at(0), [groupByItems, currentGroupBy]);

const groupByOptions = useMemo(
Expand Down Expand Up @@ -714,6 +723,19 @@ function MoneyRequestReportTransactionList({
PopoverComponent={groupByPopoverComponent}
/>
)}
{!shouldUseNarrowLayout && !isExpenseReportViewFromIOUReport && (
<Button
link
small
shouldUseDefaultHover={false}
text={translate('search.columns')}
iconFill={theme.link}
iconHoverFill={theme.linkHover}
icon={expensifyIcons.Columns}
textStyles={[styles.textMicroBold]}
onPress={openColumnsPage}
/>
)}
</View>
{!shouldUseNarrowLayout && !shouldScrollHorizontally && tableHeaderContent}
{shouldScrollHorizontally ? (
Expand Down
4 changes: 4 additions & 0 deletions src/components/Search/SearchTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [
columnName: CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT,
translationKey: 'common.originalAmount',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.TOTAL,
translationKey: 'common.total',
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID,
translationKey: 'common.withdrawalID',
Expand Down
23 changes: 22 additions & 1 deletion src/components/TransactionItemRow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import StringUtils from '@libs/StringUtils';
import {
getAmount,
getAttendees,
getConvertedAmount,
getCurrency,
getDescription,
getExchangeRate,
Expand Down Expand Up @@ -259,7 +260,7 @@ function TransactionItemRow({
}
}, [transactionItem, translate, report]);

const exchangeRateMessage = getExchangeRate(transactionItem, report?.currency);
const exchangeRateMessage = getExchangeRate(transactionItem, report?.currency ?? policy?.outputCurrency);
const cardName = getCompanyCardDescription(translate, transactionItem?.cardName, transactionItem?.cardID, nonPersonalAndWorkspaceCards);
const transactionAttendees = useMemo(() => getAttendees(transactionItem, currentUserPersonalDetails), [transactionItem, currentUserPersonalDetails]);

Expand Down Expand Up @@ -584,6 +585,26 @@ function TransactionItemRow({
/>
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.TOTAL: {
const isFromExpenseReport = isExpenseReport(transactionItem.report ?? report);
const hasConvertedAmount = transactionItem.convertedAmount != null;
// Offline expenses don't have a BE-computed convertedAmount yet — fall back to the unconverted
// amount in the transaction's own currency so users don't see a misleading $0.00 placeholder.
const totalAmount = hasConvertedAmount ? getConvertedAmount(transactionItem, isFromExpenseReport, false, true) : getAmount(transactionItem, isFromExpenseReport, false, true);
// When converted, display in the report's output currency; otherwise use the transaction's own currency.
const totalCurrency = hasConvertedAmount ? (report?.currency ?? policy?.outputCurrency ?? getCurrency(transactionItem)) : getCurrency(transactionItem);
return (
<View
key={column}
style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, {isAmountColumnWide})]}
>
<AmountCell
total={totalAmount}
currency={totalCurrency}
/>
</View>
);
}
case CONST.SEARCH.TABLE_COLUMNS.REPORT_ID:
return (
<View
Expand Down
7 changes: 2 additions & 5 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
PersonalDetailsList,
Policy,
PrivatePersonalDetails,
Transaction,
WorkspaceCardsList,
} from '@src/types/onyx';
import type {UnassignedCard} from '@src/types/onyx/Card';
Expand Down Expand Up @@ -1724,11 +1725,7 @@ function getCardHintText(validFrom: string | undefined, validThru: string | unde
* The search API pre-resolves cardName, but local Onyx transactions have raw values.
* This ensures the report layout matches the search page.
*/
function resolveTransactionCardFields<T extends {cardID?: number; cardName?: string; bank?: string}>(
transactions: T[],
cardList: CardList | undefined,
translate: LocalizedTranslate,
): Array<T & {isCardFeedDeleted?: boolean}> {
function resolveTransactionCardFields<T extends Transaction>(transactions: T[], cardList: CardList | undefined, translate: LocalizedTranslate): Array<T & {isCardFeedDeleted?: boolean}> {
return transactions.map((transaction) => {
let updates: Partial<T & {isCardFeedDeleted?: boolean}> = {};

Expand Down
3 changes: 3 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@
};

let conciergeReportIDOnyxConnect: OnyxEntry<string>;
Onyx.connect({

Check warning on line 1040 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.CONCIERGE_REPORT_ID,
callback: (value) => {
conciergeReportIDOnyxConnect = value;
Expand All @@ -1045,7 +1045,7 @@
});

const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon';
Onyx.connect({

Check warning on line 1048 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -1063,7 +1063,7 @@
let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry<PersonalDetails>;
Onyx.connect({

Check warning on line 1066 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
if (deprecatedCurrentUserAccountID) {
Expand All @@ -1075,7 +1075,7 @@
});

let allReportsDraft: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 1078 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_DRAFT,
waitForCollectionCallback: true,
callback: (value) => (allReportsDraft = value),
Expand All @@ -1083,7 +1083,7 @@

let allPolicies: OnyxCollection<Policy>;
let policiesArray: Policy[] = [];
Onyx.connect({

Check warning on line 1086 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1093,7 +1093,7 @@
});

let allPolicyDrafts: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 1096 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_DRAFTS,
waitForCollectionCallback: true,
callback: (value) => (allPolicyDrafts = value),
Expand All @@ -1101,7 +1101,7 @@

let deprecatedAllReports: OnyxCollection<Report>;
let deprecatedReportsByPolicyID: ReportByPolicyMap;
Onyx.connect({

Check warning on line 1104 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -1137,14 +1137,14 @@
});

let betaConfiguration: OnyxEntry<BetaConfiguration> = {};
Onyx.connect({

Check warning on line 1140 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.BETA_CONFIGURATION,
callback: (value) => (betaConfiguration = value ?? {}),
});

let deprecatedAllTransactions: OnyxCollection<Transaction> = {};
let deprecatedReportsTransactions: Record<string, Transaction[]> = {};
Onyx.connect({

Check warning on line 1147 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1170,7 +1170,7 @@
});

let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 1173 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -13309,6 +13309,7 @@
CONST.SEARCH.TABLE_COLUMNS.CATEGORY,
CONST.SEARCH.TABLE_COLUMNS.TAG,
CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT,
CONST.SEARCH.TABLE_COLUMNS.TOTAL,
CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE,
CONST.SEARCH.TABLE_COLUMNS.BILLABLE,
CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE,
Expand Down Expand Up @@ -13343,6 +13344,8 @@
return getTag(transaction);
case CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT:
return getTransactionAmount(transaction, isExpenseReport(report), transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID);
case CONST.SEARCH.TABLE_COLUMNS.TOTAL:
return Math.abs(transaction.convertedAmount ?? 0);
case CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION:
return Parser.htmlToText(transaction.comment?.comment ?? '');
case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE:
Expand Down
39 changes: 27 additions & 12 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5035,6 +5035,7 @@ function getColumnsToShow({
isExpenseReportViewFromIOUReport = false,
shouldShowBillableColumn = false,
shouldShowReimbursableColumn = false,
shouldShowCommentsColumn = false,
reportCurrency,
shouldUseStrictDefaultExpenseColumns = false,
}: {
Expand All @@ -5047,8 +5048,10 @@ function getColumnsToShow({
isExpenseReportViewFromIOUReport?: boolean;
shouldShowBillableColumn?: boolean;
shouldShowReimbursableColumn?: boolean;
shouldUseStrictDefaultExpenseColumns?: boolean;
shouldShowCommentsColumn?: boolean;
reportCurrency?: string;
shouldUseStrictDefaultExpenseColumns?: boolean;
policy?: OnyxTypes.Policy;
}): SearchColumnType[] {
if (type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT) {
const defaultReportColumns: SearchColumnType[] = [
Expand Down Expand Up @@ -5162,11 +5165,11 @@ function getColumnsToShow({
[CONST.SEARCH.TABLE_COLUMNS.TAX_RATE]: false,
[CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: false,
[CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE]: false,
[CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT]: false,
[CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE]: shouldShowReimbursableColumn,
[CONST.SEARCH.TABLE_COLUMNS.BILLABLE]: shouldShowBillableColumn,
[CONST.SEARCH.TABLE_COLUMNS.COMMENTS]: true,
[CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: true,
[CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: false,
Copy link
Copy Markdown
Contributor

@ahmedGaber93 ahmedGaber93 May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Setting the amount column to false force hides it on the expense report page, even when it is selected in the column configuration.

Steps:

  1. Log in with a new account.
  2. Create a new workspace.
  3. Create 2 expenses.
  4. Open the expense report page.
  5. Configure the columns to display Amount.

Current: The Amount column is not displayed.

Expected: The user should be able to see the Amount column since it is selected in the column configuration.

Screen.Recording.2026-05-05.at.10.41.59.PM.mov

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahmedGaber93 this seems expected and aligns with OldDot.

2026-04-15_12-51-15.mp4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it. Now we need to have an expense with foreign currency to display the amount column on NewDot.

We need the Amount column to always be visible so that users can edit it inline.

Will discuss this on our PR, thanks!

[CONST.SEARCH.TABLE_COLUMNS.TOTAL]: true,
[CONST.SEARCH.TABLE_COLUMNS.COMMENTS]: shouldShowCommentsColumn,
}
: {
[CONST.SEARCH.TABLE_COLUMNS.AVATAR]: true,
Expand Down Expand Up @@ -5227,7 +5230,7 @@ function getColumnsToShow({
}
}

if (!addedColumns.has(CONST.SEARCH.TABLE_COLUMNS.COMMENTS)) {
if (shouldShowCommentsColumn && !addedColumns.has(CONST.SEARCH.TABLE_COLUMNS.COMMENTS)) {
result.push(CONST.SEARCH.TABLE_COLUMNS.COMMENTS);
}

Expand Down Expand Up @@ -5290,19 +5293,28 @@ function getColumnsToShow({
columns[CONST.SEARCH.TABLE_COLUMNS.CARD] = true;
}

if (transaction.taxCode) {
// If the transaction has any tax info (code, value, or amount),
// show both TAX_RATE and TAX_AMOUNT columns. Zero is valid tax data.
const hasTaxInfo = !!transaction.taxCode || transaction.taxAmount != null || (transaction.taxValue !== undefined && transaction.taxValue !== '');
if (hasTaxInfo) {
columns[CONST.SEARCH.TABLE_COLUMNS.TAX_RATE] = true;
}

if (transaction.taxAmount) {
columns[CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT] = true;
}
Comment on lines 5295 to 5302
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change caused regression - Tax columns shown when split expense

Please fix as follow-up.


const hasExchangeRate = getExchangeRate(transaction, reportCurrency) !== '';
if (hasExchangeRate || transaction.originalAmount) {
columns[CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT] = true;
if (hasExchangeRate) {
columns[CONST.SEARCH.TABLE_COLUMNS.EXCHANGE_RATE] = true;
}
// Expense report view: TOTAL (workspace currency) is always shown; add AMOUNT
// (transaction's own currency) when a conversion exists so both are visible.
// Search page: show ORIGINAL_AMOUNT column (transaction's original amount).
if (hasExchangeRate) {
if (isExpenseReportView) {
columns[CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT] = true;
} else {
columns[CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT] = true;
}
}

if (!Array.isArray(data)) {
const report = data[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`];
Expand Down Expand Up @@ -5385,8 +5397,11 @@ function getColumnsToShow({
CONST.SEARCH.TABLE_COLUMNS.TYPE,
CONST.SEARCH.TABLE_COLUMNS.DATE,
CONST.SEARCH.TABLE_COLUMNS.STATUS,
// TOTAL_AMOUNT (Amount) is data-driven in expense report view: shown only when a
// conversion exists. In search view, TOTAL_AMOUNT is always-true via the default
// columns map, so we don't need to list it here as non-data for either surface.
CONST.SEARCH.TABLE_COLUMNS.TOTAL,
CONST.SEARCH.TABLE_COLUMNS.COMMENTS,
CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT,
CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE,
CONST.SEARCH.TABLE_COLUMNS.BILLABLE,
CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID,
Expand Down
30 changes: 24 additions & 6 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1291,17 +1291,35 @@ function getTagArrayFromName(tagName: string): string[] {
*/
function getExchangeRate(transaction: TransactionWithOptionalSearchFields, reportCurrency?: string) {
const fromCurrency = getCurrency(transaction);
const toCurrency = transaction.groupCurrency ?? reportCurrency ?? fromCurrency;

if (transaction.groupExchangeRate && Number(transaction.groupExchangeRate) !== 1) {
return `${transaction.groupExchangeRate} ${fromCurrency}/${toCurrency}`;
// On the report view, "unconverted" means the transaction currency matches the report currency.
// groupCurrency reflects the user's default workspace and is unrelated to the report being viewed,
// so we must gate on reportCurrency here to match Expensify Classic behavior.
if (reportCurrency && fromCurrency === reportCurrency) {
return '';
}

if (!transaction.currencyConversionRate || Number(transaction.currencyConversionRate) === 1) {
return '';
// groupExchangeRate: search-page rate (fromCurrency → groupCurrency).
if (transaction.groupExchangeRate != null && transaction.groupCurrency && fromCurrency !== transaction.groupCurrency) {
const groupRate = Number(transaction.groupExchangeRate);
if (groupRate !== 1) {
return `${transaction.groupExchangeRate} ${fromCurrency}/${transaction.groupCurrency}`;
}
}

return `${transaction.currencyConversionRate} ${fromCurrency}/${toCurrency}`;
// currencyConversionRate: report-layout rate (fromCurrency → reportCurrency).
// When no reportCurrency is provided (e.g. search sort), fall back to groupCurrency so we can still
// surface a meaningful rate. We intentionally do not use transaction.currency here, since it reflects
// the pre-modification currency and is almost never the correct conversion target.
const conversionToCurrency = reportCurrency ?? transaction.groupCurrency;
if (conversionToCurrency && transaction.currencyConversionRate != null && fromCurrency !== conversionToCurrency) {
const conversionRate = Number(transaction.currencyConversionRate);
if (conversionRate !== 1) {
return `${transaction.currencyConversionRate} ${fromCurrency}/${conversionToCurrency}`;
}
}

return '';
}

/**
Expand Down
Loading
Loading