diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 0c75fcc9352..98dd30c7fbe 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2626,8 +2626,13 @@ class TestSalesInvoice(unittest.TestCase): # Normal Itemized Discount si = get_sales_invoice_for_e_invoice() si.apply_discount_on = "" - si.items[0].discount_amount = 2 + si.items[0].price_list_rate = 12 + si.items[0].discount_percentage = 16.6666666667 + si.items[0].rate = 10 + + si.items[1].price_list_rate = 15 si.items[1].discount_amount = 5 + si.items[1].rate = 10 si.save() einvoice = make_einvoice(si) @@ -2635,8 +2640,15 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(einvoice["ItemList"][0]["Discount"], 4000) self.assertEqual(einvoice["ItemList"][1]["Discount"], 2100) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 222) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 5555) self.assertEqual(einvoice["ValDtls"]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 12) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 15) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 20) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 10) + # Invoice Discount on net total si = get_sales_invoice_for_e_invoice() si.apply_discount_on = "Net Total" @@ -2646,10 +2658,17 @@ class TestSalesInvoice(unittest.TestCase): einvoice = make_einvoice(si) validate_totals(einvoice) - self.assertEqual(einvoice["ItemList"][0]["Discount"], 316.83) - self.assertEqual(einvoice["ItemList"][1]["Discount"], 83.17) + self.assertEqual(einvoice["ItemList"][0]["Discount"], 253.61) + self.assertEqual(einvoice["ItemList"][1]["Discount"], 66.57) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 243.11) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 5613.71) self.assertEqual(einvoice["ValDtls"]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 12) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 15) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 20) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 10) + # Invoice Discount on grand total (Itemized Discount) si = get_sales_invoice_for_e_invoice() si.apply_discount_on = "Grand Total" @@ -2659,10 +2678,17 @@ class TestSalesInvoice(unittest.TestCase): einvoice = make_einvoice(si) validate_totals(einvoice) - self.assertEqual(einvoice["ItemList"][0]["Discount"], 268.5) - self.assertEqual(einvoice["ItemList"][1]["Discount"], 70.48) + self.assertEqual(einvoice["ItemList"][0]["Discount"], 214.93) + self.assertEqual(einvoice["ItemList"][1]["Discount"], 56.42) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 239.89) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 5604.75) self.assertEqual(einvoice["ValDtls"]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 12) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 15) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 20) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 10) + # Invoice Discount on grand total (Cash/Non-Trade Discount) si = get_sales_invoice_for_e_invoice() si.apply_discount_on = "Grand Total" @@ -2675,8 +2701,107 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(einvoice["ItemList"][0]["Discount"], 0) self.assertEqual(einvoice["ItemList"][1]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 222.0) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 5555.0) self.assertEqual(einvoice["ValDtls"]["Discount"], 400) + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 12) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 15) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 20) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 10) + + def test_einvoice_without_discounts(self): + from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals + + frappe.db.set_single_value("E Invoice Settings", "dont_show_discounts_in_e_invoice", True) + + # Normal Itemized Discount + si = get_sales_invoice_for_e_invoice() + si.apply_discount_on = "" + si.items[0].price_list_rate = 12 + si.items[0].discount_percentage = 16.6666666667 + si.items[0].rate = 10 + + si.items[1].price_list_rate = 15 + si.items[1].discount_amount = 5 + si.items[1].rate = 10 + si.save() + + einvoice = make_einvoice(si) + validate_totals(einvoice) + + self.assertEqual(einvoice["ItemList"][0]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][1]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 0) + self.assertEqual(einvoice["ValDtls"]["Discount"], 0) + + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 10) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 10) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 18) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 5) + + # Invoice Discount on net total + si = get_sales_invoice_for_e_invoice() + si.apply_discount_on = "Net Total" + si.discount_amount = 400 + si.save() + + einvoice = make_einvoice(si) + validate_totals(einvoice) + + self.assertEqual(einvoice["ItemList"][0]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][1]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 0) + self.assertEqual(einvoice["ValDtls"]["Discount"], 0) + + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 11.87) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 14.84) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 17.81) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 4.95) + + # Invoice Discount on grand total (Itemized Discount) + si = get_sales_invoice_for_e_invoice() + si.apply_discount_on = "Grand Total" + si.discount_amount = 400 + si.save() + + einvoice = make_einvoice(si) + validate_totals(einvoice) + + self.assertEqual(einvoice["ItemList"][0]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][1]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 0) + self.assertEqual(einvoice["ValDtls"]["Discount"], 0) + + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 11.89) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 14.87) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 17.84) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 4.96) + + # Invoice Discount on grand total (Cash/Non-Trade Discount) + si = get_sales_invoice_for_e_invoice() + si.apply_discount_on = "Grand Total" + si.is_cash_or_non_trade_discount = 1 + si.discount_amount = 400 + si.save() + + einvoice = make_einvoice(si) + validate_totals(einvoice) + + self.assertEqual(einvoice["ItemList"][0]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][1]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][2]["Discount"], 0) + self.assertEqual(einvoice["ItemList"][3]["Discount"], 0) + self.assertEqual(einvoice["ValDtls"]["Discount"], 400) + + self.assertEqual(einvoice["ItemList"][0]["UnitPrice"], 12) + self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 15) + self.assertEqual(einvoice["ItemList"][2]["UnitPrice"], 18) + self.assertEqual(einvoice["ItemList"][3]["UnitPrice"], 5) + def test_item_tax_net_range(self): item = create_item("T Shirt") @@ -3276,6 +3401,36 @@ def get_sales_invoice_for_e_invoice(): }, ) + si.append( + "items", + { + "item_code": "_Test Item", + "uom": "Nos", + "warehouse": "_Test Warehouse - _TC", + "qty": 111, + "price_list_rate": 20, + "discount_percentage": 10, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC", + }, + ) + + si.append( + "items", + { + "item_code": "_Test Item 2", + "uom": "Nos", + "warehouse": "_Test Warehouse - _TC", + "qty": 1111, + "price_list_rate": 10, + "rate": 5, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC", + }, + ) + return si diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json index 0d224c7a20b..14fad403213 100644 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json +++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json @@ -89,7 +89,7 @@ "fieldtype": "Column Break" }, { - "default": "0", + "default": "1", "description": "Enabling this will directly report net rates in e-Invoice post discounts", "fieldname": "dont_show_discounts_in_e_invoice", "fieldtype": "Check", @@ -104,7 +104,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-07-16 09:45:23.862046", + "modified": "2022-07-17 14:57:50.783517", "modified_by": "Administrator", "module": "Regional", "name": "E Invoice Settings", diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 4e9687f31e8..b0f837b51e7 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -273,27 +273,41 @@ def get_item_list(invoice): item.qty = abs(item.qty) - if invoice.get("apply_discount_on") and (abs(invoice.get("base_discount_amount") or 0.0) > 0.0): - # TODO: need to handle case when tax included in basic rate is checked. - item.discount_amount = (item.discount_amount * item.qty) + ( - abs(item.base_amount) - abs(item.base_net_amount) - ) - else: - item.discount_amount = item.discount_amount * item.qty + hide_discount_in_einvoice = cint( + frappe.db.get_single_value("E Invoice Settings", "dont_show_discounts_in_e_invoice") + ) + + if hide_discount_in_einvoice: + if flt(item.qty) != 0.0: + item.unit_rate = abs(item.taxable_value / item.qty) + else: + item.unit_rate = abs(item.taxable_value) + item.gross_amount = abs(item.taxable_value) + item.taxable_value = abs(item.taxable_value) + item.discount_amount = 0 - if invoice.get("is_return") or invoice.get("is_debit_note"): - item.unit_rate = (abs(item.taxable_value) + item.discount_amount) / ( - 1 if (item.qty == 0) else item.qty - ) else: - try: - item.unit_rate = abs(item.taxable_value + item.discount_amount) / item.qty - except ZeroDivisionError: - # This will never run but added as safety measure - frappe.throw( - title=_("Error: Qty is Zero"), - msg=_("Quantity can't be zero unless it's Credit/Debit Note."), + if invoice.get("apply_discount_on") and (abs(invoice.get("base_discount_amount") or 0.0) > 0.0): + # TODO: need to handle case when tax included in basic rate is checked. + item.discount_amount = (item.discount_amount * item.qty) + ( + abs(item.base_amount) - abs(item.base_net_amount) ) + else: + item.discount_amount = item.discount_amount * item.qty + + if invoice.get("is_return") or invoice.get("is_debit_note"): + item.unit_rate = (abs(item.taxable_value) + item.discount_amount) / ( + 1 if (item.qty == 0) else item.qty + ) + else: + try: + item.unit_rate = abs(item.taxable_value + item.discount_amount) / item.qty + except ZeroDivisionError: + # This will never run but added as safety measure + frappe.throw( + title=_("Error: Qty is Zero"), + msg=_("Quantity can't be zero unless it's Credit/Debit Note."), + ) item.gross_amount = abs(item.taxable_value) + item.discount_amount item.taxable_value = abs(item.taxable_value)