Skip to content
Merged
24 changes: 23 additions & 1 deletion src/libs/actions/IOU/MoneyRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,22 @@ import {setTransactionReport} from '@userActions/Transaction';
import type {IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import type {TranslationParameters, TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import type {Beta, IntroSelected, LastSelectedDistanceRates, PersonalDetailsList, Policy, QuickAction, RecentWaypoint, Report, Transaction, TransactionViolation} from '@src/types/onyx';
import type {
Beta,
IntroSelected,
LastSelectedDistanceRates,
PersonalDetailsList,
Policy,
PolicyTagLists,
QuickAction,
RecentWaypoint,
Report,
Transaction,
TransactionViolation,
} from '@src/types/onyx';
import type {ReportAttributes, ReportAttributesDerivedValue} from '@src/types/onyx/DerivedValues';
import type {Participant} from '@src/types/onyx/IOU';
import type {Unit} from '@src/types/onyx/Policy';
Expand All @@ -27,6 +40,7 @@ import type {GpsPoint} from './index';
import {
createDistanceRequest,
getMoneyRequestParticipantsFromReport,
getPolicyTags,
requestMoney,
setCustomUnitRateID,
setMoneyRequestDistance,
Expand Down Expand Up @@ -359,6 +373,13 @@ function handleMoneyRequestStepScanParticipants({
const splitReceipt: Receipt = firstReceiptFile.file ?? {};
splitReceipt.source = firstReceiptFile.source;
splitReceipt.state = CONST.IOU.RECEIPT_STATE.SCAN_READY;
const allPolicyTags: OnyxCollection<PolicyTagLists> = getPolicyTags();
const participantsPolicyTags = participants.reduce<Record<string, PolicyTagLists>>((acc, participant) => {
if (participant.policyID) {
acc[participant.policyID] = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {};
}
return acc;
}, {});
startSplitBill({
participants,
currentUserLogin: currentUserLogin ?? '',
Expand All @@ -376,6 +397,7 @@ function handleMoneyRequestStepScanParticipants({
policyRecentlyUsedCurrencies: policyRecentlyUsedCurrencies ?? [],
// No need to update recently used tags because no tags are used when the confirmation step is skipped
policyRecentlyUsedTags: undefined,
participantsPolicyTags,
});
return;
}
Expand Down
6 changes: 3 additions & 3 deletions src/libs/actions/IOU/Split.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ function startSplitBill({
policyRecentlyUsedTags,
quickAction,
policyRecentlyUsedCurrencies,
participantsPolicyTags,
}: StartSplitBilActionParams) {
const currentUserEmailForIOUSplit = addSMSDomainIfPhoneNumber(currentUserLogin);
const participantAccountIDs = participants.map((participant) => Number(participant.accountID));
Expand Down Expand Up @@ -546,6 +547,7 @@ function startSplitBill({
quickAction,
policyRecentlyUsedCurrencies,
policyRecentlyUsedTags,
participantsPolicyTags,
};

if (existingSplitChatReport) {
Expand Down Expand Up @@ -639,9 +641,7 @@ function startSplitBill({
}
const optimisticPolicyRecentlyUsedCategories = mergePolicyRecentlyUsedCategories(category, policyRecentlyUsedCategories);
const optimisticPolicyRecentlyUsedTags = buildOptimisticPolicyRecentlyUsedTags({
// TODO: remove `allPolicyTags` from this file [https://github.com/Expensify/App/issues/80401]
// eslint-disable-next-line @typescript-eslint/no-deprecated
policyTags: getPolicyTagsData(participant.policyID),
policyTags: participant.policyID ? participantsPolicyTags[participant.policyID] : {},
policyRecentlyUsedTags,
transactionTags: tag,
});
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/IOU/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@
};

let allPersonalDetails: OnyxTypes.PersonalDetailsList = {};
Onyx.connect({

Check warning on line 814 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand All @@ -837,6 +837,7 @@
policyRecentlyUsedTags: OnyxEntry<RecentlyUsedTags>;
quickAction: OnyxEntry<OnyxTypes.QuickAction>;
policyRecentlyUsedCurrencies: string[];
participantsPolicyTags: Record<string, OnyxTypes.PolicyTagLists>;
};

type ReplaceReceipt = {
Expand Down Expand Up @@ -907,7 +908,7 @@
};

let allTransactions: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 911 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -921,7 +922,7 @@
});

let allTransactionDrafts: NonNullable<OnyxCollection<OnyxTypes.Transaction>> = {};
Onyx.connect({

Check warning on line 925 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -930,7 +931,7 @@
});

let allTransactionViolations: NonNullable<OnyxCollection<OnyxTypes.TransactionViolations>> = {};
Onyx.connect({

Check warning on line 934 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -944,7 +945,7 @@
});

let allPolicyTags: OnyxCollection<OnyxTypes.PolicyTagLists> = {};
Onyx.connect({

Check warning on line 948 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -957,7 +958,7 @@
});

let allReports: OnyxCollection<OnyxTypes.Report>;
Onyx.connect({

Check warning on line 961 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -966,7 +967,7 @@
});

let allReportNameValuePairs: OnyxCollection<OnyxTypes.ReportNameValuePairs>;
Onyx.connect({

Check warning on line 970 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -976,7 +977,7 @@

let userAccountID = -1;
let currentUserEmail = '';
Onyx.connect({

Check warning on line 980 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
currentUserEmail = value?.email ?? '';
Expand All @@ -985,7 +986,7 @@
});

let deprecatedCurrentUserPersonalDetails: OnyxEntry<OnyxTypes.PersonalDetails>;
Onyx.connect({

Check warning on line 989 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
deprecatedCurrentUserPersonalDetails = value?.[userAccountID] ?? undefined;
Expand All @@ -993,7 +994,7 @@
});

let allReportActions: OnyxCollection<OnyxTypes.ReportActions>;
Onyx.connect({

Check warning on line 997 in src/libs/actions/IOU/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down
12 changes: 11 additions & 1 deletion src/pages/iou/request/step/IOURequestStepConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {RecentlyUsedCategories, Report} from '@src/types/onyx';
import type {PolicyTagLists, RecentlyUsedCategories, Report} from '@src/types/onyx';
import type {Participant} from '@src/types/onyx/IOU';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type {InvoiceReceiver} from '@src/types/onyx/Report';
Expand Down Expand Up @@ -207,6 +207,7 @@ function IOURequestStepConfirmation({

const [policyCategoriesDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES_DRAFT}${draftPolicyID}`);
const [policyRecentlyUsedCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`);
const [allPolicyTags] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS);
const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`);
const [policyRecentlyUsedTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`);
const [policyRecentlyUsedCurrencies] = useOnyx(ONYXKEYS.RECENTLY_USED_CURRENCIES);
Expand Down Expand Up @@ -1084,6 +1085,13 @@ function IOURequestStepConfirmation({
}
const itemTrimmedComment = item?.comment?.comment?.trim() ?? '';

const participantsPolicyTags = selectedParticipants.reduce<Record<string, PolicyTagLists>>((acc, participant) => {
if (participant.policyID) {
acc[participant.policyID] = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {};
}
return acc;
}, {});

// If we have a receipt let's start the split expense by creating only the action, the transaction, and the group DM if needed
startSplitBill({
participants: selectedParticipants,
Expand All @@ -1104,6 +1112,7 @@ function IOURequestStepConfirmation({
policyRecentlyUsedTags,
quickAction,
policyRecentlyUsedCurrencies: policyRecentlyUsedCurrencies ?? [],
participantsPolicyTags,
});
}
}
Expand Down Expand Up @@ -1327,6 +1336,7 @@ function IOURequestStepConfirmation({
reportID,
requestType,
betas,
allPolicyTags,
],
);

Expand Down
31 changes: 28 additions & 3 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ import * as SearchQueryUtils from '@src/libs/SearchQueryUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {IntroSelected, PersonalDetailsList, Policy, PolicyTagLists, RecentlyUsedTags, RecentWaypoint, Report, ReportNameValuePairs, SearchResults} from '@src/types/onyx';
import type {Accountant, Attendee, SplitExpense} from '@src/types/onyx/IOU';
import type {Accountant, Attendee, Participant as IOUParticipant, SplitExpense} from '@src/types/onyx/IOU';
import type {CurrentUserPersonalDetails} from '@src/types/onyx/PersonalDetails';
import type {Participant} from '@src/types/onyx/Report';
import type ReportAction from '@src/types/onyx/ReportAction';
Expand Down Expand Up @@ -207,6 +207,23 @@ describe('actions/IOU', () => {
avatar: 'https://example.com/avatar.jpg',
};

const getParticipantsPolicyTags = async (participants: IOUParticipant[]) => {
let participantsPolicyTags: Record<string, PolicyTagLists> = {};
await getOnyxData({
waitForCollectionCallback: true,
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`,
callback: (tags) => {
participantsPolicyTags = participants.reduce<Record<string, PolicyTagLists>>((acc, participant) => {
if (participant.policyID) {
acc[participant.policyID] = tags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {};
}
return acc;
}, {});
},
});
return participantsPolicyTags;
};

beforeAll(() => {
Onyx.init({
keys: ONYXKEYS,
Expand Down Expand Up @@ -5514,9 +5531,12 @@ describe('actions/IOU', () => {
},
});

const participants: IOUParticipant[] = [{accountID: CARLOS_ACCOUNT_ID, login: CARLOS_EMAIL}];
const participantsPolicyTags = await getParticipantsPolicyTags(participants);

// Start a scan split bill
const {splitTransactionID} = startSplitBill({
participants: [{accountID: CARLOS_ACCOUNT_ID, login: CARLOS_EMAIL}],
participants,
currentUserLogin: RORY_EMAIL,
currentUserAccountID: RORY_ACCOUNT_ID,
comment: '# test',
Expand All @@ -5530,6 +5550,7 @@ describe('actions/IOU', () => {
quickAction: undefined,
policyRecentlyUsedCurrencies: [],
policyRecentlyUsedTags: undefined,
participantsPolicyTags,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -5730,9 +5751,12 @@ describe('actions/IOU', () => {
});
await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`, policyRecentlyUsedTags);

const participants: IOUParticipant[] = [{isPolicyExpenseChat: true, policyID}];
const participantsPolicyTags = await getParticipantsPolicyTags(participants);

// When doing a split bill with a receipt
startSplitBill({
participants: [{isPolicyExpenseChat: true, policyID}],
participants,
currentUserLogin: currentUserPersonalDetails.login ?? '',
currentUserAccountID: currentUserPersonalDetails.accountID,
comment: '',
Expand All @@ -5745,6 +5769,7 @@ describe('actions/IOU', () => {
policyRecentlyUsedTags,
quickAction: {},
policyRecentlyUsedCurrencies: [],
participantsPolicyTags,
});

waitForBatchedUpdates();
Expand Down
115 changes: 113 additions & 2 deletions tests/actions/IOUTest/SplitTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import CONST from '@src/CONST';
import IntlStore from '@src/languages/IntlStore';
import DateUtils from '@src/libs/DateUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, RecentlyUsedTags, Report, ReportNameValuePairs, SearchResults} from '@src/types/onyx';
import type {Policy, PolicyTagLists, RecentlyUsedTags, Report, ReportNameValuePairs, SearchResults} from '@src/types/onyx';
import type {Participant as IOUParticipant, SplitExpense} from '@src/types/onyx/IOU';
import type {CurrentUserPersonalDetails} from '@src/types/onyx/PersonalDetails';
import type {Participant} from '@src/types/onyx/Report';
Expand Down Expand Up @@ -129,6 +129,23 @@ const currentUserPersonalDetails: CurrentUserPersonalDetails = {
avatar: 'https://example.com/avatar.jpg',
};

const getParticipantsPolicyTags = async (participants: IOUParticipant[]) => {
let participantsPolicyTags: Record<string, PolicyTagLists> = {};
await getOnyxData({
waitForCollectionCallback: true,
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`,
callback: (tags) => {
participantsPolicyTags = participants.reduce<Record<string, PolicyTagLists>>((acc, participant) => {
if (participant.policyID) {
acc[participant.policyID] = tags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {};
}
return acc;
}, {});
},
});
return participantsPolicyTags;
};

let mockFetch: MockFetch;

beforeAll(() => {
Expand Down Expand Up @@ -1017,6 +1034,9 @@ describe('split expense', () => {
},
});

const participants: IOUParticipant[] = [{accountID: CARLOS_ACCOUNT_ID, login: CARLOS_EMAIL}];
const participantsPolicyTags = await getParticipantsPolicyTags(participants);

// Start a scan split bill
const {splitTransactionID} = startSplitBill({
participants: [{accountID: CARLOS_ACCOUNT_ID, login: CARLOS_EMAIL}],
Expand All @@ -1033,6 +1053,7 @@ describe('split expense', () => {
quickAction: undefined,
policyRecentlyUsedCurrencies: [],
policyRecentlyUsedTags: undefined,
participantsPolicyTags,
});

await waitForBatchedUpdates();
Expand Down Expand Up @@ -1231,9 +1252,12 @@ describe('startSplitBill', () => {
});
await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`, policyRecentlyUsedTags);

const participants: IOUParticipant[] = [{isPolicyExpenseChat: true, policyID}];
const participantsPolicyTags = await getParticipantsPolicyTags(participants);

// When doing a split bill with a receipt
startSplitBill({
participants: [{isPolicyExpenseChat: true, policyID}],
participants,
currentUserLogin: currentUserPersonalDetails.login ?? '',
currentUserAccountID: currentUserPersonalDetails.accountID,
comment: '',
Expand All @@ -1246,6 +1270,7 @@ describe('startSplitBill', () => {
policyRecentlyUsedTags,
quickAction: {},
policyRecentlyUsedCurrencies: [],
participantsPolicyTags,
});

waitForBatchedUpdates();
Expand All @@ -1263,6 +1288,92 @@ describe('startSplitBill', () => {
expect(newPolicyRecentlyUsedTags[tagName].length).toBe(2);
expect(newPolicyRecentlyUsedTags[tagName].at(0)).toBe(transactionTag);
});

it('should return splitTransactionID and create the transaction in Onyx with correct values', async () => {
// Given a participant
const policyID = 'A';
const testComment = 'Test split comment';
const testCategory = 'Food';
const testCurrency = CONST.CURRENCY.USD;

const participants: IOUParticipant[] = [{isPolicyExpenseChat: true, policyID, accountID: RORY_ACCOUNT_ID}];
const participantsPolicyTags = await getParticipantsPolicyTags(participants);

// When starting a split bill
const {splitTransactionID} = startSplitBill({
participants,
currentUserLogin: currentUserPersonalDetails.login ?? '',
currentUserAccountID: currentUserPersonalDetails.accountID,
comment: testComment,
receipt: {},
category: testCategory,
tag: '',
currency: testCurrency,
taxCode: '',
taxAmount: 0,
quickAction: undefined,
policyRecentlyUsedCurrencies: [],
policyRecentlyUsedTags: undefined,
participantsPolicyTags,
});

await waitForBatchedUpdates();

// Then the returned splitTransactionID should be defined
expect(splitTransactionID).toBeDefined();

// And the transaction should be created in Onyx with correct values
const createdTransaction = await getOnyxValue(`${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransactionID}`);
expect(createdTransaction).toBeDefined();
expect(createdTransaction?.transactionID).toBe(splitTransactionID);
expect(createdTransaction?.comment?.comment).toBe(testComment);
expect(createdTransaction?.category).toBe(testCategory);
expect(createdTransaction?.currency).toBe(testCurrency);
expect(createdTransaction?.merchant).toBe(CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT);
expect(createdTransaction?.reportID).toBe(CONST.REPORT.SPLIT_REPORT_ID);
});

it('should update NVP_QUICK_ACTION_GLOBAL_CREATE with SPLIT_SCAN action', async () => {
// Given an existing quick action
const policyID = 'B';
const existingQuickAction = {
action: CONST.QUICK_ACTIONS.REQUEST_MANUAL,
chatReportID: '12345',
};

await Onyx.merge(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE, existingQuickAction);

const participants: IOUParticipant[] = [{isPolicyExpenseChat: true, policyID, accountID: RORY_ACCOUNT_ID}];
const participantsPolicyTags = await getParticipantsPolicyTags(participants);

// When starting a split bill
const {splitTransactionID} = startSplitBill({
participants,
currentUserLogin: currentUserPersonalDetails.login ?? '',
currentUserAccountID: currentUserPersonalDetails.accountID,
comment: '',
receipt: {},
category: '',
tag: '',
currency: CONST.CURRENCY.USD,
taxCode: '',
taxAmount: 0,
quickAction: existingQuickAction,
policyRecentlyUsedCurrencies: [],
policyRecentlyUsedTags: undefined,
participantsPolicyTags,
});

await waitForBatchedUpdates();

expect(splitTransactionID).toBeDefined();

// Then NVP_QUICK_ACTION_GLOBAL_CREATE should be updated with SPLIT_SCAN action
const quickAction = await getOnyxValue(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE);
expect(quickAction?.action).toBe(CONST.QUICK_ACTIONS.SPLIT_SCAN);
expect(quickAction?.chatReportID).toBeDefined();
expect(quickAction?.isFirstQuickAction).toBe(false);
});
});

describe('updateSplitTransactionsFromSplitExpensesFlow', () => {
Expand Down
Loading