Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4c7c1bf
Revert "Revert "Add merge option when selecting transactions in the r…
youssef-lr Dec 20, 2025
bb69b49
Fix #78211: missing rate & distance field in confirmation page
youssef-lr Dec 20, 2025
e34dacc
Fix #78208: same receipt showing when previewing both receipts
youssef-lr Dec 20, 2025
46ee13a
Fix #78206: receipt not selected
youssef-lr Dec 21, 2025
3bee083
Fix #78182: Prevent two card transactions from being selected for mer…
youssef-lr Dec 21, 2025
09c00e7
Fix #78195: amount can be selected for merging in card transaction merge
youssef-lr Dec 21, 2025
4645592
Fix #78189: transactionID in path changes and cause not found page
youssef-lr Dec 21, 2025
570f8c0
Prevent overriding mergeTransaction values if we're selecting source …
youssef-lr Dec 21, 2025
abaf435
Clean up how we get data from search results
youssef-lr Dec 21, 2025
614b551
Fix #78179: Admins can select members expenses and their own for merging
youssef-lr Dec 21, 2025
a472f00
Merge branch 'main' into revert-78228-revert-73248-youssef_merge_from…
youssef-lr Dec 22, 2025
ab51686
Fix #78181: Allow merging negative expense from the "More" menu
youssef-lr Dec 22, 2025
e997ae0
Use utility and existing field to find out if card is coming form any…
youssef-lr Dec 22, 2025
6cbefc2
Fix #78190: Category field and Reimbursable toggle are missing in con…
youssef-lr Dec 22, 2025
771240b
Tidy up code
youssef-lr Dec 23, 2025
22f3569
Fix #78174: Fix navigating away from Reports page when merging from it
youssef-lr Dec 23, 2025
e740513
Merge branch 'main' into revert-78228-revert-73248-youssef_merge_from…
youssef-lr Dec 23, 2025
55906db
Fix TS
youssef-lr Dec 23, 2025
dde5865
Fix tests & lint
youssef-lr Dec 23, 2025
47f457b
Small perf improvement & test fix
youssef-lr Dec 23, 2025
9c91fbb
Add comment
youssef-lr Dec 23, 2025
9b97e43
Add some tests
youssef-lr Dec 23, 2025
21497de
Lint
youssef-lr Dec 23, 2025
93a61cd
Fix flakey test
youssef-lr Dec 23, 2025
199ca0e
Fix rate & distance not showing
youssef-lr Dec 23, 2025
0d6380b
Merge branch 'main' into revert-78228-revert-73248-youssef_merge_from…
youssef-lr Dec 23, 2025
6a2e49e
Fix #78209: Missing From field after merging
youssef-lr Dec 23, 2025
63f7317
Fix test
youssef-lr Dec 24, 2025
4d9ff20
Fetch report from search snapshot if not found in main collaction
youssef-lr Dec 24, 2025
1a72113
Lint
youssef-lr Dec 24, 2025
179304c
Spell fix
youssef-lr Dec 24, 2025
819bfa6
Fix navigation after merging from search for good
youssef-lr Dec 25, 2025
f10d2a7
TS fixes
youssef-lr Dec 25, 2025
4687f5b
Fix tax & attendees showing when policy has them disabled
youssef-lr Dec 25, 2025
59a31e2
Lint
youssef-lr Dec 25, 2025
5e3f6b2
Lint
youssef-lr Dec 25, 2025
4b7ebff
Use transactionThreadReportID set in search transactions
youssef-lr Dec 25, 2025
51b1156
Test forcing eslint ignore cache
youssef-lr Dec 25, 2025
7b661c4
Revert testing changes
youssef-lr Dec 25, 2025
093382a
TS
youssef-lr Dec 25, 2025
5bc6b91
Merge branch 'main' into revert-78228-revert-73248-youssef_merge_from…
youssef-lr Dec 26, 2025
28bfc6c
Fix test
youssef-lr Dec 26, 2025
a64a717
Add check in isFromCreditCardImport for transactions coming from search
youssef-lr Dec 26, 2025
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/CONFIG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default {
SUFFIX: ENVIRONMENT === CONST.ENVIRONMENT.DEV ? get(Config, 'PUSHER_DEV_SUFFIX', '') : '',
CLUSTER: 'mt1',
},
SITE_TITLE: 'New Expensify',
SITE_TITLE: ENVIRONMENT === CONST.ENVIRONMENT.DEV ? 'New Expensify [dev]' : 'New Expensify',
FAVICON: {
DEFAULT: '/favicon.png',
UNREAD: '/favicon-unread.png',
Expand Down
1 change: 1 addition & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6697,6 +6697,7 @@ const CONST = {
PAY: 'pay',
SUBMIT: 'submit',
HOLD: 'hold',
MERGE: 'merge',
UNHOLD: 'unhold',
DELETE: 'delete',
REJECT: 'reject',
Expand Down
42 changes: 26 additions & 16 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2767,16 +2767,14 @@ const ROUTES = {

TRANSACTION_RECEIPT: {
route: 'r/:reportID/transaction/:transactionID/receipt/:action?/:iouType?',
getRoute: (reportID: string | undefined, transactionID: string | undefined, readonly = false, isFromReviewDuplicates = false, mergeTransactionID?: string) => {
getRoute: (reportID: string | undefined, transactionID: string | undefined, readonly = false, mergeTransactionID?: string) => {
if (!reportID) {
Log.warn('Invalid reportID is used to build the TRANSACTION_RECEIPT route');
}
if (!transactionID) {
Log.warn('Invalid transactionID is used to build the TRANSACTION_RECEIPT route');
}
return `r/${reportID}/transaction/${transactionID}/receipt?readonly=${readonly}${
isFromReviewDuplicates ? '&isFromReviewDuplicates=true' : ''
}${mergeTransactionID ? `&mergeTransactionID=${mergeTransactionID}` : ''}` as const;
return `r/${reportID}/transaction/${transactionID}/receipt?readonly=${readonly}${mergeTransactionID ? `&mergeTransactionID=${mergeTransactionID}` : ''}` as const;
},
},

Expand Down Expand Up @@ -2835,28 +2833,40 @@ const ROUTES = {
getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/confirm` as const, backTo),
},
MERGE_TRANSACTION_LIST_PAGE: {
route: 'r/:transactionID/merge',
route: 'merge/:transactionID',

// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getRoute: (transactionID: string | undefined, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge` as const, backTo),
getRoute: (transactionID: string, backTo: string, isOnSearch = false) => {
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
const url = getUrlWithBackToParam(`merge/${transactionID}` as const, backTo);
return isOnSearch ? (`${url}&isOnSearch=true` as const) : url;
},
},
MERGE_TRANSACTION_RECEIPT_PAGE: {
route: 'r/:transactionID/merge/receipt',
route: 'merge/:transactionID/receipt',

// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getRoute: (transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge/receipt` as const, backTo),
getRoute: (transactionID: string, backTo: string, isOnSearch = false) => {
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
const url = getUrlWithBackToParam(`merge/${transactionID}/receipt` as const, backTo);
return isOnSearch ? (`${url}&isOnSearch=true` as const) : url;
},
},
MERGE_TRANSACTION_DETAILS_PAGE: {
route: 'r/:transactionID/merge/details',
route: 'merge/:transactionID/details',

// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getRoute: (transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge/details` as const, backTo),
getRoute: (transactionID: string, backTo: string, isOnSearch = false) => {
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
const url = getUrlWithBackToParam(`merge/${transactionID}/details` as const, backTo);
return isOnSearch ? (`${url}&isOnSearch=true` as const) : url;
},
},
MERGE_TRANSACTION_CONFIRMATION_PAGE: {
route: 'r/:transactionID/merge/confirmation',
route: 'merge/:transactionID/confirmation',

// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
getRoute: (transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${transactionID}/merge/confirmation` as const, backTo),
getRoute: (transactionID: string, backTo: string, isOnSearch = false) => {
// eslint-disable-next-line no-restricted-syntax -- Legacy route generation
const url = getUrlWithBackToParam(`merge/${transactionID}/confirmation` as const, backTo);
return isOnSearch ? (`${url}&isOnSearch=true` as const) : url;
},
},
POLICY_ACCOUNTING_XERO_IMPORT: {
route: 'workspaces/:policyID/accounting/xero/import',
Expand Down
9 changes: 5 additions & 4 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import useThrottledButtonState from '@hooks/useThrottledButtonState';
import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport';
import useTransactionViolations from '@hooks/useTransactionViolations';
import {openOldDotLink} from '@libs/actions/Link';
import {setupMergeTransactionData} from '@libs/actions/MergeTransaction';
import {setupMergeTransactionDataAndNavigate} from '@libs/actions/MergeTransaction';
import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import {createTransactionThreadReport, deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF, exportToIntegration, markAsManuallyExported} from '@libs/actions/Report';
import {getExportTemplates, queueExportSearchWithTemplate, search} from '@libs/actions/Search';
Expand Down Expand Up @@ -244,7 +244,7 @@ function MoneyReportHeader({
'ReceiptPlus',
] as const);
const [lastDistanceExpenseType] = useOnyx(ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE, {canBeMissing: true});
const {translate} = useLocalize();
const {translate, localeCompare} = useLocalize();

const exportTemplates = useMemo(
() => getExportTemplates(integrationsExportTemplates ?? [], csvExportLayouts ?? {}, translate, policy),
Expand Down Expand Up @@ -424,6 +424,7 @@ function MoneyReportHeader({
);

const [offlineModalVisible, setOfflineModalVisible] = useState(false);
const isOnSearch = route.name.toLowerCase().startsWith('search');
const {options: originalSelectedTransactionsOptions, handleDeleteTransactions} = useSelectedTransactionsActions({
report: moneyRequestReport,
reportActions,
Expand All @@ -433,6 +434,7 @@ function MoneyReportHeader({
onExportOffline: () => setOfflineModalVisible(true),
policy,
beginExportWithTemplate: (templateName, templateType, transactionIDList, policyID) => beginExportWithTemplate(templateName, templateType, transactionIDList, policyID),
isOnSearch,
});

const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]);
Expand Down Expand Up @@ -1254,8 +1256,7 @@ function MoneyReportHeader({
return;
}

setupMergeTransactionData(currentTransaction.transactionID, {targetTransactionID: currentTransaction.transactionID});
Navigation.navigate(ROUTES.MERGE_TRANSACTION_LIST_PAGE.getRoute(currentTransaction.transactionID, Navigation.getActiveRoute()));
setupMergeTransactionDataAndNavigate(currentTransaction.transactionID, [currentTransaction], localeCompare);
},
},
[CONST.REPORT.SECONDARY_ACTIONS.DUPLICATE]: {
Expand Down
8 changes: 4 additions & 4 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useThrottledButtonState from '@hooks/useThrottledButtonState';
import useTransactionViolations from '@hooks/useTransactionViolations';
import {deleteTrackExpense, initSplitExpense, markRejectViolationAsResolved} from '@libs/actions/IOU';
import {setupMergeTransactionData} from '@libs/actions/MergeTransaction';
import {setupMergeTransactionDataAndNavigate} from '@libs/actions/MergeTransaction';
import {setNameValuePair} from '@libs/actions/User';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -130,7 +130,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
const shouldShowLoadingBar = useLoadingBarVisibility();
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const {translate, localeCompare} = useLocalize();
const icons = useMemoizedLazyExpensifyIcons(['CreditCardHourglass', 'ReceiptScan']);
const {login: currentUserLogin, email, accountID} = useCurrentUserPersonalDetails();
const defaultExpensePolicy = useDefaultExpensePolicy();
Expand Down Expand Up @@ -400,8 +400,8 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
return;
}

setupMergeTransactionData(transaction.transactionID, {targetTransactionID: transaction.transactionID});
Navigation.navigate(ROUTES.MERGE_TRANSACTION_LIST_PAGE.getRoute(transaction.transactionID, Navigation.getActiveRoute()));
const isOnSearch = route.name.toLowerCase().startsWith('search');
setupMergeTransactionDataAndNavigate(transaction.transactionID, [transaction], localeCompare, [], false, isOnSearch);
},
},
[CONST.REPORT.SECONDARY_ACTIONS.DUPLICATE]: {
Expand Down
29 changes: 6 additions & 23 deletions src/components/ReportActionItem/MoneyRequestReceiptView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import mapValues from 'lodash/mapValues';
import React, {useCallback, useMemo, useState} from 'react';
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
Expand Down Expand Up @@ -27,7 +27,7 @@ import {
getCreationReportErrors,
isInvoiceReport,
isPaidGroupPolicy,
isTrackExpenseReport,
isTrackExpenseReportNew,
} from '@libs/ReportUtils';
import {
didReceiptScanSucceed as didReceiptScanSucceedTransactionUtils,
Expand Down Expand Up @@ -59,9 +59,6 @@ type MoneyRequestReceiptViewProps = {
/** Whether we should show Money Request with disabled all fields */
readonly?: boolean;

/** whether or not this report is from review duplicates */
isFromReviewDuplicates?: boolean;

/** Updated transaction to show in duplicate & merge transaction flow */
updatedTransaction?: OnyxEntry<OnyxTypes.Transaction>;

Expand Down Expand Up @@ -91,7 +88,6 @@ function MoneyRequestReceiptView({
report,
readonly = false,
updatedTransaction,
isFromReviewDuplicates = false,
fillSpace = false,
mergeTransactionID,
isDisplayedInWideRHP = false,
Expand All @@ -112,7 +108,7 @@ function MoneyRequestReceiptView({
const [isLoading, setIsLoading] = useState(true);
const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report.parentReportActionID] : undefined;
const {iouReport, chatReport: chatIOUReport, isChatIOUReportArchived} = useGetIOUReportFromReportAction(parentReportAction);
const isTrackExpense = isTrackExpenseReport(report);
const isTrackExpense = !mergeTransactionID && isTrackExpenseReportNew(report, parentReport, parentReportAction);
const moneyRequestReport = parentReport;
const linkedTransactionID = useMemo(() => {
if (!parentReportAction) {
Expand Down Expand Up @@ -163,7 +159,7 @@ function MoneyRequestReceiptView({

const transactionToCheck = updatedTransaction ?? transaction;
const doesTransactionHaveReceipt = !!transactionToCheck?.receipt && !isEmptyObject(transactionToCheck?.receipt);
const shouldShowReceiptEmptyState = !isInvoice && !hasReceipt && !!transaction && !doesTransactionHaveReceipt;
const shouldShowReceiptEmptyState = !isInvoice && !hasReceipt && !!transactionToCheck && !doesTransactionHaveReceipt;

const [receiptImageViolations, receiptViolations] = useMemo(() => {
const imageViolations = [];
Expand Down Expand Up @@ -213,7 +209,7 @@ function MoneyRequestReceiptView({
[transaction?.errors, parentReportAction?.errors],
);

const dismissReceiptError = useCallback(() => {
const dismissReceiptError = () => {
if (!report?.reportID) {
return;
}
Expand Down Expand Up @@ -248,19 +244,7 @@ function MoneyRequestReceiptView({
}
navigateToConciergeChatAndDeleteReport(report.reportID, true, true);
}
}, [
transaction,
chatReport,
parentReportAction,
linkedTransactionID,
report?.reportID,
iouReport,
chatIOUReport,
isChatIOUReportArchived,
errorsWithoutReportCreation,
reportCreationError,
isInNarrowPaneModal,
]);
};

let receiptStyle: StyleProp<ViewStyle>;

Expand Down Expand Up @@ -352,7 +336,6 @@ function MoneyRequestReceiptView({
transaction={updatedTransaction ?? transaction}
enablePreviewModal
readonly={readonly || !canEditReceipt}
isFromReviewDuplicates={isFromReviewDuplicates}
mergeTransactionID={mergeTransactionID}
report={report}
onLoad={() => setIsLoading(false)}
Expand Down
Loading
Loading