From 19ea7218d9d6191c739c9ed2be4857228e1cd266 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 11 Aug 2020 20:34:57 +0530 Subject: [PATCH] fix: Calculate taxes if tax is based on item quantity and inclusive on item price --- .../sales_invoice/test_sales_invoice.py | 17 ++++++++--- erpnext/controllers/accounts_controller.py | 2 +- erpnext/controllers/taxes_and_totals.py | 26 ++++++++++++----- .../public/js/controllers/taxes_and_totals.js | 29 ++++++++++++------- 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 964566a17e..9660c9570e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -206,10 +206,19 @@ class TestSalesInvoice(unittest.TestCase): "rate": 14, 'included_in_print_rate': 1 }) + si.append("taxes", { + "charge_type": "On Item Quantity", + "account_head": "_Test Account Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "CESS", + "rate": 5, + 'included_in_print_rate': 1 + }) si.insert() # with inclusive tax - self.assertEqual(si.net_total, 4385.96) + self.assertEqual(si.items[0].net_amount, 3947.368421052631) + self.assertEqual(si.net_total, 3947.37) self.assertEqual(si.grand_total, 5000) si.reload() @@ -222,8 +231,8 @@ class TestSalesInvoice(unittest.TestCase): si.save() # with inclusive tax and additional discount - self.assertEqual(si.net_total, 4285.96) - self.assertEqual(si.grand_total, 4885.99) + self.assertEqual(si.net_total, 3847.37) + self.assertEqual(si.grand_total, 4886) si.reload() @@ -235,7 +244,7 @@ class TestSalesInvoice(unittest.TestCase): si.save() # with inclusive tax and additional discount - self.assertEqual(si.net_total, 4298.25) + self.assertEqual(si.net_total, 3859.65) self.assertEqual(si.grand_total, 4900.00) def test_sales_invoice_discount_amount(self): diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 66b5f3035d..3091193b8d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -985,7 +985,7 @@ def validate_inclusive_tax(tax, doc): # all rows about the reffered tax should be inclusive _on_previous_row_error("1 - %d" % (tax.row_id,)) elif tax.get("category") == "Valuation": - frappe.throw(_("Valuation type charges can not marked as Inclusive")) + frappe.throw(_("Valuation type charges can not be marked as Inclusive")) def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 572e1ca239..8f86dce436 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -161,8 +161,9 @@ class calculate_taxes_and_totals(object): for item in self.doc.get("items"): item_tax_map = self._load_item_tax_rate(item.item_tax_rate) cumulated_tax_fraction = 0 + total_inclusive_tax_amount_per_qty = 0 for i, tax in enumerate(self.doc.get("taxes")): - tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map) + tax.tax_fraction_for_current_item, inclusive_tax_amount_per_qty = self.get_current_tax_fraction(tax, item_tax_map) if i==0: tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item @@ -172,9 +173,12 @@ class calculate_taxes_and_totals(object): + tax.tax_fraction_for_current_item cumulated_tax_fraction += tax.tax_fraction_for_current_item + total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.stock_qty) - if cumulated_tax_fraction and not self.discount_amount_applied and item.qty: - item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction)) + if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty): + amount = flt(item.amount) - total_inclusive_tax_amount_per_qty + + item.net_amount = flt(amount / (1 + cumulated_tax_fraction)) item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate")) item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage")) @@ -190,6 +194,7 @@ class calculate_taxes_and_totals(object): from tax inclusive amount """ current_tax_fraction = 0 + inclusive_tax_amount_per_qty = 0 if cint(tax.included_in_print_rate): tax_rate = self._get_tax_rate(tax, item_tax_map) @@ -204,10 +209,15 @@ class calculate_taxes_and_totals(object): elif tax.charge_type == "On Previous Row Total": current_tax_fraction = (tax_rate / 100.0) * \ self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item + + elif tax.charge_type == "On Item Quantity": + inclusive_tax_amount_per_qty = flt(tax_rate) - if getattr(tax, "add_deduct_tax", None): - current_tax_fraction *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0 - return current_tax_fraction + if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct": + current_tax_fraction *= -1.0 + inclusive_tax_amount_per_qty *= -1.0 + + return current_tax_fraction, inclusive_tax_amount_per_qty def _get_tax_rate(self, tax, item_tax_map): if tax.account_head in item_tax_map: @@ -321,7 +331,7 @@ class calculate_taxes_and_totals(object): current_tax_amount = (tax_rate / 100.0) * \ self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item elif tax.charge_type == "On Item Quantity": - current_tax_amount = tax_rate * item.stock_qty + current_tax_amount = tax_rate * item.qty self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount) @@ -472,7 +482,7 @@ class calculate_taxes_and_totals(object): actual_taxes_dict = {} for tax in self.doc.get("taxes"): - if tax.charge_type == "Actual": + if tax.charge_type in ["Actual", "On Item Quantity"]: tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax) actual_taxes_dict.setdefault(tax.idx, tax_amount) elif tax.row_id in actual_taxes_dict: diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 405a33c72a..c0d2e6f3ce 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -163,9 +163,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ $.each(me.frm.doc["items"] || [], function(n, item) { var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); var cumulated_tax_fraction = 0.0; - + var total_inclusive_tax_amount_per_qty = 0; $.each(me.frm.doc["taxes"] || [], function(i, tax) { - tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map); + var current_tax_fraction = me.get_current_tax_fraction(tax, item_tax_map); + tax.tax_fraction_for_current_item = current_tax_fraction[0]; + var inclusive_tax_amount_per_qty = current_tax_fraction[1]; if(i==0) { tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item; @@ -176,10 +178,12 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } cumulated_tax_fraction += tax.tax_fraction_for_current_item; + total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty); }); - if(cumulated_tax_fraction && !me.discount_amount_applied) { - item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction)); + if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) { + var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty; + item.net_amount = flt(amount / (1 + cumulated_tax_fraction)); item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0; me.set_in_company_currency(item, ["net_rate", "net_amount"]); @@ -191,6 +195,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ // Get tax fraction for calculating tax exclusive amount // from tax inclusive amount var current_tax_fraction = 0.0; + var inclusive_tax_amount_per_qty = 0; if(cint(tax.included_in_print_rate)) { var tax_rate = this._get_tax_rate(tax, item_tax_map); @@ -205,13 +210,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else if(tax.charge_type == "On Previous Row Total") { current_tax_fraction = (tax_rate / 100.0) * this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item; + } else if (tax.charge_type == "On Item Quantity") { + inclusive_tax_amount_per_qty = flt(tax_rate); } } - if(tax.add_deduct_tax) { - current_tax_fraction *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; + if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") { + current_tax_fraction *= -1; + inclusive_tax_amount_per_qty *= -1 } - return current_tax_fraction; + return [current_tax_fraction, inclusive_tax_amount_per_qty]; }, _get_tax_rate: function(tax, item_tax_map) { @@ -360,8 +368,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else if(tax.charge_type == "On Previous Row Total") { current_tax_amount = (tax_rate / 100.0) * this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item; + } else if (tax.charge_type == "On Item Quantity") { + current_tax_amount = tax_rate * item.qty; } - this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount); return current_tax_amount; @@ -573,7 +582,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var actual_taxes_dict = {}; $.each(this.frm.doc["taxes"] || [], function(i, tax) { - if (tax.charge_type == "Actual") { + if (in_list(["Actual", "On Item Quantity"], tax.charge_type)) { var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount; tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; actual_taxes_dict[tax.idx] = tax_amount; @@ -586,7 +595,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ $.each(actual_taxes_dict, function(key, value) { if (value) total_actual_tax += value; }); - + return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total")); } },