From 31823ccb0903ffe4f5eafa435379a35ae4abed3f Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 11 Oct 2024 13:55:28 +0200 Subject: [PATCH 01/17] add strings --- src/languages/en.ts | 2 ++ src/languages/es.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index d2226e1c1290..28d38928dab6 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3687,6 +3687,8 @@ const translations = { business: 'Business', chooseInvoiceMethod: 'Choose a payment method below:', addBankAccount: 'Add bank account', + addDebitOrCreditCard: 'Add debit or credit card', + addCorporateCard: 'Add corporate card', payingAsIndividual: 'Paying as an individual', payingAsBusiness: 'Paying as a business', }, diff --git a/src/languages/es.ts b/src/languages/es.ts index fd807854e410..c9992f258066 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3729,6 +3729,8 @@ const translations = { business: 'Empresas', chooseInvoiceMethod: 'Elija un método de pago:', addBankAccount: 'Añadir cuenta bancaria', + addDebitOrCreditCard: 'Agrega una tarjeta de débito o crédito', + addCorporateCard: 'Agrega una tarjeta corporativa', payingAsIndividual: 'Pago individual', payingAsBusiness: 'Pagar como una empresa', }, From 3948c12a1dae63c141b02d82304c7f1d473de3d3 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 11 Oct 2024 14:12:35 +0200 Subject: [PATCH 02/17] add invoice case for getBankAccountRoute --- src/libs/ReportUtils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e9108956cb1d..973a2932de22 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1141,7 +1141,15 @@ function isPublicAnnounceRoom(report: OnyxEntry): boolean { * else since the report is a personal IOU, the route should be for personal bank account. */ function getBankAccountRoute(report: OnyxEntry): Route { - return isPolicyExpenseChat(report) ? ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('', report?.policyID) : ROUTES.SETTINGS_ADD_BANK_ACCOUNT; + if (isPolicyExpenseChat(report)) { + return ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('', report?.policyID); + } + + if (isInvoiceRoom(report) && report?.invoiceReceiver?.type === CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS) { + return ROUTES.WORKSPACE_INVOICES.getRoute(report?.invoiceReceiver?.policyID); + } + + return ROUTES.SETTINGS_ADD_BANK_ACCOUNT; } /** From a2d1b9a70bd9500b3da86824bc537cb19b124422 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 11 Oct 2024 14:29:34 +0200 Subject: [PATCH 03/17] add invoice payment menu items --- src/components/SettlementButton/index.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index f371545ab7b0..a1d7eafe3320 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -127,6 +127,16 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.individual'), subMenuItems: [ + { + text: translate('workspace.invoices.paymentMethods.addBankAccount'), + icon: Expensicons.Bank, + onSelected: () => Navigation.navigate(addBankAccountRoute), + }, + { + text: translate('workspace.invoices.paymentMethods.addDebitOrCreditCard'), + icon: Expensicons.CreditCard, + onSelected: () => Navigation.navigate(addDebitCardRoute), + }, { text: translate('iou.payElsewhere', {formattedAmount: ''}), icon: Expensicons.Cash, @@ -143,6 +153,16 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.business'), subMenuItems: [ + { + text: translate('workspace.invoices.paymentMethods.addBankAccount'), + icon: Expensicons.Bank, + onSelected: () => Navigation.navigate(addBankAccountRoute), + }, + { + text: translate('workspace.invoices.paymentMethods.addCorporateCard'), + icon: Expensicons.CreditCard, + onSelected: () => Navigation.navigate(addDebitCardRoute), // TODO: Clarify if this is the correct route + }, { text: translate('iou.payElsewhere', {formattedAmount: ''}), icon: Expensicons.Cash, From 905f8e5c0f83db7adc74477abd7a3b1c5950b9ef Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 14 Oct 2024 15:53:04 +0200 Subject: [PATCH 04/17] add payment method button --- src/components/SettlementButton/index.tsx | 91 ++++++++++++++--------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index a1d7eafe3320..9a8a1a4bda39 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -7,7 +7,9 @@ import * as Expensicons from '@components/Icon/Expensicons'; import KYCWall from '@components/KYCWall'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import * as PaymentUtils from '@libs/PaymentUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import * as SubscriptionUtils from '@libs/SubscriptionUtils'; @@ -60,6 +62,7 @@ function SettlementButton({ onPaymentOptionsHide, onlyShowPayElsewhere, }: SettlementButtonProps) { + const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. @@ -68,6 +71,9 @@ function SettlementButton({ const [lastPaymentMethod = '-1', lastPaymentMethodResult] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod?.[policyID]}); const isLoadingLastPaymentMethod = isLoadingOnyxValue(lastPaymentMethodResult); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const [bankAccountList = {}] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + const [fundList = {}] = useOnyx(ONYXKEYS.FUND_LIST); + const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; const shouldShowPaywithExpensifyOption = !shouldHidePaymentOptions && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL; const shouldShowPayElsewhereOption = !shouldHidePaymentOptions && !isInvoiceReport; @@ -120,56 +126,69 @@ function SettlementButton({ } if (isInvoiceReport) { - if (ReportUtils.isIndividualInvoiceRoom(chatReport)) { - buttonOptions.push({ - text: translate('iou.settlePersonal', {formattedAmount}), - icon: Expensicons.User, - value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - backButtonText: translate('iou.individual'), - subMenuItems: [ - { - text: translate('workspace.invoices.paymentMethods.addBankAccount'), - icon: Expensicons.Bank, - onSelected: () => Navigation.navigate(addBankAccountRoute), - }, - { - text: translate('workspace.invoices.paymentMethods.addDebitOrCreditCard'), - icon: Expensicons.CreditCard, - onSelected: () => Navigation.navigate(addDebitCardRoute), - }, - { - text: translate('iou.payElsewhere', {formattedAmount: ''}), - icon: Expensicons.Cash, - value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE), - }, - ], - }); - } + const formattedPaymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList, fundList, styles); + const defaultPaymentMethod = formattedPaymentMethods.find((paymentMethod) => !!paymentMethod.isDefault) ?? formattedPaymentMethods.at(0) ?? null; + const paymentSubitem = { + text: defaultPaymentMethod?.title ?? '', + description: defaultPaymentMethod?.description ?? '', + icon: defaultPaymentMethod?.icon ?? Expensicons.Bank, + onSelected: () => {}, + }; - buttonOptions.push({ - text: translate('iou.settleBusiness', {formattedAmount}), - icon: Expensicons.Building, - value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - backButtonText: translate('iou.business'), - subMenuItems: [ + if (ReportUtils.isIndividualInvoiceRoom(chatReport)) { + const subMenuItems = [ { text: translate('workspace.invoices.paymentMethods.addBankAccount'), icon: Expensicons.Bank, onSelected: () => Navigation.navigate(addBankAccountRoute), }, { - text: translate('workspace.invoices.paymentMethods.addCorporateCard'), + text: translate('workspace.invoices.paymentMethods.addDebitOrCreditCard'), icon: Expensicons.CreditCard, - onSelected: () => Navigation.navigate(addDebitCardRoute), // TODO: Clarify if this is the correct route + onSelected: () => Navigation.navigate(addDebitCardRoute), }, { text: translate('iou.payElsewhere', {formattedAmount: ''}), icon: Expensicons.Cash, value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE, true), + onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE), }, - ], + ]; + + buttonOptions.push({ + text: translate('iou.settlePersonal', {formattedAmount}), + icon: Expensicons.User, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + backButtonText: translate('iou.individual'), + subMenuItems: defaultPaymentMethod ? [paymentSubitem, ...subMenuItems] : subMenuItems, + }); + } + + const subMenuItems = [ + { + text: translate('workspace.invoices.paymentMethods.addBankAccount'), + icon: Expensicons.Bank, + onSelected: () => Navigation.navigate(addBankAccountRoute), + }, + { + text: translate('workspace.invoices.paymentMethods.addCorporateCard'), + icon: Expensicons.CreditCard, + onSelected: () => Navigation.navigate(addDebitCardRoute), // TODO: Clarify if this is the correct route + }, + { + text: translate('iou.payElsewhere', {formattedAmount: ''}), + icon: Expensicons.Cash, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE, true), + }, + ]; + + buttonOptions.push({ + text: translate('iou.settleBusiness', {formattedAmount}), + icon: Expensicons.Building, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + backButtonText: translate('iou.business'), + subMenuItems: defaultPaymentMethod ? [paymentSubitem, ...subMenuItems] : subMenuItems, }); } From 94647a4e09c3652458d184991210fc147a868e79 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 14 Oct 2024 17:03:58 +0200 Subject: [PATCH 05/17] pay invoice command accepts method id --- src/libs/API/parameters/PayInvoiceParams.ts | 2 ++ src/libs/actions/IOU.ts | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/libs/API/parameters/PayInvoiceParams.ts b/src/libs/API/parameters/PayInvoiceParams.ts index 7f80e5d20c4c..20c1e96d40f2 100644 --- a/src/libs/API/parameters/PayInvoiceParams.ts +++ b/src/libs/API/parameters/PayInvoiceParams.ts @@ -6,6 +6,8 @@ type PayInvoiceParams = Partial & { reportActionID: string; paymentMethodType: PaymentMethodType; payAsBusiness: boolean; + bankAccountID?: number; + fundID?: number; }; export default PayInvoiceParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index de896e6f72f5..66696d925bb1 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -7584,7 +7584,14 @@ function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.R API.write(apiCommand, params, {optimisticData, successData, failureData}); } -function payInvoice(paymentMethodType: PaymentMethodType, chatReport: OnyxTypes.Report, invoiceReport: OnyxEntry, payAsBusiness = false) { +function payInvoice( + paymentMethodType: PaymentMethodType, + chatReport: OnyxTypes.Report, + invoiceReport: OnyxEntry, + payAsBusiness = false, + bankAccountID?: number, + fundID?: number, +) { const recipient = {accountID: invoiceReport?.ownerAccountID ?? -1}; const { optimisticData, @@ -7614,6 +7621,14 @@ function payInvoice(paymentMethodType: PaymentMethodType, chatReport: OnyxTypes. payAsBusiness, }; + if (bankAccountID) { + params.bankAccountID = bankAccountID; + } + + if (fundID) { + params.fundID = fundID; + } + if (policyID) { params = { ...params, From 0080e8255d25ddb663e195eff231a0df3cc46db7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 14 Oct 2024 17:04:25 +0200 Subject: [PATCH 06/17] integrate payment logic --- src/components/MoneyReportHeader.tsx | 12 ++++++++++-- src/components/ReportActionItem/ReportPreview.tsx | 12 ++++++++++-- src/components/SettlementButton/index.tsx | 9 ++++++--- src/components/SettlementButton/types.ts | 2 +- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index fdf6f8edd825..8149270b8919 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -31,6 +31,7 @@ import DelegateNoAccessModal from './DelegateNoAccessModal'; import HeaderWithBackButton from './HeaderWithBackButton'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; +import type {PaymentMethod} from './KYCWall/types'; import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; @@ -150,7 +151,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); const confirmPayment = useCallback( - (type?: PaymentMethodType | undefined, payAsBusiness?: boolean) => { + (type?: PaymentMethodType | undefined, payAsBusiness?: boolean, methodID?: number, paymentMethod?: PaymentMethod) => { if (!type || !chatReport) { return; } @@ -161,7 +162,14 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea } else if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); } else if (ReportUtils.isInvoiceReport(moneyRequestReport)) { - IOU.payInvoice(type, chatReport, moneyRequestReport, payAsBusiness); + IOU.payInvoice( + type, + chatReport, + moneyRequestReport, + payAsBusiness, + paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? methodID : undefined, + paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD ? methodID : undefined, + ); } else { IOU.payMoneyRequest(type, chatReport, moneyRequestReport, true); } diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index f10951f2b1a0..c35f89153b0d 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -8,6 +8,7 @@ import Button from '@components/Button'; import DelegateNoAccessModal from '@components/DelegateNoAccessModal'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; +import type {PaymentMethod} from '@components/KYCWall/types'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ProcessMoneyReportHoldMenu from '@components/ProcessMoneyReportHoldMenu'; @@ -197,7 +198,7 @@ function ReportPreview({ HapticFeedback.longPress(); }, []); const confirmPayment = useCallback( - (type: PaymentMethodType | undefined, payAsBusiness?: boolean) => { + (type: PaymentMethodType | undefined, payAsBusiness?: boolean, methodID?: number, paymentMethod?: PaymentMethod) => { if (!type) { return; } @@ -211,7 +212,14 @@ function ReportPreview({ setIsPaidAnimationRunning(true); HapticFeedback.longPress(); if (ReportUtils.isInvoiceReport(iouReport)) { - IOU.payInvoice(type, chatReport, iouReport, payAsBusiness); + IOU.payInvoice( + type, + chatReport, + iouReport, + payAsBusiness, + paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? methodID : undefined, + paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD ? methodID : undefined, + ); } else { IOU.payMoneyRequest(type, chatReport, iouReport); } diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 9a8a1a4bda39..dfc3e0983b97 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -132,7 +132,6 @@ function SettlementButton({ text: defaultPaymentMethod?.title ?? '', description: defaultPaymentMethod?.description ?? '', icon: defaultPaymentMethod?.icon ?? Expensicons.Bank, - onSelected: () => {}, }; if (ReportUtils.isIndividualInvoiceRoom(chatReport)) { @@ -160,7 +159,9 @@ function SettlementButton({ icon: Expensicons.User, value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.individual'), - subMenuItems: defaultPaymentMethod ? [paymentSubitem, ...subMenuItems] : subMenuItems, + subMenuItems: defaultPaymentMethod + ? [{...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, false, defaultPaymentMethod?.methodID)}, ...subMenuItems] + : subMenuItems, }); } @@ -188,7 +189,9 @@ function SettlementButton({ icon: Expensicons.Building, value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.business'), - subMenuItems: defaultPaymentMethod ? [paymentSubitem, ...subMenuItems] : subMenuItems, + subMenuItems: defaultPaymentMethod + ? [{...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, true, defaultPaymentMethod?.methodID)}, ...subMenuItems] + : subMenuItems, }); } diff --git a/src/components/SettlementButton/types.ts b/src/components/SettlementButton/types.ts index b3ad0c1c9bd0..4f942623bd54 100644 --- a/src/components/SettlementButton/types.ts +++ b/src/components/SettlementButton/types.ts @@ -11,7 +11,7 @@ type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEN type SettlementButtonProps = { /** Callback to execute when this button is pressed. Receives a single payment type argument. */ - onPress: (paymentType?: PaymentMethodType, payAsBusiness?: boolean) => void; + onPress: (paymentType?: PaymentMethodType, payAsBusiness?: boolean, methodID?: number) => void; /** Callback when the payment options popover is shown */ onPaymentOptionsShow?: () => void; From abed35dd358c1f8a7af7330ec6b592ebfa1479e2 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 15 Oct 2024 12:28:20 +0200 Subject: [PATCH 07/17] pass account type --- src/components/SettlementButton/index.tsx | 7 +++++-- src/components/SettlementButton/types.ts | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index dfc3e0983b97..e56c4603dd14 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -160,7 +160,10 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.individual'), subMenuItems: defaultPaymentMethod - ? [{...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, false, defaultPaymentMethod?.methodID)}, ...subMenuItems] + ? [ + {...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, false, defaultPaymentMethod.methodID, defaultPaymentMethod.accountType)}, + ...subMenuItems, + ] : subMenuItems, }); } @@ -190,7 +193,7 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.business'), subMenuItems: defaultPaymentMethod - ? [{...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, true, defaultPaymentMethod?.methodID)}, ...subMenuItems] + ? [{...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, true, defaultPaymentMethod.methodID, defaultPaymentMethod.accountType)}, ...subMenuItems] : subMenuItems, }); } diff --git a/src/components/SettlementButton/types.ts b/src/components/SettlementButton/types.ts index 4f942623bd54..5cad036ab3bb 100644 --- a/src/components/SettlementButton/types.ts +++ b/src/components/SettlementButton/types.ts @@ -1,5 +1,6 @@ import type {StyleProp, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; +import type {PaymentMethod} from '@components/KYCWall/types'; import type ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; @@ -11,7 +12,7 @@ type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEN type SettlementButtonProps = { /** Callback to execute when this button is pressed. Receives a single payment type argument. */ - onPress: (paymentType?: PaymentMethodType, payAsBusiness?: boolean, methodID?: number) => void; + onPress: (paymentType?: PaymentMethodType, payAsBusiness?: boolean, methodID?: number, paymentMethod?: PaymentMethod) => void; /** Callback when the payment options popover is shown */ onPaymentOptionsShow?: () => void; From e45613d64d1675aa93e64a05becbdb4e2b7a6a34 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 15 Oct 2024 16:10:55 +0200 Subject: [PATCH 08/17] set add debit card route --- src/components/MoneyReportHeader.tsx | 6 ++++-- src/components/ReportActionItem/ReportPreview.tsx | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 8149270b8919..d775ca478528 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -145,6 +145,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport?.currency); const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy); const isAnyTransactionOnHold = ReportUtils.hasHeldExpenses(moneyRequestReport?.reportID); + const isInvoiceReport = ReportUtils.isInvoiceReport(moneyRequestReport); const displayedAmount = isAnyTransactionOnHold && canAllowSettlement ? nonHeldAmount : formattedAmount; const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout); const {isDelegateAccessRestricted, delegatorEmail} = useDelegateUserDetails(); @@ -161,7 +162,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea setIsNoDelegateAccessMenuVisible(true); } else if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); - } else if (ReportUtils.isInvoiceReport(moneyRequestReport)) { + } else if (isInvoiceReport) { IOU.payInvoice( type, chatReport, @@ -174,7 +175,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea IOU.payMoneyRequest(type, chatReport, moneyRequestReport, true); } }, - [chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport], + [chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, isInvoiceReport, moneyRequestReport], ); const confirmApproval = () => { @@ -314,6 +315,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea onPress={confirmPayment} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} + addDebitCardRoute={isInvoiceReport ? ROUTES.SETTINGS_WALLET : undefined} shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} shouldDisableApproveButton={shouldDisableApproveButton} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index c35f89153b0d..f366fc16788b 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -546,6 +546,7 @@ function ReportPreview({ confirmApproval={confirmApproval} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} + addDebitCardRoute={isInvoiceRoom ? ROUTES.SETTINGS_WALLET : undefined} shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} shouldDisableApproveButton={shouldDisableApproveButton} From 8b081a7f3f82b6f580664bd96a3720bfeda3ac88 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 15 Oct 2024 16:26:13 +0200 Subject: [PATCH 09/17] render all payment methods --- src/components/SettlementButton/index.tsx | 98 +++++++++++------------ 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index e56c4603dd14..2ce12a389387 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -127,74 +127,66 @@ function SettlementButton({ if (isInvoiceReport) { const formattedPaymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList, fundList, styles); - const defaultPaymentMethod = formattedPaymentMethods.find((paymentMethod) => !!paymentMethod.isDefault) ?? formattedPaymentMethods.at(0) ?? null; - const paymentSubitem = { - text: defaultPaymentMethod?.title ?? '', - description: defaultPaymentMethod?.description ?? '', - icon: defaultPaymentMethod?.icon ?? Expensicons.Bank, - }; + const getPaymentSubitems = (payAsBusiness: boolean) => + formattedPaymentMethods.map((formattedPaymentMethod) => ({ + text: formattedPaymentMethod?.title ?? '', + description: formattedPaymentMethod?.description ?? '', + icon: formattedPaymentMethod?.icon, + onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, payAsBusiness, formattedPaymentMethod.methodID, formattedPaymentMethod.accountType), + })); if (ReportUtils.isIndividualInvoiceRoom(chatReport)) { - const subMenuItems = [ - { - text: translate('workspace.invoices.paymentMethods.addBankAccount'), - icon: Expensicons.Bank, - onSelected: () => Navigation.navigate(addBankAccountRoute), - }, - { - text: translate('workspace.invoices.paymentMethods.addDebitOrCreditCard'), - icon: Expensicons.CreditCard, - onSelected: () => Navigation.navigate(addDebitCardRoute), - }, - { - text: translate('iou.payElsewhere', {formattedAmount: ''}), - icon: Expensicons.Cash, - value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE), - }, - ]; - buttonOptions.push({ text: translate('iou.settlePersonal', {formattedAmount}), icon: Expensicons.User, value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.individual'), - subMenuItems: defaultPaymentMethod - ? [ - {...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, false, defaultPaymentMethod.methodID, defaultPaymentMethod.accountType)}, - ...subMenuItems, - ] - : subMenuItems, + subMenuItems: [ + ...getPaymentSubitems(false), + { + text: translate('workspace.invoices.paymentMethods.addBankAccount'), + icon: Expensicons.Bank, + onSelected: () => Navigation.navigate(addBankAccountRoute), + }, + { + text: translate('workspace.invoices.paymentMethods.addDebitOrCreditCard'), + icon: Expensicons.CreditCard, + onSelected: () => Navigation.navigate(addDebitCardRoute), + }, + { + text: translate('iou.payElsewhere', {formattedAmount: ''}), + icon: Expensicons.Cash, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE), + }, + ], }); } - const subMenuItems = [ - { - text: translate('workspace.invoices.paymentMethods.addBankAccount'), - icon: Expensicons.Bank, - onSelected: () => Navigation.navigate(addBankAccountRoute), - }, - { - text: translate('workspace.invoices.paymentMethods.addCorporateCard'), - icon: Expensicons.CreditCard, - onSelected: () => Navigation.navigate(addDebitCardRoute), // TODO: Clarify if this is the correct route - }, - { - text: translate('iou.payElsewhere', {formattedAmount: ''}), - icon: Expensicons.Cash, - value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE, true), - }, - ]; - buttonOptions.push({ text: translate('iou.settleBusiness', {formattedAmount}), icon: Expensicons.Building, value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.business'), - subMenuItems: defaultPaymentMethod - ? [{...paymentSubitem, onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, true, defaultPaymentMethod.methodID, defaultPaymentMethod.accountType)}, ...subMenuItems] - : subMenuItems, + subMenuItems: [ + ...getPaymentSubitems(true), + { + text: translate('workspace.invoices.paymentMethods.addBankAccount'), + icon: Expensicons.Bank, + onSelected: () => Navigation.navigate(addBankAccountRoute), + }, + { + text: translate('workspace.invoices.paymentMethods.addCorporateCard'), + icon: Expensicons.CreditCard, + onSelected: () => Navigation.navigate(addDebitCardRoute), // TODO: Clarify if this is the correct route + }, + { + text: translate('iou.payElsewhere', {formattedAmount: ''}), + icon: Expensicons.Cash, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE, true), + }, + ], }); } From 1302948009dc00c6d95fa2082b39dedb0358b27e Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 15 Oct 2024 16:26:29 +0200 Subject: [PATCH 10/17] clear todo --- src/components/SettlementButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 2ce12a389387..34d687224d4c 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -178,7 +178,7 @@ function SettlementButton({ { text: translate('workspace.invoices.paymentMethods.addCorporateCard'), icon: Expensicons.CreditCard, - onSelected: () => Navigation.navigate(addDebitCardRoute), // TODO: Clarify if this is the correct route + onSelected: () => Navigation.navigate(addDebitCardRoute), }, { text: translate('iou.payElsewhere', {formattedAmount: ''}), From 16f01362231c8933572ac1d35042dc440f1b14d3 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 17 Oct 2024 14:25:00 +0200 Subject: [PATCH 11/17] Revert "set add debit card route" This reverts commit e45613d64d1675aa93e64a05becbdb4e2b7a6a34. --- src/components/MoneyReportHeader.tsx | 6 ++---- src/components/ReportActionItem/ReportPreview.tsx | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 07c0db265502..5c4dca377757 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -153,7 +153,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport?.currency); const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy); const isAnyTransactionOnHold = ReportUtils.hasHeldExpenses(moneyRequestReport?.reportID); - const isInvoiceReport = ReportUtils.isInvoiceReport(moneyRequestReport); const displayedAmount = isAnyTransactionOnHold && canAllowSettlement ? nonHeldAmount : formattedAmount; const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout); const {isDelegateAccessRestricted, delegatorEmail} = useDelegateUserDetails(); @@ -170,7 +169,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea setIsNoDelegateAccessMenuVisible(true); } else if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); - } else if (isInvoiceReport) { + } else if (ReportUtils.isInvoiceReport(moneyRequestReport)) { IOU.payInvoice( type, chatReport, @@ -183,7 +182,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea IOU.payMoneyRequest(type, chatReport, moneyRequestReport, true); } }, - [chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, isInvoiceReport, moneyRequestReport], + [chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport], ); const confirmApproval = () => { @@ -335,7 +334,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea onPress={confirmPayment} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} - addDebitCardRoute={isInvoiceReport ? ROUTES.SETTINGS_WALLET : undefined} shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} shouldDisableApproveButton={shouldDisableApproveButton} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index a2bfc073cc8a..13eedc8971c6 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -543,7 +543,6 @@ function ReportPreview({ confirmApproval={confirmApproval} enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} addBankAccountRoute={bankAccountRoute} - addDebitCardRoute={isInvoiceRoom ? ROUTES.SETTINGS_WALLET : undefined} shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} shouldDisableApproveButton={shouldDisableApproveButton} From 529231e100f5235ed3b5bfc15d310c5f016f44b1 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 9 Dec 2024 11:22:23 +0100 Subject: [PATCH 12/17] use expensify payment type --- src/components/SettlementButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 4327b6f5b6d9..cff220dd2521 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -137,7 +137,7 @@ function SettlementButton({ text: formattedPaymentMethod?.title ?? '', description: formattedPaymentMethod?.description ?? '', icon: formattedPaymentMethod?.icon, - onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.VBBA, payAsBusiness, formattedPaymentMethod.methodID, formattedPaymentMethod.accountType), + onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.EXPENSIFY, payAsBusiness, formattedPaymentMethod.methodID, formattedPaymentMethod.accountType), })); if (ReportUtils.isIndividualInvoiceRoom(chatReport)) { From 91fceceda6e9f5595d70941deee525db95f13bc1 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 7 Jan 2025 14:52:15 +0100 Subject: [PATCH 13/17] fix rulesdir/no-default-id-values --- src/ROUTES.ts | 2 +- .../BrokenConnectionDescription.tsx | 2 +- src/components/MoneyReportHeader.tsx | 41 ++++++++++--------- src/components/MoneyRequestHeader.tsx | 26 +++++++----- src/components/SettlementButton/index.tsx | 2 +- src/libs/TransactionUtils/index.ts | 4 +- src/libs/actions/Transaction.ts | 2 +- 7 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c8e8ae582a5b..152c6f7e39fd 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1473,7 +1473,7 @@ const ROUTES = { TRANSACTION_DUPLICATE_REVIEW_PAGE: { route: 'r/:threadReportID/duplicates/review', - getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review` as const, backTo), + getRoute: (threadReportID: string | undefined | null, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID ?? ''}/duplicates/review` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE: { route: 'r/:threadReportID/duplicates/review/merchant', diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx index c54bd0058f99..bfcec0ed0d82 100644 --- a/src/components/BrokenConnectionDescription.tsx +++ b/src/components/BrokenConnectionDescription.tsx @@ -14,7 +14,7 @@ import TextLink from './TextLink'; type BrokenConnectionDescriptionProps = { /** Transaction id of the corresponding report */ - transactionID: string; + transactionID: string | undefined; /** Current report */ report: OnyxEntry; diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 88327290df44..d52aa23e2edd 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -68,8 +68,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout(); const route = useRoute(); - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID ?? '-1'}`); - const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID ?? '-1'}`); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID ?? CONST.DEFAULT_NUMBER_ID}`); + const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID ?? CONST.DEFAULT_NUMBER_ID}`); const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`); const [session] = useOnyx(ONYXKEYS.SESSION); const requestParentReportAction = useMemo(() => { @@ -84,7 +84,9 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const transaction = transactions?.[ `${ONYXKEYS.COLLECTION.TRANSACTION}${ - ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? -1 : -1 + ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) + ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID + : CONST.DEFAULT_NUMBER_ID }` ] ?? undefined; @@ -97,7 +99,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport); const isOnHold = TransactionUtils.isOnHold(transaction); const isDeletedParentAction = !!requestParentReportAction && ReportActionsUtils.isDeletedAction(requestParentReportAction); - const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID ?? ''); + const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID); // Only the requestor can delete the request, admins can only edit it. const isActionOwner = @@ -116,12 +118,12 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const hasScanningReceipt = ReportUtils.getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => TransactionUtils.isReceiptBeingScanned(t)); const hasOnlyPendingTransactions = allTransactions.length > 0 && allTransactions.every((t) => TransactionUtils.isExpensifyCardTransaction(t) && TransactionUtils.isPending(t)); const transactionIDs = allTransactions.map((t) => t.transactionID); - const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID ?? '-1']); - const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID ?? '-1', moneyRequestReport, policy); - const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport?.reportID ?? ''); + const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID]); + const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID, moneyRequestReport, policy); + const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport?.reportID); const isPayAtEndExpense = TransactionUtils.isPayAtEndExpense(transaction); const isArchivedReport = ReportUtils.isArchivedRoomWithID(moneyRequestReport?.reportID); - const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID ?? '-1'}`, {selector: ReportUtils.getArchiveReason}); + const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID ?? CONST.DEFAULT_NUMBER_ID}`, {selector: ReportUtils.getArchiveReason}); const getCanIOUBePaid = useCallback( (onlyShowPayElsewhere = false) => IOU.canIOUBePaid(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere), @@ -132,8 +134,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); const shouldShowMarkAsCashButton = - hasAllPendingRTERViolations || - (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(moneyRequestReport?.reportID ?? ''))); + hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(moneyRequestReport?.reportID))); const shouldShowPayButton = canIOUBePaid || onlyShowPayElsewhere; @@ -223,10 +224,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const deleteTransaction = useCallback(() => { if (requestParentReportAction) { const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) - ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? '-1' - : '-1'; + ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID + : undefined; if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { - navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '-1', iouTransactionID, requestParentReportAction, true); + navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, true); } else { navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); } @@ -240,11 +241,11 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea return; } const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) - ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? '-1' - : '-1'; - const reportID = transactionThreadReport?.reportID ?? '-1'; + ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID + : undefined; + const reportID = transactionThreadReport?.reportID; - TransactionActions.markAsCash(iouTransactionID, reportID); + TransactionActions.markAsCash(reportID, iouTransactionID); }, [requestParentReportAction, transactionThreadReport?.reportID]); const getStatusIcon: (src: IconAsset) => React.ReactNode = (src) => ( @@ -273,7 +274,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea icon: getStatusIcon(Expensicons.Hourglass), description: ( @@ -362,7 +363,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea text={translate('iou.reviewDuplicates')} style={styles.p0} onPress={() => { - Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID ?? '', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID, Navigation.getReportRHPActiveRoute())); }} /> @@ -430,7 +431,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea text={translate('iou.reviewDuplicates')} style={[styles.flex1, styles.pr0]} onPress={() => { - Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID ?? '', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID, Navigation.getReportRHPActiveRoute())); }} /> )} diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index f253c757050f..a9747502ee72 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -16,6 +16,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import variables from '@styles/variables'; import * as IOU from '@userActions/IOU'; import * as TransactionActions from '@userActions/Transaction'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; @@ -50,10 +51,12 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout(); const route = useRoute(); - const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? CONST.DEFAULT_NUMBER_ID}`); const [transaction] = useOnyx( `${ONYXKEYS.COLLECTION.TRANSACTION}${ - ReportActionsUtils.isMoneyRequestAction(parentReportAction) ? ReportActionsUtils.getOriginalMessage(parentReportAction)?.IOUTransactionID ?? -1 : -1 + ReportActionsUtils.isMoneyRequestAction(parentReportAction) + ? ReportActionsUtils.getOriginalMessage(parentReportAction)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID + : CONST.DEFAULT_NUMBER_ID }`, ); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); @@ -64,21 +67,21 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre const {translate} = useLocalize(); const [shouldShowHoldMenu, setShouldShowHoldMenu] = useState(false); const isOnHold = TransactionUtils.isOnHold(transaction); - const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID ?? ''); + const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID); const reportID = report?.reportID; const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP; const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth; - const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID ?? '-1']); + const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID]); - const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID ?? '-1', parentReport, policy); + const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID, parentReport, policy); const shouldShowMarkAsCashButton = - hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(parentReport?.reportID ?? ''))); + hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(parentReport?.reportID))); const markAsCash = useCallback(() => { - TransactionActions.markAsCash(transaction?.transactionID ?? '-1', reportID ?? ''); + TransactionActions.markAsCash(reportID, transaction?.transactionID); }, [reportID, transaction?.transactionID]); const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); @@ -105,14 +108,14 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre icon: getStatusIcon(Expensicons.Hourglass), description: ( ), }; } - if (TransactionUtils.hasPendingRTERViolation(TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '-1', transactionViolations))) { + if (TransactionUtils.hasPendingRTERViolation(TransactionUtils.getTransactionViolations(transaction?.transactionID, transactionViolations))) { return {icon: getStatusIcon(Expensicons.Hourglass), description: translate('iou.pendingMatchWithCreditCardDescription')}; } if (isScanning) { @@ -157,6 +160,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre shouldShowPinButton={false} report={{ ...report, + // eslint-disable-next-line rulesdir/no-default-id-values reportID: reportID ?? '', ownerAccountID: parentReport?.ownerAccountID, }} @@ -179,7 +183,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre text={translate('iou.reviewDuplicates')} style={[styles.p0, styles.ml2]} onPress={() => { - Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID ?? '', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute())); }} /> )} @@ -201,7 +205,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre text={translate('iou.reviewDuplicates')} style={[styles.w100, styles.pr0]} onPress={() => { - Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID ?? '', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute())); }} /> diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 94745a276910..af4ce1d25384 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -68,7 +68,7 @@ function SettlementButton({ const {translate} = useLocalize(); const {isOffline} = useNetwork(); // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`); const [isUserValidated] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.validated}); const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; const reportBelongsToWorkspace = policyID ? ReportUtils.doesReportBelongToWorkspace(chatReport, policyEmployeeAccountIDs, policyID) : false; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 528481dae237..2e7794e2243b 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -792,7 +792,7 @@ function shouldShowBrokenConnectionViolation(transactionID: string | undefined, /** * Check if there is pending rter violation in all transactionViolations with given transactionIDs. */ -function allHavePendingRTERViolation(transactionIds: string[]): boolean { +function allHavePendingRTERViolation(transactionIds: Array): boolean { const transactionsWithRTERViolations = transactionIds.map((transactionId) => { const transactionViolations = getTransactionViolations(transactionId, allTransactionViolations); return hasPendingRTERViolation(transactionViolations); @@ -891,7 +891,7 @@ function getRecentTransactions(transactions: Record, size = 2): * @param transactionID - the transaction to check * @param checkDismissed - whether to check if the violation has already been dismissed as well */ -function isDuplicate(transactionID: string, checkDismissed = false): boolean { +function isDuplicate(transactionID: string | undefined, checkDismissed = false): boolean { const hasDuplicatedViolation = !!allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]?.some( (violation: TransactionViolation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION, ); diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index c8a007458242..a06e54066a9f 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -461,7 +461,7 @@ function clearError(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null, waypoints: null, routes: null}}); } -function markAsCash(transactionID: string, transactionThreadReportID: string) { +function markAsCash(transactionThreadReportID: string | undefined, transactionID: string | undefined = '') { const optimisticReportAction = buildOptimisticDismissedViolationReportAction({ reason: 'manual', violationName: CONST.VIOLATIONS.RTER, From 83ee03869f7840178a0a89a542b76a25cdeba42b Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 7 Jan 2025 17:41:51 +0100 Subject: [PATCH 14/17] Add logging for invalid policyID and threadReportID in route generation --- src/ROUTES.ts | 17 +++++++++++++++-- src/components/BrokenConnectionDescription.tsx | 4 ++-- src/libs/actions/Transaction.ts | 14 +++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 152c6f7e39fd..183e9bd2d484 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -3,6 +3,7 @@ import type {SearchQueryString} from './components/Search/types'; import type CONST from './CONST'; import type {IOUAction, IOUType} from './CONST'; import type {IOURequestType} from './libs/actions/IOU'; +import Log from './libs/Log'; import type {ConnectionName, SageIntacctMappingName} from './types/onyx/Policy'; import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual'; @@ -1221,7 +1222,13 @@ const ROUTES = { }, WORKSPACE_COMPANY_CARDS: { route: 'settings/workspaces/:policyID/company-cards', - getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards` as const, + getRoute: (policyID: string | undefined) => { + if (!policyID) { + Log.warn('Invalid policyID while building route WORKSPACE_COMPANY_CARDS'); + } + + return `settings/workspaces/${policyID}/company-cards` as const; + }, }, WORKSPACE_COMPANY_CARDS_ADD_NEW: { route: 'settings/workspaces/:policyID/company-cards/add-card-feed', @@ -1473,7 +1480,13 @@ const ROUTES = { TRANSACTION_DUPLICATE_REVIEW_PAGE: { route: 'r/:threadReportID/duplicates/review', - getRoute: (threadReportID: string | undefined | null, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID ?? ''}/duplicates/review` as const, backTo), + getRoute: (threadReportID: string | undefined | null, backTo?: string) => { + if (!threadReportID) { + Log.warn('Invalid threadReportID while building route TRANSACTION_DUPLICATE_REVIEW_PAGE'); + } + + return getUrlWithBackToParam(`r/${threadReportID}/duplicates/review` as const, backTo); + }, }, TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE: { route: 'r/:threadReportID/duplicates/review/merchant', diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx index bfcec0ed0d82..a1b9bd62ffa2 100644 --- a/src/components/BrokenConnectionDescription.tsx +++ b/src/components/BrokenConnectionDescription.tsx @@ -40,13 +40,13 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn return translate('violations.brokenConnection530Error'); } - if (isPolicyAdmin && !ReportUtils.isCurrentUserSubmitter(report?.reportID ?? '')) { + if (isPolicyAdmin && !ReportUtils.isCurrentUserSubmitter(report?.reportID)) { return ( <> {`${translate('violations.adminBrokenConnectionError')}`} Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy?.id ?? '-1'))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy?.id))} >{`${translate('workspace.common.companyCards')}`} . diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index a06e54066a9f..3937d5e6af97 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -355,7 +355,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmissedPersonalDetails: PersonalDetails) { const currentTransactionViolations = transactionIDs.map((id) => ({transactionID: id, violations: allTransactionViolation?.[id] ?? []})); const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]); - const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID ?? '', transaction.transactionID ?? '')); + const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID)); const optimisticDissmidedViolationReportActions = transactionsReportActions.map(() => { return buildOptimisticDismissedViolationReportAction({reason: 'manual', violationName: CONST.VIOLATIONS.DUPLICATED_TRANSACTION}); }); @@ -365,9 +365,9 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const optimisticReportActions: OnyxUpdate[] = transactionsReportActions.map((action, index) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID}`, value: { - [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: optimisticDissmidedViolationReportActions.at(index) as ReportAction, + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? CONST.DEFAULT_NUMBER_ID]: optimisticDissmidedViolationReportActions.at(index) as ReportAction, }, })); const optimisticDataTransactionViolations: OnyxUpdate[] = currentTransactionViolations.map((transactionViolations) => ({ @@ -413,9 +413,9 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const failureReportActions: OnyxUpdate[] = transactionsReportActions.map((action, index) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID}`, value: { - [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: null, + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? CONST.DEFAULT_NUMBER_ID]: null, }, })); @@ -425,9 +425,9 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const successData: OnyxUpdate[] = transactionsReportActions.map((action, index) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID}`, value: { - [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: null, + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? CONST.DEFAULT_NUMBER_ID]: null, }, })); // We are creating duplicate resolved report actions for each duplicate transactions and all the report actions From 4b39e70b049b7b3b71366c3bacf5c2c1347169ed Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 20 Jan 2025 11:55:57 +0100 Subject: [PATCH 15/17] Refactor SettlementButton component to use named imports for better clarity and maintainability --- src/components/SettlementButton/index.tsx | 30 ++++++++++------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index af4ce1d25384..6377ea7816ef 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -9,12 +9,12 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import * as PaymentUtils from '@libs/PaymentUtils'; +import {formatPaymentMethods} from '@libs/PaymentUtils'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as SubscriptionUtils from '@libs/SubscriptionUtils'; -import * as BankAccounts from '@userActions/BankAccounts'; -import * as IOU from '@userActions/IOU'; +import {doesReportBelongToWorkspace, isExpenseReport as isExpenseReportReportUtils, isIndividualInvoiceRoom, isInvoiceReport as isInvoiceReportReportUtils} from '@libs/ReportUtils'; +import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; +import {setPersonalBankAccountContinueKYCOnSuccess} from '@userActions/BankAccounts'; +import {approveMoneyRequest, savePreferredPaymentMethod} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -71,7 +71,7 @@ function SettlementButton({ const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`); const [isUserValidated] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.validated}); const policyEmployeeAccountIDs = policyID ? getPolicyEmployeeAccountIDs(policyID) : []; - const reportBelongsToWorkspace = policyID ? ReportUtils.doesReportBelongToWorkspace(chatReport, policyEmployeeAccountIDs, policyID) : false; + const reportBelongsToWorkspace = policyID ? doesReportBelongToWorkspace(chatReport, policyEmployeeAccountIDs, policyID) : false; const policyIDKey = reportBelongsToWorkspace ? policyID : CONST.POLICY.ID_FAKE; const [lastPaymentMethod = '-1', lastPaymentMethodResult] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod?.[policyIDKey]}); @@ -80,12 +80,12 @@ function SettlementButton({ const [bankAccountList = {}] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [fundList = {}] = useOnyx(ONYXKEYS.FUND_LIST); - const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false; + const isInvoiceReport = (!isEmptyObject(iouReport) && isInvoiceReportReportUtils(iouReport)) || false; const shouldShowPaywithExpensifyOption = !shouldHidePaymentOptions; const shouldShowPayElsewhereOption = !shouldHidePaymentOptions && !isInvoiceReport; const paymentButtonOptions = useMemo(() => { const buttonOptions = []; - const isExpenseReport = ReportUtils.isExpenseReport(iouReport); + const isExpenseReport = isExpenseReportReportUtils(iouReport); const paymentMethods = { [CONST.IOU.PAYMENT_TYPE.EXPENSIFY]: { text: translate('iou.settleExpensify', {formattedAmount}), @@ -132,7 +132,7 @@ function SettlementButton({ } if (isInvoiceReport) { - const formattedPaymentMethods = PaymentUtils.formatPaymentMethods(bankAccountList, fundList, styles); + const formattedPaymentMethods = formatPaymentMethods(bankAccountList, fundList, styles); const getPaymentSubitems = (payAsBusiness: boolean) => formattedPaymentMethods.map((formattedPaymentMethod) => ({ text: formattedPaymentMethod?.title ?? '', @@ -141,7 +141,7 @@ function SettlementButton({ onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.EXPENSIFY, payAsBusiness, formattedPaymentMethod.methodID, formattedPaymentMethod.accountType), })); - if (ReportUtils.isIndividualInvoiceRoom(chatReport)) { + if (isIndividualInvoiceRoom(chatReport)) { buttonOptions.push({ text: translate('iou.settlePersonal', {formattedAmount}), icon: Expensicons.User, @@ -226,7 +226,7 @@ function SettlementButton({ ]); const selectPaymentType = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => { - if (policy && SubscriptionUtils.shouldRestrictUserBillableActions(policy.id)) { + if (policy && shouldRestrictUserBillableActions(policy.id)) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id)); return; } @@ -237,7 +237,7 @@ function SettlementButton({ return; } triggerKYCFlow(event, iouPaymentType); - BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS); + setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS); return; } @@ -245,7 +245,7 @@ function SettlementButton({ if (confirmApproval) { confirmApproval(); } else { - IOU.approveMoneyRequest(iouReport); + approveMoneyRequest(iouReport); } return; } @@ -253,10 +253,6 @@ function SettlementButton({ onPress(iouPaymentType); }; - const savePreferredPaymentMethod = (id: string, value: PaymentMethodType) => { - IOU.savePreferredPaymentMethod(id, value); - }; - return ( onPress(paymentType)} From 5f094bc9275e452bfe9ef99f4e989528d429b942 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 7 Mar 2025 18:18:41 +0100 Subject: [PATCH 16/17] Conditionally show payment methods based on currency support --- src/components/SettlementButton/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 6a0ff7dbab46..1c2af1dd181a 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -8,6 +8,7 @@ import KYCWall from '@components/KYCWall'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; +import {isCurrencySupportedForDirectReimbursement} from '@libs/actions/Policy/Policy'; import Navigation from '@libs/Navigation/Navigation'; import {formatPaymentMethods} from '@libs/PaymentUtils'; import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils'; @@ -145,6 +146,7 @@ function SettlementButton({ if (isInvoiceReport) { const formattedPaymentMethods = formatPaymentMethods(bankAccountList, fundList, styles); + const isCurrencySupported = isCurrencySupportedForDirectReimbursement(currency); const getPaymentSubitems = (payAsBusiness: boolean) => formattedPaymentMethods.map((formattedPaymentMethod) => ({ text: formattedPaymentMethod?.title ?? '', @@ -160,7 +162,7 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.individual'), subMenuItems: [ - ...getPaymentSubitems(false), + ...(isCurrencySupported ? getPaymentSubitems(false) : []), { text: translate('workspace.invoices.paymentMethods.addBankAccount'), icon: Expensicons.Bank, @@ -187,7 +189,7 @@ function SettlementButton({ value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, backButtonText: translate('iou.business'), subMenuItems: [ - ...getPaymentSubitems(true), + ...(isCurrencySupported ? getPaymentSubitems(true) : []), { text: translate('workspace.invoices.paymentMethods.addBankAccount'), icon: Expensicons.Bank, From 047c8f8d995878e4ec1bcdb9b1497371ca0c87d7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 17 Mar 2025 14:59:57 +0100 Subject: [PATCH 17/17] Refactor payInvoice function calls to simplify parameters in MoneyReportHeader and ReportPreview components. Update IOU.ts to align with new parameter structure for improved readability and maintainability. --- src/components/MoneyReportHeader.tsx | 9 +-------- src/components/ReportActionItem/ReportPreview.tsx | 9 +-------- src/libs/actions/IOU.ts | 13 +++++++------ 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 8f899740c455..fa4195a8673e 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -254,14 +254,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea setIsHoldMenuVisible(true); } else if (isInvoiceReport(moneyRequestReport)) { startAnimation(); - payInvoice( - type, - chatReport, - moneyRequestReport, - payAsBusiness, - paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? methodID : undefined, - paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD ? methodID : undefined, - ); + payInvoice(type, chatReport, moneyRequestReport, payAsBusiness, methodID, paymentMethod); } else { startAnimation(); payMoneyRequest(type, chatReport, moneyRequestReport, true); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index de814dd39246..1135a7469d5a 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -276,14 +276,7 @@ function ReportPreview({ } else if (chatReport && iouReport) { startAnimation(); if (isInvoiceReportUtils(iouReport)) { - payInvoice( - type, - chatReport, - iouReport, - payAsBusiness, - paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ? methodID : undefined, - paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD ? methodID : undefined, - ); + payInvoice(type, chatReport, iouReport, payAsBusiness, methodID, paymentMethod); } else { payMoneyRequest(type, chatReport, iouReport); } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index d81a1f7d434a..07bade3c8d71 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5,6 +5,7 @@ import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} import Onyx from 'react-native-onyx'; import type {PartialDeep, SetRequired, ValueOf} from 'type-fest'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; +import type {PaymentMethod} from '@components/KYCWall/types'; import * as API from '@libs/API'; import type { ApproveMoneyRequestParams, @@ -9053,8 +9054,8 @@ function payInvoice( chatReport: OnyxTypes.Report, invoiceReport: OnyxEntry, payAsBusiness = false, - bankAccountID?: number, - fundID?: number, + methodID?: number, + paymentMethod?: PaymentMethod, ) { const recipient = {accountID: invoiceReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID}; const { @@ -9085,12 +9086,12 @@ function payInvoice( payAsBusiness, }; - if (bankAccountID) { - params.bankAccountID = bankAccountID; + if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { + params.bankAccountID = methodID; } - if (fundID) { - params.fundID = fundID; + if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { + params.fundID = methodID; } if (policyID) {