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

GBR and Settlement button for the receiver on the invoice report preview #41859

Merged
merged 25 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
665b508
updated settlement button, added invoice payment
waterim May 8, 2024
22edb59
remove unused activeUserID
waterim May 8, 2024
5e56c7f
fixed payments and ts issues
waterim May 9, 2024
1ea1fc3
remove unused canIOUbePaid part, remove unused useState
waterim May 10, 2024
8afc6fc
added translations
waterim May 10, 2024
a4daf61
Merge remote-tracking branch 'upstream/main' into feat-40437-invoice
waterim May 13, 2024
faaa568
update params in payInvoice
waterim May 13, 2024
1505652
remove PERSONAL & BUSINESS payment types
waterim May 13, 2024
a52e68d
update isSettled
waterim May 14, 2024
bff509d
Merge remote-tracking branch 'upstream/main' into feat-40437-invoice
waterim May 16, 2024
6c2b1a1
new view of the settlement button
waterim May 17, 2024
86f6fb2
fix modal, show amount, update text
waterim May 21, 2024
524d640
update hover, remove business button
waterim May 21, 2024
e0ed718
Revert "draft fix"
waterim May 21, 2024
6da0e8e
Revert "Revert "draft fix""
waterim May 21, 2024
7ca70bf
update wallet for invoiceReport
waterim May 22, 2024
97a41aa
stylings change
waterim May 23, 2024
cba930e
updated stylings
waterim May 23, 2024
35da70f
fixed focus
waterim May 24, 2024
e5e73cc
translations
waterim May 24, 2024
1003662
Merge branch 'main' into feat-40437-invoice
VickyStash May 29, 2024
bb88d41
Update back title inside the submenu
VickyStash May 29, 2024
e600f02
Merge branch 'main' into feat-40437-invoice
VickyStash May 31, 2024
9b7056f
Minor UI fix
VickyStash May 31, 2024
54de5b5
Merge branch 'main' into feat-40437-invoice
VickyStash Jun 4, 2024
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
3 changes: 3 additions & 0 deletions assets/images/checkmark-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import ChatBubbleReply from '@assets/images/chatbubble-reply.svg';
import ChatBubbleUnread from '@assets/images/chatbubble-unread.svg';
import ChatBubble from '@assets/images/chatbubble.svg';
import ChatBubbles from '@assets/images/chatbubbles.svg';
import CheckmarkCircle from '@assets/images/checkmark-circle.svg';
import Checkmark from '@assets/images/checkmark.svg';
import Close from '@assets/images/close.svg';
import ClosedSign from '@assets/images/closed-sign.svg';
Expand Down Expand Up @@ -338,4 +339,5 @@ export {
Lightbulb,
DocumentPlus,
Clear,
CheckmarkCircle,
};
8 changes: 5 additions & 3 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function MoneyReportHeader({

const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport);

const shouldShowSettlementButton = !ReportUtils.isInvoiceReport(moneyRequestReport) && (shouldShowPayButton || shouldShowApproveButton) && !allHavePendingRTERViolation;
const shouldShowSettlementButton = (shouldShowPayButton || shouldShowApproveButton) && !allHavePendingRTERViolation;

const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0 && !allHavePendingRTERViolation;
const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport);
Expand All @@ -135,14 +135,16 @@ function MoneyReportHeader({
const isMoreContentShown = shouldShowNextStep || (shouldShowAnyButton && shouldUseNarrowLayout);

const confirmPayment = (type?: PaymentMethodType | undefined) => {
if (!type) {
if (!type || !chatReport) {
return;
}
setPaymentType(type);
setRequestType('pay');
if (ReportUtils.hasHeldExpenses(moneyRequestReport.reportID)) {
setIsHoldMenuVisible(true);
} else if (chatReport) {
} else if (ReportUtils.isInvoiceReport(moneyRequestReport)) {
IOU.payInvoice(type, chatReport, moneyRequestReport);
} else {
IOU.payMoneyRequest(type, chatReport, moneyRequestReport, true);
}
};
Expand Down
21 changes: 19 additions & 2 deletions src/components/PopoverMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import lodashIsEqual from 'lodash/isEqual';
import type {RefObject} from 'react';
import React, {useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
Expand Down Expand Up @@ -191,7 +192,7 @@ function PopoverMenu({
shouldSetModalVisibility={shouldSetModalVisibility}
>
<View style={isSmallScreenWidth ? {} : styles.createMenuContainer}>
{!!headerText && <Text style={[styles.createMenuHeaderText, styles.ml3]}>{headerText}</Text>}
{!!headerText && enteredSubMenuIndexes.length === 0 && <Text style={[styles.createMenuHeaderText, styles.ml3, styles.mb3]}>{headerText}</Text>}
{enteredSubMenuIndexes.length > 0 && renderBackButtonItem()}
{currentMenuItems.map((item, menuIndex) => (
<FocusableMenuItem
Expand Down Expand Up @@ -229,5 +230,21 @@ function PopoverMenu({

PopoverMenu.displayName = 'PopoverMenu';

export default React.memo(PopoverMenu);
export default React.memo(
PopoverMenu,
(prevProps, nextProps) =>
!lodashIsEqual(prevProps.menuItems, nextProps.menuItems) &&
prevProps.isVisible === nextProps.isVisible &&
lodashIsEqual(prevProps.anchorPosition, nextProps.anchorPosition) &&
prevProps.anchorRef === nextProps.anchorRef &&
prevProps.headerText === nextProps.headerText &&
prevProps.fromSidebarMediumScreen === nextProps.fromSidebarMediumScreen &&
lodashIsEqual(prevProps.anchorAlignment, nextProps.anchorAlignment) &&
prevProps.animationIn === nextProps.animationIn &&
prevProps.animationOut === nextProps.animationOut &&
prevProps.animationInTiming === nextProps.animationInTiming &&
prevProps.disableAnimation === nextProps.disableAnimation &&
prevProps.withoutOverlay === nextProps.withoutOverlay &&
prevProps.shouldSetModalVisibility === nextProps.shouldSetModalVisibility,
);
export type {PopoverMenuItem, PopoverMenuProps};
16 changes: 14 additions & 2 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ function ReportPreview({

const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport);

const shouldShowSettlementButton = !ReportUtils.isInvoiceReport(iouReport) && (shouldShowPayButton || shouldShowApproveButton) && !showRTERViolationMessage;
const shouldShowSettlementButton = (shouldShowPayButton || shouldShowApproveButton) && !showRTERViolationMessage;

const shouldPromptUserToAddBankAccount = ReportUtils.hasMissingPaymentMethod(userWallet, iouReportID);
const shouldShowRBR = !iouSettled && hasErrors;
Expand Down Expand Up @@ -263,6 +263,17 @@ function ReportPreview({
};
}, [formattedMerchant, formattedDescription, moneyRequestComment, translate, numberOfRequests, numberOfScanningReceipts, numberOfPendingRequests]);

const confirmPayment = (paymentMethodType?: PaymentMethodType) => {
if (!paymentMethodType || !chatReport || !iouReport) {
return;
}
if (ReportUtils.isInvoiceReport(iouReport)) {
IOU.payInvoice(paymentMethodType, chatReport, iouReport);
} else {
IOU.payMoneyRequest(paymentMethodType, chatReport, iouReport);
}
};

return (
<OfflineWithFeedback
pendingAction={iouReport?.pendingFields?.preview}
Expand Down Expand Up @@ -347,11 +358,12 @@ function ReportPreview({
</View>
{shouldShowSettlementButton && (
<SettlementButton
formattedAmount={getDisplayAmount() ?? ''}
currency={iouReport?.currency}
policyID={policyID}
chatReportID={chatReportID}
iouReport={iouReport}
onPress={(paymentType?: PaymentMethodType) => chatReport && iouReport && paymentType && IOU.payMoneyRequest(paymentType, chatReport, iouReport)}
onPress={confirmPayment}
enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldHidePaymentOptions={!shouldShowPayButton}
Expand Down
27 changes: 25 additions & 2 deletions src/components/SettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {LastPaymentMethod, Policy, Report} from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import ButtonWithDropdownMenu from './ButtonWithDropdownMenu';
import type {PaymentType} from './ButtonWithDropdownMenu/types';
import * as Expensicons from './Icon/Expensicons';
Expand Down Expand Up @@ -149,9 +150,10 @@ function SettlementButton({

const session = useSession();
const chatReport = ReportUtils.getReport(chatReportID);
const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false;
const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicyExpenseChat(chatReport);
const shouldShowPaywithExpensifyOption = !isPaidGroupPolicy || (!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport as OnyxEntry<Report>));
const shouldShowPayElsewhereOption = !isPaidGroupPolicy || policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL;
const shouldShowPayElsewhereOption = (!isPaidGroupPolicy || policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL) && !isInvoiceReport;
const paymentButtonOptions = useMemo(() => {
const buttonOptions = [];
const isExpenseReport = ReportUtils.isExpenseReport(iouReport);
Expand All @@ -178,7 +180,7 @@ function SettlementButton({
value: CONST.IOU.REPORT_ACTION_TYPE.APPROVE,
disabled: !!shouldDisableApproveButton,
};
const canUseWallet = !isExpenseReport && currency === CONST.CURRENCY.USD;
const canUseWallet = !isExpenseReport && !isInvoiceReport && currency === CONST.CURRENCY.USD;

// Only show the Approve button if the user cannot pay the expense
if (shouldHidePaymentOptions && shouldShowApproveButton) {
Expand All @@ -199,6 +201,22 @@ function SettlementButton({
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]);
}

if (isInvoiceReport) {
buttonOptions.push({
text: translate('iou.settlePersonal', {formattedAmount}),
icon: Expensicons.User,
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
subMenuItems: [
{
text: translate('iou.payElsewhere', {formattedAmount: ''}),
icon: Expensicons.Cash,
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE),
},
],
});
}

if (shouldShowApproveButton) {
buttonOptions.push(approveButtonOption);
}
Expand All @@ -211,6 +229,7 @@ function SettlementButton({
// We don't want to reorder the options when the preferred payment method changes while the button is still visible
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currency, formattedAmount, iouReport, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton, shouldDisableApproveButton]);

const selectPaymentType = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => {
if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
triggerKYCFlow(event, iouPaymentType);
Expand Down Expand Up @@ -252,6 +271,10 @@ function SettlementButton({
<ButtonWithDropdownMenu<PaymentType>
success
buttonRef={buttonRef}
shouldAlwaysShowDropdownMenu={isInvoiceReport}
customText={isInvoiceReport ? translate('iou.settlePayment', {formattedAmount}) : undefined}
menuHeaderText={isInvoiceReport ? translate('workspace.invoices.paymentMethods.chooseInvoiceMethod') : undefined}
isSplitButton={!isInvoiceReport}
isDisabled={isDisabled}
isLoading={isLoading}
onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)}
Expand Down
11 changes: 11 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,9 @@ export default {
settledExpensify: 'Paid',
settledElsewhere: 'Paid elsewhere',
settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with Expensify` : `Pay with Expensify`),
settlePersonal: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as an individual` : `Pay as an individual`),
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pay ${formattedAmount}`,
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as a business` : `Pay as a business`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} elsewhere` : `Pay elsewhere`),
nextStep: 'Next Steps',
finished: 'Finished',
Expand Down Expand Up @@ -2458,6 +2461,14 @@ export default {
viewUnpaidInvoices: 'View unpaid invoices',
sendInvoice: 'Send invoice',
sendFrom: 'Send from',
paymentMethods: {
personal: 'Personal',
business: 'Business',
chooseInvoiceMethod: 'Choose a payment method below:',
addBankAccount: 'Add bank account',
payingAsIndividual: 'Paying as an individual',
payingAsBusiness: 'Paying as a business',
},
},
travel: {
unlockConciergeBookingTravel: 'Unlock Concierge travel booking',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/es.ts
waterim marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,9 @@ export default {
settledElsewhere: 'Pagado de otra forma',
settleExpensify: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} con Expensify` : `Pagar con Expensify`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} de otra forma` : `Pagar de otra forma`),
settlePersonal: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as an individual` : `Pay as an individual`),
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pagar ${formattedAmount}`,
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as a business` : `Pay as a business`),
Copy link
Contributor

Choose a reason for hiding this comment

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

These need spanish translation.

nextStep: 'Pasos Siguientes',
finished: 'Finalizado',
sendInvoice: ({amount}: RequestAmountParams) => `Enviar factura de ${amount}`,
Expand Down Expand Up @@ -2495,6 +2498,14 @@ export default {
viewUnpaidInvoices: 'Ver facturas emitidas pendientes',
sendInvoice: 'Enviar factura',
sendFrom: 'Enviar desde',
paymentMethods: {
personal: 'Personal',
business: 'Empresas',
chooseInvoiceMethod: 'Elija un método de pago:',
addBankAccount: 'Añadir cuenta bancaria',
payingAsIndividual: 'Pago individual',
payingAsBusiness: 'Pagar como una empresa',
},
},
travel: {
unlockConciergeBookingTravel: 'Desbloquea la reserva de viajes con Concierge',
Expand Down
9 changes: 9 additions & 0 deletions src/libs/API/parameters/PayInvoiceParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';

type PayInvoiceParams = {
reportID: string;
reportActionID: string;
paymentMethodType: PaymentMethodType;
};

export default PayInvoiceParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,5 @@ export type {default as LeavePolicyParams} from './LeavePolicyParams';
export type {default as OpenPolicyAccountingPageParams} from './OpenPolicyAccountingPageParams';
export type {default as SearchParams} from './Search';
export type {default as SendInvoiceParams} from './SendInvoiceParams';
export type {default as PayInvoiceParams} from './PayInvoiceParams';
export type {default as MarkAsCashParams} from './MarkAsCashParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ const WRITE_COMMANDS = {
LEAVE_POLICY: 'LeavePolicy',
ACCEPT_SPOTNANA_TERMS: 'AcceptSpotnanaTerms',
SEND_INVOICE: 'SendInvoice',
PAY_INVOICE: 'PayInvoice',
MARK_AS_CASH: 'MarkAsCash',
} as const;

Expand Down Expand Up @@ -435,6 +436,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.LEAVE_POLICY]: Parameters.LeavePolicyParams;
[WRITE_COMMANDS.ACCEPT_SPOTNANA_TERMS]: EmptyObject;
[WRITE_COMMANDS.SEND_INVOICE]: Parameters.SendInvoiceParams;
[WRITE_COMMANDS.PAY_INVOICE]: Parameters.PayInvoiceParams;
[WRITE_COMMANDS.MARK_AS_CASH]: Parameters.MarkAsCashParams;
};

Expand Down
2 changes: 1 addition & 1 deletion src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ function isPolicyExpenseChat(report: OnyxEntry<Report> | Participant | EmptyObje
return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false);
}

function isInvoiceRoom(report: OnyxEntry<Report>): boolean {
function isInvoiceRoom(report: OnyxEntry<Report> | EmptyObject): boolean {
return getChatType(report) === CONST.REPORT.CHAT_TYPE.INVOICE;
}

Expand Down
Loading
Loading