diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 2e64f99dff..6c6737913e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -790,6 +790,58 @@ class TestSalesInvoice(unittest.TestCase): set_perpetual_inventory(0) + def test_discount_on_net_total(self): + si = frappe.copy_doc(test_records[2]) + si.apply_discount_on = "Net Total" + si.discount_amount = 625 + si.insert() + + expected_values = { + "keys": ["price_list_rate", "discount_percentage", "rate", "amount", + "base_price_list_rate", "base_rate", "base_amount", + "net_rate", "base_net_rate", "net_amount", "base_net_amount"], + "_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500, 25, 25, 250, 250], + "_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750, 75, 75, 375, 375], + } + + # check if children are saved + self.assertEquals(len(si.get("items")), + len(expected_values)-1) + + # check if item values are calculated + for d in si.get("items"): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.base_total, 1250) + self.assertEquals(si.total, 1250) + self.assertEquals(si.base_net_total, 625) + self.assertEquals(si.net_total, 625) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "tax_amount_after_discount_amount", + "base_tax_amount_after_discount_amount"], + "_Test Account Shipping Charges - _TC": [100, 100, 100], + "_Test Account Customs Duty - _TC": [62.5, 62.5, 62.5], + "_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 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]) + + + self.assertEquals(si.total_taxes_and_charges, 234.44) + self.assertEquals(si.base_grand_total, 859.44) + self.assertEquals(si.grand_total, 859.44) + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 6b59cea00d..d526f66baf 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -219,7 +219,7 @@ class calculate_taxes_and_totals(object): # 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.discount_amount and self.doc.apply_discount_on == "Grand Total": self.adjust_discount_amount_loss(tax) @@ -303,9 +303,9 @@ class calculate_taxes_and_totals(object): for tax in self.doc.get("taxes"): if tax.category in ["Valuation and Total", "Total"]: if tax.add_deduct_tax == "Add": - self.doc.taxes_and_charges_added += flt(tax.tax_amount) + self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount) else: - self.doc.taxes_and_charges_deducted += flt(tax.tax_amount) + self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount) self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78e1609990..de26ff3f71 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -184,4 +184,5 @@ execute:frappe.delete_doc("Module Def", "Contacts") erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items # 30-07-2015 execute:frappe.reload_doctype("Leave Type") execute:frappe.db.sql("update `tabLeave Type` set include_holiday=0") -erpnext.patches.v5_4.set_root_and_report_type \ No newline at end of file +erpnext.patches.v5_4.set_root_and_report_type +erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation diff --git a/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py b/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py new file mode 100644 index 0000000000..8096a37a00 --- /dev/null +++ b/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py @@ -0,0 +1,39 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.email import sendmail_to_system_managers +from frappe.utils import get_url_to_form + +def execute(): + wrong_records = [] + for dt in ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice", + "Purchase Order", "Purchase Receipt", "Purchase Invoice"): + records = frappe.db.sql_list("""select name from `tab{0}` + where apply_discount_on = 'Net Total' and ifnull(discount_amount, 0) != 0 + and modified >= '2015-02-17' and docstatus=1""".format(dt)) + + if records: + records = [get_url_to_form(dt, d) for d in records] + wrong_records.append([dt, records]) + + if wrong_records: + content = """Dear System Manager, + +Due to an error related to Discount Amount on Net Total, tax calculation might be wrong in the following records. We did not fix the tax amount automatically because it can corrupt the entries, so we request you to check these records and amend if you found the calculation wrong. + +Please check following Entries: + +%s + + +Regards, + +Administrator""" % "\n".join([(d[0] + ": " + ", ".join(d[1])) for d in wrong_records]) + + sendmail_to_system_managers("[Important] [ERPNext] Tax calculation might be wrong, please check.", content) + + print "="*50 + print content + print "="*50 \ No newline at end of file diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 1e238d6934..0b3ca7f2a8 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -256,7 +256,8 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({ me.round_off_totals(tax); // 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") + 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) me.adjust_discount_amount_loss(tax); } }); @@ -365,9 +366,9 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({ $.each(this.frm.doc["taxes"] || [], function(i, tax) { if (in_list(["Valuation and Total", "Total"], tax.category)) { if(tax.add_deduct_tax == "Add") { - me.frm.doc.taxes_and_charges_added += flt(tax.tax_amount); + me.frm.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount); } else { - me.frm.doc.taxes_and_charges_deducted += flt(tax.tax_amount); + me.frm.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount); } } })