diff --git a/src/CONST.ts b/src/CONST.ts index a977b164e8bf..22894c68cf73 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2059,6 +2059,7 @@ const CONST = { INVOICE: 'invoice', SUBMIT: 'submit', TRACK: 'track', + CREATE: 'create', }, REQUEST_TYPE: { DISTANCE: 'distance', diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index 68f060d22e6c..97e611f0bafe 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -46,7 +46,10 @@ function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextP const welcomeMessage = SidebarUtils.getWelcomeMessage(report, policy); const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, policy, participantAccountIDs); const additionalText = moneyRequestOptions - .filter((item): item is Exclude => item !== CONST.IOU.TYPE.INVOICE) + .filter( + (item): item is Exclude => + item !== CONST.IOU.TYPE.INVOICE, + ) .map((item) => translate(`reportActionsView.iouTypes.${item}`)) .join(', '); const canEditPolicyDescription = ReportUtils.canEditPolicyDescription(policy); diff --git a/src/languages/en.ts b/src/languages/en.ts index ae5a9314d72d..5fc7cf339f70 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -825,6 +825,7 @@ const translations = { share: 'Share', participants: 'Participants', submitExpense: 'Submit expense', + createExpense: 'Create expense', trackExpense: 'Track expense', pay: 'Pay', cancelPayment: 'Cancel payment', @@ -1019,6 +1020,7 @@ const translations = { bookingPendingDescription: "This booking is pending because it hasn't been paid yet.", bookingArchived: 'This booking is archived', bookingArchivedDescription: 'This booking is archived because the trip date has passed. Add an expense for the final amount if needed.', + justTrackIt: 'Just track it (don’t submit it)', }, notificationPreferencesPage: { header: 'Notification preferences', diff --git a/src/languages/es.ts b/src/languages/es.ts index 71cb5037029d..06bf78659040 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -817,6 +817,7 @@ const translations = { share: 'Compartir', participants: 'Participantes', submitExpense: 'Presentar gasto', + createExpense: 'Crear gasto', paySomeone: ({name}: PaySomeoneParams = {}) => `Pagar a ${name ?? 'alguien'}`, trackExpense: 'Seguimiento de gastos', pay: 'Pagar', @@ -1013,6 +1014,7 @@ const translations = { bookingPendingDescription: 'Esta reserva está pendiente porque aún no se ha pagado.', bookingArchived: 'Esta reserva está archivada', bookingArchivedDescription: 'Esta reserva está archivada porque la fecha del viaje ha pasado. Agregue un gasto por el monto final si es necesario.', + justTrackIt: 'Solo guardarlo (no enviarlo)', }, notificationPreferencesPage: { header: 'Preferencias de avisos', diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 55ac4ba2ac36..4b56a3f460c1 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -116,16 +116,9 @@ function isValidMoneyRequestType(iouType: string): boolean { CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.TRACK, CONST.IOU.TYPE.INVOICE, + CONST.IOU.TYPE.CREATE, ]; - return moneyRequestType.includes(iouType); -} -/** - * Checks if the iou type is one of submit, pay, track, or split. - */ -// eslint-disable-next-line @typescript-eslint/naming-convention -function temporary_isValidMoneyRequestType(iouType: string): boolean { - const moneyRequestType: string[] = [CONST.IOU.TYPE.SUBMIT, CONST.IOU.TYPE.SPLIT, CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.TRACK, CONST.IOU.TYPE.INVOICE]; return moneyRequestType.includes(iouType); } @@ -169,5 +162,4 @@ export { isValidMoneyRequestType, navigateToStartMoneyRequestStep, updateIOUOwnerAndTotal, - temporary_isValidMoneyRequestType, }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f5ed8f606c98..55aa845ffa42 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6700,8 +6700,10 @@ function temporary_getMoneyRequestOptions( report: OnyxEntry, policy: OnyxEntry, reportParticipants: number[], -): Array> { - return getMoneyRequestOptions(report, policy, reportParticipants, true) as Array>; +): Array> { + return getMoneyRequestOptions(report, policy, reportParticipants, true) as Array< + Exclude + >; } /** @@ -6942,10 +6944,17 @@ function getReportOfflinePendingActionAndErrors(report: OnyxEntry): Repo */ function canCreateRequest(report: OnyxEntry, policy: OnyxEntry, iouType: ValueOf): boolean { const participantAccountIDs = Object.keys(report?.participants ?? {}).map(Number); + if (!canUserPerformWriteAction(report)) { return false; } - return getMoneyRequestOptions(report, policy, participantAccountIDs).includes(iouType); + + const requestOptions = getMoneyRequestOptions(report, policy, participantAccountIDs); + if (Permissions.canUseCombinedTrackSubmit(allBetas ?? [])) { + requestOptions.push(CONST.IOU.TYPE.CREATE); + } + + return requestOptions.includes(iouType); } function getWorkspaceChats(policyID: string, accountIDs: number[]): Array> { diff --git a/src/libs/getIconForAction/index.ts b/src/libs/getIconForAction/index.ts index ffe3dd8b76f2..18c8d7d8741e 100644 --- a/src/libs/getIconForAction/index.ts +++ b/src/libs/getIconForAction/index.ts @@ -12,6 +12,8 @@ const getIconForAction = (actionType: ValueOf) => { return Expensicons.Cash; case CONST.IOU.TYPE.SPLIT: return Expensicons.Transfer; + case CONST.IOU.TYPE.CREATE: + return Expensicons.Receipt; default: return Expensicons.MoneyCircle; } diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 4fc7cc137068..005ca4df153a 100644 --- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -33,7 +33,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; -type MoneyRequestOptions = Record, PopoverMenuItem>; +type MoneyRequestOptions = Record, PopoverMenuItem>; type AttachmentPickerWithMenuItemsProps = { /** The report currently being looked at */ diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 8791a2fc7ec8..8800cf35cdfd 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -8,6 +8,7 @@ import {useOnyx, withOnyx} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; import FloatingActionButton from '@components/FloatingActionButton'; import * as Expensicons from '@components/Icon/Expensicons'; +import type {PopoverMenuItem} from '@components/PopoverMenu'; import PopoverMenu from '@components/PopoverMenu'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; @@ -186,7 +187,7 @@ function FloatingActionButtonAndPopover( const prevIsFocused = usePrevious(isFocused); const {isOffline} = useNetwork(); - const {canUseSpotnanaTravel} = usePermissions(); + const {canUseSpotnanaTravel, canUseCombinedTrackSubmit} = usePermissions(); const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection, session?.email), [allPolicies, session?.email]); const quickActionAvatars = useMemo(() => { @@ -348,9 +349,70 @@ function FloatingActionButtonAndPopover( showCreateMenu(); } }; + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps const selfDMReportID = useMemo(() => ReportUtils.findSelfDMReportID(), [isLoading]); + const expenseMenuItems = useMemo((): PopoverMenuItem[] => { + if (canUseCombinedTrackSubmit) { + return [ + { + icon: getIconForAction(CONST.IOU.TYPE.CREATE), + text: translate('iou.createExpense'), + onSelected: () => + interceptAnonymousUser(() => + IOU.startMoneyRequest( + CONST.IOU.TYPE.CREATE, + // When starting to create an expense from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used + // for all of the routes in the creation flow. + ReportUtils.generateReportID(), + ), + ), + }, + ]; + } + + return [ + ...(selfDMReportID + ? [ + { + icon: getIconForAction(CONST.IOU.TYPE.TRACK), + text: translate('iou.trackExpense'), + onSelected: () => { + interceptAnonymousUser(() => + IOU.startMoneyRequest( + CONST.IOU.TYPE.TRACK, + // When starting to create a track expense from the global FAB, we need to retrieve selfDM reportID. + // If it doesn't exist, we generate a random optimistic reportID and use it for all of the routes in the creation flow. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + ReportUtils.findSelfDMReportID() || ReportUtils.generateReportID(), + ), + ); + if (!hasSeenTrackTraining && !isOffline) { + setTimeout(() => { + Navigation.navigate(ROUTES.TRACK_TRAINING_MODAL); + }, CONST.ANIMATED_TRANSITION); + } + }, + }, + ] + : []), + { + icon: getIconForAction(CONST.IOU.TYPE.REQUEST), + text: translate('iou.submitExpense'), + onSelected: () => + interceptAnonymousUser(() => + IOU.startMoneyRequest( + CONST.IOU.TYPE.SUBMIT, + // When starting to create an expense from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used + // for all of the routes in the creation flow. + ReportUtils.generateReportID(), + ), + ), + }, + ]; + }, [canUseCombinedTrackSubmit, translate, selfDMReportID, hasSeenTrackTraining, isOffline]); + return ( interceptAnonymousUser(Report.startNewChat), }, - ...(selfDMReportID - ? [ - { - icon: getIconForAction(CONST.IOU.TYPE.TRACK), - text: translate('iou.trackExpense'), - onSelected: () => { - interceptAnonymousUser(() => - IOU.startMoneyRequest( - CONST.IOU.TYPE.TRACK, - // When starting to create a track expense from the global FAB, we need to retrieve selfDM reportID. - // If it doesn't exist, we generate a random optimistic reportID and use it for all of the routes in the creation flow. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - ReportUtils.findSelfDMReportID() || ReportUtils.generateReportID(), - ), - ); - if (!hasSeenTrackTraining && !isOffline) { - setTimeout(() => { - Navigation.navigate(ROUTES.TRACK_TRAINING_MODAL); - }, CONST.ANIMATED_TRANSITION); - } - }, - }, - ] - : []), - { - icon: getIconForAction(CONST.IOU.TYPE.REQUEST), - text: translate('iou.submitExpense'), - onSelected: () => - interceptAnonymousUser(() => - IOU.startMoneyRequest( - CONST.IOU.TYPE.SUBMIT, - // When starting to create an expense from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used - // for all of the routes in the creation flow. - ReportUtils.generateReportID(), - ), - ), - }, + ...expenseMenuItems, ...(canSendInvoice ? [ { diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index eac30b8839d2..c4abf714502a 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -54,9 +54,10 @@ function IOURequestStartPage({ [CONST.IOU.TYPE.SPLIT]: translate('iou.splitExpense'), [CONST.IOU.TYPE.TRACK]: translate('iou.trackExpense'), [CONST.IOU.TYPE.INVOICE]: translate('workspace.invoices.sendInvoice'), + [CONST.IOU.TYPE.CREATE]: translate('iou.createExpense'), }; const transactionRequestType = useRef(TransactionUtils.getRequestType(transaction)); - const {canUseP2PDistanceRequests} = usePermissions(iouType); + const {canUseP2PDistanceRequests, canUseCombinedTrackSubmit} = usePermissions(iouType); const isFromGlobalCreate = isEmptyObject(report?.reportID); // Clear out the temporary expense if the reportID in the URL has changed from the transaction's reportID @@ -69,7 +70,8 @@ function IOURequestStartPage({ const isExpenseChat = ReportUtils.isPolicyExpenseChat(report); const isExpenseReport = ReportUtils.isExpenseReport(report); - const shouldDisplayDistanceRequest = !!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || (isFromGlobalCreate && iouType !== CONST.IOU.TYPE.SPLIT); + const shouldDisplayDistanceRequest = + !!canUseCombinedTrackSubmit || !!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || (isFromGlobalCreate && iouType !== CONST.IOU.TYPE.SPLIT); const navigateBack = () => { Navigation.closeRHPFlow(); diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 0168133154ee..a2652b8693ee 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -7,6 +7,8 @@ import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import EmptySelectionListContent from '@components/EmptySelectionListContent'; import FormHelpMessage from '@components/FormHelpMessage'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; import {usePersonalDetails} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; import ReferralProgramCTA from '@components/ReferralProgramCTA'; @@ -41,6 +43,9 @@ type MoneyRequestParticipantsSelectorProps = { /** Callback to add participants in MoneyRequestModal */ onParticipantsAdded: (value: Participant[]) => void; + /** Callback to navigate to Track Expense confirmation flow */ + onTrackExpensePress?: () => void; + /** Selected participants from MoneyRequestModal with login */ participants?: Participant[] | typeof CONST.EMPTY_ARRAY; @@ -52,9 +57,21 @@ type MoneyRequestParticipantsSelectorProps = { /** The action of the IOU, i.e. create, split, move */ action: IOUAction; + + /** Whether we should display the Track Expense button at the top of the participants list */ + shouldDisplayTrackExpenseButton?: boolean; }; -function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onFinish, onParticipantsAdded, iouType, iouRequestType, action}: MoneyRequestParticipantsSelectorProps) { +function MoneyRequestParticipantsSelector({ + participants = CONST.EMPTY_ARRAY, + onTrackExpensePress, + onFinish, + onParticipantsAdded, + iouType, + iouRequestType, + action, + shouldDisplayTrackExpenseButton, +}: MoneyRequestParticipantsSelectorProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); @@ -105,9 +122,9 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF participants as Participant[], CONST.EXPENSIFY_EMAILS, - // If we are using this component in the "Submit expense" flow then we pass the includeOwnedWorkspaceChats argument so that the current user + // If we are using this component in the "Submit expense" or the combined submit/track flow then we pass the includeOwnedWorkspaceChats argument so that the current user // sees the option to submit an expense from their admin on their own Workspace Chat. - (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.SPLIT) && action !== CONST.IOU.ACTION.SUBMIT, + (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.CREATE || iouType === CONST.IOU.TYPE.SPLIT) && action !== CONST.IOU.ACTION.SUBMIT, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) && !isCategorizeOrShareAction, @@ -364,6 +381,22 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF const shouldShowReferralBanner = !isDismissed && iouType !== CONST.IOU.TYPE.INVOICE && !shouldShowListEmptyContent; + const headerContent = useMemo(() => { + if (!shouldDisplayTrackExpenseButton) { + return; + } + + // We only display the track expense button if the user is coming from the combined submit/track flow. + return ( + + ); + }, [shouldDisplayTrackExpenseButton, translate, onTrackExpensePress]); + const footerContent = useMemo(() => { if (isDismissed && !shouldShowSplitBillErrorMessage && !participants.length) { return; @@ -449,6 +482,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} onSelectRow={onSelectRow} shouldSingleExecuteRowSelect + headerContent={headerContent} footerContent={footerContent} listEmptyContent={} headerMessage={header} diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 2a0aa438fe98..2e4fb87a34f3 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -178,10 +178,13 @@ function IOURequestStepAmount({ return; } - // If a reportID exists in the report object, it's because the user started this flow from using the + button in the composer - // inside a report. In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight + // If a reportID exists in the report object, it's because either: + // - The user started this flow from using the + button in the composer inside a report. + // - The user started this flow from using the global create menu by selecting the Track expense option. + // In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight // to the confirm step. - if (report?.reportID && !ReportUtils.isArchivedRoom(report, reportNameValuePairs)) { + // If the user is started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. + if (report?.reportID && !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? -1; diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 14597df8e313..e0eb9389a1d3 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -245,10 +245,13 @@ function IOURequestStepDistance({ return; } - // If a reportID exists in the report object, it's because the user started this flow from using the + button in the composer - // inside a report. In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight + // If a reportID exists in the report object, it's because either: + // - The user started this flow from using the + button in the composer inside a report. + // - The user started this flow from using the global create menu by selecting the Track expense option. + // In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight // to the confirm step. - if (report?.reportID && !ReportUtils.isArchivedRoom(report, reportNameValuePairs)) { + // If the user started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. + if (report?.reportID && !ReportUtils.isArchivedRoom(report, reportNameValuePairs) && iouType !== CONST.IOU.TYPE.CREATE) { const selectedParticipants = IOU.setMoneyRequestParticipantsFromReport(transactionID, report); const participants = selectedParticipants.map((participant) => { const participantAccountID = participant?.accountID ?? -1; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 552ad4d54e39..481f0423e072 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -75,6 +75,9 @@ function IOURequestStepParticipants({ return translate('iou.submitExpense'); }, [iouType, translate, isSplitRequest, action]); + const selfDMReportID = useMemo(() => ReportUtils.findSelfDMReportID(), []); + const shouldDisplayTrackExpenseButton = !!selfDMReportID; + const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; const receiptType = transaction?.receipt?.type; @@ -132,7 +135,14 @@ function IOURequestStepParticipants({ return; } - const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, selectedReportID.current || reportID); + // If coming from the combined submit/track flow and the user proceeds to submit the expense + // we will use the submit IOU type in the confirmation flow. + const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute( + action, + iouType === CONST.IOU.TYPE.CREATE ? CONST.IOU.TYPE.SUBMIT : iouType, + transactionID, + selectedReportID.current || reportID, + ); if (isCategorizing) { Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CATEGORY.getRoute(action, iouType, transactionID, selectedReportID.current || reportID, iouConfirmationPageRoute)); } else { @@ -144,6 +154,18 @@ function IOURequestStepParticipants({ IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action); }, [iouRequestType, iouType, transactionID, reportID, action]); + const trackExpense = () => { + // If coming from the combined submit/track flow and the user proceeds to just track the expense, + // we will use the track IOU type in the confirmation flow. + if (!selfDMReportID) { + return; + } + + IOU.setMoneyRequestParticipantsFromReport(transactionID, ReportUtils.getReport(selfDMReportID)); + const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, CONST.IOU.TYPE.TRACK, transactionID, selfDMReportID); + Navigation.navigate(iouConfirmationPageRoute); + }; + useEffect(() => { const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; const isShareAction = action === CONST.IOU.ACTION.SHARE; @@ -173,9 +195,11 @@ function IOURequestStepParticipants({ participants={isSplitRequest ? participants : []} onParticipantsAdded={addParticipant} onFinish={goToNextStep} + onTrackExpensePress={trackExpense} iouType={iouType} iouRequestType={iouRequestType} action={action} + shouldDisplayTrackExpenseButton={shouldDisplayTrackExpenseButton} /> ); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index f6b4c44c80ae..a2ba86009a5e 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -234,7 +234,9 @@ function IOURequestStepScan({ } // If the transaction was created from the global create, the person needs to select participants, so take them there. - if (transaction?.isFromGlobalCreate && iouType !== CONST.IOU.TYPE.TRACK && !report?.reportID) { + // If the user started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if ((transaction?.isFromGlobalCreate && iouType !== CONST.IOU.TYPE.TRACK && !report?.reportID) || iouType === CONST.IOU.TYPE.CREATE) { navigateToParticipantPage(); return; } diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 19a82b9011e6..bd92dfd69f93 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -266,7 +266,9 @@ function IOURequestStepScan({ } // If the transaction was created from the global create, the person needs to select participants, so take them there. - if (transaction?.isFromGlobalCreate && iouType !== CONST.IOU.TYPE.TRACK && !report?.reportID) { + // If the user started this flow using the Create expense option (combined submit/track flow), they should be redirected to the participants page. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if ((transaction?.isFromGlobalCreate && iouType !== CONST.IOU.TYPE.TRACK && !report?.reportID) || iouType === CONST.IOU.TYPE.CREATE) { navigateToParticipantPage(); return; } diff --git a/tests/unit/IOUUtilsTest.ts b/tests/unit/IOUUtilsTest.ts index 04a5b3babd5e..2ab1247ec5b0 100644 --- a/tests/unit/IOUUtilsTest.ts +++ b/tests/unit/IOUUtilsTest.ts @@ -1,4 +1,5 @@ import Onyx from 'react-native-onyx'; +import CONST from '@src/CONST'; import * as IOUUtils from '@src/libs/IOUUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; import * as TransactionUtils from '@src/libs/TransactionUtils'; @@ -136,13 +137,12 @@ describe('IOUUtils', () => { describe('isValidMoneyRequestType', () => { test('Return true for valid iou type', () => { - expect(IOUUtils.temporary_isValidMoneyRequestType('submit')).toBe(true); - expect(IOUUtils.temporary_isValidMoneyRequestType('split')).toBe(true); - expect(IOUUtils.temporary_isValidMoneyRequestType('pay')).toBe(true); - expect(IOUUtils.temporary_isValidMoneyRequestType('track')).toBe(true); + Object.values(CONST.IOU.TYPE).forEach((iouType) => { + expect(IOUUtils.isValidMoneyRequestType(iouType)).toBe(true); + }); }); test('Return false for invalid iou type', () => { - expect(IOUUtils.temporary_isValidMoneyRequestType('money')).toBe(false); + expect(IOUUtils.isValidMoneyRequestType('money')).toBe(false); }); });