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

Add Pay as Business option for invoices sent to any individual #47862

Merged
merged 11 commits into from
Sep 11, 2024
35 changes: 14 additions & 21 deletions src/components/SettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import {useOnyx, withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import playSound, {SOUNDS} from '@libs/Sound';
import * as SubscriptionUtils from '@libs/SubscriptionUtils';
import * as BankAccounts from '@userActions/BankAccounts';
import * as IOU from '@userActions/IOU';
import * as PolicyActions from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
Expand Down Expand Up @@ -157,10 +155,7 @@ function SettlementButton({
}: SettlementButtonProps) {
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const session = useSession();
const primaryPolicy = useMemo(() => PolicyActions.getPrimaryPolicy(activePolicyID, session?.email), [activePolicyID, session?.email]);

// 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 isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false;
Expand Down Expand Up @@ -234,22 +229,20 @@ function SettlementButton({
});
}

if (PolicyUtils.isPolicyAdmin(primaryPolicy) && PolicyUtils.isPaidGroupPolicy(primaryPolicy)) {
buttonOptions.push({
text: translate('iou.settleBusiness', {formattedAmount}),
icon: Expensicons.Building,
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
backButtonText: translate('iou.business'),
subMenuItems: [
{
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: [
{
text: translate('iou.payElsewhere', {formattedAmount: ''}),
icon: Expensicons.Cash,
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
onSelected: () => onPress(CONST.IOU.PAYMENT_TYPE.ELSEWHERE, true),
},
],
});
}

if (shouldShowApproveButton) {
Expand Down
3 changes: 2 additions & 1 deletion src/libs/API/parameters/PayInvoiceParams.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type CreateWorkspaceParams from './CreateWorkspaceParams';

type PayInvoiceParams = {
type PayInvoiceParams = Partial<CreateWorkspaceParams> & {
reportID: string;
reportActionID: string;
paymentMethodType: PaymentMethodType;
Expand Down
116 changes: 96 additions & 20 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ type UpdateMoneyRequestData = {
};

type PayMoneyRequestData = {
params: PayMoneyRequestParams;
params: PayMoneyRequestParams & Partial<PayInvoiceParams>;
optimisticData: OnyxUpdate[];
successData: OnyxUpdate[];
failureData: OnyxUpdate[];
Expand Down Expand Up @@ -6556,7 +6556,54 @@ function getPayMoneyRequestParams(
payAsBusiness?: boolean,
): PayMoneyRequestData {
const isInvoiceReport = ReportUtils.isInvoiceReport(iouReport);
const activePolicy = PolicyUtils.getPolicy(activePolicyID);
let payerPolicyID = activePolicyID;
let chatReport = initialChatReport;
let policyParams = {};
const optimisticData: OnyxUpdate[] = [];
const successData: OnyxUpdate[] = [];
const failureData: OnyxUpdate[] = [];
const shouldCreatePolicy = !activePolicy || !PolicyUtils.isPolicyAdmin(activePolicy) || !PolicyUtils.isPaidGroupPolicy(activePolicy);

if (ReportUtils.isIndividualInvoiceRoom(chatReport) && payAsBusiness && shouldCreatePolicy) {
payerPolicyID = Policy.generatePolicyID();
const {
optimisticData: policyOptimisticData,
failureData: policyFailureData,
successData: policySuccessData,
params,
} = Policy.buildPolicyData(currentUserEmail, true, undefined, payerPolicyID);
const {
announceChatReportID,
announceCreatedReportActionID,
adminsChatReportID,
adminsCreatedReportActionID,
expenseChatReportID,
expenseCreatedReportActionID,
customUnitRateID,
customUnitID,
ownerEmail,
policyName,
} = params;

policyParams = {
policyID: payerPolicyID,
announceChatReportID,
announceCreatedReportActionID,
adminsChatReportID,
adminsCreatedReportActionID,
expenseChatReportID,
expenseCreatedReportActionID,
customUnitRateID,
customUnitID,
ownerEmail,
policyName,
};

optimisticData.push(...policyOptimisticData, {onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_ACTIVE_POLICY_ID, value: payerPolicyID});
successData.push(...policySuccessData);
failureData.push(...policyFailureData, {onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_ACTIVE_POLICY_ID, value: activePolicyID ?? null});
}

if (ReportUtils.isIndividualInvoiceRoom(chatReport) && payAsBusiness && activePolicyID) {
const existingB2BInvoiceRoom = ReportUtils.getInvoiceChatByParticipants(chatReport.policyID ?? '', activePolicyID);
Expand Down Expand Up @@ -6605,14 +6652,14 @@ function getPayMoneyRequestParams(
lastMessageText: ReportActionsUtils.getReportActionText(optimisticIOUReportAction),
lastMessageHtml: ReportActionsUtils.getReportActionHtml(optimisticIOUReportAction),
};
if (ReportUtils.isIndividualInvoiceRoom(chatReport) && payAsBusiness && activePolicyID) {
if (ReportUtils.isIndividualInvoiceRoom(chatReport) && payAsBusiness && payerPolicyID) {
optimisticChatReport.invoiceReceiver = {
type: CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS,
policyID: activePolicyID,
policyID: payerPolicyID,
};
}

const optimisticData: OnyxUpdate[] = [
optimisticData.push(
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`,
Expand Down Expand Up @@ -6654,23 +6701,21 @@ function getPayMoneyRequestParams(
key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`,
value: optimisticNextStep,
},
];
);

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`,
value: {
pendingFields: {
preview: null,
reimbursed: null,
partial: null,
},
successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`,
value: {
pendingFields: {
preview: null,
reimbursed: null,
partial: null,
},
},
];
});

const failureData: OnyxUpdate[] = [
failureData.push(
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`,
Expand All @@ -6697,7 +6742,7 @@ function getPayMoneyRequestParams(
key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`,
value: currentNextStep,
},
];
);

// In case the report preview action is loaded locally, let's update it.
if (optimisticReportPreviewAction) {
Expand Down Expand Up @@ -6768,6 +6813,7 @@ function getPayMoneyRequestParams(
optimisticHoldReportID,
optimisticHoldActionID,
optimisticHoldReportExpenseActionIDs,
...policyParams,
},
optimisticData,
successData,
Expand Down Expand Up @@ -7411,16 +7457,46 @@ function payInvoice(paymentMethodType: PaymentMethodType, chatReport: OnyxTypes.
optimisticData,
successData,
failureData,
params: {reportActionID},
params: {
reportActionID,
policyID,
announceChatReportID,
announceCreatedReportActionID,
adminsChatReportID,
adminsCreatedReportActionID,
expenseChatReportID,
expenseCreatedReportActionID,
customUnitRateID,
customUnitID,
ownerEmail,
policyName,
},
} = getPayMoneyRequestParams(chatReport, invoiceReport, recipient, paymentMethodType, true, payAsBusiness);

const params: PayInvoiceParams = {
let params: PayInvoiceParams = {
reportID: invoiceReport.reportID,
reportActionID,
paymentMethodType,
payAsBusiness,
};

if (policyID) {
params = {
...params,
policyID,
announceChatReportID,
announceCreatedReportActionID,
adminsChatReportID,
adminsCreatedReportActionID,
expenseChatReportID,
expenseCreatedReportActionID,
customUnitRateID,
customUnitID,
ownerEmail,
policyName,
};
}

API.write(WRITE_COMMANDS.PAY_INVOICE, params, {optimisticData, successData, failureData});
}

Expand Down
Loading