Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ function MoneyReportHeaderSecondaryActionsInner({reportID, primaryAction, isRepo
`${ONYXKEYS.COLLECTION.POLICY}${chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : undefined}`,
{},
);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);

const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const {login: currentUserLogin, accountID, email} = currentUserPersonalDetails;
Expand Down Expand Up @@ -195,6 +196,7 @@ function MoneyReportHeaderSecondaryActionsInner({reportID, primaryAction, isRepo
amountOwed,
ownerBillingGracePeriodEnd,
methodID: type === CONST.IOU.PAYMENT_TYPE.VBBA ? methodID : undefined,
conciergeReportID,
onPaid: () => {
startAnimation();
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ function MoneyReportHeaderSelectionDropdown({reportID, primaryAction, isReportIn

const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const activePolicy = usePolicy(activePolicyID);
const chatReportPolicy = usePolicy(chatReport?.policyID);
const existingB2BInvoiceReport = useParticipantsInvoiceReport(activePolicyID, CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS, chatReport?.policyID);
Expand Down Expand Up @@ -293,6 +294,7 @@ function MoneyReportHeaderSelectionDropdown({reportID, primaryAction, isReportIn
amountOwed,
ownerBillingGracePeriodEnd,
methodID: type === CONST.IOU.PAYMENT_TYPE.VBBA ? methodID : undefined,
conciergeReportID,
onPaid: () => {
startAnimation();
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ function PayPrimaryAction({reportID, chatReportID}: PayPrimaryActionProps) {
const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END);
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${moneyRequestReport?.reportID}`);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);

const activePolicy = usePolicy(activePolicyID);
const chatReportPolicy = usePolicy(chatReport?.policyID);
Expand Down Expand Up @@ -164,6 +165,7 @@ function PayPrimaryAction({reportID, chatReportID}: PayPrimaryActionProps) {
amountOwed,
ownerBillingGracePeriodEnd,
methodID: type === CONST.IOU.PAYMENT_TYPE.VBBA ? methodID : undefined,
conciergeReportID,
onPaid: startAnimation,
});
if (currentSearchQueryJSON && !isOffline) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ function PayActionButton({
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector});
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);

const reportTransactionsCollection = useReportTransactionsCollection(iouReportID);
const transactions = Object.values(reportTransactionsCollection ?? {}).filter(
Expand Down Expand Up @@ -205,6 +206,7 @@ function PayActionButton({
userBillingGracePeriodEnds,
amountOwed,
ownerBillingGracePeriodEnd,
conciergeReportID,
onPaid: startAnimation,
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/useHoldMenuSubmit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function useHoldMenuSubmit({moneyRequestReport, chatReport, requestType, payment
const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector});
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
const [moneyRequestReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID}`);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const {isBetaEnabled} = usePermissions();
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT);
Expand Down Expand Up @@ -96,6 +97,7 @@ function useHoldMenuSubmit({moneyRequestReport, chatReport, requestType, payment
amountOwed,
ownerBillingGracePeriodEnd,
methodID,
conciergeReportID,
onPaid: animationCallback,
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/useSelectionModeReportActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function useSelectionModeReportActions({
const {isOffline} = useNetwork();

const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const activePolicy = usePolicy(activePolicyID);
const chatReportPolicy = usePolicy(chatReport?.policyID);
const [invoiceReceiverPolicy] = useOnyx(
Expand Down Expand Up @@ -396,6 +397,7 @@ function useSelectionModeReportActions({
amountOwed,
ownerBillingGracePeriodEnd,
methodID: type === CONST.IOU.PAYMENT_TYPE.VBBA ? methodID : undefined,
conciergeReportID,
});
if (currentSearchQueryJSON && !isOffline) {
search({
Expand Down
4 changes: 4 additions & 0 deletions src/libs/actions/IOU/Hold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@ function getReportFromHoldRequestsOnyxData({
createdTimestamp,
betas,
isApprovalFlow = false,
conciergeReportID,
}: {
chatReport: OnyxTypes.Report;
iouReport: OnyxEntry<OnyxTypes.Report>;
Expand All @@ -693,6 +694,8 @@ function getReportFromHoldRequestsOnyxData({
createdTimestamp?: string;
betas: OnyxEntry<OnyxTypes.Beta[]>;
isApprovalFlow?: boolean;
// TODO: This will be required eventually. Ref: https://github.com/Expensify/App/issues/66411
conciergeReportID?: string;
}): {
optimisticHoldReportID: string;
optimisticHoldActionID: string;
Expand Down Expand Up @@ -752,6 +755,7 @@ function getReportFromHoldRequestsOnyxData({
firstHoldTransaction,
optimisticExpenseReport.reportID,
newParentReportActionID,
conciergeReportID,
);

let optimisticCreatedReportForUnapprovedAction: OnyxTypes.ReportAction | null = null;
Expand Down
8 changes: 7 additions & 1 deletion src/libs/actions/IOU/PayMoneyRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type PayMoneyRequestFunctionParams = {
amountOwed: OnyxEntry<number>;
ownerBillingGracePeriodEnd?: OnyxEntry<number>;
methodID?: number;
conciergeReportID: string | undefined;
onPaid?: () => void;
};

Expand All @@ -116,6 +117,7 @@ function getPayMoneyRequestParams({
betas,
isSelfTourViewed,
defaultWorkspaceName,
conciergeReportID,
}: {
initialChatReport: OnyxTypes.Report;
iouReport: OnyxEntry<OnyxTypes.Report>;
Expand All @@ -136,6 +138,8 @@ function getPayMoneyRequestParams({
betas: OnyxEntry<OnyxTypes.Beta[]>;
isSelfTourViewed: boolean | undefined;
defaultWorkspaceName?: string;
// TODO: This will be required eventually. Ref: https://github.com/Expensify/App/issues/66411
conciergeReportID?: string;
}): PayMoneyRequestData {
const allTransactionViolations = getAllTransactionViolations();

Expand Down Expand Up @@ -441,7 +445,7 @@ function getPayMoneyRequestParams({
let optimisticHoldActionID;
let optimisticHoldReportExpenseActionIDs;
if (!full) {
const holdReportOnyxData = getReportFromHoldRequestsOnyxData({chatReport, iouReport, recipient, policy: reportPolicy, betas});
const holdReportOnyxData = getReportFromHoldRequestsOnyxData({chatReport, iouReport, recipient, policy: reportPolicy, betas, conciergeReportID});

onyxData.optimisticData?.push(...holdReportOnyxData.optimisticData);
onyxData.successData?.push(...holdReportOnyxData.successData);
Expand Down Expand Up @@ -746,6 +750,7 @@ function payMoneyRequest(params: PayMoneyRequestFunctionParams) {
amountOwed,
ownerBillingGracePeriodEnd,
methodID,
conciergeReportID,
onPaid,
} = params;
const policyForBillingRestriction = chatReportPolicy ?? (policy?.id === chatReport.policyID ? policy : undefined);
Expand Down Expand Up @@ -777,6 +782,7 @@ function payMoneyRequest(params: PayMoneyRequestFunctionParams) {
betas,
isSelfTourViewed,
bankAccountID: paymentType === CONST.IOU.PAYMENT_TYPE.VBBA ? methodID : undefined,
conciergeReportID,
});

// For now, we need to call the PayMoneyRequestWithWallet API since PayMoneyRequest was not updated to work with
Expand Down
104 changes: 104 additions & 0 deletions tests/actions/IOUTest/PayMoneyRequestTest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import Onyx from 'react-native-onyx';
import type {OnyxEntry, OnyxInputValue} from 'react-native-onyx';
import * as Hold from '@libs/actions/IOU/Hold';
import {putOnHold} from '@libs/actions/IOU/Hold';
import {cancelPayment, completePaymentOnboarding, payMoneyRequest} from '@libs/actions/IOU/PayMoneyRequest';
import {requestMoney} from '@libs/actions/IOU/TrackExpense';
Expand All @@ -9,6 +10,7 @@ import {createWorkspace, generatePolicyID} from '@libs/actions/Policy/Policy';
import {notifyNewAction} from '@libs/actions/Report';
import type * as PolicyUtils from '@libs/PolicyUtils';
import {getOriginalMessage, getReportActionHtml, getReportActionText, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import {buildOptimisticIOUReport, buildOptimisticIOUReportAction} from '@libs/ReportUtils';
import {buildOptimisticTransaction} from '@libs/TransactionUtils';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -252,6 +254,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(chatReport),
conciergeReportID: undefined,
});
return waitForBatchedUpdates();
})
Expand Down Expand Up @@ -464,6 +467,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(chatReport),
conciergeReportID: undefined,
});
return waitForBatchedUpdates();
})
Expand Down Expand Up @@ -630,6 +634,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(chatReport),
conciergeReportID: undefined,
});
return waitForBatchedUpdates();
})
Expand Down Expand Up @@ -684,6 +689,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -801,6 +807,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(partialPayChatReport),
conciergeReportID: undefined,
});
return waitForBatchedUpdates();
})
Expand Down Expand Up @@ -898,6 +905,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
isSelfTourViewed: false,
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
conciergeReportID: undefined,
});
await waitForBatchedUpdates();
const newExpenseReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${newExpenseReportID}`);
Expand Down Expand Up @@ -935,6 +943,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyTrueTour,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -985,6 +994,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFalseTour,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -1056,6 +1066,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
ownerBillingGracePeriodEnd: pastDate,
policy,
chatReportPolicy: policy,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -1122,6 +1133,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
ownerBillingGracePeriodEnd: pastDate,
policy: expensePolicy,
chatReportPolicy: workspacePolicy,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -1178,6 +1190,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
ownerBillingGracePeriodEnd: futureGraceEnd,
chatReportPolicy: workspacePolicy,
policy: workspacePolicy,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -1214,6 +1227,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyAmountZero,
conciergeReportID: undefined,
});

await waitForBatchedUpdates();
Expand All @@ -1233,6 +1247,94 @@ describe('actions/IOU/PayMoneyRequest', () => {

mockFetch?.resume?.();
});

it('forwards the provided conciergeReportID to buildOptimisticReportPreview when partially paying', async () => {
// Given an iou report with two transactions, one of which is held (so partial pay is possible)
const iouReport = buildOptimisticIOUReport(1, 2, 100, '1', 'USD');
const transaction1 = buildOptimisticTransaction({
transactionParams: {amount: 100, currency: 'USD', reportID: iouReport.reportID},
});
const transaction2 = buildOptimisticTransaction({
transactionParams: {amount: 100, currency: 'USD', reportID: iouReport.reportID},
});
const concierge42TransactionDataSet: TransactionCollectionDataSet = {
[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction1.transactionID}`]: transaction1,
[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction2.transactionID}`]: transaction2,
};
await Onyx.multiSet(concierge42TransactionDataSet);
putOnHold(transaction1.transactionID, 'comment', iouReport.reportID, false, RORY_EMAIL, RORY_ACCOUNT_ID);
await waitForBatchedUpdates();

const buildOptimisticReportPreviewSpy = jest.spyOn(ReportUtils, 'buildOptimisticReportPreview');
const partialPayChatReport = {reportID: topMostReportID, policyID: CONST.POLICY.ID_FAKE};
const conciergeReportID = 'concierge_report_id_42';

// When partial paying with a specific conciergeReportID
payMoneyRequest({
paymentType: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
chatReport: partialPayChatReport,
iouReport,
introSelected: undefined,
iouReportCurrentNextStepDeprecated: undefined,
currentUserAccountID: CARLOS_ACCOUNT_ID,
currentUserLogin: CARLOS_EMAIL,
full: false,
betas: [CONST.BETAS.ALL],
isSelfTourViewed: false,
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(partialPayChatReport),
conciergeReportID,
});
await waitForBatchedUpdates();

// Then buildOptimisticReportPreview should receive the same conciergeReportID
expect(buildOptimisticReportPreviewSpy).toHaveBeenCalled();
const callsWithConciergeID = buildOptimisticReportPreviewSpy.mock.calls.filter((args) => args.at(6) === conciergeReportID);
expect(callsWithConciergeID.length).toBeGreaterThan(0);

buildOptimisticReportPreviewSpy.mockRestore();
});

it('does not invoke getReportFromHoldRequestsOnyxData when full paying, so conciergeReportID is not forwarded', async () => {
// Given an iou report and a non-partial pay (full=true is the default)
const chatReport = {
...createRandomReport(0, undefined),
lastReadTime: DateUtils.getDBTime(),
lastVisibleActionCreated: DateUtils.getDBTime(),
};
const iouReport = {
...createRandomReport(1, undefined),
chatType: undefined,
type: CONST.REPORT.TYPE.IOU,
total: 10,
};
const getReportFromHoldRequestsOnyxDataSpy = jest.spyOn(Hold, 'getReportFromHoldRequestsOnyxData');
const conciergeReportID = 'concierge_report_id_should_not_propagate';

// When fully paying
payMoneyRequest({
paymentType: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
chatReport,
iouReport,
introSelected: undefined,
iouReportCurrentNextStepDeprecated: undefined,
currentUserAccountID: CARLOS_ACCOUNT_ID,
currentUserLogin: CARLOS_EMAIL,
betas: [CONST.BETAS.ALL],
isSelfTourViewed: false,
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(chatReport),
conciergeReportID,
});
await waitForBatchedUpdates();

// Then getReportFromHoldRequestsOnyxData is not called at all because full pay skips the hold flow
expect(getReportFromHoldRequestsOnyxDataSpy).not.toHaveBeenCalled();

getReportFromHoldRequestsOnyxDataSpy.mockRestore();
});
});

describe('a expense chat with a cancelled payment', () => {
Expand Down Expand Up @@ -1338,6 +1440,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(chatReport),
conciergeReportID: undefined,
});
return waitForBatchedUpdates();
})
Expand Down Expand Up @@ -1563,6 +1666,7 @@ describe('actions/IOU/PayMoneyRequest', () => {
userBillingGracePeriodEnds: undefined,
amountOwed: 0,
chatReportPolicy: chatReportPolicyFromChat(chatReport),
conciergeReportID: undefined,
});
}
await waitForBatchedUpdates();
Expand Down
Loading