From 1d64373c26838e8442abab7886762b78664fb743 Mon Sep 17 00:00:00 2001 From: Lakshit Jain Date: Mon, 19 Jan 2026 12:30:29 +0530 Subject: [PATCH 1/2] Merge pull request #51787 from ljain112/fix-taxes-disc fix: recalculate taxes when item tax template changes after discount (cherry picked from commit f00aeec9b47878761ccc225c504a0412f6488a0b) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../sales_invoice/test_sales_invoice.py | 58 +++++++++++++++++++ erpnext/controllers/taxes_and_totals.py | 15 ++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index aeb266b6d87..b78145be995 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2974,7 +2974,65 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map) +<<<<<<< HEAD @change_settings("Selling Settings", {"enable_discount_accounting": 1}) +======= + def test_item_tax_template_change_with_grand_total_discount(self): + """ + Test that when item tax template changes due to discount on Grand Total, + the tax calculations are consistent. + """ + item = create_item("Test Item With Multiple Tax Templates") + + item.set("taxes", []) + item.append( + "taxes", + { + "item_tax_template": "_Test Account Excise Duty @ 10 - _TC", + "minimum_net_rate": 0, + "maximum_net_rate": 500, + }, + ) + + item.append( + "taxes", + { + "item_tax_template": "_Test Account Excise Duty @ 12 - _TC", + "minimum_net_rate": 501, + "maximum_net_rate": 1000, + }, + ) + + item.save() + + si = create_sales_invoice(item=item.name, rate=700, do_not_save=True) + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Excise Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "rate": 0, + }, + ) + si.insert() + + self.assertEqual(si.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC") + + si.apply_discount_on = "Grand Total" + si.discount_amount = 300 + si.save() + + # Verify template changed to 10% + self.assertEqual(si.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") + self.assertEqual(si.taxes[0].tax_amount, 70) # 10% of 700 + self.assertEqual(si.grand_total, 470) # 700 + 70 - 300 + + si.submit() + + @IntegrationTestCase.change_settings("Selling Settings", {"enable_discount_accounting": 1}) +>>>>>>> f00aeec9b4 (Merge pull request #51787 from ljain112/fix-taxes-disc) def test_sales_invoice_with_discount_accounting_enabled(self): discount_account = create_account( account_name="Discount Account", diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 571f9ad4b14..1bd55786f1f 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -46,17 +46,23 @@ class calculate_taxes_and_totals: items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items"))) return items - def calculate(self): + def calculate(self, ignore_tax_template_validation=False): if not len(self.doc.items): return self.discount_amount_applied = False + self.need_recomputation = False + self.ignore_tax_template_validation = ignore_tax_template_validation + self._calculate() if self.doc.meta.get_field("discount_amount"): self.set_discount_amount() self.apply_discount_amount() + if not ignore_tax_template_validation and self.need_recomputation: + return self.calculate(ignore_tax_template_validation=True) + # Update grand total as per cash and non trade discount if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"): self.doc.grand_total -= self.doc.discount_amount @@ -100,6 +106,9 @@ class calculate_taxes_and_totals: self.doc.base_tax_withholding_net_total = sum_base_net_amount def validate_item_tax_template(self): + if self.ignore_tax_template_validation: + return + if self.doc.get("is_return") and self.doc.get("return_against"): return @@ -141,6 +150,10 @@ class calculate_taxes_and_totals: ) ) + # For correct tax_amount calculation re-computation is required + if self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total": + self.need_recomputation = True + def update_item_tax_map(self): for item in self.doc.items: item.item_tax_rate = get_item_tax_map( From 2bf75a2c24d8eceaed2c1d3280de560a519f4578 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 19 Jan 2026 13:41:40 +0530 Subject: [PATCH 2/2] chore: resolve conflicts --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index b78145be995..bd8af98b16f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2974,9 +2974,6 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map) -<<<<<<< HEAD - @change_settings("Selling Settings", {"enable_discount_accounting": 1}) -======= def test_item_tax_template_change_with_grand_total_discount(self): """ Test that when item tax template changes due to discount on Grand Total, @@ -3031,8 +3028,7 @@ class TestSalesInvoice(FrappeTestCase): si.submit() - @IntegrationTestCase.change_settings("Selling Settings", {"enable_discount_accounting": 1}) ->>>>>>> f00aeec9b4 (Merge pull request #51787 from ljain112/fix-taxes-disc) + @change_settings("Selling Settings", {"enable_discount_accounting": 1}) def test_sales_invoice_with_discount_accounting_enabled(self): discount_account = create_account( account_name="Discount Account",