Merge pull request #23001 from nabinhait/inclusive-tax-based-on-item-quantity

fix: Calculate taxes if tax is based on item quantity and inclusive on item price
This commit is contained in:
Deepesh Garg 2020-08-11 22:05:02 +05:30 committed by GitHub
commit 1d5ab618d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 23 deletions

View File

@ -206,10 +206,19 @@ class TestSalesInvoice(unittest.TestCase):
"rate": 14, "rate": 14,
'included_in_print_rate': 1 '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() si.insert()
# with inclusive tax # 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) self.assertEqual(si.grand_total, 5000)
si.reload() si.reload()
@ -222,8 +231,8 @@ class TestSalesInvoice(unittest.TestCase):
si.save() si.save()
# with inclusive tax and additional discount # with inclusive tax and additional discount
self.assertEqual(si.net_total, 4285.96) self.assertEqual(si.net_total, 3847.37)
self.assertEqual(si.grand_total, 4885.99) self.assertEqual(si.grand_total, 4886)
si.reload() si.reload()
@ -235,7 +244,7 @@ class TestSalesInvoice(unittest.TestCase):
si.save() si.save()
# with inclusive tax and additional discount # 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) self.assertEqual(si.grand_total, 4900.00)
def test_sales_invoice_discount_amount(self): def test_sales_invoice_discount_amount(self):

View File

@ -985,7 +985,7 @@ def validate_inclusive_tax(tax, doc):
# all rows about the reffered tax should be inclusive # all rows about the reffered tax should be inclusive
_on_previous_row_error("1 - %d" % (tax.row_id,)) _on_previous_row_error("1 - %d" % (tax.row_id,))
elif tax.get("category") == "Valuation": 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): def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None):

View File

@ -161,8 +161,9 @@ class calculate_taxes_and_totals(object):
for item in self.doc.get("items"): for item in self.doc.get("items"):
item_tax_map = self._load_item_tax_rate(item.item_tax_rate) item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
cumulated_tax_fraction = 0 cumulated_tax_fraction = 0
total_inclusive_tax_amount_per_qty = 0
for i, tax in enumerate(self.doc.get("taxes")): 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: if i==0:
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item 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 + tax.tax_fraction_for_current_item
cumulated_tax_fraction += 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: if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction)) 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.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
item.discount_percentage = flt(item.discount_percentage, item.discount_percentage = flt(item.discount_percentage,
item.precision("discount_percentage")) item.precision("discount_percentage"))
@ -190,6 +194,7 @@ class calculate_taxes_and_totals(object):
from tax inclusive amount from tax inclusive amount
""" """
current_tax_fraction = 0 current_tax_fraction = 0
inclusive_tax_amount_per_qty = 0
if cint(tax.included_in_print_rate): if cint(tax.included_in_print_rate):
tax_rate = self._get_tax_rate(tax, item_tax_map) 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": elif tax.charge_type == "On Previous Row Total":
current_tax_fraction = (tax_rate / 100.0) * \ current_tax_fraction = (tax_rate / 100.0) * \
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item 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): if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
current_tax_fraction *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0 current_tax_fraction *= -1.0
return current_tax_fraction 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): def _get_tax_rate(self, tax, item_tax_map):
if tax.account_head in 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) * \ current_tax_amount = (tax_rate / 100.0) * \
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
elif tax.charge_type == "On Item Quantity": 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) 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 = {} actual_taxes_dict = {}
for tax in self.doc.get("taxes"): 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) tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
actual_taxes_dict.setdefault(tax.idx, tax_amount) actual_taxes_dict.setdefault(tax.idx, tax_amount)
elif tax.row_id in actual_taxes_dict: elif tax.row_id in actual_taxes_dict:

View File

@ -163,9 +163,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
$.each(me.frm.doc["items"] || [], function(n, item) { $.each(me.frm.doc["items"] || [], function(n, item) {
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
var cumulated_tax_fraction = 0.0; var cumulated_tax_fraction = 0.0;
var total_inclusive_tax_amount_per_qty = 0;
$.each(me.frm.doc["taxes"] || [], function(i, tax) { $.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) { if(i==0) {
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item; 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; 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) { if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) {
item.net_amount = flt(item.amount / (1 + 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; 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"]); 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 // Get tax fraction for calculating tax exclusive amount
// from tax inclusive amount // from tax inclusive amount
var current_tax_fraction = 0.0; var current_tax_fraction = 0.0;
var inclusive_tax_amount_per_qty = 0;
if(cint(tax.included_in_print_rate)) { if(cint(tax.included_in_print_rate)) {
var tax_rate = this._get_tax_rate(tax, item_tax_map); 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") { } else if(tax.charge_type == "On Previous Row Total") {
current_tax_fraction = (tax_rate / 100.0) * current_tax_fraction = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item; 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) { if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
current_tax_fraction *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; 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) { _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") { } else if(tax.charge_type == "On Previous Row Total") {
current_tax_amount = (tax_rate / 100.0) * current_tax_amount = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item; 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); this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
return current_tax_amount; return current_tax_amount;
@ -573,7 +582,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
var actual_taxes_dict = {}; var actual_taxes_dict = {};
$.each(this.frm.doc["taxes"] || [], function(i, tax) { $.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; var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount;
tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
actual_taxes_dict[tax.idx] = tax_amount; 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) { $.each(actual_taxes_dict, function(key, value) {
if (value) total_actual_tax += value; if (value) total_actual_tax += value;
}); });
return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total")); return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total"));
} }
}, },