From 6734a5acaf65fd64d8b551e3b3691759b00ec7a2 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 26 Dec 2024 18:15:07 +0700 Subject: [PATCH 1/6] don't apply the category tax rules for distance request --- src/libs/TransactionUtils/index.ts | 2 +- tests/actions/IOUTest.ts | 201 +++++++++++++++++++++-------- 2 files changed, 146 insertions(+), 57 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 21c346f613b5..bb7ea0dc5c42 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1258,7 +1258,7 @@ function buildTransactionsMergeParams(reviewDuplicates: OnyxEntry, policy: OnyxEntry) { const taxRules = policy?.rules?.expenseRules?.filter((rule) => rule.tax); - if (!taxRules || taxRules?.length === 0) { + if (!taxRules || taxRules?.length === 0 || isDistanceRequest(transaction)) { return {categoryTaxCode: undefined, categoryTaxAmount: undefined}; } diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 49e1150f1a57..b0f42838df72 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -3520,40 +3520,82 @@ describe('actions/IOU', () => { }); }); - it('should not change the tax if there are no tax expense rules', async () => { - // Given a policy without tax expense rules - const transactionID = '1'; - const category = 'Advertising'; - const policyID = '2'; - const taxCode = 'id_TAX_EXEMPT'; - const taxAmount = 0; - const fakePolicy: OnyxTypes.Policy = { - ...createRandomPolicy(Number(policyID)), - taxRates: CONST.DEFAULT_TAX, - rules: {}, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, { - taxCode, - taxAmount, - amount: 100, + describe('should not change the tax', () => { + it('if the transaction type is distance', async () => { + // Given a policy with tax expense rules associated with category and a distance transaction + const transactionID = '1'; + const category = 'Advertising'; + const policyID = '2'; + const taxCode = 'id_TAX_EXEMPT'; + const ruleTaxCode = 'id_TAX_RATE_1'; + const taxAmount = 0; + const fakePolicy: OnyxTypes.Policy = { + ...createRandomPolicy(Number(policyID)), + taxRates: CONST.DEFAULT_TAX, + rules: {expenseRules: createCategoryTaxExpenseRules(category, ruleTaxCode)}, + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, { + taxCode, + taxAmount, + amount: 100, + iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE, + }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + + // When setting the money request category + IOU.setMoneyRequestCategory(transactionID, category, policyID); + + await waitForBatchedUpdates(); + + // Then the transaction tax rate and amount shouldn't be updated + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, + callback: (transaction) => { + Onyx.disconnect(connection); + expect(transaction?.taxCode).toBe(taxCode); + expect(transaction?.taxAmount).toBe(taxAmount); + resolve(); + }, + }); + }); }); - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); - // When setting the money request category - IOU.setMoneyRequestCategory(transactionID, category, policyID); + it('if there are no tax expense rules', async () => { + // Given a policy without tax expense rules + const transactionID = '1'; + const category = 'Advertising'; + const policyID = '2'; + const taxCode = 'id_TAX_EXEMPT'; + const taxAmount = 0; + const fakePolicy: OnyxTypes.Policy = { + ...createRandomPolicy(Number(policyID)), + taxRates: CONST.DEFAULT_TAX, + rules: {}, + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, { + taxCode, + taxAmount, + amount: 100, + }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); - await waitForBatchedUpdates(); + // When setting the money request category + IOU.setMoneyRequestCategory(transactionID, category, policyID); - // Then the transaction tax rate and amount shouldn't be updated - await new Promise((resolve) => { - const connection = Onyx.connect({ - key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, - callback: (transaction) => { - Onyx.disconnect(connection); - expect(transaction?.taxCode).toBe(taxCode); - expect(transaction?.taxAmount).toBe(taxAmount); - resolve(); - }, + await waitForBatchedUpdates(); + + // Then the transaction tax rate and amount shouldn't be updated + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, + callback: (transaction) => { + Onyx.disconnect(connection); + expect(transaction?.taxCode).toBe(taxCode); + expect(transaction?.taxAmount).toBe(taxAmount); + resolve(); + }, + }); }); }); }); @@ -3627,34 +3669,81 @@ describe('actions/IOU', () => { }); }); - it('should not update the tax when there are no tax expense rules', async () => { - // Given a policy without tax expense rules - const transactionID = '1'; - const policyID = '2'; - const category = 'Advertising'; - const fakePolicy: OnyxTypes.Policy = { - ...createRandomPolicy(Number(policyID)), - taxRates: CONST.DEFAULT_TAX, - rules: {}, - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {amount: 100}); - await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + describe('should not update the tax', () => { + it('if the transaction type is distance', async () => { + // Given a policy with tax expense rules associated with category and a distance transaction + const transactionID = '1'; + const policyID = '2'; + const category = 'Advertising'; + const taxCode = 'id_TAX_EXEMPT'; + const taxAmount = 0; + const ruleTaxCode = 'id_TAX_RATE_1'; + const fakePolicy: OnyxTypes.Policy = { + ...createRandomPolicy(Number(policyID)), + taxRates: CONST.DEFAULT_TAX, + rules: {expenseRules: createCategoryTaxExpenseRules(category, ruleTaxCode)}, + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { + taxCode, + taxAmount, + amount: 100, + comment: { + type: CONST.TRANSACTION.TYPE.CUSTOM_UNIT, + customUnit: { + name: CONST.CUSTOM_UNITS.NAME_DISTANCE, + }, + }, + }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); - // When updating the money request category - IOU.updateMoneyRequestCategory(transactionID, '3', category, fakePolicy, undefined, undefined); + // When updating a money request category + IOU.updateMoneyRequestCategory(transactionID, '3', category, fakePolicy, undefined, undefined); - await waitForBatchedUpdates(); + await waitForBatchedUpdates(); - // Then the transaction tax rate and amount shouldn't be updated - await new Promise((resolve) => { - const connection = Onyx.connect({ - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - callback: (transaction) => { - Onyx.disconnect(connection); - expect(transaction?.taxCode).toBeUndefined(); - expect(transaction?.taxAmount).toBeUndefined(); - resolve(); - }, + // Then the transaction tax rate and amount shouldn't be updated + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + callback: (transaction) => { + Onyx.disconnect(connection); + expect(transaction?.taxCode).toBe(taxCode); + expect(transaction?.taxAmount).toBe(taxAmount); + resolve(); + }, + }); + }); + }); + + it('if there are no tax expense rules', async () => { + // Given a policy without tax expense rules + const transactionID = '1'; + const policyID = '2'; + const category = 'Advertising'; + const fakePolicy: OnyxTypes.Policy = { + ...createRandomPolicy(Number(policyID)), + taxRates: CONST.DEFAULT_TAX, + rules: {}, + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {amount: 100}); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + + // When updating the money request category + IOU.updateMoneyRequestCategory(transactionID, '3', category, fakePolicy, undefined, undefined); + + await waitForBatchedUpdates(); + + // Then the transaction tax rate and amount shouldn't be updated + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + callback: (transaction) => { + Onyx.disconnect(connection); + expect(transaction?.taxCode).toBeUndefined(); + expect(transaction?.taxAmount).toBeUndefined(); + resolve(); + }, + }); }); }); }); @@ -3700,7 +3789,7 @@ describe('actions/IOU', () => { }); describe('should not change the tax', () => { - it('if there is no tax expense rules', async () => { + it('if there are no tax expense rules', async () => { // Given a policy without tax expense rules const transactionID = '1'; const category = 'Advertising'; From b7ce3b7938e596e3cd01ce1798ac23432b186ea1 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 26 Dec 2024 18:27:57 +0700 Subject: [PATCH 2/6] add test --- tests/unit/TransactionUtilsTest.ts | 53 +++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index a6c847e5f7f4..fa91210dc6c4 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -133,22 +133,45 @@ describe('TransactionUtils', () => { expect(categoryTaxAmount).toBe(0); }); - it('should return and undefined tax when there are no policy tax expense rules', () => { - // Given a policy without tax expense rules - const category = 'Advertising'; - const fakePolicy: Policy = { - ...createRandomPolicy(0), - taxRates: CONST.DEFAULT_TAX, - rules: {}, - }; - - // When retrieving the tax from a category - const transaction = generateTransaction(); - const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, fakePolicy); + describe('should return undefined tax', () => { + it('if the transaction type is distance', () => { + // Given a policy with tax expense rules associated with a category + const category = 'Advertising'; + const fakePolicy: Policy = { + ...createRandomPolicy(0), + taxRates: CONST.DEFAULT_TAX, + rules: {expenseRules: createCategoryTaxExpenseRules(category, 'id_TAX_RATE_1')}, + }; + + // When retrieving the tax from the associated category + const transaction: Transaction = { + ...generateTransaction(), + iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE, + }; + const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, fakePolicy); + + // Then it should return undefined for both the tax code and the tax amount + expect(categoryTaxCode).toBe(undefined); + expect(categoryTaxAmount).toBe(undefined); + }); - // Then it should return undefined for both the tax code and the tax amount - expect(categoryTaxCode).toBe(undefined); - expect(categoryTaxAmount).toBe(undefined); + it('if there are no policy tax expense rules', () => { + // Given a policy without tax expense rules + const category = 'Advertising'; + const fakePolicy: Policy = { + ...createRandomPolicy(0), + taxRates: CONST.DEFAULT_TAX, + rules: {}, + }; + + // When retrieving the tax from a category + const transaction = generateTransaction(); + const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, fakePolicy); + + // Then it should return undefined for both the tax code and the tax amount + expect(categoryTaxCode).toBe(undefined); + expect(categoryTaxAmount).toBe(undefined); + }); }); }); }); From 1e792b85f8d0be0e75455ca826ffc31966bc685b Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 26 Dec 2024 19:41:24 +0700 Subject: [PATCH 3/6] get the correct default tax rate --- src/libs/TransactionUtils/index.ts | 3 ++- tests/unit/TransactionUtilsTest.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index bb7ea0dc5c42..379f68009d3c 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1262,7 +1262,8 @@ function getCategoryTaxCodeAndAmount(category: string, transaction: OnyxEntry { expect(categoryTaxAmount).toBe(0); }); + it("should return the foreign default tax when the category doesn't match the tax expense rules and using a foreign currency", () => { + // Given a policy with tax expense rules associated with a category + const ruleCategory = 'Advertising'; + const selectedCategory = 'Benefits'; + const fakePolicy: Policy = { + ...createRandomPolicy(0), + taxRates: { + ...CONST.DEFAULT_TAX, + foreignTaxDefault: 'id_TAX_RATE_2', + taxes: { + ...CONST.DEFAULT_TAX.taxes, + id_TAX_RATE_2: { + name: 'Tax rate 2', + value: '10%', + }, + }, + }, + outputCurrency: 'IDR', + rules: {expenseRules: createCategoryTaxExpenseRules(ruleCategory, 'id_TAX_RATE_1')}, + }; + + // When retrieving the tax from a category that is not associated with the tax expense rules + const transaction = generateTransaction(); + const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(selectedCategory, transaction, fakePolicy); + + // Then it should return the default tax code and amount + expect(categoryTaxCode).toBe('id_TAX_RATE_2'); + expect(categoryTaxAmount).toBe(9); + }); + describe('should return undefined tax', () => { it('if the transaction type is distance', () => { // Given a policy with tax expense rules associated with a category From 6552ca8a1d6db538450a58a78c0e9ad1edb99c82 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 26 Dec 2024 19:45:06 +0700 Subject: [PATCH 4/6] lint --- tests/unit/TransactionUtilsTest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index 42c0534c16ed..27b0a2d90605 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -144,6 +144,7 @@ describe('TransactionUtils', () => { foreignTaxDefault: 'id_TAX_RATE_2', taxes: { ...CONST.DEFAULT_TAX.taxes, + // eslint-disable-next-line @typescript-eslint/naming-convention id_TAX_RATE_2: { name: 'Tax rate 2', value: '10%', From 296948e49625994549b0461f0857920bbb159111 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 26 Dec 2024 19:45:26 +0700 Subject: [PATCH 5/6] update comment --- tests/unit/TransactionUtilsTest.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index 27b0a2d90605..0ed8d0f0461b 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -104,9 +104,9 @@ describe('TransactionUtils', () => { taxRates: CONST.DEFAULT_TAX, rules: {expenseRules: createCategoryTaxExpenseRules(category, 'id_TAX_RATE_1')}, }; + const transaction = generateTransaction(); // When retrieving the tax from the associated category - const transaction = generateTransaction(); const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, fakePolicy); // Then it should return the associated tax code and amount @@ -123,9 +123,9 @@ describe('TransactionUtils', () => { taxRates: CONST.DEFAULT_TAX, rules: {expenseRules: createCategoryTaxExpenseRules(ruleCategory, 'id_TAX_RATE_1')}, }; + const transaction = generateTransaction(); // When retrieving the tax from a category that is not associated with the tax expense rules - const transaction = generateTransaction(); const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(selectedCategory, transaction, fakePolicy); // Then it should return the default tax code and amount @@ -134,7 +134,7 @@ describe('TransactionUtils', () => { }); it("should return the foreign default tax when the category doesn't match the tax expense rules and using a foreign currency", () => { - // Given a policy with tax expense rules associated with a category + // Given a policy with tax expense rules associated with a category and a transaction with a foreign currency const ruleCategory = 'Advertising'; const selectedCategory = 'Benefits'; const fakePolicy: Policy = { @@ -154,9 +154,9 @@ describe('TransactionUtils', () => { outputCurrency: 'IDR', rules: {expenseRules: createCategoryTaxExpenseRules(ruleCategory, 'id_TAX_RATE_1')}, }; + const transaction = generateTransaction(); // When retrieving the tax from a category that is not associated with the tax expense rules - const transaction = generateTransaction(); const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(selectedCategory, transaction, fakePolicy); // Then it should return the default tax code and amount @@ -173,12 +173,12 @@ describe('TransactionUtils', () => { taxRates: CONST.DEFAULT_TAX, rules: {expenseRules: createCategoryTaxExpenseRules(category, 'id_TAX_RATE_1')}, }; - - // When retrieving the tax from the associated category const transaction: Transaction = { ...generateTransaction(), iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE, }; + + // When retrieving the tax from the associated category const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, fakePolicy); // Then it should return undefined for both the tax code and the tax amount @@ -194,9 +194,9 @@ describe('TransactionUtils', () => { taxRates: CONST.DEFAULT_TAX, rules: {}, }; + const transaction = generateTransaction(); // When retrieving the tax from a category - const transaction = generateTransaction(); const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, fakePolicy); // Then it should return undefined for both the tax code and the tax amount From c35bddc90ed186898f32f2700535ac5a678b9837 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 26 Dec 2024 20:59:49 +0700 Subject: [PATCH 6/6] fix system message shows tax updates too when changing category with tax rules --- src/libs/TransactionUtils/index.ts | 5 +++++ src/libs/actions/IOU.ts | 19 ++----------------- tests/actions/IOUTest.ts | 23 ++++++++++++++++++++++- tests/unit/TransactionUtilsTest.ts | 27 +++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 379f68009d3c..e40facb21c48 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -376,6 +376,11 @@ function getUpdatedTransaction({ if (Object.hasOwn(transactionChanges, 'category') && typeof transactionChanges.category === 'string') { updatedTransaction.category = transactionChanges.category; + const {categoryTaxCode, categoryTaxAmount} = getCategoryTaxCodeAndAmount(transactionChanges.category, transaction, policy); + if (categoryTaxCode && categoryTaxAmount !== undefined) { + updatedTransaction.taxCode = categoryTaxCode; + updatedTransaction.taxAmount = categoryTaxAmount; + } } if (Object.hasOwn(transactionChanges, 'tag') && typeof transactionChanges.tag === 'string') { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c0906f77850a..78546c6636f3 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3441,15 +3441,8 @@ function updateMoneyRequestCategory( policyTagList: OnyxEntry, policyCategories: OnyxEntry, ) { - const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; - const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(category, transaction, policy); const transactionChanges: TransactionChanges = { category, - ...(categoryTaxCode && - categoryTaxAmount !== undefined && { - taxCode: categoryTaxCode, - taxAmount: categoryTaxAmount, - }), }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); @@ -5377,27 +5370,19 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA } function setDraftSplitTransaction(transactionID: string, transactionChanges: TransactionChanges = {}, policy?: OnyxEntry) { - const newTransactionChanges = {...transactionChanges}; let draftSplitTransaction = allDraftSplitTransactions[`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`]; if (!draftSplitTransaction) { draftSplitTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; } - if (transactionChanges.category) { - const {categoryTaxCode, categoryTaxAmount} = TransactionUtils.getCategoryTaxCodeAndAmount(transactionChanges.category, draftSplitTransaction, policy); - if (categoryTaxCode && categoryTaxAmount !== undefined) { - newTransactionChanges.taxCode = categoryTaxCode; - newTransactionChanges.taxAmount = categoryTaxAmount; - } - } - const updatedTransaction = draftSplitTransaction ? TransactionUtils.getUpdatedTransaction({ transaction: draftSplitTransaction, - transactionChanges: newTransactionChanges, + transactionChanges, isFromExpenseReport: false, shouldUpdateReceiptState: false, + policy, }) : null; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index b0f42838df72..59d3ec9f2f0c 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -3635,6 +3635,7 @@ describe('actions/IOU', () => { // Given a policy with tax expense rules associated with category const transactionID = '1'; const policyID = '2'; + const transactionThreadReportID = '3'; const category = 'Advertising'; const taxCode = 'id_TAX_EXEMPT'; const ruleTaxCode = 'id_TAX_RATE_1'; @@ -3649,9 +3650,10 @@ describe('actions/IOU', () => { amount: 100, }); await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, fakePolicy); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {reportID: transactionThreadReportID}); // When updating a money request category - IOU.updateMoneyRequestCategory(transactionID, '3', category, fakePolicy, undefined, undefined); + IOU.updateMoneyRequestCategory(transactionID, transactionThreadReportID, category, fakePolicy, undefined, undefined); await waitForBatchedUpdates(); @@ -3667,6 +3669,25 @@ describe('actions/IOU', () => { }, }); }); + + // But the original message should only contains the old and new category data + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + callback: (reportActions) => { + Onyx.disconnect(connection); + const reportAction = Object.values(reportActions ?? {}).at(0); + if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE)) { + const originalMessage = ReportActionsUtils.getOriginalMessage(reportAction); + expect(originalMessage?.oldCategory).toBe(''); + expect(originalMessage?.category).toBe(category); + expect(originalMessage?.oldTaxRate).toBeUndefined(); + expect(originalMessage?.oldTaxAmount).toBeUndefined(); + resolve(); + } + }, + }); + }); }); describe('should not update the tax', () => { diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index 0ed8d0f0461b..558d09e6a0fa 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -205,4 +205,31 @@ describe('TransactionUtils', () => { }); }); }); + + describe('getUpdatedTransaction', () => { + it('should return updated category and tax when updating category with a category tax rules', () => { + // Given a policy with tax expense rules associated with a category + const category = 'Advertising'; + const taxCode = 'id_TAX_RATE_1'; + const fakePolicy: Policy = { + ...createRandomPolicy(0), + taxRates: CONST.DEFAULT_TAX, + rules: {expenseRules: createCategoryTaxExpenseRules(category, taxCode)}, + }; + const transaction = generateTransaction(); + + // When updating the transaction category to the category associated with the rule + const updatedTransaction = TransactionUtils.getUpdatedTransaction({ + transaction, + isFromExpenseReport: true, + policy: fakePolicy, + transactionChanges: {category}, + }); + + // Then the updated transaction should contain the tax from the matched rule + expect(updatedTransaction.category).toBe(category); + expect(updatedTransaction.taxCode).toBe(taxCode); + expect(updatedTransaction.taxAmount).toBe(5); + }); + }); });