diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 3d3410e0d8f9..fb2148f12435 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -1033,7 +1033,6 @@ const CONST = { SUGGESTED_FOLLOWUPS: 'suggestedFollowups', BULK_EDIT: 'bulkEdit', BULK_EDIT_WORKSPACES: 'bulkEditWorkspaces', - NEW_MANUAL_EXPENSE_FLOW: 'newManualExpenseFlow', SUBMIT_2026: 'submit2026', DATE_BOUND_MILEAGE_RATE: 'dateBoundMileageRate', BULK_SUBMIT_APPROVE_PAY: 'bulkSubmitApprovePay', diff --git a/src/components/MoneyRequestConfirmationFields/Provider.tsx b/src/components/MoneyRequestConfirmationFields/Provider.tsx index e023dda0ce57..27f97b847ff2 100644 --- a/src/components/MoneyRequestConfirmationFields/Provider.tsx +++ b/src/components/MoneyRequestConfirmationFields/Provider.tsx @@ -32,9 +32,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; @@ -73,7 +70,6 @@ function Provider({ isReadOnly = false, didConfirm = false, isEditingSplitBill = false, - isNewManualExpenseFlowEnabled = false, isPolicyExpenseChat = false, isDistanceRequest = false, isPerDiemRequest = false, @@ -94,7 +90,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 2ece24c6f7a5..0ffec635f1de 100644 --- a/src/components/MoneyRequestConfirmationFields/context.ts +++ b/src/components/MoneyRequestConfirmationFields/context.ts @@ -20,7 +20,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 f2bfe1000b3c..71c0ac41faa8 100644 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -7,7 +7,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'; @@ -16,7 +15,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {isCategoryDescriptionRequired} from '@libs/CategoryUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import {isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpenseUtil} from '@libs/IOUUtils'; -import Navigation from '@libs/Navigation/Navigation'; import {hasEnabledOptions} from '@libs/OptionsListUtils'; import {isTaxTrackingEnabled} from '@libs/PolicyUtils'; import type {OptionData} from '@libs/ReportUtils'; @@ -32,7 +30,6 @@ import { } from '@libs/TransactionUtils'; 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'; @@ -211,8 +208,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(); @@ -309,7 +304,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(); @@ -372,7 +367,6 @@ function MoneyRequestConfirmationList({ receiptPath, isDistanceRequestWithPendingRoute, isPerDiemRequest, - isNewManualExpenseFlowEnabled, }); const selectedParticipants = selectedParticipantsProp.filter((participant) => participant.selected); @@ -429,13 +423,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({ @@ -465,7 +453,6 @@ function MoneyRequestConfirmationList({ isPerDiemRequest, isTimeRequest, routeError, - isNewManualExpenseFlowEnabled, }); const confirm = buildConfirmAction({ diff --git a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts index d20376e50478..bafbf328ab02 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts +++ b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationCtaText.ts @@ -41,17 +41,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 and + * bulk-expense count, returning a single-entry DropdownOption array shaped for the + * ButtonWithDropdownMenu consumer. */ function useConfirmationCtaText({ expensesNumber, @@ -60,13 +57,12 @@ function useConfirmationCtaText({ isTypeSplit, isTypeRequest, iouAmount, - iouType, policy, + iouType, formattedAmount, receiptPath, isDistanceRequestWithPendingRoute, isPerDiemRequest, - isNewManualExpenseFlowEnabled, }: UseConfirmationCtaTextParams): Array> { const {translate} = useLocalize(); @@ -81,27 +77,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 0162e8f6fbd1..f49d779c51b9 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationValidation.ts +++ b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationValidation.ts @@ -107,9 +107,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; }; /** @@ -153,7 +150,6 @@ function useConfirmationValidation({ isPerDiemRequest, isTimeRequest, routeError, - isNewManualExpenseFlowEnabled, }: UseConfirmationValidationParams): {validate: (paymentType?: PaymentMethodType) => ValidationResult | null} { const {getCurrencyDecimals} = useCurrencyListActions(); const selectedParticipantsCount = selectedParticipants.length; @@ -174,11 +170,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) && @@ -189,8 +184,8 @@ function useConfirmationValidation({ ) { return {errorKey: 'common.error.invalidAmount'}; } - // The date is an inline required field in the new manual flow; block confirmation when the user cleared it. - if (isNewManualExpenseFlowEnabled && transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL && isCreatedMissing(transaction)) { + // The date is an inline required field in the manual flow; block confirmation when the user cleared it. + if (transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL && isCreatedMissing(transaction)) { return {errorKey: 'common.error.fieldRequired'}; } const merchantValue = iouMerchant ?? ''; @@ -248,10 +243,10 @@ function useConfirmationValidation({ return {errorKey: 'violations.taxOutOfPolicy'}; } - // In the new manual expense flow the tax amount is edited inline, so the standalone tax amount step's + // In the 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/sections/AmountField.tsx b/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx index 05de888d82dc..7d11181aac48 100644 --- a/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/AmountField.tsx @@ -34,7 +34,6 @@ type AmountFieldProps = { distanceRateCurrency: string; iouCurrencyCode: string | undefined; isDistanceRequest: boolean; - isNewManualExpenseFlowEnabled: boolean; didConfirm: boolean; isReadOnly: boolean; shouldShowTimeRequestFields: boolean; @@ -58,7 +57,6 @@ function AmountField({ distanceRateCurrency, iouCurrencyCode, isDistanceRequest, - isNewManualExpenseFlowEnabled, didConfirm, isReadOnly, shouldShowTimeRequestFields, @@ -92,10 +90,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; @@ -110,21 +105,20 @@ function AmountField({ const effectiveCurrency = isDistanceRequest ? distanceRateCurrency : (iouCurrencyCode ?? CONST.CURRENCY.USD); const decimals = getCurrencyDecimals(effectiveCurrency); - // In the new manual expense flow the amount field starts empty (transaction.amount defaults to 0 before the user + // In the manual expense flow the amount field starts empty (transaction.amount defaults to 0 before the user // 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); // `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 - // (visible → hidden) so the amount input gains focus once the user selects a participant in the new manual - // expense flow. The setTimeout defers focus past the RHP entry / picker close animation so the input reliably - // receives focus. + // (visible → hidden) so the amount input gains focus once the user selects a participant. 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; } @@ -136,7 +130,7 @@ function AmountField({ } clearTimeout(focusTimeoutRef.current); }; - }, [autoFocus, isAmountFieldDisabled, isNewManualExpenseFlowEnabled, isParticipantPickerVisible]); + }, [autoFocus, isAmountFieldDisabled, isParticipantPickerVisible]); const showCurrencyPicker = () => { setIsCurrencyPickerVisible(true); @@ -297,7 +291,7 @@ function AmountField({ value={effectiveCurrency} onInputChange={updateCurrency} /> - {isNewManualExpenseFlowEnabled && !isAmountFieldDisabled ? ( + {!isAmountFieldDisabled ? ( void; transactionID: string | undefined; @@ -36,19 +35,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(); @@ -107,7 +94,7 @@ function DateField({ } }; - if (isNewManualExpenseFlowEnabled && !isReadOnly) { + if (!isReadOnly) { return ( void; }; -function DescriptionField({ - isNewManualExpenseFlowEnabled, - isReadOnly, - didConfirm, - isDescriptionRequired, - transactionID, - action, - iouType, - reportID, - reportActionID, - policy, - onSubmitForm, -}: DescriptionFieldProps) { +function DescriptionField({isReadOnly, didConfirm, isDescriptionRequired, transactionID, action, iouType, reportID, reportActionID, policy, onSubmitForm}: DescriptionFieldProps) { const {isEditingSplitBill} = useConfirmationFields(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -99,7 +86,7 @@ function DescriptionField({ - {isNewManualExpenseFlowEnabled && !isReadOnly ? ( + {!isReadOnly ? ( (null); const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`); @@ -90,18 +90,18 @@ function TaxFields({policy, policyForMovingExpenses, iouCurrencyCode, canModifyT }; useEffect(() => { - if (!isNewManualExpenseFlowEnabled || (numberFormRef?.current && numberFormRef.current.getNumber() === taxAmountInput)) { + if (numberFormRef?.current && numberFormRef.current.getNumber() === taxAmountInput) { return; } numberFormRef.current?.updateNumber(taxAmountInput); - }, [isNewManualExpenseFlowEnabled, taxAmountInput]); + }, [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 ( <> @@ -125,7 +125,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 ? ( @@ -131,7 +131,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 4f2641b97b5a..ba94fe0fc42b 100644 --- a/src/components/MoneyRequestConfirmationListFooter/fieldGroups/TransactionDetailsFields.tsx +++ b/src/components/MoneyRequestConfirmationListFooter/fieldGroups/TransactionDetailsFields.tsx @@ -64,20 +64,8 @@ function TransactionDetailsFields({ onSubmitForm, 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 ( @@ -90,7 +78,6 @@ function TransactionDetailsFields({ distanceRateCurrency={distanceData.distanceRateCurrency} iouCurrencyCode={iouCurrencyCode} isDistanceRequest={fieldVisibility.distance} - isNewManualExpenseFlowEnabled={isNewManualExpenseFlowEnabled} didConfirm={didConfirm} isReadOnly={isReadOnly} shouldShowTimeRequestFields={fieldVisibility.time} @@ -111,7 +98,6 @@ function TransactionDetailsFields({ {!isCompactMode && fieldVisibility.merchant && ( { - if (!isNewManualExpenseFlowEnabled) { - return []; - } - const reportParticipants = getMoneyRequestParticipantsFromReport(sourceReport, accountID).filter((participant) => participant.selected); if (reportParticipants.length > 0) { return reportParticipants; @@ -73,7 +66,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 c533f6fca50d..2e7b14c2aa41 100644 --- a/src/hooks/useResetIOUType.ts +++ b/src/hooks/useResetIOUType.ts @@ -47,10 +47,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; }; /** @@ -68,7 +64,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}); @@ -86,14 +81,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/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index fb3ab429bfa1..c43da994b859 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1949,7 +1949,7 @@ function isSelfDMOrSelfDMThread(report: OnyxEntry): boolean { /** * Returns true if the report is an expense report, a group policy, a self-DM, or the iouType is create, and the iouType is not split or invoice. */ -function shouldEnableNegative(report: OnyxEntry, policy?: OnyxEntry, iouType?: string, participants?: Participant[], isNewManualExpenseFlow = false) { +function shouldEnableNegative(report: OnyxEntry, policy?: OnyxEntry, iouType?: string, participants?: Participant[]) { const isSelfDMReport = isSelfDMOrSelfDMThread(report); const isUserInRecipients = participants?.some((participant) => !participant.isSender && !participant.isPolicyExpenseChat && participant.accountID); @@ -1957,7 +1957,7 @@ function shouldEnableNegative(report: OnyxEntry, policy?: OnyxEntry { @@ -211,17 +206,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 @@ -246,14 +231,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(); @@ -344,7 +341,7 @@ function IOURequestStepConfirmation({ setTransactionReport(transaction.transactionID, {reportID: CONST.REPORT.UNREPORTED_REPORT_ID}, true); navigation.setParams({iouType: CONST.IOU.TYPE.TRACK}); } - }, [transaction?.transactionID, transaction?.participants, defaultParticipants, isNewManualExpenseFlowEnabled, isManualRequest, navigation]); + }, [transaction?.transactionID, transaction?.participants, defaultParticipants, isManualRequest, navigation]); const isPolicyExpenseChat = useMemo(() => { const hasPolicyExpenseChat = (participantList: typeof defaultParticipants) => @@ -878,20 +875,18 @@ function IOURequestStepConfirmation({ /> )} - {isNewManualExpenseFlowEnabled && ( - - )} + diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 8c53277b0837..effd2c61ac95 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -4,9 +4,8 @@ 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'; @@ -42,8 +41,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) { @@ -116,8 +113,6 @@ 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. - const isWorkspacesOnly = isNewManualExpenseFlowEnabled ? false : getIsWorkspacesOnlyForTransaction(initialTransaction, iouRequestType); 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 @@ -147,7 +142,7 @@ function IOURequestStepParticipants({ action={action} isPerDiemRequest={isPerDiem} isTimeRequest={isTime} - isWorkspacesOnly={isWorkspacesOnly} + isWorkspacesOnly={false} isTransactionFromCreditCardImport={isTransactionFromCreditCardImport} initiallySelectedReportID={selectedParticipant?.reportID} shouldMoveSelectedToTop diff --git a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts index b3878e80e96c..9f9f257fd420 100644 --- a/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts +++ b/src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts @@ -4,7 +4,6 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; 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'; import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction'; @@ -104,8 +103,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 buildParticipants = (report: OnyxEntry) => [ { @@ -155,18 +152,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 3a8081b9de1f..5f919e3471dd 100644 --- a/tests/unit/hooks/useConfirmationCtaText.test.tsx +++ b/tests/unit/hooks/useConfirmationCtaText.test.tsx @@ -22,7 +22,6 @@ const baseParams: Params = { receiptPath: '', isDistanceRequestWithPendingRoute: false, isPerDiemRequest: false, - isNewManualExpenseFlowEnabled: false, }; function Wrapper({children}: {children: React.ReactNode}) { @@ -57,14 +56,10 @@ 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 without the amount for a submit with a 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'); + expect(result.current.at(0)?.text).not.toContain('$42.00'); }); it('uses next copy for invoice without invoicing details', () => { @@ -89,7 +84,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 without the amount for track expense with non-zero amount', () => { const {result} = renderHook( () => useConfirmationCtaText({ @@ -101,7 +96,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', () => { @@ -118,7 +114,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 without the amount for per-diem request with non-zero amount', () => { const {result} = renderHook( () => useConfirmationCtaText({ @@ -129,10 +125,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({ @@ -140,11 +137,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 eb6f61b7b0c3..87dddb82b81a 100644 --- a/tests/unit/hooks/useConfirmationValidation.test.ts +++ b/tests/unit/hooks/useConfirmationValidation.test.ts @@ -71,7 +71,6 @@ const baseParams = { isPerDiemRequest: false, isTimeRequest: false, routeError: undefined, - isNewManualExpenseFlowEnabled: false, } satisfies UseConfirmationValidationParams; function createValidationParamsForParticipant( @@ -197,13 +196,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}, @@ -213,11 +211,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, @@ -228,11 +225,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, @@ -254,9 +250,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, }; @@ -375,26 +370,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'}); }); @@ -463,7 +442,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', () => { @@ -637,7 +615,6 @@ describe('useConfirmationValidation', () => { P2P_PARTICIPANT, { iouType: CONST.IOU.TYPE.SPLIT, - isNewManualExpenseFlowEnabled: true, iouAmount: 0, selectedParticipants: splitParticipants, }, @@ -655,7 +632,6 @@ describe('useConfirmationValidation', () => { POLICY_EXPENSE_CHAT_PARTICIPANT, { iouType: CONST.IOU.TYPE.SPLIT, - isNewManualExpenseFlowEnabled: true, iouAmount: 0, }, {isAmountSet: false}, @@ -673,7 +649,6 @@ describe('useConfirmationValidation', () => { POLICY_EXPENSE_CHAT_PARTICIPANT, { iouType: CONST.IOU.TYPE.SPLIT, - isNewManualExpenseFlowEnabled: true, iouAmount: 0, selectedParticipants: splitParticipants, },