diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 90b36e06883..62d9a1eaff3 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -167,15 +167,15 @@ class TestSalesInvoice(unittest.TestCase): "_Test Account S&H Education Cess - _TC": [1.5, 1619.5, 0.03, 32.39], "_Test Account CST - _TC": [32.5, 1652, 0.65, 33.04], "_Test Account VAT - _TC": [156.5, 1808.5, 3.13, 36.17], - "_Test Account Discount - _TC": [-180.5, 1628, -3.61, 32.56] + "_Test Account Discount - _TC": [-181.0, 1627.5, -3.62, 32.55] } for d in si.get("taxes"): for i, k in enumerate(expected_values["keys"]): self.assertEquals(d.get(k), expected_values[d.account_head][i]) - self.assertEquals(si.base_grand_total, 1628) - self.assertEquals(si.grand_total, 32.56) + self.assertEquals(si.base_grand_total, 1627.5) + self.assertEquals(si.grand_total, 32.55) def test_sales_invoice_with_discount_and_inclusive_tax(self): si = create_sales_invoice(qty=100, rate=50, do_not_save=True) @@ -235,21 +235,29 @@ class TestSalesInvoice(unittest.TestCase): "item_code": "_Test Item Home Desktop 100", "price_list_rate": 62.5, "discount_percentage": 0, - "rate": 62.5, "amount": 625, + "rate": 62.5, + "amount": 625, "base_price_list_rate": 62.5, - "base_rate": 62.5, "base_amount": 625, - "net_rate": 46.54, "net_amount": 465.37, - "base_net_rate": 46.54, "base_net_amount": 465.37 + "base_rate": 62.5, + "base_amount": 625, + "net_rate": 46.54, + "net_amount": 465.37, + "base_net_rate": 46.54, + "base_net_amount": 465.37 }, { "item_code": "_Test Item Home Desktop 200", "price_list_rate": 190.66, "discount_percentage": 0, - "rate": 190.66, "amount": 953.3, + "rate": 190.66, + "amount": 953.3, "base_price_list_rate": 190.66, - "base_rate": 190.66, "base_amount": 953.3, - "net_rate": 139.62, "net_amount": 698.08, - "base_net_rate": 139.62, "base_net_amount": 698.08 + "base_rate": 190.66, + "base_amount": 953.3, + "net_rate": 139.62, + "net_amount": 698.08, + "base_net_rate": 139.62, + "base_net_amount": 698.08 } ] @@ -270,13 +278,13 @@ class TestSalesInvoice(unittest.TestCase): "keys": ["tax_amount", "tax_amount_after_discount_amount", "total"], "_Test Account Excise Duty - _TC": [140, 130.31, 1293.76], "_Test Account Education Cess - _TC": [2.8, 2.61, 1296.37], - "_Test Account S&H Education Cess - _TC": [1.4, 1.31, 1297.68], - "_Test Account CST - _TC": [27.88, 25.96, 1323.64], - "_Test Account VAT - _TC": [156.25, 145.43, 1469.07], - "_Test Account Customs Duty - _TC": [125, 116.35, 1585.42], - "_Test Account Shipping Charges - _TC": [100, 100, 1685.42], - "_Test Account Discount - _TC": [-180.33, -168.54, 1516.88], - "_Test Account Service Tax - _TC": [-18.03, -16.88, 1500] + "_Test Account S&H Education Cess - _TC": [1.4, 1.30, 1297.67], + "_Test Account CST - _TC": [27.88, 25.95, 1323.62], + "_Test Account VAT - _TC": [156.25, 145.43, 1469.05], + "_Test Account Customs Duty - _TC": [125, 116.35, 1585.40], + "_Test Account Shipping Charges - _TC": [100, 100, 1685.40], + "_Test Account Discount - _TC": [-180.33, -168.54, 1516.86], + "_Test Account Service Tax - _TC": [-18.03, -16.86, 1500] } for d in si.get("taxes"): @@ -312,13 +320,13 @@ class TestSalesInvoice(unittest.TestCase): [test_records[3]["items"][0]["income_account"], 0.0, 1163.45], [test_records[3]["taxes"][0]["account_head"], 0.0, 130.31], [test_records[3]["taxes"][1]["account_head"], 0.0, 2.61], - [test_records[3]["taxes"][2]["account_head"], 0.0, 1.31], - [test_records[3]["taxes"][3]["account_head"], 0.0, 25.96], + [test_records[3]["taxes"][2]["account_head"], 0.0, 1.30], + [test_records[3]["taxes"][3]["account_head"], 0.0, 25.95], [test_records[3]["taxes"][4]["account_head"], 0.0, 145.43], [test_records[3]["taxes"][5]["account_head"], 0.0, 116.35], [test_records[3]["taxes"][6]["account_head"], 0.0, 100], [test_records[3]["taxes"][7]["account_head"], 168.54, 0.0], - ["_Test Account Service Tax - _TC", 16.88, 0.0], + ["_Test Account Service Tax - _TC", 16.86, 0.0] ]) for gle in gl_entries: @@ -334,6 +342,61 @@ class TestSalesInvoice(unittest.TestCase): self.assertFalse(gle) + def test_tax_calculation_with_multiple_items(self): + si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True) + item_row = si.get("items")[0] + for qty in (54, 288, 144, 430): + item_row_copy = copy.deepcopy(item_row) + item_row_copy.qty = qty + si.append("items", item_row_copy) + + si.append("taxes", { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "rate": 19 + }) + si.insert() + + self.assertEquals(si.net_total, 4600) + + self.assertEquals(si.get("taxes")[0].tax_amount, 874.0) + self.assertEquals(si.get("taxes")[0].total, 5474.0) + + self.assertEquals(si.grand_total, 5474.0) + + def test_tax_calculation_with_multiple_items_and_discount(self): + si = create_sales_invoice(qty=1, rate=75, do_not_save=True) + item_row = si.get("items")[0] + for rate in (500, 200, 100, 50, 50): + item_row_copy = copy.deepcopy(item_row) + item_row_copy.price_list_rate = rate + item_row_copy.rate = rate + si.append("items", item_row_copy) + + si.apply_discount_on = "Net Total" + si.discount_amount = 75.0 + + si.append("taxes", { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "rate": 24 + }) + si.insert() + + self.assertEquals(si.total, 975) + self.assertEquals(si.net_total, 900) + + self.assertEquals(si.get("taxes")[0].tax_amount, 216.0) + self.assertEquals(si.get("taxes")[0].total, 1116.0) + + self.assertEquals(si.grand_total, 1116.0) + def test_inclusive_rate_validations(self): si = frappe.copy_doc(test_records[2]) for i, tax in enumerate(si.get("taxes")): @@ -416,21 +479,29 @@ class TestSalesInvoice(unittest.TestCase): "item_code": "_Test Item Home Desktop 100", "price_list_rate": 55.56, "discount_percentage": 10, - "rate": 50, "amount": 500, + "rate": 50, + "amount": 500, "base_price_list_rate": 2778, - "base_rate": 2500, "base_amount": 25000, - "net_rate": 40, "net_amount": 399.98, - "base_net_rate": 2000, "base_net_amount": 19999 + "base_rate": 2500, + "base_amount": 25000, + "net_rate": 40, + "net_amount": 399.98, + "base_net_rate": 2000, + "base_net_amount": 19999 }, { "item_code": "_Test Item Home Desktop 200", "price_list_rate": 187.5, "discount_percentage": 20, - "rate": 150, "amount": 750, + "rate": 150, + "amount": 750, "base_price_list_rate": 9375, - "base_rate": 7500, "base_amount": 37500, - "net_rate": 118.01, "net_amount": 590.05, - "base_net_rate": 5900.5, "base_net_amount": 29502.5 + "base_rate": 7500, + "base_amount": 37500, + "net_rate": 118.01, + "net_amount": 590.05, + "base_net_rate": 5900.5, + "base_net_amount": 29502.5 } ] @@ -450,22 +521,22 @@ class TestSalesInvoice(unittest.TestCase): # check tax calculation expected_values = { "keys": ["base_tax_amount", "base_total", "tax_amount", "total"], - "_Test Account Excise Duty - _TC": [5540.5, 55042, 110.81, 1100.84], - "_Test Account Education Cess - _TC": [111, 55153, 2.22, 1103.06], - "_Test Account S&H Education Cess - _TC": [55.5, 55208.5, 1.11, 1104.17], - "_Test Account CST - _TC": [1104, 56312.5, 22.08, 1126.25], - "_Test Account VAT - _TC": [6188, 62500.5, 123.76, 1250.01], - "_Test Account Customs Duty - _TC": [4950.5, 67451, 99.01, 1349.02], - "_Test Account Shipping Charges - _TC": [ 100, 67551, 2, 1351.02], - "_Test Account Discount - _TC": [ -6755, 60796, -135.10, 1215.92] + "_Test Account Excise Duty - _TC": [5540.0, 55041.5, 110.80, 1100.83], + "_Test Account Education Cess - _TC": [111, 55152.5, 2.22, 1103.05], + "_Test Account S&H Education Cess - _TC": [55.5, 55208.0, 1.11, 1104.16], + "_Test Account CST - _TC": [1104, 56312.0, 22.08, 1126.24], + "_Test Account VAT - _TC": [6187.5, 62499.5, 123.75, 1249.99], + "_Test Account Customs Duty - _TC": [4950.0, 67449.5, 99.0, 1348.99], + "_Test Account Shipping Charges - _TC": [ 100, 67549.5, 2, 1350.99], + "_Test Account Discount - _TC": [ -6755, 60794.5, -135.10, 1215.89] } for d in si.get("taxes"): for i, k in enumerate(expected_values["keys"]): self.assertEquals(d.get(k), expected_values[d.account_head][i]) - self.assertEquals(si.base_grand_total, 60796) - self.assertEquals(si.grand_total, 1215.92) + self.assertEquals(si.base_grand_total, 60794.5) + self.assertEquals(si.grand_total, 1215.89) def test_outstanding(self): w = self.make() @@ -910,19 +981,20 @@ class TestSalesInvoice(unittest.TestCase): "_Test Account Excise Duty - _TC": [70, 70, 70], "_Test Account Education Cess - _TC": [1.4, 1.4, 1.4], "_Test Account S&H Education Cess - _TC": [.7, 0.7, 0.7], - "_Test Account CST - _TC": [17.2, 17.2, 17.2], + "_Test Account CST - _TC": [17.19, 17.19, 17.19], "_Test Account VAT - _TC": [78.13, 78.13, 78.13], "_Test Account Discount - _TC": [-95.49, -95.49, -95.49] } for d in si.get("taxes"): for i, k in enumerate(expected_values["keys"]): - self.assertEquals(d.get(k), expected_values[d.account_head][i]) + if expected_values.get(d.account_head): + self.assertEquals(d.get(k), expected_values[d.account_head][i]) - self.assertEquals(si.total_taxes_and_charges, 234.44) - self.assertEquals(si.base_grand_total, 859.44) - self.assertEquals(si.grand_total, 859.44) + self.assertEquals(si.total_taxes_and_charges, 234.43) + self.assertEquals(si.base_grand_total, 859.43) + self.assertEquals(si.grand_total, 859.43) def test_multi_currency_gle(self): set_perpetual_inventory(0) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index b6f8b7deef9..97bd771fa20 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -179,7 +179,6 @@ class calculate_taxes_and_totals(object): for n, item in enumerate(self.doc.get("items")): item_tax_map = self._load_item_tax_rate(item.item_tax_rate) - for i, tax in enumerate(self.doc.get("taxes")): # tax_amount represents the amount of tax for the current step current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) @@ -202,36 +201,45 @@ class calculate_taxes_and_totals(object): # set tax after discount tax.tax_amount_after_discount_amount += current_tax_amount - if getattr(tax, "category", None): - # if just for valuation, do not add the tax amount in total - # hence, setting it as 0 for further steps - current_tax_amount = 0.0 if (tax.category == "Valuation") \ - else current_tax_amount + current_tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(current_tax_amount, tax) - current_tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0 - - # Calculate tax.total viz. grand total till that step # note: grand_total_for_current_item contains the contribution of # item's amount, previously applied tax and the current tax on that item if i==0: - tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount, tax.precision("total")) + tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount) else: tax.grand_total_for_current_item = \ - flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount, tax.precision("total")) - - # in tax.total, accumulate grand total of each item - tax.total += tax.grand_total_for_current_item + flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount) # set precision in the last item iteration if n == len(self.doc.get("items")) - 1: self.round_off_totals(tax) + self.set_cumulative_total(i, tax) + + self._set_in_company_currency(tax, + ["total", "tax_amount", "tax_amount_after_discount_amount"]) # adjust Discount Amount loss in last tax iteration if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \ and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total": self.adjust_discount_amount_loss(tax) + def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax): + # if just for valuation, do not add the tax amount in total + # if tax/charges is for deduction, multiply by -1 + if getattr(tax, "category", None): + tax_amount = 0.0 if (tax.category == "Valuation") else tax_amount + tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0 + return tax_amount + def set_cumulative_total(self, row_idx, tax): + tax_amount = tax.tax_amount_after_discount_amount + tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax_amount, tax) + + if row_idx == 0: + tax.total = flt(self.doc.net_total + tax_amount, tax.precision("total")) + else: + tax.total = flt(self.doc.get("taxes")[row_idx-1].total + tax_amount, tax.precision("total")) def get_current_tax_amount(self, item, tax, item_tax_map): tax_rate = self._get_tax_rate(tax, item_tax_map) @@ -251,8 +259,6 @@ 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 - current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount")) - self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount) return current_tax_amount @@ -267,11 +273,9 @@ class calculate_taxes_and_totals(object): tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount, tax.precision("base_tax_amount"))] def round_off_totals(self, tax): - tax.total = flt(tax.total, tax.precision("total")) tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount")) - tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount")) - - self._set_in_company_currency(tax, ["total", "tax_amount", "tax_amount_after_discount_amount"]) + tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, + tax.precision("tax_amount")) def adjust_discount_amount_loss(self, tax): discount_amount_loss = self.doc.grand_total - flt(self.doc.discount_amount) - tax.total diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 83644db26c4..520929560ab 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -5,7 +5,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ setup: function() {}, apply_pricing_rule_on_item: function(item){ if(item.margin_type == "Percentage"){ - item.rate_with_margin = flt(item.price_list_rate) + item.rate_with_margin = flt(item.price_list_rate) + flt(item.price_list_rate) * ( flt(item.margin_rate_or_amount) / 100); } else { item.rate_with_margin = flt(item.price_list_rate) + flt(item.margin_rate_or_amount); @@ -70,12 +70,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else { frappe.throw(repl('%(conversion_rate_label)s' + __(' is mandatory. Maybe Currency Exchange record is not created for ') + - '%(from_currency)s' + __(" to ") + '%(to_currency)s', - { - "conversion_rate_label": conversion_rate_label, - "from_currency": this.frm.doc.currency, - "to_currency": company_currency - })); + '%(from_currency)s' + __(" to ") + '%(to_currency)s', { + "conversion_rate_label": conversion_rate_label, + "from_currency": this.frm.doc.currency, + "to_currency": company_currency + })); } } @@ -101,7 +100,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var me = this; $.each(fields, function(i, f) { doc["base_"+f] = flt(flt(doc[f], precision(f, doc)) * me.frm.doc.conversion_rate, precision("base_" + f, doc)); - }) + }); }, initialize_taxes: function() { @@ -111,14 +110,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ tax.item_wise_tax_detail = {}; var tax_fields = ["total", "tax_amount_after_discount_amount", "tax_amount_for_current_item", "grand_total_for_current_item", - "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"] + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]; if (cstr(tax.charge_type) != "Actual" && !(me.discount_amount_applied && me.frm.doc.apply_discount_on=="Grand Total")) { tax_fields.push("tax_amount"); } - $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0 }); + $.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; }); if (!this.discount_amount_applied && cur_frm) { cur_frm.cscript.validate_taxes_and_charges(tax.doctype, tax.name); @@ -134,7 +133,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var has_inclusive_tax = false; $.each(me.frm.doc["taxes"] || [], function(i, row) { if(cint(row.included_in_print_rate)) has_inclusive_tax = true; - }) + }); if(has_inclusive_tax==false) return; $.each(me.frm.doc["items"] || [], function(n, item) { @@ -223,7 +222,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ $.each(this.frm.doc["items"] || [], function(n, item) { var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); - $.each(me.frm.doc["taxes"] || [], function(i, tax) { // tax_amount represents the amount of tax for the current step var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map); @@ -232,7 +230,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if (tax.charge_type == "Actual") { actual_tax_dict[tax.idx] -= current_tax_amount; if (n == me.frm.doc["items"].length - 1) { - current_tax_amount += actual_tax_dict[tax.idx] + current_tax_amount += actual_tax_dict[tax.idx]; } } @@ -258,23 +256,25 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; } - // Calculate tax.total viz. grand total till that step // note: grand_total_for_current_item contains the contribution of // item's amount, previously applied tax and the current tax on that item if(i==0) { - tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount, precision("total", tax)); + tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount); } else { tax.grand_total_for_current_item = - flt(me.frm.doc["taxes"][i-1].grand_total_for_current_item + current_tax_amount, precision("total", tax)); + flt(me.frm.doc["taxes"][i-1].grand_total_for_current_item + current_tax_amount); } - // in tax.total, accumulate grand total for each item - tax.total += tax.grand_total_for_current_item; - // set precision in the last item iteration if (n == me.frm.doc["items"].length - 1) { me.round_off_totals(tax); + // in tax.total, accumulate grand total for each item + tax.total = me.set_cumulative_total(i, tax); + + this.set_in_company_currency(tax, + ["total", "tax_amount", "tax_amount_after_discount_amount"]); + // adjust Discount Amount loss in last tax iteration if ((i == me.frm.doc["taxes"].length - 1) && me.discount_amount_applied && me.frm.doc.apply_discount_on == "Grand Total" && me.frm.doc.discount_amount) @@ -284,6 +284,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }); }, + set_cumulative_total: function(row_idx, tax) { + if(row_idx==0) { + tax.total = flt(this.frm.doc.net_total + tax.tax_amount_after_discount_amount, + precision("total", tax)); + } else { + tax.total = flt(this.frm.doc.get("taxes")[row_idx-1].total + tax.tax_amount_after_discount_amount, + precision("total", tax)); + } + }, + _load_item_tax_rate: function(item_tax_rate) { return item_tax_rate ? JSON.parse(item_tax_rate) : {}; }, @@ -300,7 +310,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else if(tax.charge_type == "On Net Total") { current_tax_amount = (tax_rate / 100.0) * item.net_amount; - } else if(tax.charge_type == "On Previous Row Amount") { current_tax_amount = (tax_rate / 100.0) * this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount_for_current_item; @@ -310,8 +319,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item; } - current_tax_amount = flt(current_tax_amount, precision("tax_amount", tax)); - this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount); return current_tax_amount; @@ -322,18 +329,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var key = item.item_code || item.item_name; var item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate; if (tax.item_wise_tax_detail && tax.item_wise_tax_detail[key]) - item_wise_tax_amount += tax.item_wise_tax_detail[key][1] - - tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount, precision("base_tax_amount", tax))] + item_wise_tax_amount += tax.item_wise_tax_detail[key][1]; + tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))]; }, round_off_totals: function(tax) { - tax.total = flt(tax.total, precision("total", tax)); tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax)); tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax)); - - this.set_in_company_currency(tax, ["total", "tax_amount", "tax_amount_after_discount_amount"]); }, adjust_discount_amount_loss: function(tax) { @@ -391,7 +394,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ me.frm.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount); } } - }) + }); frappe.model.round_floats_in(this.frm.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]); } @@ -437,7 +440,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if(this.frm.doc["taxes"] && this.frm.doc["taxes"].length) { var temporary_fields = ["tax_amount_for_current_item", "grand_total_for_current_item", - "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"] + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]; if(!frappe.meta.get_docfield(this.frm.doc["taxes"][0].doctype, "tax_amount_after_discount_amount", this.frm.doctype)) { temporary_fields.push("tax_amount_after_discount_amount"); @@ -473,13 +476,24 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ precision("base_discount_amount")); var total_for_discount_amount = this.get_total_for_discount_amount(); + var net_total = 0; // calculate item amount after Discount Amount if (total_for_discount_amount) { $.each(this.frm.doc["items"] || [], function(i, item) { distributed_amount = flt(me.frm.doc.discount_amount) * item.net_amount / total_for_discount_amount; - item.net_amount = flt(item.net_amount - distributed_amount, precision("base_amount", item)); - item.net_rate = flt(item.net_amount / item.qty, precision("net_rate", item)); + item.net_amount = flt(item.net_amount - distributed_amount, + precision("base_amount", item)); + net_total += item.net_amount; + // discount amount rounding loss adjustment if no taxes + if ((!(me.frm.doc.taxes || []).length || (me.frm.doc.apply_discount_on == "Net Total")) + && i == (me.frm.doc.items || []).length - 1) { + var discount_amount_loss = flt(me.frm.doc.net_total - net_total + - me.frm.doc.discount_amount, precision("net_total")); + item.net_amount = flt(item.net_amount + discount_amount_loss, + precision("net_amount", item)); + } + item.net_rate = flt(item.net_amount / item.qty, precision("net_rate", item)); me.set_in_company_currency(item, ["net_rate", "net_amount"]); }); @@ -490,10 +504,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, get_total_for_discount_amount: function() { - var me = this; - if(this.frm.doc.apply_discount_on == "Net Total") { - return this.frm.doc.net_total + return this.frm.doc.net_total; } else { var total_actual_tax = 0.0; var actual_taxes_dict = {}; @@ -517,7 +529,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ calculate_total_advance: function(update_paid_amount) { var total_allocated_amount = frappe.utils.sum($.map(this.frm.doc["advances"] || [], function(adv) { - return flt(adv.allocated_amount, precision("allocated_amount", adv)) + return flt(adv.allocated_amount, precision("allocated_amount", adv)); })); this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance")); @@ -528,9 +540,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ // NOTE: // paid_amount and write_off_amount is only for POS Invoice // total_advance is only for non POS Invoice - + if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.is_return){ - this.calculate_paid_amount() + this.calculate_paid_amount(); } if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return; @@ -558,25 +570,21 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } if(this.frm.doc.doctype == "Sales Invoice"){ - this.set_default_payment(total_amount_to_pay, update_paid_amount) - this.calculate_paid_amount() + this.set_default_payment(total_amount_to_pay, update_paid_amount); + this.calculate_paid_amount(); } - this.calculate_change_amount() - var outstanding_amount = 0.0 + this.calculate_change_amount(); var paid_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ? this.frm.doc.paid_amount : this.frm.doc.base_paid_amount; - var change_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ? - this.frm.doc.change_amount : this.frm.doc.base_change_amount; - this.frm.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(this.frm.doc.change_amount * this.frm.doc.conversion_rate), precision("outstanding_amount")); } else if(this.frm.doc.doctype == "Purchase Invoice") { this.frm.doc.outstanding_amount = flt(total_amount_to_pay, precision("outstanding_amount")); - } + } }, set_default_payment: function(total_amount_to_pay, update_paid_amount){ @@ -591,7 +599,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }else if(me.frm.doc.paid_amount){ data.amount = 0.0; } - }) + }); } }, @@ -604,7 +612,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ data.base_amount = flt(data.amount * me.frm.doc.conversion_rate, precision("base_amount")); paid_amount += data.amount; base_paid_amount += data.base_amount; - }) + }); } else if(!this.frm.doc.is_return){ this.frm.doc.payments = []; } @@ -617,30 +625,28 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.doc.change_amount = 0.0; this.frm.doc.base_change_amount = 0.0; if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) { - var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type }); + var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type; }); if (in_list(payment_types, 'Cash')) { this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total + this.frm.doc.write_off_amount, precision("change_amount")); - - this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount - - this.frm.doc.base_grand_total + this.frm.doc.base_write_off_amount, + + this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount - + this.frm.doc.base_grand_total + this.frm.doc.base_write_off_amount, precision("base_change_amount")); - } } }, calculate_write_off_amount: function(){ if(this.frm.doc.paid_amount > this.frm.doc.grand_total){ - this.frm.doc.write_off_amount = flt(this.frm.doc.grand_total - this.frm.doc.paid_amount + this.frm.doc.change_amount, - precision("write_off_amount")) - + this.frm.doc.write_off_amount = flt(this.frm.doc.grand_total - this.frm.doc.paid_amount + + this.frm.doc.change_amount, precision("write_off_amount")); + this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, precision("base_write_off_amount")); }else{ - this.frm.doc.paid_amount = 0.0 + this.frm.doc.paid_amount = 0.0; } - - this.calculate_outstanding_amount(false) + this.calculate_outstanding_amount(false); } -}) +}); \ No newline at end of file