Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create v1 Collect workspace Bottom Up flow #30582

Merged
merged 33 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8f961ac
Add a new button to the payment methods popover
mountiny Oct 20, 2023
6c8bd19
Add the business bank account option
mountiny Oct 21, 2023
1a46590
Work on the Bottom Up flow command
mountiny Oct 22, 2023
e68e8a8
Merge main
mountiny Oct 23, 2023
34064f5
Merge main
mountiny Oct 30, 2023
b1b8349
Fix merge conflict
mountiny Nov 1, 2023
7ab658f
Merge branch 'main' into vit-bottomUp
mountiny Nov 2, 2023
c814ade
Fix issues in the flow
mountiny Nov 2, 2023
cfe141b
Update the personal bank account const name
mountiny Nov 2, 2023
ae33908
Flow clean up
mountiny Nov 2, 2023
b6030a9
Merge branch 'main' into vit-bottomUp
mountiny Nov 2, 2023
93d176c
Offer correct payment options
mountiny Nov 3, 2023
ec8974c
Merge branch 'main' into vit-bottomUp
mountiny Nov 3, 2023
b9844f6
Make sure to update the report total and transaction amounts
mountiny Nov 3, 2023
fc25fde
Add moved report action
mountiny Nov 3, 2023
4655e8f
Fix crashes
mountiny Nov 4, 2023
c9a9ec4
Merge main
mountiny Nov 8, 2023
23dda79
Move the navigation action out of Policy action
mountiny Nov 9, 2023
bcaece0
Merge branch 'main' into vit-bottomUp
mountiny Nov 9, 2023
16df5ae
Merge branch 'main' into vit-bottomUp
mountiny Nov 10, 2023
59e4026
Remove logs
mountiny Nov 10, 2023
95d7849
Merge branch 'main' into vit-bottomUp
mountiny Nov 10, 2023
f9a2af1
Clean up the code
mountiny Nov 10, 2023
93f3475
Fix lint and clean up some code
mountiny Nov 10, 2023
43aaa43
Fix lint
mountiny Nov 10, 2023
c599cde
Merge branch 'main' into vit-bottomUp
mountiny Nov 15, 2023
2437282
Destructure props and change session propTypes in the AddPaymentMetho…
mountiny Nov 15, 2023
733067b
Handle transactions in merge collection, prettier and translations
mountiny Nov 15, 2023
6765abd
Clean up the GBR in the bottom up flow
mountiny Nov 15, 2023
7aeeef6
Add props for onItemSelected
mountiny Nov 15, 2023
2e5baf2
Merge branch 'main' into vit-bottomUp
mountiny Nov 16, 2023
5a5e494
Merge branch 'main' into vit-bottomUp
mountiny Nov 20, 2023
7bf4260
Address PR review feedback
mountiny Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ const CONST = {
CREATED: 'CREATED',
IOU: 'IOU',
MODIFIEDEXPENSE: 'MODIFIEDEXPENSE',
MOVED: 'MOVED',
REIMBURSEMENTQUEUED: 'REIMBURSEMENTQUEUED',
RENAMED: 'RENAMED',
REPORTPREVIEW: 'REPORTPREVIEW',
Expand Down Expand Up @@ -1189,7 +1190,8 @@ const CONST = {

PAYMENT_METHODS: {
DEBIT_CARD: 'debitCard',
BANK_ACCOUNT: 'bankAccount',
PERSONAL_BANK_ACCOUNT: 'bankAccount',
BUSINESS_BANK_ACCOUNT: 'businessBankAccount',
},

PAYMENT_METHOD_ID_KEYS: {
Expand Down Expand Up @@ -1278,7 +1280,11 @@ const CONST = {
TYPE: {
FREE: 'free',
PERSONAL: 'personal',

// Often referred to as "control" workspaces
CORPORATE: 'corporate',

// Often referred to as "collect" workspaces
TEAM: 'team',
},
ROLE: {
Expand Down
73 changes: 53 additions & 20 deletions src/components/AddPaymentMethodMenu.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React from 'react';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
import compose from '@libs/compose';
import Permissions from '@libs/Permissions';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import iouReportPropTypes from '@pages/iouReportPropTypes';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import * as Expensicons from './Icon/Expensicons';
import PopoverMenu from './PopoverMenu';
import refPropTypes from './refPropTypes';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import withWindowDimensions from './withWindowDimensions';

const propTypes = {
Expand All @@ -19,6 +23,12 @@ const propTypes = {
/** Callback to execute when the component closes. */
onClose: PropTypes.func.isRequired,

/** Callback to execute when the payment method is selected. */
onItemSelected: PropTypes.func.isRequired,

/** The IOU/Expense report we are paying */
iouReport: iouReportPropTypes,

/** Anchor position for the AddPaymentMenu. */
anchorPosition: PropTypes.shape({
horizontal: PropTypes.number,
Expand All @@ -37,42 +47,63 @@ const propTypes = {
/** Popover anchor ref */
anchorRef: refPropTypes,

...withLocalizePropTypes,
/** Session info for the currently logged in user. */
session: PropTypes.shape({
mountiny marked this conversation as resolved.
Show resolved Hide resolved
/** Currently logged in user accountID */
accountID: PropTypes.number,
}),
};

const defaultProps = {
iouReport: {},
anchorPosition: {},
anchorAlignment: {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
},
betas: [],
anchorRef: () => {},
session: {},
};

function AddPaymentMethodMenu(props) {
function AddPaymentMethodMenu({isVisible, onClose, anchorPosition, anchorAlignment, anchorRef, iouReport, onItemSelected, session, betas}) {
const {translate} = useLocalize();

return (
<PopoverMenu
isVisible={props.isVisible}
onClose={props.onClose}
anchorPosition={props.anchorPosition}
anchorAlignment={props.anchorAlignment}
anchorRef={props.anchorRef}
onItemSelected={props.onClose}
isVisible={isVisible}
onClose={onClose}
anchorPosition={anchorPosition}
anchorAlignment={anchorAlignment}
anchorRef={anchorRef}
onItemSelected={onClose}
menuItems={[
{
text: props.translate('common.bankAccount'),
icon: Expensicons.Bank,
onSelected: () => {
props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT);
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also removed personal bank account option in Wallet page. Issue: #32079

...(Permissions.canUseWallet(props.betas)
...(ReportUtils.isIOUReport(iouReport)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Send money case was missing in this condition, which caused #32228

? [
{
text: props.translate('common.debitCard'),
text: translate('common.personalBankAccount'),
icon: Expensicons.Bank,
onSelected: () => {
onItemSelected(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT);
},
},
]
: []),
...(!ReportActionsUtils.hasRequestFromCurrentAccount(lodashGet(iouReport, 'reportID', 0), lodashGet(session, 'accountID', 0))
? [
{
text: translate('common.businessBankAccount'),
icon: Expensicons.Building,
onSelected: () => onItemSelected(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This caused crash - #31797 as it fallback to throw here:

throw new Error('Invalid payment method type selected');

},
]
: []),
...(Permissions.canUseWallet(betas)
? [
{
text: translate('common.debitCard'),
icon: Expensicons.CreditCard,
onSelected: () => props.onItemSelected(CONST.PAYMENT_METHODS.DEBIT_CARD),
onSelected: () => onItemSelected(CONST.PAYMENT_METHODS.DEBIT_CARD),
},
]
: []),
Expand All @@ -88,10 +119,12 @@ AddPaymentMethodMenu.displayName = 'AddPaymentMethodMenu';

export default compose(
withWindowDimensions,
withLocalize,
withOnyx({
betas: {
key: ONYXKEYS.BETAS,
},
session: {
key: ONYXKEYS.SESSION,
},
}),
)(AddPaymentMethodMenu);
16 changes: 14 additions & 2 deletions src/components/KYCWall/BaseKYCWall.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import Navigation from '@libs/Navigation/Navigation';
import * as PaymentUtils from '@libs/PaymentUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as PaymentMethods from '@userActions/PaymentMethods';
import * as Policy from '@userActions/Policy';
import * as Wallet from '@userActions/Wallet';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {defaultProps, propTypes} from './kycWallPropTypes';

// This component allows us to block various actions by forcing the user to first add a default payment method and successfully make it through our Know Your Customer flow
Expand Down Expand Up @@ -93,10 +95,19 @@ class KYCWall extends React.Component {
*/
selectPaymentMethod(paymentMethod) {
this.props.onSelectPaymentMethod(paymentMethod);
if (paymentMethod === CONST.PAYMENT_METHODS.BANK_ACCOUNT) {
if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) {
Navigation.navigate(this.props.addBankAccountRoute);
} else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) {
Navigation.navigate(this.props.addDebitCardRoute);
} else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) {
if (ReportUtils.isIOUReport(this.props.iouReport)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this additional check necessary? We also make the same check within the function.

I think it is because of the navigation. And it's better to check within the function instead of outside. But I just want to double-check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is as we want to separate the navigation from the actions/ Policy file to separate the concerns. Given we want to take different action based on the report type, I think we need to keep it here.

const policyID = Policy.createWorkspaceFromIOUPayment(this.props.iouReport);

// Navigate to the bank account set up flow for this specific policy
Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('', policyID));
return;
}
Navigation.navigate(this.props.addBankAccountRoute);
Copy link
Contributor

@luacmartins luacmartins Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this default might be incorrect. I think this defaults to a personal bank account since we pass it here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be correct because we pass it to the SettlementButton in both cases using this method which checks if the parent report is workspace chat or DM

const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport);

}
}

Expand Down Expand Up @@ -135,7 +146,7 @@ class KYCWall extends React.Component {
) {
Log.info('[KYC Wallet] User does not have valid payment method');
if (!this.props.shouldIncludeDebitCard) {
this.selectPaymentMethod(CONST.PAYMENT_METHODS.BANK_ACCOUNT);
this.selectPaymentMethod(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT);
return;
}
const clickedElementLocation = getClickedTargetLocation(targetElement);
Expand Down Expand Up @@ -164,6 +175,7 @@ class KYCWall extends React.Component {
<>
<AddPaymentMethodMenu
isVisible={this.state.shouldShowAddPaymentMenu}
iouReport={this.props.iouReport}
onClose={() => this.setState({shouldShowAddPaymentMenu: false})}
anchorRef={this.anchorRef}
anchorPosition={{
Expand Down
2 changes: 0 additions & 2 deletions src/components/MoneyReportHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt
onPress={(paymentType) => IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport)}
enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldShowPaymentOptions
style={[styles.pv2]}
formattedAmount={formattedAmount}
/>
Expand Down Expand Up @@ -164,7 +163,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt
onPress={(paymentType) => IOU.payMoneyRequest(paymentType, chatReport, moneyRequestReport)}
enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldShowPaymentOptions
formattedAmount={formattedAmount}
/>
</View>
Expand Down
1 change: 0 additions & 1 deletion src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,6 @@ function MoneyRequestConfirmationList(props) {
addDebitCardRoute={ROUTES.IOU_SEND_ADD_DEBIT_CARD}
currency={props.iouCurrencyCode}
policyID={props.policyID}
shouldShowPaymentOptions
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE}
kycWallAnchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
Expand Down
1 change: 0 additions & 1 deletion src/components/ReportActionItem/ReportPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ function ReportPreview(props) {
onPress={(paymentType) => IOU.payMoneyRequest(paymentType, props.chatReport, props.iouReport)}
enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldShowPaymentOptions
style={[styles.mt3]}
kycWallAnchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
Expand Down
34 changes: 3 additions & 31 deletions src/components/SettlementButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ const propTypes = {
/** The route to redirect if user does not have a payment method setup */
enablePaymentsRoute: PropTypes.string.isRequired,

/** Should we show the payment options? */
shouldShowPaymentOptions: PropTypes.bool,

/** The last payment method used per policy */
nvp_lastPaymentMethod: PropTypes.objectOf(PropTypes.string),

Expand Down Expand Up @@ -97,7 +94,6 @@ const defaultProps = {
betas: CONST.EMPTY_ARRAY,
iouReport: CONST.EMPTY_OBJECT,
nvp_lastPaymentMethod: CONST.EMPTY_OBJECT,
shouldShowPaymentOptions: false,
style: [],
policyID: '',
formattedAmount: '',
Expand Down Expand Up @@ -130,7 +126,6 @@ function SettlementButton({
onPress,
pressOnEnter,
policyID,
shouldShowPaymentOptions,
style,
}) {
const {translate} = useLocalize();
Expand Down Expand Up @@ -164,42 +159,19 @@ function SettlementButton({

// To achieve the one tap pay experience we need to choose the correct payment type as default,
// if user already paid for some request or expense, let's use the last payment method or use default.
let paymentMethod = nvp_lastPaymentMethod[policyID] || '';
if (!shouldShowPaymentOptions) {
if (!paymentMethod) {
// In case the user hasn't paid a request yet, let's default to VBBA payment type in case of expense reports
if (isExpenseReport) {
paymentMethod = CONST.IOU.PAYMENT_TYPE.VBBA;
} else if (canUseWallet) {
// If they have Wallet set up, use that payment method as default
paymentMethod = CONST.IOU.PAYMENT_TYPE.EXPENSIFY;
} else {
paymentMethod = CONST.IOU.PAYMENT_TYPE.ELSEWHERE;
}
}

// In case of the settlement button in the report preview component, we do not show payment options and the label for Wallet and ACH type is simply "Pay".
return [
{
...paymentMethods[paymentMethod],
text: paymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE ? translate('iou.payElsewhere') : translate('iou.pay'),
},
];
}
const paymentMethod = nvp_lastPaymentMethod[policyID] || '';
if (canUseWallet) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
}
if (isExpenseReport) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]);
}
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]);

// Put the preferred payment method to the front of the array so its shown as default
if (paymentMethod) {
return _.sortBy(buttonOptions, (method) => (method.value === paymentMethod ? 0 : 1));
}
return buttonOptions;
}, [betas, currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, shouldShowPaymentOptions, translate]);
}, [betas, currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, translate]);

const selectPaymentType = (event, iouPaymentType, triggerKYCFlow) => {
if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
Expand Down
2 changes: 2 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ export default {
more: 'More',
debitCard: 'Debit card',
bankAccount: 'Bank account',
personalBankAccount: 'Personal bank account',
businessBankAccount: 'Business bank account',
join: 'Join',
leave: 'Leave',
decline: 'Decline',
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ export default {
more: 'Más',
debitCard: 'Tarjeta de débito',
bankAccount: 'Cuenta bancaria',
personalBankAccount: 'Cuenta bancaria personal',
businessBankAccount: 'Cuenta bancaria comercial',
join: 'Unirse',
leave: 'Salir',
decline: 'Rechazar',
Expand Down
2 changes: 1 addition & 1 deletion src/libs/PaymentUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function hasExpensifyPaymentMethod(fundList: Record<string, Fund>, bankAccountLi

function getPaymentMethodDescription(accountType: AccountType, account: BankAccount['accountData'] | Fund['accountData']): string {
if (account) {
if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT && 'accountNumber' in account) {
if (accountType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT && 'accountNumber' in account) {
return `${Localize.translateLocal('paymentMethodList.accountLastFour')} ${account.accountNumber?.slice(-4)}`;
}
if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD && 'cardNumber' in account) {
Expand Down
23 changes: 22 additions & 1 deletion src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function isDeletedParentAction(reportAction: OnyxEntry<ReportAction>): boolean {
}

function isReversedTransaction(reportAction: OnyxEntry<ReportAction>) {
return (reportAction?.message?.[0].isReversedTransaction ?? false) && (reportAction?.childVisibleActionCount ?? 0) > 0;
return (reportAction?.message?.[0]?.isReversedTransaction ?? false) && (reportAction?.childVisibleActionCount ?? 0) > 0;
}

function isPendingRemove(reportAction: OnyxEntry<ReportAction>): boolean {
Expand Down Expand Up @@ -628,6 +628,26 @@ function isNotifiableReportAction(reportAction: OnyxEntry<ReportAction>): boolea
return actions.includes(reportAction.actionName);
}

/**
* Helper method to determine if the provided accountID has made a request on the specified report.
*
* @param reportID
* @param currentAccountID
* @returns
*/
function hasRequestFromCurrentAccount(reportID: string, currentAccountID: number): boolean {
if (!reportID) {
return false;
}

const reportActions = Object.values(getAllReportActions(reportID));
if (reportActions.length === 0) {
return false;
}

return reportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && action.actorAccountID === currentAccountID);
}

export {
extractLinksFromMessageHtml,
getAllReportActions,
Expand Down Expand Up @@ -668,6 +688,7 @@ export {
isReimbursementQueuedAction,
shouldReportActionBeVisible,
shouldReportActionBeVisibleAsLastAction,
hasRequestFromCurrentAccount,
getFirstVisibleReportActionID,
isChannelLogMemberAction,
};
Loading
Loading