From cf49c32b942734c43539fbbf0b6508d6805d5258 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 # erpnext/controllers/taxes_and_totals.py --- .../sales_invoice/test_sales_invoice.py | 58 +++++++++++++++++++ erpnext/controllers/taxes_and_totals.py | 18 ++++++ 2 files changed, 76 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dd8acec196e..7e5917ad742 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2828,7 +2828,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 ced62b71cf9..f871e7d1b3b 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -42,17 +42,28 @@ class calculate_taxes_and_totals: items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items"))) return items +<<<<<<< HEAD def calculate(self): if not len(self._items): +======= + def calculate(self, ignore_tax_template_validation=False): + if not len(self.doc.items): +>>>>>>> f00aeec9b4 (Merge pull request #51787 from ljain112/fix-taxes-disc) 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 @@ -96,6 +107,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 @@ -136,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 b4dc1be5ed18dd0d07c748b30ee5a96a0ab7302d Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 19 Jan 2026 13:45:11 +0530 Subject: [PATCH 2/2] chore: resolve conflicts --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 6 +----- erpnext/controllers/taxes_and_totals.py | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 7e5917ad742..535046aad1b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2828,9 +2828,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, @@ -2885,8 +2882,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", diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index f871e7d1b3b..102622adf7a 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -42,13 +42,8 @@ class calculate_taxes_and_totals: items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items"))) return items -<<<<<<< HEAD - def calculate(self): - if not len(self._items): -======= def calculate(self, ignore_tax_template_validation=False): - if not len(self.doc.items): ->>>>>>> f00aeec9b4 (Merge pull request #51787 from ljain112/fix-taxes-disc) + if not len(self._items): return self.discount_amount_applied = False