mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-09 16:21:19 +00:00
Merge pull request #51839 from frappe/mergify/bp/version-16-hotfix/pr-51787
fix: recalculate taxes when item tax template changes after discount (backport #51787)
This commit is contained in:
@@ -2951,6 +2951,60 @@ class TestSalesInvoice(ERPNextTestSuite):
|
|||||||
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
|
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)
|
self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map)
|
||||||
|
|
||||||
|
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})
|
@IntegrationTestCase.change_settings("Selling Settings", {"enable_discount_accounting": 1})
|
||||||
def test_sales_invoice_with_discount_accounting_enabled(self):
|
def test_sales_invoice_with_discount_accounting_enabled(self):
|
||||||
discount_account = create_account(
|
discount_account = create_account(
|
||||||
|
|||||||
@@ -39,17 +39,23 @@ class calculate_taxes_and_totals:
|
|||||||
items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
|
items = list(filter(lambda item: not item.get("is_alternative"), self.doc.get("items")))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def calculate(self):
|
def calculate(self, ignore_tax_template_validation=False):
|
||||||
if not len(self.doc.items):
|
if not len(self.doc.items):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.discount_amount_applied = False
|
self.discount_amount_applied = False
|
||||||
|
self.need_recomputation = False
|
||||||
|
self.ignore_tax_template_validation = ignore_tax_template_validation
|
||||||
|
|
||||||
self._calculate()
|
self._calculate()
|
||||||
|
|
||||||
if self.doc.meta.get_field("discount_amount"):
|
if self.doc.meta.get_field("discount_amount"):
|
||||||
self.set_discount_amount()
|
self.set_discount_amount()
|
||||||
self.apply_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
|
# 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"):
|
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
|
self.doc.grand_total -= self.doc.discount_amount
|
||||||
@@ -79,6 +85,9 @@ class calculate_taxes_and_totals:
|
|||||||
self.calculate_total_net_weight()
|
self.calculate_total_net_weight()
|
||||||
|
|
||||||
def validate_item_tax_template(self):
|
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"):
|
if self.doc.get("is_return") and self.doc.get("return_against"):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -122,6 +131,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):
|
def update_item_tax_map(self):
|
||||||
for item in self.doc.items:
|
for item in self.doc.items:
|
||||||
item.item_tax_rate = get_item_tax_map(
|
item.item_tax_rate = get_item_tax_map(
|
||||||
|
|||||||
Reference in New Issue
Block a user