fix: (india) (e-invoice) CN / DN with 0 qty and discount issues
* fix: (india) (e-invoice) CN / DN with 0 qty
This commit is contained in:
@@ -2623,20 +2623,34 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
def test_einvoice_discounts(self):
|
def test_einvoice_discounts(self):
|
||||||
from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
|
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", False)
|
||||||
|
|
||||||
# Normal Itemized Discount
|
# Normal Itemized Discount
|
||||||
si = get_sales_invoice_for_e_invoice()
|
si = get_sales_invoice_for_e_invoice()
|
||||||
si.apply_discount_on = ""
|
si.apply_discount_on = ""
|
||||||
si.items[0].discount_amount = 4000
|
si.items[0].price_list_rate = 12
|
||||||
si.items[1].discount_amount = 300
|
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()
|
si.save()
|
||||||
|
|
||||||
einvoice = make_einvoice(si)
|
einvoice = make_einvoice(si)
|
||||||
validate_totals(einvoice)
|
validate_totals(einvoice)
|
||||||
|
|
||||||
self.assertEqual(einvoice["ItemList"][0]["Discount"], 4000)
|
self.assertEqual(einvoice["ItemList"][0]["Discount"], 4000)
|
||||||
self.assertEqual(einvoice["ItemList"][1]["Discount"], 300)
|
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["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
|
# Invoice Discount on net total
|
||||||
si = get_sales_invoice_for_e_invoice()
|
si = get_sales_invoice_for_e_invoice()
|
||||||
si.apply_discount_on = "Net Total"
|
si.apply_discount_on = "Net Total"
|
||||||
@@ -2646,10 +2660,17 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
einvoice = make_einvoice(si)
|
einvoice = make_einvoice(si)
|
||||||
validate_totals(einvoice)
|
validate_totals(einvoice)
|
||||||
|
|
||||||
self.assertEqual(einvoice["ItemList"][0]["Discount"], 316.83)
|
self.assertEqual(einvoice["ItemList"][0]["Discount"], 253.61)
|
||||||
self.assertEqual(einvoice["ItemList"][1]["Discount"], 83.17)
|
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["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)
|
# Invoice Discount on grand total (Itemized Discount)
|
||||||
si = get_sales_invoice_for_e_invoice()
|
si = get_sales_invoice_for_e_invoice()
|
||||||
si.apply_discount_on = "Grand Total"
|
si.apply_discount_on = "Grand Total"
|
||||||
@@ -2659,10 +2680,17 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
einvoice = make_einvoice(si)
|
einvoice = make_einvoice(si)
|
||||||
validate_totals(einvoice)
|
validate_totals(einvoice)
|
||||||
|
|
||||||
self.assertEqual(einvoice["ItemList"][0]["Discount"], 268.5)
|
self.assertEqual(einvoice["ItemList"][0]["Discount"], 214.93)
|
||||||
self.assertEqual(einvoice["ItemList"][1]["Discount"], 70.48)
|
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["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)
|
# Invoice Discount on grand total (Cash/Non-Trade Discount)
|
||||||
si = get_sales_invoice_for_e_invoice()
|
si = get_sales_invoice_for_e_invoice()
|
||||||
si.apply_discount_on = "Grand Total"
|
si.apply_discount_on = "Grand Total"
|
||||||
@@ -2675,8 +2703,107 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(einvoice["ItemList"][0]["Discount"], 0)
|
self.assertEqual(einvoice["ItemList"][0]["Discount"], 0)
|
||||||
self.assertEqual(einvoice["ItemList"][1]["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["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):
|
def test_item_tax_net_range(self):
|
||||||
item = create_item("T Shirt")
|
item = create_item("T Shirt")
|
||||||
|
|
||||||
@@ -3276,6 +3403,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
|
return si
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
"section_break_2",
|
"section_break_2",
|
||||||
"sandbox_mode",
|
"sandbox_mode",
|
||||||
"applicable_from",
|
"applicable_from",
|
||||||
|
"column_break_4",
|
||||||
|
"dont_show_discounts_in_e_invoice",
|
||||||
|
"section_break_7",
|
||||||
"credentials",
|
"credentials",
|
||||||
"advanced_settings_section",
|
"advanced_settings_section",
|
||||||
"client_id",
|
"client_id",
|
||||||
@@ -80,12 +83,28 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_8",
|
"fieldname": "column_break_8",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"description": "Enabling this will directly report net rates in e-Invoice post discounts",
|
||||||
|
"fieldname": "dont_show_discounts_in_e_invoice",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Don't show discounts in e-Invoice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_7",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-11-16 19:50:28.029517",
|
"modified": "2022-07-17 14:57:50.783517",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Regional",
|
"module": "Regional",
|
||||||
"name": "E Invoice Settings",
|
"name": "E Invoice Settings",
|
||||||
|
|||||||
@@ -273,10 +273,41 @@ def get_item_list(invoice):
|
|||||||
|
|
||||||
item.qty = abs(item.qty)
|
item.qty = abs(item.qty)
|
||||||
|
|
||||||
if invoice.get("apply_discount_on"):
|
hide_discount_in_einvoice = cint(
|
||||||
item.discount_amount = item.base_amount - item.base_net_amount
|
frappe.db.get_single_value("E Invoice Settings", "dont_show_discounts_in_e_invoice")
|
||||||
|
)
|
||||||
|
|
||||||
item.unit_rate = abs(item.taxable_value - item.discount_amount) / item.qty
|
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
|
||||||
|
|
||||||
|
else:
|
||||||
|
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.gross_amount = abs(item.taxable_value) + item.discount_amount
|
||||||
item.taxable_value = abs(item.taxable_value)
|
item.taxable_value = abs(item.taxable_value)
|
||||||
|
|||||||
@@ -164,4 +164,13 @@ class TestItemPrice(FrappeTestCase):
|
|||||||
self.assertEqual(price, 21)
|
self.assertEqual(price, 21)
|
||||||
|
|
||||||
|
|
||||||
|
def make_item_price(item_code, price_list, price_list_rate):
|
||||||
|
item_price = frappe.new_doc("Item Price")
|
||||||
|
item_price.price_list = price_list
|
||||||
|
item_price.item_code = item_code
|
||||||
|
item_price.price_list_rate = price_list_rate
|
||||||
|
item_price.insert()
|
||||||
|
return item_price
|
||||||
|
|
||||||
|
|
||||||
test_records = frappe.get_test_records("Item Price")
|
test_records = frappe.get_test_records("Item Price")
|
||||||
|
|||||||
Reference in New Issue
Block a user