diff --git a/src/libs/actions/IOU/MoneyRequest.ts b/src/libs/actions/IOU/MoneyRequest.ts index 777bd6a562606..e851d5cf2f526 100644 --- a/src/libs/actions/IOU/MoneyRequest.ts +++ b/src/libs/actions/IOU/MoneyRequest.ts @@ -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'; @@ -27,6 +40,7 @@ import type {GpsPoint} from './index'; import { createDistanceRequest, getMoneyRequestParticipantsFromReport, + getPolicyTags, requestMoney, setCustomUnitRateID, setMoneyRequestDistance, @@ -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 = getPolicyTags(); + const participantsPolicyTags = participants.reduce>((acc, participant) => { + if (participant.policyID) { + acc[participant.policyID] = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {}; + } + return acc; + }, {}); startSplitBill({ participants, currentUserLogin: currentUserLogin ?? '', @@ -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; } diff --git a/src/libs/actions/IOU/Split.ts b/src/libs/actions/IOU/Split.ts index a24b957f5b72e..c313f5463093e 100644 --- a/src/libs/actions/IOU/Split.ts +++ b/src/libs/actions/IOU/Split.ts @@ -363,6 +363,7 @@ function startSplitBill({ policyRecentlyUsedTags, quickAction, policyRecentlyUsedCurrencies, + participantsPolicyTags, }: StartSplitBilActionParams) { const currentUserEmailForIOUSplit = addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); @@ -546,6 +547,7 @@ function startSplitBill({ quickAction, policyRecentlyUsedCurrencies, policyRecentlyUsedTags, + participantsPolicyTags, }; if (existingSplitChatReport) { @@ -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, }); diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index f6e92b2b67641..0f61eed08ede8 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -837,6 +837,7 @@ type StartSplitBilActionParams = { policyRecentlyUsedTags: OnyxEntry; quickAction: OnyxEntry; policyRecentlyUsedCurrencies: string[]; + participantsPolicyTags: Record; }; type ReplaceReceipt = { diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index e34c62f7fbfb3..749ca5052be92 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -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'; @@ -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); @@ -1084,6 +1085,13 @@ function IOURequestStepConfirmation({ } const itemTrimmedComment = item?.comment?.comment?.trim() ?? ''; + const participantsPolicyTags = selectedParticipants.reduce>((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, @@ -1104,6 +1112,7 @@ function IOURequestStepConfirmation({ policyRecentlyUsedTags, quickAction, policyRecentlyUsedCurrencies: policyRecentlyUsedCurrencies ?? [], + participantsPolicyTags, }); } } @@ -1327,6 +1336,7 @@ function IOURequestStepConfirmation({ reportID, requestType, betas, + allPolicyTags, ], ); diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index b77d7abf57d32..5bea71f7ab901 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -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'; @@ -207,6 +207,23 @@ describe('actions/IOU', () => { avatar: 'https://example.com/avatar.jpg', }; + const getParticipantsPolicyTags = async (participants: IOUParticipant[]) => { + let participantsPolicyTags: Record = {}; + await getOnyxData({ + waitForCollectionCallback: true, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`, + callback: (tags) => { + participantsPolicyTags = participants.reduce>((acc, participant) => { + if (participant.policyID) { + acc[participant.policyID] = tags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {}; + } + return acc; + }, {}); + }, + }); + return participantsPolicyTags; + }; + beforeAll(() => { Onyx.init({ keys: ONYXKEYS, @@ -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', @@ -5530,6 +5550,7 @@ describe('actions/IOU', () => { quickAction: undefined, policyRecentlyUsedCurrencies: [], policyRecentlyUsedTags: undefined, + participantsPolicyTags, }); await waitForBatchedUpdates(); @@ -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: '', @@ -5745,6 +5769,7 @@ describe('actions/IOU', () => { policyRecentlyUsedTags, quickAction: {}, policyRecentlyUsedCurrencies: [], + participantsPolicyTags, }); waitForBatchedUpdates(); diff --git a/tests/actions/IOUTest/SplitTest.ts b/tests/actions/IOUTest/SplitTest.ts index ce544d1c17cf8..ded1cf5eac35e 100644 --- a/tests/actions/IOUTest/SplitTest.ts +++ b/tests/actions/IOUTest/SplitTest.ts @@ -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'; @@ -129,6 +129,23 @@ const currentUserPersonalDetails: CurrentUserPersonalDetails = { avatar: 'https://example.com/avatar.jpg', }; +const getParticipantsPolicyTags = async (participants: IOUParticipant[]) => { + let participantsPolicyTags: Record = {}; + await getOnyxData({ + waitForCollectionCallback: true, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}`, + callback: (tags) => { + participantsPolicyTags = participants.reduce>((acc, participant) => { + if (participant.policyID) { + acc[participant.policyID] = tags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${participant.policyID}`] ?? {}; + } + return acc; + }, {}); + }, + }); + return participantsPolicyTags; +}; + let mockFetch: MockFetch; beforeAll(() => { @@ -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}], @@ -1033,6 +1053,7 @@ describe('split expense', () => { quickAction: undefined, policyRecentlyUsedCurrencies: [], policyRecentlyUsedTags: undefined, + participantsPolicyTags, }); await waitForBatchedUpdates(); @@ -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: '', @@ -1246,6 +1270,7 @@ describe('startSplitBill', () => { policyRecentlyUsedTags, quickAction: {}, policyRecentlyUsedCurrencies: [], + participantsPolicyTags, }); waitForBatchedUpdates(); @@ -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', () => {