From b4980907d2991ffe11c87b34e49a6fe41f2279e7 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 26 Nov 2025 01:19:43 +0700 Subject: [PATCH 01/22] Update create transaction Manual flow to ask for merchant after asking for amount --- src/libs/IOUUtils.ts | 33 +++++++++++++++++-- .../iou/request/step/IOURequestStepAmount.tsx | 11 +++++-- .../request/step/IOURequestStepMerchant.tsx | 9 +++++ .../step/IOURequestStepParticipants.tsx | 14 +++++++- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index b4639e4fe93a8..2d8b9322447c5 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -4,14 +4,15 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {OnyxInputOrEntry, PersonalDetails, Policy, Report} from '@src/types/onyx'; import type {Attendee} from '@src/types/onyx/IOU'; +import type Transaction from '@src/types/onyx/Transaction'; import SafeString from '@src/utils/SafeString'; import type {IOURequestType} from './actions/IOU'; import {getCurrencyUnit} from './CurrencyUtils'; import Navigation from './Navigation/Navigation'; import Performance from './Performance'; import {isPaidGroupPolicy} from './PolicyUtils'; -import {getReportTransactions} from './ReportUtils'; -import {getCurrency, getTagArrayFromName} from './TransactionUtils'; +import {getReportTransactions, isExpenseRequest, isPolicyExpenseChat} from './ReportUtils'; +import {getCurrency, getTagArrayFromName, isMerchantMissing, isScanRequest} from './TransactionUtils'; function navigateToStartMoneyRequestStep(requestType: IOURequestType, iouType: IOUType, transactionID: string, reportID: string, iouAction?: IOUAction): void { if (iouAction === CONST.IOU.ACTION.CATEGORIZE || iouAction === CONST.IOU.ACTION.SUBMIT || iouAction === CONST.IOU.ACTION.SHARE) { @@ -239,6 +240,33 @@ function formatCurrentUserToAttendee(currentUser?: PersonalDetails, reportID?: s return [initialAttendee]; } +/** + * Checks if merchant is required and missing for a transaction. + * Merchant is required for policy expense chats, expense requests, or when any participant is a policy expense chat. + * For scan requests, merchant is not required unless it's a split bill being edited. + * + * @param transaction - The transaction to check + * @param report - The report associated with the transaction + * @param isEditingSplitBill - Whether this is editing a split bill + * @returns true if merchant is required and missing, false otherwise + */ +function shouldRequireMerchant(transaction: OnyxInputOrEntry | undefined, report: OnyxInputOrEntry | undefined, isEditingSplitBill = false): boolean { + if (!transaction || !report) { + return false; + } + + // Check if merchant is required based on report type and participants + const isMerchantRequired = !!(isPolicyExpenseChat(report) || isExpenseRequest(report) || transaction?.participants?.some((participant) => !!participant.isPolicyExpenseChat)); + + // For scan requests, merchant is not required unless it's a split bill being edited + if (isScanRequest(transaction) && !isEditingSplitBill) { + return false; + } + + // Return true if merchant is required and missing + return isMerchantRequired && isMerchantMissing(transaction); +} + export { calculateAmount, insertTagIntoTransactionTagsString, @@ -251,4 +279,5 @@ export { formatCurrentUserToAttendee, navigateToParticipantPage, shouldShowReceiptEmptyState, + shouldRequireMerchant, }; diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index d6ff8ae34078e..ceaa616149f97 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -16,7 +16,7 @@ import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; import {setTransactionReport} from '@libs/actions/Transaction'; import {createDraftTransaction, removeDraftTransaction} from '@libs/actions/TransactionEdit'; import {convertToBackendAmount, isValidCurrencyCode} from '@libs/CurrencyUtils'; -import {navigateToParticipantPage} from '@libs/IOUUtils'; +import {navigateToParticipantPage, shouldRequireMerchant} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import {isPaidGroupPolicy} from '@libs/PolicyUtils'; @@ -252,8 +252,15 @@ function IOURequestStepAmount({ setSplitShares(transaction, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, participantAccountIDs); } setMoneyRequestParticipantsFromReport(transactionID, report).then(() => { + // Check if merchant is required and missing before proceeding + // If so, navigate to merchant step first + if (shouldRequireMerchant(transaction, report, isEditingSplitBill)) { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID, undefined, reportActionID)); + return; + } navigateToConfirmationPage(); }); + return; } @@ -273,7 +280,7 @@ function IOURequestStepAmount({ const resetToDefaultWorkspace = () => { setTransactionReport(transactionID, {reportID: transactionReportID}, true); setMoneyRequestParticipantsFromReport(transactionID, activePolicyExpenseChat).then(() => { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.SUBMIT, transactionID, activePolicyExpenseChat?.reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, activePolicyExpenseChat?.reportID, undefined, reportActionID)); }); }; diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 5b478dc50dad9..2e73c0eb639e3 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -17,6 +17,7 @@ import {isValidInputLength} from '@libs/ValidationUtils'; import {setDraftSplitTransaction, setMoneyRequestMerchant, updateMoneyRequestMerchant} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/MoneyRequestMerchantForm'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -109,7 +110,15 @@ function IOURequestStepMerchant({ setMoneyRequestMerchant(transactionID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, !isEditing); if (isEditing) { updateMoneyRequestMerchant(transactionID, reportID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, policy, policyTags, policyCategories); + navigateBack(); + return; + } + + if (!backTo) { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID)); + return; } + navigateBack(); }; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 9b3d7ef220379..075ce78263936 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -338,6 +338,9 @@ function IOURequestStepParticipants({ return; } + const firstParticipant = participants?.at(0); + const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat; + const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( action, iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.SUBMIT : iouType, @@ -348,9 +351,18 @@ function IOURequestStepParticipants({ action === CONST.IOU.ACTION.SHARE ? Navigation.getActiveRoute() : undefined, ); + // eslint-disable-next-line no-nested-ternary const route = isCategorizing ? ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, initialTransactionID, selectedReportID.current || reportID, iouConfirmationPageRoute) - : iouConfirmationPageRoute; + : isMerchantRequired + ? ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute( + action, + iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.SUBMIT : iouType, + initialTransactionID, + newReportID, + undefined, + ) + : iouConfirmationPageRoute; Performance.markStart(CONST.TIMING.OPEN_CREATE_EXPENSE_APPROVE); waitForKeyboardDismiss(() => { From 5dff27316b1492bf6db4e508f925434f98202c2a Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 3 Dec 2025 15:39:30 +0700 Subject: [PATCH 02/22] fix participant step --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 54cda14213cec..658d123c69b87 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -96,6 +96,7 @@ function IOURequestStepParticipants({ // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant const selectedReportID = useRef(participants?.length === 1 ? (participants.at(0)?.reportID ?? reportID) : reportID); + const selectedParticipants = useRef(participants); // We can assume that shouldAutoReport is true as the initial value is not used. shouldAutoReport is only used after the selectedReportID changes in addParticipant where we'd update shouldAutoReport too const shouldAutoReport = useRef(true); const numberOfParticipants = useRef(participants?.length ?? 0); @@ -223,6 +224,7 @@ function IOURequestStepParticipants({ (val: Participant[]) => { HttpUtils.cancelPendingRequests(READ_COMMANDS.SEARCH_FOR_REPORTS); + selectedParticipants.current = val; const firstParticipant = val.at(0); if (firstParticipant?.isSelfDM && !isSplitRequest) { @@ -346,7 +348,7 @@ function IOURequestStepParticipants({ return; } - const firstParticipant = participants?.at(0); + const firstParticipant = selectedParticipants.current?.at(0); const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat; const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( From 56db3b119f952ea2c6f8f6e581975f4a1866ad7d Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 3 Dec 2025 15:41:20 +0700 Subject: [PATCH 03/22] correct shouldRequireMerchant --- src/libs/IOUUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 2d8b9322447c5..31734543bde88 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -263,8 +263,7 @@ function shouldRequireMerchant(transaction: OnyxInputOrEntry | unde return false; } - // Return true if merchant is required and missing - return isMerchantRequired && isMerchantMissing(transaction); + return isMerchantRequired; } export { From 8a5396901b72b4b50d6ec57fee267f4a5bf4c4b6 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 3 Dec 2025 15:48:53 +0700 Subject: [PATCH 04/22] fix lint --- src/libs/IOUUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 31734543bde88..d0c0d661821ef 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -12,7 +12,7 @@ import Navigation from './Navigation/Navigation'; import Performance from './Performance'; import {isPaidGroupPolicy} from './PolicyUtils'; import {getReportTransactions, isExpenseRequest, isPolicyExpenseChat} from './ReportUtils'; -import {getCurrency, getTagArrayFromName, isMerchantMissing, isScanRequest} from './TransactionUtils'; +import {getCurrency, getTagArrayFromName, isScanRequest} from './TransactionUtils'; function navigateToStartMoneyRequestStep(requestType: IOURequestType, iouType: IOUType, transactionID: string, reportID: string, iouAction?: IOUAction): void { if (iouAction === CONST.IOU.ACTION.CATEGORIZE || iouAction === CONST.IOU.ACTION.SUBMIT || iouAction === CONST.IOU.ACTION.SHARE) { From 66d0de0ce197a4feb7200f6a4ed3cf76cc15ed07 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Sun, 7 Dec 2025 21:52:35 +0700 Subject: [PATCH 05/22] fix navigate issue --- src/libs/IOUUtils.ts | 2 +- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 9748a23408993..b93c07bbe82aa 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -251,7 +251,7 @@ function formatCurrentUserToAttendee(currentUser?: PersonalDetails, reportID?: s * @returns true if merchant is required and missing, false otherwise */ function shouldRequireMerchant(transaction: OnyxInputOrEntry | undefined, report: OnyxInputOrEntry | undefined, isEditingSplitBill = false): boolean { - if (!transaction || !report) { + if (!transaction) { return false; } diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 9cac1c23ad3f3..c6a18d5e9e4ff 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -292,6 +292,10 @@ function IOURequestStepAmount({ const chatReportID = selectedReport?.chatReportID ?? iouReportID; Navigation.setNavigationActionToMicrotaskQueue(() => { + if (shouldRequireMerchant(transaction, selectedReport, isEditingSplitBill)) { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(CONST.IOU.ACTION.CREATE, navigationIOUType, transactionID, chatReportID, undefined, reportActionID)); + return; + } Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, navigationIOUType, transactionID, chatReportID)); }); } else { From 316fe35bc6004eccd9bea4ad686c4410dbc64e17 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 9 Dec 2025 00:40:18 +0700 Subject: [PATCH 06/22] fix scan request case --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index f01a2e7e5a860..7cb662868efd2 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -357,7 +357,7 @@ function IOURequestStepParticipants({ } const firstParticipant = selectedParticipants.current?.at(0); - const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction); + const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && !isMovingTransactionFromTrackExpense && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( action, @@ -396,7 +396,7 @@ function IOURequestStepParticipants({ }); } }); - }, [action, participants, iouType, initialTransaction, transactions, initialTransactionID, reportID, waitForKeyboardDismiss, isMovingTransactionFromTrackExpense, backTo, introSelected]); + }, [action, participants, iouType, initialTransaction, transactions, initialTransactionID, reportID, waitForKeyboardDismiss, isMovingTransactionFromTrackExpense, backTo, introSelected, iouRequestType]); const navigateBack = useCallback(() => { if (backTo) { From 65461b45f332c16302240897b946dd25668550cc Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 9 Dec 2025 00:45:35 +0700 Subject: [PATCH 07/22] fix scan case --- .../step/IOURequestStepParticipants.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 7cb662868efd2..3beca2dde78de 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -357,7 +357,8 @@ function IOURequestStepParticipants({ } const firstParticipant = selectedParticipants.current?.at(0); - const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && !isMovingTransactionFromTrackExpense && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; + const isMerchantRequired = + !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && !isMovingTransactionFromTrackExpense && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( action, @@ -396,7 +397,20 @@ function IOURequestStepParticipants({ }); } }); - }, [action, participants, iouType, initialTransaction, transactions, initialTransactionID, reportID, waitForKeyboardDismiss, isMovingTransactionFromTrackExpense, backTo, introSelected, iouRequestType]); + }, [ + action, + participants, + iouType, + initialTransaction, + transactions, + initialTransactionID, + reportID, + waitForKeyboardDismiss, + isMovingTransactionFromTrackExpense, + backTo, + introSelected, + iouRequestType, + ]); const navigateBack = useCallback(() => { if (backTo) { From 858426f13edd3331f824f8b1525414ab364528ab Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 11 Dec 2025 16:15:13 +0700 Subject: [PATCH 08/22] add backTo param --- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index b8aaf8d42ddf3..0521287c8ebb1 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -132,7 +132,7 @@ function IOURequestStepMerchant({ } if (!backTo) { - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID, undefined, undefined, Navigation.getActiveRoute())); return; } From fc0971fe4a00ee41be627710c43b038ae128ff33 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 15 Dec 2025 15:24:47 +0700 Subject: [PATCH 09/22] run prettier --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index b541738f2ccdc..72e464ad1331f 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -411,7 +411,7 @@ function IOURequestStepParticipants({ policyForMovingExpenses, introSelected, backTo, - iouRequestType + iouRequestType, ]); const navigateBack = useCallback(() => { From b1bff4ce0d4095c14319d00be5d34f8b3e3b33bc Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Mon, 15 Dec 2025 15:27:31 +0700 Subject: [PATCH 10/22] fix submit to someone case --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 72e464ad1331f..512aa8f453cf9 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -359,7 +359,7 @@ function IOURequestStepParticipants({ const firstParticipant = selectedParticipants.current?.at(0); const isMerchantRequired = - !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && !isMovingTransactionFromTrackExpense && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; + !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( action, From bf0bc56334edcc9dd5c6302aab52fad85feb44ea Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 16 Dec 2025 00:44:10 +0700 Subject: [PATCH 11/22] fix prettier --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 512aa8f453cf9..857111708939f 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -358,8 +358,7 @@ function IOURequestStepParticipants({ } const firstParticipant = selectedParticipants.current?.at(0); - const isMerchantRequired = - !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; + const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( action, From 9db4198469d7616a251c27174a4c37a5b5ca9c0a Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 17 Dec 2025 14:47:26 +0700 Subject: [PATCH 12/22] fix dependency --- .../request/step/IOURequestStepParticipants.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index d4ac29cc4e3f7..21393ced9dbe8 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -396,7 +396,20 @@ function IOURequestStepParticipants({ }); } }); - }, [action, participants, iouType, initialTransaction, transactions, initialTransactionID, reportID, waitForKeyboardDismiss, isMovingTransactionFromTrackExpense, backTo, introSelected]); + }, [ + action, + participants, + iouType, + initialTransaction, + iouRequestType, + initialTransactionID, + reportID, + waitForKeyboardDismiss, + transactions, + isMovingTransactionFromTrackExpense, + introSelected, + backTo, + ]); const navigateBack = useCallback(() => { if (backTo) { From 1d5c45c5f03afac25075bf9204130df0ce330826 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Sun, 21 Dec 2025 22:24:03 +0700 Subject: [PATCH 13/22] fix saving without changing merchant case --- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 79ed3a978e668..fc539150662a0 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -110,6 +110,10 @@ function IOURequestStepMerchant({ // In case the merchant hasn't been changed, do not make the API request. // In case the merchant has been set to empty string while current merchant is partial, do nothing too. if (newMerchant === merchant || (newMerchant === '' && merchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT)) { + if (!isEditing && !backTo) { + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID, undefined, undefined, Navigation.getActiveRoute())); + return; + } navigateBack(); return; } From 3a22f99c629478cd13a6988ba21c731c6387d6a8 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 30 Dec 2025 16:34:28 +0700 Subject: [PATCH 14/22] fix edit merchant issue --- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index a0ac406141161..879876c11288b 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -139,8 +139,6 @@ function IOURequestStepMerchant({ currentUserEmailParam, isASAPSubmitBetaEnabled, ); - navigateBack(); - return; } setIsSaved(true); shouldNavigateAfterSaveRef.current = true; From fed4a50a584fb9a82eddc643ea01fa5a75dc5b51 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Tue, 6 Jan 2026 01:12:11 +0700 Subject: [PATCH 15/22] fix merchant issue and navigate issue --- src/pages/iou/request/step/IOURequestStepMerchant.tsx | 10 +++++++--- .../iou/request/step/IOURequestStepParticipants.tsx | 9 ++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepMerchant.tsx b/src/pages/iou/request/step/IOURequestStepMerchant.tsx index 879876c11288b..d0d8a76d764de 100644 --- a/src/pages/iou/request/step/IOURequestStepMerchant.tsx +++ b/src/pages/iou/request/step/IOURequestStepMerchant.tsx @@ -73,9 +73,12 @@ function IOURequestStepMerchant({ Navigation.goBack(backTo); }, [backTo]); - useFocusEffect(() => { - setIsSaved(false); - }); + useFocusEffect( + useCallback(() => { + setIsSaved(false); + setCurrentMerchant(initialMerchant); + }, [initialMerchant]), + ); useEffect(() => { if (!isSaved || !shouldNavigateAfterSaveRef.current) { @@ -169,6 +172,7 @@ function IOURequestStepMerchant({ inputID={INPUT_IDS.MONEY_REQUEST_MERCHANT} name={INPUT_IDS.MONEY_REQUEST_MERCHANT} defaultValue={initialMerchant} + value={currentMerchant} onValueChange={updateMerchantRef} label={translate('common.merchant')} accessibilityLabel={translate('common.merchant')} diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 21393ced9dbe8..40de27285bf10 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -378,21 +378,24 @@ function IOURequestStepParticipants({ iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.SUBMIT : iouType, initialTransactionID, newReportID, - undefined, ) : iouConfirmationPageRoute; Performance.markStart(CONST.TIMING.OPEN_CREATE_EXPENSE_APPROVE); waitForKeyboardDismiss(() => { // If the backTo parameter is set, we should navigate back to the confirmation screen that is already on the stack. - if (backTo) { + if (backTo && !isMerchantRequired) { // We don't want to compare params because we just changed the participants. Navigation.goBack(route, {compareParams: false}); } else { + // If the merchant step is required and the backTo parameter is set, we need to go back the the confirmation screen first and then navigate to the merchant page with forceReplace to remove this screen from the stack + if (isMerchantRequired && backTo) { + Navigation.goBack(); + } // We wrap navigation in setNavigationActionToMicrotaskQueue so that data loading in Onyx and navigation do not occur simultaneously, which resets the amount to 0. // More information can be found here: https://github.com/Expensify/App/issues/73728 Navigation.setNavigationActionToMicrotaskQueue(() => { - Navigation.navigate(route); + Navigation.navigate(route, {forceReplace: isMerchantRequired && !!backTo}); }); } }); From 1e38ac7c7e6576505ed4fe49b3cdd22d7c1b8a92 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Sat, 10 Jan 2026 20:27:25 +0700 Subject: [PATCH 16/22] run prettier --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 50ac9222abd31..40de27285bf10 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -21,7 +21,7 @@ import {isPaidGroupPolicy} from '@libs/PolicyUtils'; import {findSelfDMReportID, generateReportID, isInvoiceRoomWithID} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {endSpan} from '@libs/telemetry/activeSpans'; -import {getRequestType, isCorporateCardTransaction, isPerDiemRequest, isMerchantMissing} from '@libs/TransactionUtils'; +import {getRequestType, isCorporateCardTransaction, isMerchantMissing, isPerDiemRequest} from '@libs/TransactionUtils'; import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestParticipantsSelector'; import { navigateToStartStepIfScanFileCannotBeRead, From 1944da84bd7ec23904a93ab9ed20468f550e8c02 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 16 Jan 2026 14:22:26 +0700 Subject: [PATCH 17/22] remove disable lint --- .../step/IOURequestStepParticipants.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 268ba9b3b98ed..f84f7ba209cc7 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -37,6 +37,7 @@ import {createDraftWorkspace} from '@userActions/Policy/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; @@ -373,17 +374,17 @@ function IOURequestStepParticipants({ action === CONST.IOU.ACTION.SHARE ? Navigation.getActiveRoute() : undefined, ); - // eslint-disable-next-line no-nested-ternary - const route = isCategorizing - ? ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, initialTransactionID, selectedReportID.current || reportID, iouConfirmationPageRoute) - : isMerchantRequired - ? ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute( - action, - iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.SUBMIT : iouType, - initialTransactionID, - newReportID, - ) - : iouConfirmationPageRoute; + let route: Route = iouConfirmationPageRoute; + if (isCategorizing) { + route = ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, initialTransactionID, selectedReportID.current || reportID, iouConfirmationPageRoute); + } else if (isMerchantRequired) { + route = ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute( + action, + iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.SUBMIT : iouType, + initialTransactionID, + newReportID, + ); + } Performance.markStart(CONST.TIMING.OPEN_CREATE_EXPENSE_APPROVE); waitForKeyboardDismiss(() => { From ed9831fb73f1895839e9c09dbcd76bb43f7244c7 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:22:46 +0700 Subject: [PATCH 18/22] Update src/pages/iou/request/step/IOURequestStepParticipants.tsx Co-authored-by: Carlos Alvarez --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index f84f7ba209cc7..c9cc8cdefe17b 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -97,6 +97,7 @@ function IOURequestStepParticipants({ // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant const selectedReportID = useRef(participants?.length === 1 ? (participants.at(0)?.reportID ?? reportID) : reportID); const selectedParticipants = useRef(participants); + // We can assume that shouldAutoReport is true as the initial value is not used. shouldAutoReport is only used after the selectedReportID changes in addParticipant where we'd update shouldAutoReport too const shouldAutoReport = useRef(true); const numberOfParticipants = useRef(participants?.length ?? 0); From 010c06da8cdf3b211e675a0fe5bf4899d4dbc671 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:23:01 +0700 Subject: [PATCH 19/22] Update src/pages/iou/request/step/IOURequestStepParticipants.tsx Co-authored-by: Carlos Alvarez --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index c9cc8cdefe17b..9ef3ab8501b15 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -364,7 +364,6 @@ function IOURequestStepParticipants({ const firstParticipant = selectedParticipants.current?.at(0); const isMerchantRequired = !!firstParticipant?.isPolicyExpenseChat && isMerchantMissing(initialTransaction) && iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; - const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( action, iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.SUBMIT : iouType, From 11e6ef34b75b5107d7b0107e36517fde2242ed73 Mon Sep 17 00:00:00 2001 From: nkdengineer <161821005+nkdengineer@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:23:13 +0700 Subject: [PATCH 20/22] Update src/pages/iou/request/step/IOURequestStepAmount.tsx Co-authored-by: Carlos Alvarez --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index a99244e0d0902..ef4cc91ef003d 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -277,8 +277,7 @@ function IOURequestStepAmount({ setSplitShares(transaction, amountInSmallestCurrencyUnits, selectedCurrency || CONST.CURRENCY.USD, participantAccountIDs); } setMoneyRequestParticipantsFromReport(transactionID, report, currentUserPersonalDetails.accountID).then(() => { - // Check if merchant is required and missing before proceeding - // If so, navigate to merchant step first + // If merchant is required and missing, navigate to merchant step first if (shouldRequireMerchant(transaction, report, isEditingSplitBill)) { Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_MERCHANT.getRoute(action, CONST.IOU.TYPE.SUBMIT, transactionID, reportID, undefined, reportActionID)); return; From 874c1a488e74db0763937e29678c15e4b4366a8f Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Fri, 16 Jan 2026 14:54:06 +0700 Subject: [PATCH 21/22] run prettier --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 9ef3ab8501b15..8555360cb00df 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -97,7 +97,7 @@ function IOURequestStepParticipants({ // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant const selectedReportID = useRef(participants?.length === 1 ? (participants.at(0)?.reportID ?? reportID) : reportID); const selectedParticipants = useRef(participants); - + // We can assume that shouldAutoReport is true as the initial value is not used. shouldAutoReport is only used after the selectedReportID changes in addParticipant where we'd update shouldAutoReport too const shouldAutoReport = useRef(true); const numberOfParticipants = useRef(participants?.length ?? 0); From ae9d0de4c6e373e66db94da64f8e0d5d367fc42c Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Wed, 21 Jan 2026 16:14:26 +0700 Subject: [PATCH 22/22] fix lint --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index a23a12f080ab3..28598bc768fef 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -419,13 +419,14 @@ function IOURequestStepParticipants({ participants, iouType, initialTransaction, + iouRequestType, initialTransactionID, - reportID, waitForKeyboardDismiss, transactions, isMovingTransactionFromTrackExpense, allPolicies, introSelected, + reportID, backTo, ], );