diff --git a/src/hooks/usePaymentMethodState/index.ts b/src/hooks/usePaymentMethodState/index.ts new file mode 100644 index 000000000000..75ae3ea48377 --- /dev/null +++ b/src/hooks/usePaymentMethodState/index.ts @@ -0,0 +1,28 @@ +import {useCallback, useState} from 'react'; +import type {PaymentMethodState} from './types'; + +const initialState: PaymentMethodState = { + isSelectedPaymentMethodDefault: false, + selectedPaymentMethod: {}, + formattedSelectedPaymentMethod: { + title: '', + }, + methodID: '', + selectedPaymentMethodType: '', +}; + +function usePaymentMethodState() { + const [paymentMethod, setPaymentMethod] = useState(initialState); + + const resetSelectedPaymentMethodData = useCallback(() => { + setPaymentMethod(initialState); + }, [setPaymentMethod]); + + return { + paymentMethod, + setPaymentMethod, + resetSelectedPaymentMethodData, + }; +} + +export default usePaymentMethodState; diff --git a/src/hooks/usePaymentMethodState/types.ts b/src/hooks/usePaymentMethodState/types.ts new file mode 100644 index 000000000000..260a9aec27cf --- /dev/null +++ b/src/hooks/usePaymentMethodState/types.ts @@ -0,0 +1,28 @@ +import type {ViewStyle} from 'react-native'; +import type {AccountData} from '@src/types/onyx'; +import type IconAsset from '@src/types/utils/IconAsset'; + +type FormattedSelectedPaymentMethodIcon = { + icon: IconAsset; + iconHeight?: number; + iconWidth?: number; + iconStyles?: ViewStyle[]; + iconSize?: number; +}; + +type FormattedSelectedPaymentMethod = { + title: string; + icon?: FormattedSelectedPaymentMethodIcon; + description?: string; + type?: string; +}; + +type PaymentMethodState = { + isSelectedPaymentMethodDefault: boolean; + selectedPaymentMethod: AccountData; + formattedSelectedPaymentMethod: FormattedSelectedPaymentMethod; + methodID: string | number; + selectedPaymentMethodType: string; +}; + +export type {FormattedSelectedPaymentMethodIcon, FormattedSelectedPaymentMethod, PaymentMethodState}; diff --git a/src/languages/en.ts b/src/languages/en.ts index 6d579a2af2df..cb9fde8c053d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -451,6 +451,7 @@ const translations = { filterLogs: 'Filter Logs', network: 'Network', reportID: 'Report ID', + bankAccounts: 'Bank accounts', chooseFile: 'Choose file', dropTitle: 'Let it go', dropMessage: 'Drop your file here', @@ -1373,7 +1374,6 @@ const translations = { enableWalletToSendAndReceiveMoney: 'Enable your wallet to send and receive money with friends.', walletEnabledToSendAndReceiveMoney: 'Your wallet has been enabled to send and receive money with friends.', enableWallet: 'Enable wallet', - bankAccounts: 'Bank accounts', addBankAccountToSendAndReceive: 'Adding a bank account allows you to get paid back for expenses you submit to a workspace.', addBankAccount: 'Add bank account', assignedCards: 'Assigned cards', @@ -3649,6 +3649,7 @@ const translations = { payingAsIndividual: 'Paying as an individual', payingAsBusiness: 'Paying as a business', }, + bankAccountsSubtitle: 'Add a bank account to receive invoice payments.', }, invite: { member: 'Invite member', diff --git a/src/languages/es.ts b/src/languages/es.ts index cb19b091b058..c4ef82c6bcc2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -442,6 +442,7 @@ const translations = { filterLogs: 'Registros de filtrado', network: 'La red', reportID: 'ID del informe', + bankAccounts: 'Cuentas bancarias', chooseFile: 'Elegir archivo', dropTitle: 'Suéltalo', dropMessage: 'Suelta tu archivo aquí', @@ -1370,7 +1371,6 @@ const translations = { enableWalletToSendAndReceiveMoney: 'Habilita tu Billetera Expensify para comenzar a enviar y recibir dinero con amigos.', walletEnabledToSendAndReceiveMoney: 'Tu billetera ha sido habilitada para enviar y recibir dinero con amigos.', enableWallet: 'Habilitar billetera', - bankAccounts: 'Cuentas bancarias', addBankAccountToSendAndReceive: 'Agregar una cuenta bancaria te permite recibir reembolsos por los gastos que envíes a un espacio de trabajo.', addBankAccount: 'Añadir cuenta bancaria', assignedCards: 'Tarjetas asignadas', @@ -3690,6 +3690,7 @@ const translations = { payingAsIndividual: 'Pago individual', payingAsBusiness: 'Pagar como una empresa', }, + bankAccountsSubtitle: 'Agrega una cuenta bancaria para recibir pagos de facturas.', }, invite: { member: 'Invitar miembros', diff --git a/src/libs/API/parameters/SetInvoicingTransferBankAccountParams.ts b/src/libs/API/parameters/SetInvoicingTransferBankAccountParams.ts new file mode 100644 index 000000000000..198a7b6ccf9a --- /dev/null +++ b/src/libs/API/parameters/SetInvoicingTransferBankAccountParams.ts @@ -0,0 +1,6 @@ +type SetInvoicingTransferBankAccountParams = { + bankAccountID: number; + policyID: string; +}; + +export default SetInvoicingTransferBankAccountParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 9f51cab3f360..28a0e622689b 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -327,3 +327,4 @@ export type {default as UpdateCompanyCard} from './UpdateCompanyCard'; export type {default as UpdateCompanyCardNameParams} from './UpdateCompanyCardNameParams'; export type {default as SetCompanyCardExportAccountParams} from './SetCompanyCardExportAccountParams'; export type {default as SetMissingPersonalDetailsAndShipExpensifyCardParams} from './SetMissingPersonalDetailsAndShipExpensifyCardParams'; +export type {default as SetInvoicingTransferBankAccountParams} from './SetInvoicingTransferBankAccountParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index b72b77ae4739..50a7c8f58286 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -408,6 +408,7 @@ const WRITE_COMMANDS = { UPDATE_COMPANY_CARD_NAME: 'SetCardName', SET_CARD_EXPORT_ACCOUNT: 'SetCardExportAccount', SET_MISSING_PERSONAL_DETAILS_AND_SHIP_EXPENSIFY_CARD: 'SetMissingPersonalDetailsAndShipExpensifyCard', + SET_INVOICING_TRANSFER_BANK_ACCOUNT: 'SetInvoicingTransferBankAccount', } as const; type WriteCommand = ValueOf; @@ -825,6 +826,8 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_XERO_SYNC_INVOICE_COLLECTIONS_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams; [WRITE_COMMANDS.UPDATE_XERO_SYNC_SYNC_REIMBURSED_REPORTS]: Parameters.UpdateXeroGenericTypeParams; [WRITE_COMMANDS.UPDATE_XERO_SYNC_REIMBURSEMENT_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams; + + [WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT]: Parameters.SetInvoicingTransferBankAccountParams; }; const READ_COMMANDS = { diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index bac3739af810..59f1a6b06aed 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -10,6 +10,7 @@ import type { DeletePaymentCardParams, MakeDefaultPaymentMethodParams, PaymentCardParams, + SetInvoicingTransferBankAccountParams, TransferWalletBalanceParams, UpdateBillingCurrencyParams, } from '@libs/API/parameters'; @@ -531,6 +532,50 @@ function setPaymentCardForm(values: AccountData) { }); } +/** + * Sets the default bank account to use for receiving payouts from + * + */ +function setInvoicingTransferBankAccount(bankAccountID: number, policyID: string, previousBankAccountID: number) { + const parameters: SetInvoicingTransferBankAccountParams = { + bankAccountID, + policyID, + }; + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + invoice: { + bankAccount: { + transferBankAccountID: bankAccountID, + }, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + invoice: { + bankAccount: { + transferBankAccountID: previousBankAccountID, + }, + }, + }, + }, + ]; + + API.write(WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT, parameters, { + optimisticData, + failureData, + }); +} + export { deletePaymentCard, addPaymentCard, @@ -555,4 +600,5 @@ export { clearWalletTermsError, setPaymentCardForm, verifySetupIntent, + setInvoicingTransferBankAccount, }; diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index b28b88e1ba83..5cca067ce002 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -3,8 +3,7 @@ import type {ReactElement, Ref} from 'react'; import React, {useCallback, useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; import {FlatList, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg/lib/typescript/ReactNativeSVG'; import type {ValueOf} from 'type-fest'; import type {RenderSuggestionMenuItemProps} from '@components/AutoCompleteSuggestions/types'; @@ -18,6 +17,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; +import type {FormattedSelectedPaymentMethodIcon} from '@hooks/usePaymentMethodState/types'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CardUtils from '@libs/CardUtils'; @@ -30,29 +30,14 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {AccountData, BankAccountList, CardList} from '@src/types/onyx'; +import type {AccountData} from '@src/types/onyx'; import type {BankIcon} from '@src/types/onyx/Bank'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type PaymentMethod from '@src/types/onyx/PaymentMethod'; import type {FilterMethodPaymentType} from '@src/types/onyx/WalletTransfer'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import type {FormattedSelectedPaymentMethodIcon} from './WalletPage/types'; -type PaymentMethodListOnyxProps = { - /** List of bank accounts */ - bankAccountList: OnyxEntry; - - /** List of assigned cards */ - cardList: OnyxEntry; - - /** List of user's cards */ - // fundList: OnyxEntry; - - /** Are we loading payment methods? */ - isLoadingPaymentMethods: OnyxEntry; -}; - -type PaymentMethodListProps = PaymentMethodListOnyxProps & { +type PaymentMethodListProps = { /** Type of active/highlighted payment method */ actionPaymentMethodType?: string; @@ -92,6 +77,9 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & { /** Whether the add Payment button be shown on the list */ shouldShowAddPaymentMethodButton?: boolean; + /** Whether the add Bank account button be shown on the list */ + shouldShowAddBankAccountButton?: boolean; + /** Whether the assigned cards should be shown on the list */ shouldShowAssignedCards?: boolean; @@ -110,6 +98,9 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & { isDefault?: boolean, methodID?: number, ) => void; + + /** The policy invoice's transfer bank accountID */ + invoiceTransferBankAccountID?: number; }; type PaymentMethodItem = PaymentMethod & { @@ -173,17 +164,13 @@ function keyExtractor(item: PaymentMethod) { function PaymentMethodList({ actionPaymentMethodType = '', activePaymentMethodID = '', - bankAccountList = {}, buttonRef = () => {}, - cardList = {}, - // Temporarily disabled because P2P debit cards are disabled. - // fundList = {}, filterType = '', listHeaderComponent, - isLoadingPaymentMethods = true, onPress, shouldShowSelectedState = false, shouldShowAddPaymentMethodButton = true, + shouldShowAddBankAccountButton = false, shouldShowAddBankAccount = true, shouldShowEmptyListMessage = true, shouldShowAssignedCards = false, @@ -193,12 +180,18 @@ function PaymentMethodList({ style = {}, listItemStyle = {}, shouldShowRightIcon = true, + invoiceTransferBankAccountID, }: PaymentMethodListProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const [isUserValidated] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.validated}); + const [bankAccountList = {}] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); + const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST); + // Temporarily disabled because P2P debit cards are disabled. + // const [fundList = {}] = useOnyx(ONYXKEYS.FUND_LIST); + const [isLoadingPaymentMethods = true] = useOnyx(ONYXKEYS.IS_LOADING_PAYMENT_METHODS); const getDescriptionForPolicyDomainCard = (domainName: string): string => { // A domain name containing a policyID indicates that this is a workspace feed @@ -324,18 +317,29 @@ function PaymentMethodList({ const renderListEmptyComponent = () => {translate('paymentMethodList.addFirstPaymentMethod')}; const renderListFooterComponent = useCallback( - () => ( - - ), + () => + shouldShowAddBankAccountButton ? ( +