diff --git a/src/CONST/index.ts b/src/CONST/index.ts index f4f88cdd0562..e2bcb9ad67c7 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -987,7 +987,6 @@ const CONST = { SUGGESTED_FOLLOWUPS: 'suggestedFollowups', BULK_EDIT: 'bulkEdit', BULK_EDIT_WORKSPACES: 'bulkEditWorkspaces', - NEW_MANUAL_EXPENSE_FLOW: 'newManualExpenseFlow', SUBMIT_2026: 'submit2026', BULK_SUBMIT_APPROVE_PAY: 'bulkSubmitApprovePay', WORKSPACE_ROOMS_PAGE: 'workspaceRoomsPage', diff --git a/src/components/MoneyRequestConfirmationFields/Provider.tsx b/src/components/MoneyRequestConfirmationFields/Provider.tsx index f5bce0e6d3b2..5afb6994daad 100644 --- a/src/components/MoneyRequestConfirmationFields/Provider.tsx +++ b/src/components/MoneyRequestConfirmationFields/Provider.tsx @@ -37,9 +37,6 @@ type ProviderProps = { /** Whether we're editing an existing split expense */ isEditingSplitBill?: boolean; - /** Whether the new manual expense flow beta is enabled */ - isNewManualExpenseFlowEnabled?: boolean; - /** Whether the surface is in a policy-expense chat */ isPolicyExpenseChat?: boolean; @@ -84,7 +81,6 @@ function Provider({ isReadOnly = false, didConfirm = false, isEditingSplitBill = false, - isNewManualExpenseFlowEnabled = false, isPolicyExpenseChat = false, isDistanceRequest = false, isPerDiemRequest = false, @@ -107,7 +103,6 @@ function Provider({ isReadOnly, didConfirm, isEditingSplitBill, - isNewManualExpenseFlowEnabled, isPolicyExpenseChat, isDistanceRequest, isPerDiemRequest, diff --git a/src/components/MoneyRequestConfirmationFields/context.ts b/src/components/MoneyRequestConfirmationFields/context.ts index 8e7dc5adac48..7b19cf0047ff 100644 --- a/src/components/MoneyRequestConfirmationFields/context.ts +++ b/src/components/MoneyRequestConfirmationFields/context.ts @@ -23,7 +23,6 @@ type ConfirmationFieldsContextValue = { isReadOnly: boolean; didConfirm: boolean; isEditingSplitBill: boolean; - isNewManualExpenseFlowEnabled: boolean; isPolicyExpenseChat: boolean; // Mode — *what kind* of expense is being confirmed diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 3ec4d336bef8..223b54f6b735 100644 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -3,7 +3,6 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useIsInLandscapeMode from '@hooks/useIsInLandscapeMode'; import useLocalize from '@hooks/useLocalize'; import {MouseProvider} from '@hooks/useMouseContext'; -import usePermissions from '@hooks/usePermissions'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import usePolicyForTransaction from '@hooks/usePolicyForTransaction'; import usePreferredPolicy from '@hooks/usePreferredPolicy'; @@ -14,7 +13,6 @@ import {isCategoryDescriptionRequired} from '@libs/CategoryUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpenseUtil} from '@libs/IOUUtils'; import {shouldShowConfirmationDate} from '@libs/MoneyRequestUtils'; -import Navigation from '@libs/Navigation/Navigation'; import {hasEnabledOptions} from '@libs/OptionsListUtils'; import {arePolicyRulesEnabled, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -32,7 +30,6 @@ import { import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; @@ -220,8 +217,6 @@ function MoneyRequestConfirmationList({ const transactionReport = useTransactionReportForConfirmation(transaction?.reportID); const {policyForMovingExpenses, shouldSelectPolicy} = usePolicyForMovingExpenses(); const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseUtil(action); - const {isBetaEnabled} = usePermissions(); - const isNewManualExpenseFlowEnabled = isBetaEnabled(CONST.BETAS.NEW_MANUAL_EXPENSE_FLOW); const {isDelegateAccessRestricted} = useDelegateNoAccessState(); const {showDelegateNoAccessModal} = useDelegateNoAccessActions(); const isInLandscapeMode = useIsInLandscapeMode(); @@ -325,7 +320,7 @@ function MoneyRequestConfirmationList({ }); const isManualRequest = transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; - const shouldForceTopEmptySections = isNewManualExpenseFlowEnabled && (iouType === CONST.IOU.TYPE.CREATE || isManualRequest || isScanRequest); + const shouldForceTopEmptySections = iouType === CONST.IOU.TYPE.CREATE || isManualRequest || isScanRequest; const isFocused = useIsFocused(); @@ -360,7 +355,6 @@ function MoneyRequestConfirmationList({ routeError, isTypeSplit, shouldShowReadOnlySplits, - isNewManualExpenseFlowEnabled, isDistanceRequest, }); @@ -390,7 +384,6 @@ function MoneyRequestConfirmationList({ receiptPath, isDistanceRequestWithPendingRoute, isPerDiemRequest, - isNewManualExpenseFlowEnabled, }); const selectedParticipants = selectedParticipantsProp.filter((participant) => participant.selected); @@ -447,13 +440,7 @@ function MoneyRequestConfirmationList({ return; } - if (isNewManualExpenseFlowEnabled) { - onOpenParticipantPicker?.(); - return; - } - - const newIOUType = iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.TRACK ? CONST.IOU.TYPE.CREATE : iouType; - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(newIOUType, transactionID, transaction?.reportID, Navigation.getActiveRoute(), action)); + onOpenParticipantPicker?.(); }; const {validate} = useConfirmationValidation({ @@ -483,7 +470,6 @@ function MoneyRequestConfirmationList({ isPerDiemRequest, isTimeRequest, routeError, - isNewManualExpenseFlowEnabled, isReadOnly, shouldShowDate: shouldShowConfirmationDate(shouldShowSmartScanFields, isDistanceRequest), }); diff --git a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts index d599da7c823f..3066182b7651 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts +++ b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts @@ -45,17 +45,14 @@ type UseConfirmationCtaTextParams = { /** Whether the transaction is a per-diem request */ isPerDiemRequest: boolean; - - /** Whether the new manual expense flow beta is enabled */ - isNewManualExpenseFlowEnabled: boolean; }; /** * Computes the primary confirm button label for the Money Request confirmation flow. * - * Picks between create / create-with-amount / split / invoice / next variants based on - * the IOU type, manual-expense-flow beta, bulk-expense count, and amount, returning a - * single-entry DropdownOption array shaped for the ButtonWithDropdownMenu consumer. + * Picks between create / split / invoice / next variants based on the IOU type, + * bulk-expense count, and amount, returning a single-entry DropdownOption array shaped + * for the ButtonWithDropdownMenu consumer. */ function useConfirmationCtaText({ expensesNumber, @@ -70,7 +67,6 @@ function useConfirmationCtaText({ receiptPath, isDistanceRequestWithPendingRoute, isPerDiemRequest, - isNewManualExpenseFlowEnabled, }: UseConfirmationCtaTextParams): Array> { const {translate} = useLocalize(); @@ -85,27 +81,14 @@ function useConfirmationCtaText({ } } else if (isTypeTrackExpense) { text = translate('iou.createExpense'); - if (iouAmount !== 0 && !isNewManualExpenseFlowEnabled) { - text = translate('iou.createExpenseWithAmount', {amount: formattedAmount}); - } } else if (isTypeSplit && iouAmount === 0) { text = translate('iou.splitExpense'); } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute || isPerDiemRequest) { text = translate('iou.createExpense'); - if (iouAmount !== 0 && !isNewManualExpenseFlowEnabled) { - text = translate('iou.createExpenseWithAmount', {amount: formattedAmount}); - } } else if (isTypeSplit) { - text = translate('iou.splitAmount', formattedAmount); - if (isNewManualExpenseFlowEnabled) { - text = translate('iou.splitExpense'); - } - } else if (iouAmount === 0) { - text = translate('iou.createExpense'); - } else if (isNewManualExpenseFlowEnabled) { - text = translate('iou.createExpense'); + text = translate('iou.splitExpense'); } else { - text = translate('iou.createExpenseWithAmount', {amount: formattedAmount}); + text = translate('iou.createExpense'); } return [ { diff --git a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationValidation.ts b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationValidation.ts index c931c4b51319..936b95d427d7 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationValidation.ts +++ b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationValidation.ts @@ -112,9 +112,6 @@ type UseConfirmationValidationParams = { /** Truthy when the route to the confirmation page has a known error */ routeError: string | null | undefined; - /** Whether the new manual expense flow is enabled */ - isNewManualExpenseFlowEnabled: boolean; - /** Whether the confirmation fields are read-only (date is not inline-editable) */ isReadOnly: boolean; @@ -163,7 +160,6 @@ function useConfirmationValidation({ isPerDiemRequest, isTimeRequest, routeError, - isNewManualExpenseFlowEnabled, isReadOnly, shouldShowDate, }: UseConfirmationValidationParams): {validate: (paymentType?: PaymentMethodType) => ValidationResult | null} { @@ -186,11 +182,10 @@ function useConfirmationValidation({ return {errorKey: 'common.error.invalidAmount'}; } // isAmountSet only applies to manual expenses — scan, per diem, distance, and time set amount programmatically. - if (isNewManualExpenseFlowEnabled && transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL && !transaction?.isAmountSet) { + if (transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL && !transaction?.isAmountSet) { return {errorKey: 'common.error.fieldRequired'}; } if ( - isNewManualExpenseFlowEnabled && transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL && transaction?.isAmountSet && !isScanRequestUtil(transaction) && @@ -205,7 +200,7 @@ function useConfirmationValidation({ // (manual, distance, time, invoice, ...). Block confirmation when the user cleared it. Gating on the same // `shouldShowDate && !isReadOnly` condition that renders the inline picker keeps validation and UI in sync, // and skips read-only/scan flows where the date is populated server-side. - if (isNewManualExpenseFlowEnabled && shouldShowDate && !isReadOnly && isCreatedMissing(transaction)) { + if (shouldShowDate && !isReadOnly && isCreatedMissing(transaction)) { return {errorKey: 'common.error.fieldRequired'}; } const merchantValue = iouMerchant ?? ''; @@ -268,7 +263,7 @@ function useConfirmationValidation({ // In the new manual expense flow the tax amount is edited inline, so the standalone tax amount step's // guard (tax amount can't exceed the tax computed from the rate and the expense amount) runs here. // This also blocks creation when an invalid tax amount was persisted to the draft and then reloaded. - if (isNewManualExpenseFlowEnabled && shouldShowTax && !isDistanceRequest) { + if (shouldShowTax && !isDistanceRequest) { const decimals = getCurrencyDecimals(iouCurrencyCode); const maxTaxAmount = getCalculatedTaxAmount(policy, transaction, iouCurrencyCode, decimals); const currentTaxAmount = convertToFrontendAmountAsString(Math.abs(getTaxAmount(transaction, false)), decimals); diff --git a/src/components/MoneyRequestConfirmationList/hooks/useFormErrorManagement.ts b/src/components/MoneyRequestConfirmationList/hooks/useFormErrorManagement.ts index 5009f49593e7..9ed8fcec9d26 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useFormErrorManagement.ts +++ b/src/components/MoneyRequestConfirmationList/hooks/useFormErrorManagement.ts @@ -72,9 +72,6 @@ type UseFormErrorManagementParams = { /** Whether splits are rendered read-only (suppresses some field errors) */ shouldShowReadOnlySplits: boolean; - /** Whether the new manual expense flow is enabled (amount/date errors surface inline) */ - isNewManualExpenseFlowEnabled: boolean; - /** Whether the transaction is a distance request (its amount is read-only, so amount errors are not shown inline) */ isDistanceRequest: boolean; }; @@ -141,7 +138,6 @@ function useFormErrorManagement({ routeError, isTypeSplit, shouldShowReadOnlySplits, - isNewManualExpenseFlowEnabled, isDistanceRequest, }: UseFormErrorManagementParams): UseFormErrorManagementResult { const isFocused = useIsFocused(); @@ -246,15 +242,15 @@ function useFormErrorManagement({ if (formError === 'iou.error.invalidTaxAmount') { return undefined; } - // In the new manual expense flow the amount/date/merchant fields surface these required/invalid errors inline, so + // The amount/date/merchant fields surface these required/invalid errors inline, so // don't repeat them at the bottom of the form (which would show "This field is required" twice). - if (isNewManualExpenseFlowEnabled && (formError === 'common.error.fieldRequired' || formError === 'iou.error.invalidMerchant')) { + if (formError === 'common.error.fieldRequired' || formError === 'iou.error.invalidMerchant') { return undefined; } // `common.error.invalidAmount` is only surfaced inline when the editable amount input is rendered. Distance requests // disable that input (the amount falls back to a read-only menu row that doesn't show this error), so keep the // distance-amount validation error in the footer — otherwise an invalid distance expense would fail silently. - if (isNewManualExpenseFlowEnabled && !isDistanceRequest && formError === 'common.error.invalidAmount') { + if (!isDistanceRequest && formError === 'common.error.invalidAmount') { return undefined; } return formError ? translate(formError) : undefined; diff --git a/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx b/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx index 5c4be87543cf..50f09ada5cfe 100644 --- a/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx @@ -42,7 +42,6 @@ type AmountFieldProps = { distanceRateCurrency: string; iouCurrencyCode: string | undefined; isDistanceRequest: boolean; - isNewManualExpenseFlowEnabled: boolean; didConfirm: boolean; isReadOnly: boolean; shouldShowTimeRequestFields: boolean; @@ -66,7 +65,6 @@ function AmountField({ distanceRateCurrency, iouCurrencyCode, isDistanceRequest, - isNewManualExpenseFlowEnabled, didConfirm, isReadOnly, shouldShowTimeRequestFields, @@ -100,10 +98,7 @@ function AmountField({ const [isCurrencyPickerVisible, setIsCurrencyPickerVisible] = useState(false); const isAmountFieldDisabled = didConfirm || isReadOnly || shouldShowTimeRequestFields || isDistanceRequest; - const firstParticipant = transactionSlice?.participants?.at(0); - const isP2P = isNewManualExpenseFlowEnabled - ? isParticipantP2P(getMoneyRequestParticipantsFromReport(report, currentUserPersonalDetails.accountID).at(0)) - : !!(firstParticipant?.accountID && !firstParticipant?.isPolicyExpenseChat); + const isP2P = isParticipantP2P(getMoneyRequestParticipantsFromReport(report, currentUserPersonalDetails.accountID).at(0)); // `common.error.fieldRequired` is shared with the date field, so only surface it on the amount input when the // amount itself is the missing value. const shouldShowAmountRequiredError = formError === 'common.error.fieldRequired' && !transactionSlice?.isAmountSet; @@ -122,9 +117,9 @@ function AmountField({ // touches it). Once the user explicitly sets an amount – including 0 – isAmountSet becomes true and we show the // real value. This avoids showing "$0.00" as a pre-filled default. Scan and other non-manual flows populate // amount programmatically and never set isAmountSet. - const shouldShowEmptyAmount = isNewManualExpenseFlowEnabled && !transactionSlice?.isAmountSet && transactionSlice?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; + const shouldShowEmptyAmount = !transactionSlice?.isAmountSet && transactionSlice?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL; const transactionAmount = shouldShowEmptyAmount ? '' : convertToFrontendAmountAsString(amount, decimals); - const allowNegative = shouldEnableNegative(report, policy, iouType, transactionSlice?.participants, isNewManualExpenseFlowEnabled); + const allowNegative = shouldEnableNegative(report, policy, iouType, transactionSlice?.participants, true); // `autoFocus` on our TextInput only runs on mount. Closing and reopening the RHP often keeps the same mounted // instance, so autofocus does not run again. We re-focus when the parent-owned participant picker closes @@ -132,7 +127,7 @@ function AmountField({ // expense flow. The setTimeout defers focus past the RHP entry / picker close animation so the input reliably // receives focus. useEffect(() => { - if (!autoFocus || isAmountFieldDisabled || !isNewManualExpenseFlowEnabled || isParticipantPickerVisible) { + if (!autoFocus || isAmountFieldDisabled || isParticipantPickerVisible) { return; } @@ -144,7 +139,7 @@ function AmountField({ } clearTimeout(focusTimeoutRef.current); }; - }, [autoFocus, isAmountFieldDisabled, isNewManualExpenseFlowEnabled, isParticipantPickerVisible]); + }, [autoFocus, isAmountFieldDisabled, isParticipantPickerVisible]); const showCurrencyPicker = () => { setIsCurrencyPickerVisible(true); @@ -307,7 +302,7 @@ function AmountField({ value={effectiveCurrency} onInputChange={updateCurrency} /> - {isNewManualExpenseFlowEnabled && !isAmountFieldDisabled ? ( + {!isAmountFieldDisabled ? ( void; transactionID: string | undefined; @@ -43,19 +42,7 @@ type DateFieldProps = { reportActionID: string | undefined; }; -function DateField({ - shouldDisplayFieldError, - didConfirm, - isReadOnly, - isNewManualExpenseFlowEnabled, - formError, - clearFormErrors, - transactionID, - action, - iouType, - reportID, - reportActionID, -}: DateFieldProps) { +function DateField({shouldDisplayFieldError, didConfirm, isReadOnly, formError, clearFormErrors, transactionID, action, iouType, reportID, reportActionID}: DateFieldProps) { const {isEditingSplitBill} = useConfirmationFields(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -117,7 +104,7 @@ function DateField({ } }; - if (isNewManualExpenseFlowEnabled && !isReadOnly) { + if (!isReadOnly) { return ( ; }; -function DescriptionField({ - isNewManualExpenseFlowEnabled, - isReadOnly, - didConfirm, - isDescriptionRequired, - transactionID, - action, - iouType, - reportID, - reportActionID, - policy, -}: DescriptionFieldProps) { +function DescriptionField({isReadOnly, didConfirm, isDescriptionRequired, transactionID, action, iouType, reportID, reportActionID, policy}: DescriptionFieldProps) { const {isEditingSplitBill, scrollFocusedInputIntoView, onSubmitForm} = useConfirmationFields(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -115,7 +103,7 @@ function DescriptionField({ - {isNewManualExpenseFlowEnabled && !isReadOnly ? ( + {!isReadOnly ? ( (null); const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`); @@ -101,9 +101,6 @@ function TaxFields({policy, policyForMovingExpenses, iouCurrencyCode, canModifyT }; useEffect(() => { - if (!isNewManualExpenseFlowEnabled) { - return; - } // Compare the numeric value rather than the formatted string. An in-progress edit such as "5.0" (or an // empty field) represents the same stored amount as the re-padded "5.00", so it must not be overwritten // while the user is typing. Only refresh the field when the stored tax amount genuinely differs (e.g. the @@ -116,14 +113,14 @@ function TaxFields({policy, policyForMovingExpenses, iouCurrencyCode, canModifyT } } numberFormRef.current?.updateNumber(taxAmountInput); - }, [isNewManualExpenseFlowEnabled, taxAmount, taxAmountInput]); + }, [taxAmount, taxAmountInput]); useEffect(() => { - if (!isNewManualExpenseFlowEnabled || formError !== 'iou.error.invalidTaxAmount' || taxAmount > maxTaxAmount) { + if (formError !== 'iou.error.invalidTaxAmount' || taxAmount > maxTaxAmount) { return; } clearFormErrors(['iou.error.invalidTaxAmount']); - }, [isNewManualExpenseFlowEnabled, formError, taxAmount, maxTaxAmount, clearFormErrors]); + }, [formError, taxAmount, maxTaxAmount, clearFormErrors]); return ( <> @@ -147,7 +144,7 @@ function TaxFields({policy, policyForMovingExpenses, iouCurrencyCode, canModifyT errorText={shouldDisplayTaxRateError ? translate(formError as TranslationPaths) : ''} sentryLabel={CONST.SENTRY_LABEL.REQUEST_CONFIRMATION_LIST.TAX_RATE_FIELD} /> - {isNewManualExpenseFlowEnabled && canModifyTaxFields ? ( + {canModifyTaxFields ? ( @@ -135,7 +135,6 @@ function ClassificationFields({ shouldDisplayFieldError={errorState.shouldDisplayFieldError} didConfirm={didConfirm} isReadOnly={isReadOnly} - isNewManualExpenseFlowEnabled={isNewManualExpenseFlowEnabled} formError={errorState.formError} clearFormErrors={errorState.clearFormErrors} transactionID={transactionID} diff --git a/src/components/MoneyRequestConfirmationListFooter/fieldGroups/TransactionDetailsFields.tsx b/src/components/MoneyRequestConfirmationListFooter/fieldGroups/TransactionDetailsFields.tsx index fa9a352ad678..43429f939e10 100644 --- a/src/components/MoneyRequestConfirmationListFooter/fieldGroups/TransactionDetailsFields.tsx +++ b/src/components/MoneyRequestConfirmationListFooter/fieldGroups/TransactionDetailsFields.tsx @@ -65,20 +65,8 @@ function TransactionDetailsFields({ fieldVisibility, isParticipantPickerVisible, }: TransactionDetailsFieldsProps) { - const { - action, - iouType, - transactionID, - reportID, - reportActionID, - isReadOnly, - didConfirm, - isNewManualExpenseFlowEnabled, - isPolicyExpenseChat, - isManualDistanceRequest, - isOdometerDistanceRequest, - isGPSDistanceRequest, - } = useConfirmationFields(); + const {action, iouType, transactionID, reportID, reportActionID, isReadOnly, didConfirm, isPolicyExpenseChat, isManualDistanceRequest, isOdometerDistanceRequest, isGPSDistanceRequest} = + useConfirmationFields(); const shouldAutoFocusAmountField = !canUseTouchScreen(); return ( @@ -91,7 +79,6 @@ function TransactionDetailsFields({ distanceRateCurrency={distanceData.distanceRateCurrency} iouCurrencyCode={iouCurrencyCode} isDistanceRequest={fieldVisibility.distance} - isNewManualExpenseFlowEnabled={isNewManualExpenseFlowEnabled} didConfirm={didConfirm} isReadOnly={isReadOnly} shouldShowTimeRequestFields={fieldVisibility.time} @@ -112,7 +99,6 @@ function TransactionDetailsFields({ {!isCompactMode && fieldVisibility.merchant && ( { - if (!isNewManualExpenseFlowEnabled) { - return []; - } - const reportParticipants = getMoneyRequestParticipantsFromReport(sourceReport, accountID).filter((participant) => participant.selected); if (reportParticipants.length > 0) { return reportParticipants; @@ -78,7 +71,6 @@ function useDefaultParticipants({sourceReport, transaction, iouType, isNewManual const defaultTargetReport = shouldAutoReport ? getPolicyExpenseChat(accountID, defaultExpensePolicy?.id) : selfDMReport; return getMoneyRequestParticipantsFromReport(defaultTargetReport, accountID).filter((participant) => participant.selected); }, [ - isNewManualExpenseFlowEnabled, sourceReport, accountID, transaction?.isFromGlobalCreate, diff --git a/src/hooks/useResetIOUType.ts b/src/hooks/useResetIOUType.ts index 13ae257fc4ed..a544fa46a080 100644 --- a/src/hooks/useResetIOUType.ts +++ b/src/hooks/useResetIOUType.ts @@ -54,10 +54,6 @@ type UseResetIOUTypeParams = { /** Whether to skip keyboard dismiss for per diem tab */ skipKeyboardDismissForPerDiem?: boolean; - - /** Whether the new manual expense flow beta is enabled. When true, the fresh transaction is seeded with - * participants from the current report so the embedded confirmation's auto-assign useEffect short-circuits. */ - isNewManualExpenseFlowEnabled?: boolean; }; /** @@ -75,7 +71,6 @@ function useResetIOUType({ policy, isTrackDistanceExpense = false, skipKeyboardDismissForPerDiem = false, - isNewManualExpenseFlowEnabled = false, }: UseResetIOUTypeParams): (newIOUType: IOURequestType) => void { const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`); const [hasOnlyPersonalPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: hasOnlyPersonalPoliciesSelector}); @@ -93,14 +88,13 @@ function useResetIOUType({ isLoadingSelectedTab, }); - // For the new manual flow, derive participants from the current report (or the global-create fallback) so the - // freshly-rebuilt transaction already includes them. This prevents the embedded confirmation's auto-assign - // useEffect from re-firing on every cleanup and dragging back unrelated draft state (receipt, billable, etc.). + // Derive participants from the current report (or the global-create fallback) so the freshly-rebuilt + // transaction already includes them. This prevents the embedded confirmation's auto-assign useEffect from + // re-firing on every cleanup and dragging back unrelated draft state (receipt, billable, etc.). const resolvedDefaultParticipants = useDefaultParticipants({ sourceReport: report, transaction, iouType, - isNewManualExpenseFlowEnabled, }); const defaultParticipants = resolvedDefaultParticipants.length > 0 ? resolvedDefaultParticipants : undefined; diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 9ba164bb7a0f..459b737efd7e 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -10,7 +10,6 @@ import useAndroidBackButtonHandler from '@hooks/useAndroidBackButtonHandler'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import usePermissions from '@hooks/usePermissions'; import usePolicy from '@hooks/usePolicy'; import useResetIOUType from '@hooks/useResetIOUType'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -172,9 +171,6 @@ function IOURequestStartPage({ return undefined; }, [transaction?.iouRequestType, isStaleTransactionDraft, shouldUseTab, selectedTab, availableTabs]); - const {isBetaEnabled} = usePermissions(); - const isNewManualExpenseFlowEnabled = isBetaEnabled(CONST.BETAS.NEW_MANUAL_EXPENSE_FLOW); - const resetIOUTypeIfChanged = useResetIOUType({ reportID, report, @@ -185,7 +181,6 @@ function IOURequestStartPage({ iouType, policy, skipKeyboardDismissForPerDiem: true, - isNewManualExpenseFlowEnabled, }); useEffect(() => { @@ -199,10 +194,9 @@ function IOURequestStartPage({ }, []); const navigateBack = () => { - // In the new manual expense beta the confirmation is embedded with its header hidden, - // so this back button is the only way to abandon the flow. Cancel any active span - // unconditionally (mirrors IOURequestStepConfirmation.navigateBack). No-op when no - // tracking session is active. + // The confirmation is embedded with its header hidden, so this back button is the only way to abandon + // the flow. Cancel any active span unconditionally (mirrors IOURequestStepConfirmation.navigateBack). + // No-op when no tracking session is active. cancelTracking(); // Restore the pre-inserted fullscreen tab while the RHP is still on top so the clean @@ -231,17 +225,7 @@ function IOURequestStartPage({ const shouldShowWorkspaceSelectForPerDiem = moreThanOnePerDiemExist && !hasCurrentPolicyPerDiemEnabled; let manualTabContent: React.ReactNode; - if (!isNewManualExpenseFlowEnabled) { - manualTabContent = ( - - ); - } else if (isScanRequest(transaction)) { + if (isScanRequest(transaction)) { // When switching from the Scan tab, the shared draft is briefly still a scan request (with the uploaded // receipt) until the tab-switch reset rebuilds it as manual. Mounting the embedded confirmation against that // stale scan draft does throwaway work (scan loader, reading the receipt blob and a heavy first render) that @@ -266,14 +250,14 @@ function IOURequestStartPage({ allPolicies={iouType === CONST.IOU.TYPE.INVOICE ? allPolicies : undefined} > - {/* If the new manual expense flow is enabled, the confirmation screen is shown on the start page, so we do not want to disable the drag and drop provider in that case */} - + {/* The confirmation screen is shown on the start page for the manual tab, so we do not want to disable the drag and drop provider in that case */} + 0; const hasDefaultParticipants = defaultParticipants.length > 0; - return !hasTransactionParticipants && !hasDefaultParticipants && isNewManualExpenseFlowEnabled && isManualRequest; - }, [transaction?.transactionID, transaction?.participants, defaultParticipants.length, isNewManualExpenseFlowEnabled, isManualRequest]); + return !hasTransactionParticipants && !hasDefaultParticipants && isManualRequest; + }, [transaction?.transactionID, transaction?.participants, defaultParticipants.length, isManualRequest]); const activeTransactionID = transaction?.transactionID; const [manuallyOpenedParticipantPickerForTransactionID, setManuallyOpenedParticipantPickerForTransactionID] = useState(); const [dismissedAutoOpenParticipantPickerForTransactionID, setDismissedAutoOpenParticipantPickerForTransactionID] = useState(); @@ -422,7 +419,7 @@ function IOURequestStepConfirmation({ } else if (firstDefault?.reportID) { setTransactionReport(transaction.transactionID, {reportID: firstDefault.reportID}, true); } - }, [transaction?.transactionID, transaction?.participants, defaultParticipants, isNewManualExpenseFlowEnabled, isManualRequest, navigation]); + }, [transaction?.transactionID, transaction?.participants, defaultParticipants, isManualRequest, navigation]); const isPolicyExpenseChat = useMemo(() => { const hasPolicyExpenseChat = (participantList: typeof defaultParticipants) => @@ -987,24 +984,22 @@ function IOURequestStepConfirmation({ /> )} - {isNewManualExpenseFlowEnabled && ( - Navigation.dismissModal()} - /> - )} + Navigation.dismissModal()} + /> diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index e0945288fbfd..2250747dbecc 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -3,10 +3,9 @@ import FormHelpMessage from '@components/FormHelpMessage'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useParticipantSubmission from '@hooks/useParticipantSubmission'; -import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getIsWorkspacesOnlyForTransaction, isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpenseIOUUtils, navigateToStartMoneyRequestStep} from '@libs/IOUUtils'; +import {isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpenseIOUUtils, navigateToStartMoneyRequestStep} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import {endSpan} from '@libs/telemetry/activeSpans'; import {getRequestType, isFromCreditCardImport, isPerDiemRequest, isTimeRequest as isTimeRequestUtil} from '@libs/TransactionUtils'; @@ -52,8 +51,6 @@ function IOURequestStepParticipants({ const isPerDiem = isPerDiemRequest(initialTransaction); const isTime = isTimeRequestUtil(initialTransaction); const isTransactionFromCreditCardImport = isFromCreditCardImport(initialTransaction); - const {isBetaEnabled} = usePermissions(); - const isNewManualExpenseFlowEnabled = isBetaEnabled(CONST.BETAS.NEW_MANUAL_EXPENSE_FLOW); let headerTitle = translate('iou.chooseRecipient'); if (action === CONST.IOU.ACTION.CATEGORIZE) { @@ -126,9 +123,9 @@ function IOURequestStepParticipants({ navigateToStartMoneyRequestStep(iouRequestType, iouTypeValue, initialTransactionID, reportID, action); }; - // In new flow - the amount step is skipped, so we need to include the recents for all the cases. + // The amount step is skipped, so we need to include the recents for all the cases. // Submit-only implies workspaces-only (we still hide individuals/recents in the Submit-to-employer picker). - const isWorkspacesOnly = isWorkspacesOnlyFromRoute || (isNewManualExpenseFlowEnabled ? false : getIsWorkspacesOnlyForTransaction(initialTransaction, iouRequestType)); + const isWorkspacesOnly = isWorkspacesOnlyFromRoute; const selectedParticipant = isSplitRequest ? undefined : participants?.find((participant) => participant.selected && !participant.isSender); // Participants with a reportID are found in the list and highlighted via initiallySelectedReportID. // Those without one (e.g. users to invite who don't have an account yet) must be passed explicitly diff --git a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts index 69db00b66e6d..542a8dfdf798 100644 --- a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts +++ b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts @@ -2,7 +2,6 @@ import {useSearchSelectionActions} from '@components/Search/SearchContext'; import type {ListItem} from '@components/SelectionList/types'; import useOnyx from '@hooks/useOnyx'; -import usePermissions from '@hooks/usePermissions'; import {setCustomUnitID, setCustomUnitRateID} from '@libs/actions/IOU/MoneyRequest'; import {clearSubrates} from '@libs/actions/IOU/PerDiem'; @@ -108,8 +107,6 @@ function useReportSelectionActions({ const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const {removeTransaction} = useSearchSelectionActions(); - const {isBetaEnabled} = usePermissions(); - const isNewManualExpenseFlowEnabled = isBetaEnabled(CONST.BETAS.NEW_MANUAL_EXPENSE_FLOW); const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector}); const targetTransactionIDs = transaction?.transactionID ? [transaction.transactionID] : []; @@ -164,18 +161,7 @@ function useReportSelectionActions({ return; } - if (isNewManualExpenseFlowEnabled) { - Navigation.goBack(backTo); - return; - } - - const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportOrDraftReportFromValue?.chatReportID); - // If the backTo parameter is set, we should navigate back to the confirmation screen that is already on the stack. - if (backTo) { - Navigation.goBack(iouConfirmationPageRoute, {compareParams: false}); - } else { - Navigation.navigate(iouConfirmationPageRoute); - } + Navigation.goBack(backTo); }; const handleRegularReportSelection = (item: TransactionGroupListItem, report: OnyxEntry) => { diff --git a/tests/unit/hooks/useConfirmationCtaText.test.tsx b/tests/unit/hooks/useConfirmationCtaText.test.tsx index 2e7d3d2b2f6f..c2e0598ee32d 100644 --- a/tests/unit/hooks/useConfirmationCtaText.test.tsx +++ b/tests/unit/hooks/useConfirmationCtaText.test.tsx @@ -26,7 +26,6 @@ const baseParams: Params = { receiptPath: '', isDistanceRequestWithPendingRoute: false, isPerDiemRequest: false, - isNewManualExpenseFlowEnabled: false, }; function Wrapper({children}: {children: React.ReactNode}) { @@ -61,14 +60,11 @@ describe('useConfirmationCtaText', () => { expect(result.current.at(0)?.text.toLowerCase()).toContain('expense'); }); - it('uses createExpense copy when new manual expense flow is enabled', () => { - const {result} = renderHook(() => useConfirmationCtaText({...baseParams, isNewManualExpenseFlowEnabled: true}), {wrapper: Wrapper}); - expect(result.current.at(0)?.text.toLowerCase()).toContain('expense'); - }); - - it('includes formatted amount in createExpenseWithAmount copy', () => { + it('uses createExpense copy for submit with non-zero amount', () => { const {result} = renderHook(() => useConfirmationCtaText({...baseParams, formattedAmount: '$42.00'}), {wrapper: Wrapper}); - expect(result.current.at(0)?.text).toContain('$42.00'); + expect(result.current.at(0)?.text.toLowerCase()).toContain('expense'); + // The amount is no longer appended to the CTA copy. + expect(result.current.at(0)?.text).not.toContain('$42.00'); }); it('uses next copy for invoice without invoicing details', () => { @@ -93,7 +89,7 @@ describe('useConfirmationCtaText', () => { expect(result.current.at(0)?.text).toContain('$50.00'); }); - it('includes formatted amount for track expense with non-zero amount', () => { + it('uses createExpense copy for track expense with non-zero amount', () => { const {result} = renderHook( () => useConfirmationCtaText({ @@ -105,7 +101,8 @@ describe('useConfirmationCtaText', () => { }), {wrapper: Wrapper}, ); - expect(result.current.at(0)?.text).toContain('$1.23'); + expect(result.current.at(0)?.text.toLowerCase()).toContain('expense'); + expect(result.current.at(0)?.text).not.toContain('$1.23'); }); it('uses createExpense for distance request with pending route', () => { @@ -122,7 +119,7 @@ describe('useConfirmationCtaText', () => { expect(result.current.at(0)?.text.toLowerCase()).toContain('expense'); }); - it('uses createExpenseWithAmount for per-diem request with non-zero amount', () => { + it('uses createExpense copy for per-diem request with non-zero amount', () => { const {result} = renderHook( () => useConfirmationCtaText({ @@ -133,10 +130,11 @@ describe('useConfirmationCtaText', () => { }), {wrapper: Wrapper}, ); - expect(result.current.at(0)?.text).toContain('$2.00'); + expect(result.current.at(0)?.text.toLowerCase()).toContain('expense'); + expect(result.current.at(0)?.text).not.toContain('$2.00'); }); - it('uses splitAmount with formatted amount for split with non-zero amount when manual flow is disabled', () => { + it('uses splitExpense copy for split with non-zero amount', () => { const {result} = renderHook( () => useConfirmationCtaText({ @@ -144,11 +142,11 @@ describe('useConfirmationCtaText', () => { isTypeSplit: true, iouAmount: 500, formattedAmount: '$5.00', - isNewManualExpenseFlowEnabled: false, }), {wrapper: Wrapper}, ); - expect(result.current.at(0)?.text).toContain('$5.00'); + expect(result.current.at(0)?.text.toLowerCase()).toContain('split'); + expect(result.current.at(0)?.text).not.toContain('$5.00'); }); it('uses createExpense for default zero-amount fallback', () => { diff --git a/tests/unit/hooks/useConfirmationValidation.test.ts b/tests/unit/hooks/useConfirmationValidation.test.ts index e71872c02a0e..b8ef4863d2a3 100644 --- a/tests/unit/hooks/useConfirmationValidation.test.ts +++ b/tests/unit/hooks/useConfirmationValidation.test.ts @@ -99,7 +99,6 @@ const baseParams = { isPerDiemRequest: false, isTimeRequest: false, routeError: undefined, - isNewManualExpenseFlowEnabled: false, isReadOnly: false, shouldShowDate: true, } satisfies UseConfirmationValidationParams; @@ -244,13 +243,12 @@ describe('useConfirmationValidation', () => { expect(result.current.validate()).toEqual({errorKey: null}); }); - it('returns fieldRequired for manual expense when amount is not set in new manual expense flow with a policy expense chat participant', () => { + it('returns fieldRequired for manual expense when amount is not set with a policy expense chat participant', () => { const {result} = renderHook(() => useConfirmationValidation( createValidationParamsForParticipant( POLICY_EXPENSE_CHAT_PARTICIPANT, { - isNewManualExpenseFlowEnabled: true, iouAmount: 0, }, {isAmountSet: false}, @@ -260,11 +258,10 @@ describe('useConfirmationValidation', () => { expect(result.current.validate()).toEqual({errorKey: 'common.error.fieldRequired'}); }); - it('does not return fieldRequired for scan expense when amount is not set in new manual expense flow', () => { + it('does not return fieldRequired for scan expense when amount is not set', () => { const {result} = renderHook(() => useConfirmationValidation({ ...baseParams, - isNewManualExpenseFlowEnabled: true, transaction: createTransactionBase({ amount: 1000, iouRequestType: CONST.IOU.REQUEST_TYPE.SCAN, @@ -275,11 +272,10 @@ describe('useConfirmationValidation', () => { expect(result.current.validate()).toEqual({errorKey: null}); }); - it('does not return fieldRequired for per diem expense when amount is not set in new manual expense flow', () => { + it('does not return fieldRequired for per diem expense when amount is not set', () => { const {result} = renderHook(() => useConfirmationValidation({ ...baseParams, - isNewManualExpenseFlowEnabled: true, isPerDiemRequest: true, transaction: createTransactionBase({ amount: 5000, @@ -301,9 +297,8 @@ describe('useConfirmationValidation', () => { expect(result.current.validate(CONST.IOU.PAYMENT_TYPE.ELSEWHERE)).toEqual({errorKey: null}); }); - describe('amount validation — new manual expense flow (isAmountSet)', () => { + describe('amount validation — manual expense (isAmountSet)', () => { const newManualFlowParams = { - isNewManualExpenseFlowEnabled: true, iouAmount: 0, }; @@ -422,26 +417,10 @@ describe('useConfirmationValidation', () => { expect(result.current.validate()).toEqual({errorKey: null}); }); }); - - it('does not return fieldRequired when the new manual expense flow beta is disabled', () => { - const {result} = renderHook(() => - useConfirmationValidation( - createValidationParamsForParticipant( - P2P_PARTICIPANT, - { - isNewManualExpenseFlowEnabled: false, - iouAmount: 0, - }, - {isAmountSet: false}, - ), - ), - ); - expect(result.current.validate()).toEqual({errorKey: 'common.error.invalidAmount'}); - }); }); describe('amount validation — P2P zero amount guard', () => { - it('returns invalidAmount for P2P manual submit with zero amount when flow is disabled', () => { + it('returns invalidAmount for P2P manual submit with zero amount', () => { const {result} = renderHook(() => useConfirmationValidation(createValidationParamsForParticipant(P2P_PARTICIPANT, {iouAmount: 0}, {amount: 0, isAmountSet: true}))); expect(result.current.validate()).toEqual({errorKey: 'common.error.invalidAmount'}); }); @@ -510,7 +489,6 @@ describe('useConfirmationValidation', () => { describe('amount validation — programmatic request types (scan, distance, time, per diem)', () => { const newManualFlowParams = { ...baseParams, - isNewManualExpenseFlowEnabled: true, }; it('does not return fieldRequired for scan expense when amount is not set', () => { @@ -684,7 +662,6 @@ describe('useConfirmationValidation', () => { P2P_PARTICIPANT, { iouType: CONST.IOU.TYPE.SPLIT, - isNewManualExpenseFlowEnabled: true, iouAmount: 0, selectedParticipants: splitParticipants, }, @@ -702,7 +679,6 @@ describe('useConfirmationValidation', () => { POLICY_EXPENSE_CHAT_PARTICIPANT, { iouType: CONST.IOU.TYPE.SPLIT, - isNewManualExpenseFlowEnabled: true, iouAmount: 0, }, {isAmountSet: false}, @@ -720,7 +696,6 @@ describe('useConfirmationValidation', () => { POLICY_EXPENSE_CHAT_PARTICIPANT, { iouType: CONST.IOU.TYPE.SPLIT, - isNewManualExpenseFlowEnabled: true, iouAmount: 0, selectedParticipants: splitParticipants, }, @@ -752,10 +727,9 @@ describe('useConfirmationValidation', () => { }); }); - describe('date validation — inline required date in new manual expense flow', () => { + describe('date validation — inline required date', () => { const newManualFlowParams = { ...baseParams, - isNewManualExpenseFlowEnabled: true, }; it('returns fieldRequired for manual expense when the date is removed', () => { @@ -834,12 +808,5 @@ describe('useConfirmationValidation', () => { ); expect(result.current.validate()).toEqual({errorKey: null}); }); - - it('does not return fieldRequired when the new manual expense flow beta is disabled', () => { - const {result} = renderHook(() => - useConfirmationValidation(createValidationParamsForParticipant(POLICY_EXPENSE_CHAT_PARTICIPANT, {isNewManualExpenseFlowEnabled: false}, {created: '', isAmountSet: true})), - ); - expect(result.current.validate()).toEqual({errorKey: null}); - }); }); }); diff --git a/tests/unit/hooks/useFormErrorManagement.test.tsx b/tests/unit/hooks/useFormErrorManagement.test.tsx index 2159d066cdce..02bbcb3fbec3 100644 --- a/tests/unit/hooks/useFormErrorManagement.test.tsx +++ b/tests/unit/hooks/useFormErrorManagement.test.tsx @@ -44,7 +44,6 @@ const baseParams: Params = { routeError: undefined, isTypeSplit: false, shouldShowReadOnlySplits: false, - isNewManualExpenseFlowEnabled: false, isDistanceRequest: false, }; @@ -114,37 +113,25 @@ describe('useFormErrorManagement', () => { expect(result.current.errorMessage).toBeUndefined(); }); - it('errorMessage suppresses required/invalid amount errors in the new manual expense flow (surfaced inline)', () => { - const {result: required} = renderHook(() => useFormErrorManagement({...baseParams, isNewManualExpenseFlowEnabled: true}), {wrapper: Wrapper}); + it('errorMessage suppresses required/invalid amount errors (surfaced inline)', () => { + const {result: required} = renderHook(() => useFormErrorManagement(baseParams), {wrapper: Wrapper}); act(() => required.current.setFormError('common.error.fieldRequired')); expect(required.current.errorMessage).toBeUndefined(); - const {result: invalid} = renderHook(() => useFormErrorManagement({...baseParams, isNewManualExpenseFlowEnabled: true}), {wrapper: Wrapper}); + const {result: invalid} = renderHook(() => useFormErrorManagement(baseParams), {wrapper: Wrapper}); act(() => invalid.current.setFormError('common.error.invalidAmount')); expect(invalid.current.errorMessage).toBeUndefined(); }); - it('errorMessage still shows required/invalid amount errors when the new manual expense flow is disabled', () => { - const {result} = renderHook(() => useFormErrorManagement({...baseParams, isNewManualExpenseFlowEnabled: false}), {wrapper: Wrapper}); + it('errorMessage still shows the invalid amount error for a distance request (no inline surface)', () => { + const {result} = renderHook(() => useFormErrorManagement({...baseParams, isDistanceRequest: true}), {wrapper: Wrapper}); act(() => result.current.setFormError('common.error.invalidAmount')); expect(result.current.errorMessage).toBeDefined(); }); - it('errorMessage still shows the invalid amount error for a distance request in the new manual expense flow (no inline surface)', () => { - const {result} = renderHook(() => useFormErrorManagement({...baseParams, isNewManualExpenseFlowEnabled: true, isDistanceRequest: true}), {wrapper: Wrapper}); - act(() => result.current.setFormError('common.error.invalidAmount')); - expect(result.current.errorMessage).toBeDefined(); - }); - - it('errorMessage suppresses the invalid merchant error in the new manual expense flow (surfaced inline)', () => { - const {result} = renderHook(() => useFormErrorManagement({...baseParams, isNewManualExpenseFlowEnabled: true}), {wrapper: Wrapper}); + it('errorMessage suppresses the invalid merchant error (surfaced inline)', () => { + const {result} = renderHook(() => useFormErrorManagement(baseParams), {wrapper: Wrapper}); act(() => result.current.setFormError('iou.error.invalidMerchant')); expect(result.current.errorMessage).toBeUndefined(); }); - - it('errorMessage still shows the invalid merchant error when the new manual expense flow is disabled', () => { - const {result} = renderHook(() => useFormErrorManagement({...baseParams, isNewManualExpenseFlowEnabled: false}), {wrapper: Wrapper}); - act(() => result.current.setFormError('iou.error.invalidMerchant')); - expect(result.current.errorMessage).toBeDefined(); - }); });