Merge pull request #31405 from deepeshgarg007/e_invoice_discounts
feat: Cash and Non trade discounts in Sales Invoice
This commit is contained in:
commit
4038c42922
@ -476,6 +476,13 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
|||||||
this.frm.trigger("calculate_timesheet_totals");
|
this.frm.trigger("calculate_timesheet_totals");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_cash_or_non_trade_discount() {
|
||||||
|
this.frm.set_df_property("additional_discount_account", "hidden", 1 - this.frm.doc.is_cash_or_non_trade_discount);
|
||||||
|
if (!this.frm.doc.is_cash_or_non_trade_discount) {
|
||||||
|
this.frm.set_value("additional_discount_account", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// for backward compatibility: combine new and previous states
|
// for backward compatibility: combine new and previous states
|
||||||
|
@ -106,6 +106,7 @@
|
|||||||
"loyalty_redemption_cost_center",
|
"loyalty_redemption_cost_center",
|
||||||
"section_break_49",
|
"section_break_49",
|
||||||
"apply_discount_on",
|
"apply_discount_on",
|
||||||
|
"is_cash_or_non_trade_discount",
|
||||||
"base_discount_amount",
|
"base_discount_amount",
|
||||||
"additional_discount_account",
|
"additional_discount_account",
|
||||||
"column_break_51",
|
"column_break_51",
|
||||||
@ -1790,8 +1791,6 @@
|
|||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "sales_partner.commission_rate",
|
|
||||||
"fetch_if_empty": 1,
|
|
||||||
"fieldname": "commission_rate",
|
"fieldname": "commission_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hide_days": 1,
|
"hide_days": 1,
|
||||||
@ -1990,7 +1989,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "additional_discount_account",
|
"fieldname": "additional_discount_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Additional Discount Account",
|
"label": "Discount Account",
|
||||||
"options": "Account"
|
"options": "Account"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -2028,6 +2027,13 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Amount Eligible for Commission",
|
"label": "Amount Eligible for Commission",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval: doc.apply_discount_on == \"Grand Total\"",
|
||||||
|
"fieldname": "is_cash_or_non_trade_discount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Cash or Non Trade Discount"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
@ -2040,7 +2046,7 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2022-06-10 03:52:51.409913",
|
"modified": "2022-06-16 16:22:44.870575",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
@ -1030,7 +1030,7 @@ class SalesInvoice(SellingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if grand_total and not self.is_internal_transfer():
|
if grand_total and not self.is_internal_transfer():
|
||||||
# Didnot use base_grand_total to book rounding loss gle
|
# Did not use base_grand_total to book rounding loss gle
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
{
|
{
|
||||||
@ -1055,6 +1055,22 @@ class SalesInvoice(SellingController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.apply_discount_on == "Grand Total" and self.get("is_cash_or_discount_account"):
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict(
|
||||||
|
{
|
||||||
|
"account": self.additional_discount_account,
|
||||||
|
"against": self.debit_to,
|
||||||
|
"debit": self.base_discount_amount,
|
||||||
|
"debit_in_account_currency": self.discount_amount,
|
||||||
|
"cost_center": self.cost_center,
|
||||||
|
"project": self.project,
|
||||||
|
},
|
||||||
|
self.currency,
|
||||||
|
item=self,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def make_tax_gl_entries(self, gl_entries):
|
def make_tax_gl_entries(self, gl_entries):
|
||||||
enable_discount_accounting = cint(
|
enable_discount_accounting = cint(
|
||||||
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
|
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
|
||||||
|
@ -2731,6 +2731,63 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
einvoice = make_einvoice(si)
|
einvoice = make_einvoice(si)
|
||||||
validate_totals(einvoice)
|
validate_totals(einvoice)
|
||||||
|
|
||||||
|
def test_einvoice_discounts(self):
|
||||||
|
from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
|
||||||
|
|
||||||
|
# Normal Itemized Discount
|
||||||
|
si = get_sales_invoice_for_e_invoice()
|
||||||
|
si.apply_discount_on = ""
|
||||||
|
si.items[0].discount_amount = 4000
|
||||||
|
si.items[1].discount_amount = 300
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
einvoice = make_einvoice(si)
|
||||||
|
validate_totals(einvoice)
|
||||||
|
|
||||||
|
self.assertEqual(einvoice["ItemList"][0]["Discount"], 4000)
|
||||||
|
self.assertEqual(einvoice["ItemList"][1]["Discount"], 300)
|
||||||
|
self.assertEqual(einvoice["ValDtls"]["Discount"], 0)
|
||||||
|
|
||||||
|
# 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"], 316.83)
|
||||||
|
self.assertEqual(einvoice["ItemList"][1]["Discount"], 83.17)
|
||||||
|
self.assertEqual(einvoice["ValDtls"]["Discount"], 0)
|
||||||
|
|
||||||
|
# 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"], 268.5)
|
||||||
|
self.assertEqual(einvoice["ItemList"][1]["Discount"], 70.48)
|
||||||
|
self.assertEqual(einvoice["ValDtls"]["Discount"], 0)
|
||||||
|
|
||||||
|
# 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["ValDtls"]["Discount"], 400)
|
||||||
|
|
||||||
def test_item_tax_net_range(self):
|
def test_item_tax_net_range(self):
|
||||||
item = create_item("T Shirt")
|
item = create_item("T Shirt")
|
||||||
|
|
||||||
|
@ -500,6 +500,9 @@ class calculate_taxes_and_totals(object):
|
|||||||
else:
|
else:
|
||||||
self.doc.grand_total = flt(self.doc.net_total)
|
self.doc.grand_total = flt(self.doc.net_total)
|
||||||
|
|
||||||
|
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
|
||||||
|
self.doc.grand_total -= self.doc.discount_amount
|
||||||
|
|
||||||
if self.doc.get("taxes"):
|
if self.doc.get("taxes"):
|
||||||
self.doc.total_taxes_and_charges = flt(
|
self.doc.total_taxes_and_charges = flt(
|
||||||
self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
|
self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
|
||||||
@ -594,6 +597,12 @@ class calculate_taxes_and_totals(object):
|
|||||||
if not self.doc.apply_discount_on:
|
if not self.doc.apply_discount_on:
|
||||||
frappe.throw(_("Please select Apply Discount On"))
|
frappe.throw(_("Please select Apply Discount On"))
|
||||||
|
|
||||||
|
if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
|
||||||
|
"is_cash_or_non_trade_discount"
|
||||||
|
):
|
||||||
|
self.discount_amount_applied = True
|
||||||
|
return
|
||||||
|
|
||||||
self.doc.base_discount_amount = flt(
|
self.doc.base_discount_amount = flt(
|
||||||
self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
|
self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
|
||||||
)
|
)
|
||||||
|
@ -271,14 +271,14 @@ def get_item_list(invoice):
|
|||||||
item.description = sanitize_for_json(d.item_name)
|
item.description = sanitize_for_json(d.item_name)
|
||||||
|
|
||||||
item.qty = abs(item.qty)
|
item.qty = abs(item.qty)
|
||||||
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("apply_discount_on"):
|
||||||
|
item.discount_amount = item.base_amount - item.base_net_amount
|
||||||
|
|
||||||
|
item.unit_rate = abs(item.taxable_value - item.discount_amount) / item.qty
|
||||||
|
|
||||||
|
item.gross_amount = abs(item.taxable_value) + item.discount_amount
|
||||||
|
item.taxable_value = abs(item.taxable_value)
|
||||||
item.is_service_item = "Y" if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else "N"
|
item.is_service_item = "Y" if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else "N"
|
||||||
item.serial_no = ""
|
item.serial_no = ""
|
||||||
|
|
||||||
@ -352,7 +352,14 @@ def update_item_taxes(invoice, item):
|
|||||||
def get_invoice_value_details(invoice):
|
def get_invoice_value_details(invoice):
|
||||||
invoice_value_details = frappe._dict(dict())
|
invoice_value_details = frappe._dict(dict())
|
||||||
invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get("items")]))
|
invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get("items")]))
|
||||||
invoice_value_details.invoice_discount_amt = 0
|
if (
|
||||||
|
invoice.apply_discount_on == "Grand Total"
|
||||||
|
and invoice.discount_amount
|
||||||
|
and invoice.get("is_cash_or_non_trade_discount")
|
||||||
|
):
|
||||||
|
invoice_value_details.invoice_discount_amt = invoice.discount_amount
|
||||||
|
else:
|
||||||
|
invoice_value_details.invoice_discount_amt = 0
|
||||||
|
|
||||||
invoice_value_details.round_off = invoice.base_rounding_adjustment
|
invoice_value_details.round_off = invoice.base_rounding_adjustment
|
||||||
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(
|
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(
|
||||||
|
@ -1060,8 +1060,16 @@ def update_taxable_values(doc, method):
|
|||||||
considered_rows.append(prev_row_id)
|
considered_rows.append(prev_row_id)
|
||||||
|
|
||||||
for item in doc.get("items"):
|
for item in doc.get("items"):
|
||||||
proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
|
if (
|
||||||
total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
|
doc.apply_discount_on == "Grand Total"
|
||||||
|
and doc.discount_amount
|
||||||
|
and doc.get("is_cash_or_non_trade_discount")
|
||||||
|
):
|
||||||
|
proportionate_value = item.base_amount if doc.base_total else item.qty
|
||||||
|
total_value = doc.base_total if doc.base_total else doc.total_qty
|
||||||
|
else:
|
||||||
|
proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
|
||||||
|
total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
|
||||||
|
|
||||||
applicable_charges = flt(
|
applicable_charges = flt(
|
||||||
flt(
|
flt(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user