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

Don't apply the category tax rules for distance request #54579

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
10 changes: 8 additions & 2 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -1258,11 +1263,12 @@ function buildTransactionsMergeParams(reviewDuplicates: OnyxEntry<ReviewDuplicat

function getCategoryTaxCodeAndAmount(category: string, transaction: OnyxEntry<Transaction>, policy: OnyxEntry<Policy>) {
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};
}

const categoryTaxCode = getCategoryDefaultTaxRate(taxRules, category, policy?.taxRates?.defaultExternalID);
const defaultTaxCode = getDefaultTaxCode(policy, transaction, getCurrency(transaction));
const categoryTaxCode = getCategoryDefaultTaxRate(taxRules, category, defaultTaxCode);
const categoryTaxPercentage = getTaxValue(policy, transaction, categoryTaxCode ?? '');
let categoryTaxAmount;

Expand Down
19 changes: 2 additions & 17 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3441,15 +3441,8 @@ function updateMoneyRequestCategory(
policyTagList: OnyxEntry<OnyxTypes.PolicyTagLists>,
policyCategories: OnyxEntry<OnyxTypes.PolicyCategories>,
) {
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);
Expand Down Expand Up @@ -5377,27 +5370,19 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA
}

function setDraftSplitTransaction(transactionID: string, transactionChanges: TransactionChanges = {}, policy?: OnyxEntry<OnyxTypes.Policy>) {
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;

Expand Down
224 changes: 167 additions & 57 deletions tests/actions/IOUTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>((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<void>((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<void>((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();
},
});
});
});
});
Expand Down Expand Up @@ -3593,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';
Expand All @@ -3607,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();

Expand All @@ -3625,37 +3669,103 @@ 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);

// 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
// But the original message should only contains the old and new category data
await new Promise<void>((resolve) => {
const connection = Onyx.connect({
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
callback: (transaction) => {
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`,
callback: (reportActions) => {
Onyx.disconnect(connection);
expect(transaction?.taxCode).toBeUndefined();
expect(transaction?.taxAmount).toBeUndefined();
resolve();
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', () => {
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 a 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<void>((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<void>((resolve) => {
const connection = Onyx.connect({
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
callback: (transaction) => {
Onyx.disconnect(connection);
expect(transaction?.taxCode).toBeUndefined();
expect(transaction?.taxAmount).toBeUndefined();
resolve();
},
});
});
});
});
});
Expand Down Expand Up @@ -3700,7 +3810,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';
Expand Down
Loading
Loading