Skip to content

Commit 0e7d707

Browse files
authored
Merge pull request #47218 from rezkiy37/feature/45177-invoice-back-accounts-section
Invoicing bank accounts section
2 parents 8636074 + 2ffc83e commit 0e7d707

File tree

13 files changed

+441
-109
lines changed

13 files changed

+441
-109
lines changed

Diff for: src/hooks/usePaymentMethodState/index.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {useCallback, useState} from 'react';
2+
import type {PaymentMethodState} from './types';
3+
4+
const initialState: PaymentMethodState = {
5+
isSelectedPaymentMethodDefault: false,
6+
selectedPaymentMethod: {},
7+
formattedSelectedPaymentMethod: {
8+
title: '',
9+
},
10+
methodID: '',
11+
selectedPaymentMethodType: '',
12+
};
13+
14+
function usePaymentMethodState() {
15+
const [paymentMethod, setPaymentMethod] = useState<PaymentMethodState>(initialState);
16+
17+
const resetSelectedPaymentMethodData = useCallback(() => {
18+
setPaymentMethod(initialState);
19+
}, [setPaymentMethod]);
20+
21+
return {
22+
paymentMethod,
23+
setPaymentMethod,
24+
resetSelectedPaymentMethodData,
25+
};
26+
}
27+
28+
export default usePaymentMethodState;

Diff for: src/hooks/usePaymentMethodState/types.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type {ViewStyle} from 'react-native';
2+
import type {AccountData} from '@src/types/onyx';
3+
import type IconAsset from '@src/types/utils/IconAsset';
4+
5+
type FormattedSelectedPaymentMethodIcon = {
6+
icon: IconAsset;
7+
iconHeight?: number;
8+
iconWidth?: number;
9+
iconStyles?: ViewStyle[];
10+
iconSize?: number;
11+
};
12+
13+
type FormattedSelectedPaymentMethod = {
14+
title: string;
15+
icon?: FormattedSelectedPaymentMethodIcon;
16+
description?: string;
17+
type?: string;
18+
};
19+
20+
type PaymentMethodState = {
21+
isSelectedPaymentMethodDefault: boolean;
22+
selectedPaymentMethod: AccountData;
23+
formattedSelectedPaymentMethod: FormattedSelectedPaymentMethod;
24+
methodID: string | number;
25+
selectedPaymentMethodType: string;
26+
};
27+
28+
export type {FormattedSelectedPaymentMethodIcon, FormattedSelectedPaymentMethod, PaymentMethodState};

Diff for: src/languages/en.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ const translations = {
451451
filterLogs: 'Filter Logs',
452452
network: 'Network',
453453
reportID: 'Report ID',
454+
bankAccounts: 'Bank accounts',
454455
chooseFile: 'Choose file',
455456
dropTitle: 'Let it go',
456457
dropMessage: 'Drop your file here',
@@ -1373,7 +1374,6 @@ const translations = {
13731374
enableWalletToSendAndReceiveMoney: 'Enable your wallet to send and receive money with friends.',
13741375
walletEnabledToSendAndReceiveMoney: 'Your wallet has been enabled to send and receive money with friends.',
13751376
enableWallet: 'Enable wallet',
1376-
bankAccounts: 'Bank accounts',
13771377
addBankAccountToSendAndReceive: 'Adding a bank account allows you to get paid back for expenses you submit to a workspace.',
13781378
addBankAccount: 'Add bank account',
13791379
assignedCards: 'Assigned cards',
@@ -3649,6 +3649,7 @@ const translations = {
36493649
payingAsIndividual: 'Paying as an individual',
36503650
payingAsBusiness: 'Paying as a business',
36513651
},
3652+
bankAccountsSubtitle: 'Add a bank account to receive invoice payments.',
36523653
},
36533654
invite: {
36543655
member: 'Invite member',

Diff for: src/languages/es.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ const translations = {
442442
filterLogs: 'Registros de filtrado',
443443
network: 'La red',
444444
reportID: 'ID del informe',
445+
bankAccounts: 'Cuentas bancarias',
445446
chooseFile: 'Elegir archivo',
446447
dropTitle: 'Suéltalo',
447448
dropMessage: 'Suelta tu archivo aquí',
@@ -1370,7 +1371,6 @@ const translations = {
13701371
enableWalletToSendAndReceiveMoney: 'Habilita tu Billetera Expensify para comenzar a enviar y recibir dinero con amigos.',
13711372
walletEnabledToSendAndReceiveMoney: 'Tu billetera ha sido habilitada para enviar y recibir dinero con amigos.',
13721373
enableWallet: 'Habilitar billetera',
1373-
bankAccounts: 'Cuentas bancarias',
13741374
addBankAccountToSendAndReceive: 'Agregar una cuenta bancaria te permite recibir reembolsos por los gastos que envíes a un espacio de trabajo.',
13751375
addBankAccount: 'Añadir cuenta bancaria',
13761376
assignedCards: 'Tarjetas asignadas',
@@ -3690,6 +3690,7 @@ const translations = {
36903690
payingAsIndividual: 'Pago individual',
36913691
payingAsBusiness: 'Pagar como una empresa',
36923692
},
3693+
bankAccountsSubtitle: 'Agrega una cuenta bancaria para recibir pagos de facturas.',
36933694
},
36943695
invite: {
36953696
member: 'Invitar miembros',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type SetInvoicingTransferBankAccountParams = {
2+
bankAccountID: number;
3+
policyID: string;
4+
};
5+
6+
export default SetInvoicingTransferBankAccountParams;

Diff for: src/libs/API/parameters/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,4 @@ export type {default as UpdateCompanyCard} from './UpdateCompanyCard';
327327
export type {default as UpdateCompanyCardNameParams} from './UpdateCompanyCardNameParams';
328328
export type {default as SetCompanyCardExportAccountParams} from './SetCompanyCardExportAccountParams';
329329
export type {default as SetMissingPersonalDetailsAndShipExpensifyCardParams} from './SetMissingPersonalDetailsAndShipExpensifyCardParams';
330+
export type {default as SetInvoicingTransferBankAccountParams} from './SetInvoicingTransferBankAccountParams';

Diff for: src/libs/API/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ const WRITE_COMMANDS = {
408408
UPDATE_COMPANY_CARD_NAME: 'SetCardName',
409409
SET_CARD_EXPORT_ACCOUNT: 'SetCardExportAccount',
410410
SET_MISSING_PERSONAL_DETAILS_AND_SHIP_EXPENSIFY_CARD: 'SetMissingPersonalDetailsAndShipExpensifyCard',
411+
SET_INVOICING_TRANSFER_BANK_ACCOUNT: 'SetInvoicingTransferBankAccount',
411412
} as const;
412413

413414
type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
@@ -825,6 +826,8 @@ type WriteCommandParameters = {
825826
[WRITE_COMMANDS.UPDATE_XERO_SYNC_INVOICE_COLLECTIONS_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams;
826827
[WRITE_COMMANDS.UPDATE_XERO_SYNC_SYNC_REIMBURSED_REPORTS]: Parameters.UpdateXeroGenericTypeParams;
827828
[WRITE_COMMANDS.UPDATE_XERO_SYNC_REIMBURSEMENT_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams;
829+
830+
[WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT]: Parameters.SetInvoicingTransferBankAccountParams;
828831
};
829832

830833
const READ_COMMANDS = {

Diff for: src/libs/actions/PaymentMethods.ts

+46
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
DeletePaymentCardParams,
1111
MakeDefaultPaymentMethodParams,
1212
PaymentCardParams,
13+
SetInvoicingTransferBankAccountParams,
1314
TransferWalletBalanceParams,
1415
UpdateBillingCurrencyParams,
1516
} from '@libs/API/parameters';
@@ -531,6 +532,50 @@ function setPaymentCardForm(values: AccountData) {
531532
});
532533
}
533534

535+
/**
536+
* Sets the default bank account to use for receiving payouts from
537+
*
538+
*/
539+
function setInvoicingTransferBankAccount(bankAccountID: number, policyID: string, previousBankAccountID: number) {
540+
const parameters: SetInvoicingTransferBankAccountParams = {
541+
bankAccountID,
542+
policyID,
543+
};
544+
545+
const optimisticData: OnyxUpdate[] = [
546+
{
547+
onyxMethod: Onyx.METHOD.MERGE,
548+
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
549+
value: {
550+
invoice: {
551+
bankAccount: {
552+
transferBankAccountID: bankAccountID,
553+
},
554+
},
555+
},
556+
},
557+
];
558+
559+
const failureData: OnyxUpdate[] = [
560+
{
561+
onyxMethod: Onyx.METHOD.MERGE,
562+
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
563+
value: {
564+
invoice: {
565+
bankAccount: {
566+
transferBankAccountID: previousBankAccountID,
567+
},
568+
},
569+
},
570+
},
571+
];
572+
573+
API.write(WRITE_COMMANDS.SET_INVOICING_TRANSFER_BANK_ACCOUNT, parameters, {
574+
optimisticData,
575+
failureData,
576+
});
577+
}
578+
534579
export {
535580
deletePaymentCard,
536581
addPaymentCard,
@@ -555,4 +600,5 @@ export {
555600
clearWalletTermsError,
556601
setPaymentCardForm,
557602
verifySetupIntent,
603+
setInvoicingTransferBankAccount,
558604
};

Diff for: src/pages/settings/Wallet/PaymentMethodList.tsx

+46-52
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import type {ReactElement, Ref} from 'react';
33
import React, {useCallback, useMemo} from 'react';
44
import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
55
import {FlatList, View} from 'react-native';
6-
import type {OnyxEntry} from 'react-native-onyx';
7-
import {useOnyx, withOnyx} from 'react-native-onyx';
6+
import {useOnyx} from 'react-native-onyx';
87
import type {SvgProps} from 'react-native-svg/lib/typescript/ReactNativeSVG';
98
import type {ValueOf} from 'type-fest';
109
import type {RenderSuggestionMenuItemProps} from '@components/AutoCompleteSuggestions/types';
@@ -18,6 +17,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
1817
import Text from '@components/Text';
1918
import useLocalize from '@hooks/useLocalize';
2019
import useNetwork from '@hooks/useNetwork';
20+
import type {FormattedSelectedPaymentMethodIcon} from '@hooks/usePaymentMethodState/types';
2121
import useStyleUtils from '@hooks/useStyleUtils';
2222
import useThemeStyles from '@hooks/useThemeStyles';
2323
import * as CardUtils from '@libs/CardUtils';
@@ -30,29 +30,14 @@ import * as PaymentMethods from '@userActions/PaymentMethods';
3030
import CONST from '@src/CONST';
3131
import ONYXKEYS from '@src/ONYXKEYS';
3232
import ROUTES from '@src/ROUTES';
33-
import type {AccountData, BankAccountList, CardList} from '@src/types/onyx';
33+
import type {AccountData} from '@src/types/onyx';
3434
import type {BankIcon} from '@src/types/onyx/Bank';
3535
import type {Errors} from '@src/types/onyx/OnyxCommon';
3636
import type PaymentMethod from '@src/types/onyx/PaymentMethod';
3737
import type {FilterMethodPaymentType} from '@src/types/onyx/WalletTransfer';
3838
import {isEmptyObject} from '@src/types/utils/EmptyObject';
39-
import type {FormattedSelectedPaymentMethodIcon} from './WalletPage/types';
4039

41-
type PaymentMethodListOnyxProps = {
42-
/** List of bank accounts */
43-
bankAccountList: OnyxEntry<BankAccountList>;
44-
45-
/** List of assigned cards */
46-
cardList: OnyxEntry<CardList>;
47-
48-
/** List of user's cards */
49-
// fundList: OnyxEntry<FundList>;
50-
51-
/** Are we loading payment methods? */
52-
isLoadingPaymentMethods: OnyxEntry<boolean>;
53-
};
54-
55-
type PaymentMethodListProps = PaymentMethodListOnyxProps & {
40+
type PaymentMethodListProps = {
5641
/** Type of active/highlighted payment method */
5742
actionPaymentMethodType?: string;
5843

@@ -92,6 +77,9 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & {
9277
/** Whether the add Payment button be shown on the list */
9378
shouldShowAddPaymentMethodButton?: boolean;
9479

80+
/** Whether the add Bank account button be shown on the list */
81+
shouldShowAddBankAccountButton?: boolean;
82+
9583
/** Whether the assigned cards should be shown on the list */
9684
shouldShowAssignedCards?: boolean;
9785

@@ -110,6 +98,9 @@ type PaymentMethodListProps = PaymentMethodListOnyxProps & {
11098
isDefault?: boolean,
11199
methodID?: number,
112100
) => void;
101+
102+
/** The policy invoice's transfer bank accountID */
103+
invoiceTransferBankAccountID?: number;
113104
};
114105

115106
type PaymentMethodItem = PaymentMethod & {
@@ -173,17 +164,13 @@ function keyExtractor(item: PaymentMethod) {
173164
function PaymentMethodList({
174165
actionPaymentMethodType = '',
175166
activePaymentMethodID = '',
176-
bankAccountList = {},
177167
buttonRef = () => {},
178-
cardList = {},
179-
// Temporarily disabled because P2P debit cards are disabled.
180-
// fundList = {},
181168
filterType = '',
182169
listHeaderComponent,
183-
isLoadingPaymentMethods = true,
184170
onPress,
185171
shouldShowSelectedState = false,
186172
shouldShowAddPaymentMethodButton = true,
173+
shouldShowAddBankAccountButton = false,
187174
shouldShowAddBankAccount = true,
188175
shouldShowEmptyListMessage = true,
189176
shouldShowAssignedCards = false,
@@ -193,12 +180,18 @@ function PaymentMethodList({
193180
style = {},
194181
listItemStyle = {},
195182
shouldShowRightIcon = true,
183+
invoiceTransferBankAccountID,
196184
}: PaymentMethodListProps) {
197185
const styles = useThemeStyles();
198186
const StyleUtils = useStyleUtils();
199187
const {translate} = useLocalize();
200188
const {isOffline} = useNetwork();
201189
const [isUserValidated] = useOnyx(ONYXKEYS.USER, {selector: (user) => !!user?.validated});
190+
const [bankAccountList = {}] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
191+
const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST);
192+
// Temporarily disabled because P2P debit cards are disabled.
193+
// const [fundList = {}] = useOnyx(ONYXKEYS.FUND_LIST);
194+
const [isLoadingPaymentMethods = true] = useOnyx(ONYXKEYS.IS_LOADING_PAYMENT_METHODS);
202195

203196
const getDescriptionForPolicyDomainCard = (domainName: string): string => {
204197
// A domain name containing a policyID indicates that this is a workspace feed
@@ -324,18 +317,29 @@ function PaymentMethodList({
324317
const renderListEmptyComponent = () => <Text style={styles.popoverMenuItem}>{translate('paymentMethodList.addFirstPaymentMethod')}</Text>;
325318

326319
const renderListFooterComponent = useCallback(
327-
() => (
328-
<MenuItem
329-
onPress={onPress}
330-
title={translate('walletPage.addBankAccount')}
331-
icon={Expensicons.Plus}
332-
wrapperStyle={[styles.paymentMethod, listItemStyle]}
333-
ref={buttonRef}
334-
disabled={!isUserValidated}
335-
/>
336-
),
320+
() =>
321+
shouldShowAddBankAccountButton ? (
322+
<Button
323+
ref={buttonRef}
324+
key="addBankAccountButton"
325+
text={translate('walletPage.addBankAccount')}
326+
large
327+
success
328+
isDisabled={!isUserValidated}
329+
onPress={onPress}
330+
/>
331+
) : (
332+
<MenuItem
333+
onPress={onPress}
334+
title={translate('walletPage.addBankAccount')}
335+
icon={Expensicons.Plus}
336+
wrapperStyle={[styles.paymentMethod, listItemStyle]}
337+
ref={buttonRef}
338+
disabled={!isUserValidated}
339+
/>
340+
),
337341

338-
[onPress, translate, styles.paymentMethod, listItemStyle, buttonRef, isUserValidated],
342+
[shouldShowAddBankAccountButton, translate, onPress, buttonRef, styles.paymentMethod, listItemStyle, isUserValidated],
339343
);
340344

341345
/**
@@ -360,7 +364,11 @@ function PaymentMethodList({
360364
iconHeight={item.iconHeight ?? item.iconSize}
361365
iconWidth={item.iconWidth ?? item.iconSize}
362366
iconStyles={item.iconStyles}
363-
badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : undefined}
367+
badgeText={
368+
shouldShowDefaultBadge(filteredPaymentMethods, invoiceTransferBankAccountID ? invoiceTransferBankAccountID === item.methodID : item.isDefault)
369+
? translate('paymentMethodList.defaultPaymentMethod')
370+
: undefined
371+
}
364372
wrapperStyle={[styles.paymentMethod, listItemStyle]}
365373
iconRight={item.iconRight}
366374
badgeStyle={styles.badgeBordered}
@@ -374,7 +382,7 @@ function PaymentMethodList({
374382
</OfflineWithFeedback>
375383
),
376384

377-
[styles.ph6, styles.paymentMethod, styles.badgeBordered, filteredPaymentMethods, translate, listItemStyle, shouldShowSelectedState, selectedMethodID],
385+
[styles.ph6, styles.paymentMethod, styles.badgeBordered, filteredPaymentMethods, invoiceTransferBankAccountID, translate, listItemStyle, shouldShowSelectedState, selectedMethodID],
378386
);
379387

380388
return (
@@ -416,18 +424,4 @@ function PaymentMethodList({
416424

417425
PaymentMethodList.displayName = 'PaymentMethodList';
418426

419-
export default withOnyx<PaymentMethodListProps, PaymentMethodListOnyxProps>({
420-
bankAccountList: {
421-
key: ONYXKEYS.BANK_ACCOUNT_LIST,
422-
},
423-
cardList: {
424-
key: ONYXKEYS.CARD_LIST,
425-
},
426-
// Temporarily disabled - used for P2P debit cards
427-
// fundList: {
428-
// key: ONYXKEYS.FUND_LIST,
429-
// },
430-
isLoadingPaymentMethods: {
431-
key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS,
432-
},
433-
})(PaymentMethodList);
427+
export default PaymentMethodList;

0 commit comments

Comments
 (0)