From f8ba0555a8acd23fda41a710ee54e3886dc0cf71 Mon Sep 17 00:00:00 2001 From: sahilk25 Date: Fri, 16 Jul 2021 11:03:37 +0530 Subject: [PATCH] feat: bulkdiscount (#1397) --- .../selling/doctype/bulk_discount/__init__.py | 0 .../doctype/bulk_discount/bulk_discount.json | 84 +++++++++++++++++ .../doctype/bulk_discount/bulk_discount.py | 10 +++ .../doctype/bulk_discount_scheme/__init__.py | 0 .../bulk_discount_scheme.js | 8 ++ .../bulk_discount_scheme.json | 63 +++++++++++++ .../bulk_discount_scheme.py | 10 +++ .../test_bulk_discount_scheme.py | 10 +++ .../doctype/quoting_sheet/quoting_sheet.js | 89 +++++++++++++++++-- .../doctype/quoting_sheet/quoting_sheet.json | 79 +++++++++------- .../doctype/quoting_sheet/quoting_sheet.py | 71 +++++++++++++-- .../quoting_sheet_item.json | 6 +- 12 files changed, 384 insertions(+), 46 deletions(-) create mode 100644 erpnext/selling/doctype/bulk_discount/__init__.py create mode 100644 erpnext/selling/doctype/bulk_discount/bulk_discount.json create mode 100644 erpnext/selling/doctype/bulk_discount/bulk_discount.py create mode 100644 erpnext/selling/doctype/bulk_discount_scheme/__init__.py create mode 100644 erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.js create mode 100644 erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.json create mode 100644 erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.py create mode 100644 erpnext/selling/doctype/bulk_discount_scheme/test_bulk_discount_scheme.py diff --git a/erpnext/selling/doctype/bulk_discount/__init__.py b/erpnext/selling/doctype/bulk_discount/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/selling/doctype/bulk_discount/bulk_discount.json b/erpnext/selling/doctype/bulk_discount/bulk_discount.json new file mode 100644 index 000000000000..4b6a5c026694 --- /dev/null +++ b/erpnext/selling/doctype/bulk_discount/bulk_discount.json @@ -0,0 +1,84 @@ +{ + "creation": "2021-06-29 23:42:47.804616", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "minimum_qty", + "maximum_qty", + "column_break_3", + "discount_type", + "discount_percentage", + "discount_amount", + "amended_from" + ], + "fields": [ + { + "fieldname": "minimum_qty", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Minimum Qty" + }, + { + "fieldname": "maximum_qty", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Qty" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "discount_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Discount type", + "options": "\nPercentage\nAmount" + }, + { + "depends_on": "eval:doc.discount_type==\"Percentage\"", + "fieldname": "discount_percentage", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Discount Percentage" + }, + { + "depends_on": "eval:doc.discount_type==\"Amount\"", + "fieldname": "discount_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Discount Amount" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bulk Discount", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bulk Discount", + "print_hide": 1, + "read_only": 1 + } + ], + "is_submittable": 1, + "istable": 1, + "modified": "2021-06-30 01:01:34.307743", + "modified_by": "Administrator", + "module": "Selling", + "name": "Bulk Discount", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/selling/doctype/bulk_discount/bulk_discount.py b/erpnext/selling/doctype/bulk_discount/bulk_discount.py new file mode 100644 index 000000000000..6fc9becd99ee --- /dev/null +++ b/erpnext/selling/doctype/bulk_discount/bulk_discount.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class BulkDiscount(Document): + pass diff --git a/erpnext/selling/doctype/bulk_discount_scheme/__init__.py b/erpnext/selling/doctype/bulk_discount_scheme/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.js b/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.js new file mode 100644 index 000000000000..eec140e26cd9 --- /dev/null +++ b/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Bulk Discount Scheme', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.json b/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.json new file mode 100644 index 000000000000..ebd18495e851 --- /dev/null +++ b/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.json @@ -0,0 +1,63 @@ +{ + "autoname": "field:scheme_name", + "creation": "2021-06-29 23:42:53.901998", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "scheme_name", + "bulk_disount", + "amended_from" + ], + "fields": [ + { + "fieldname": "bulk_disount", + "fieldtype": "Table", + "label": "Bulk Disount", + "options": "Bulk Discount" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bulk Discount Scheme", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "scheme_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + } + ], + "is_submittable": 1, + "modified": "2021-06-29 23:53:29.887064", + "modified_by": "Administrator", + "module": "Selling", + "name": "Bulk Discount Scheme", + "name_case": "UPPER CASE", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.py b/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.py new file mode 100644 index 000000000000..09138ad81713 --- /dev/null +++ b/erpnext/selling/doctype/bulk_discount_scheme/bulk_discount_scheme.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class BulkDiscountScheme(Document): + pass diff --git a/erpnext/selling/doctype/bulk_discount_scheme/test_bulk_discount_scheme.py b/erpnext/selling/doctype/bulk_discount_scheme/test_bulk_discount_scheme.py new file mode 100644 index 000000000000..c9ee90e43ca9 --- /dev/null +++ b/erpnext/selling/doctype/bulk_discount_scheme/test_bulk_discount_scheme.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestBulkDiscountScheme(unittest.TestCase): + pass diff --git a/erpnext/selling/doctype/quoting_sheet/quoting_sheet.js b/erpnext/selling/doctype/quoting_sheet/quoting_sheet.js index 81e4d347fd80..818300b4d50d 100644 --- a/erpnext/selling/doctype/quoting_sheet/quoting_sheet.js +++ b/erpnext/selling/doctype/quoting_sheet/quoting_sheet.js @@ -1,9 +1,72 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt frappe.ui.form.on('Quoting Sheet', { - item_code: function (frm) { - if (frm.doc.currency == frappe.sys_defaults.currency) { - frm.set_value("conversion_rate", 1.0); + onload: (frm) => { + frm.set_query("quote_to", function() { + return{ + "filters": { + "name": ["in", ["Customer", "Lead"]], + } + }; + }); + + frm.set_query("bom", () => { + if (frm.doc.item_code) { + return { + filters: { + "item": frm.doc.item_code + } + }; + } + }); + }, + + refresh:(frm) => { + frm.trigger('set_dynamic_field_label'); + }, + + quote_to:(frm) => { + frm.trigger('set_dynamic_field_label'); + }, + + set_dynamic_field_label: function(frm){ + if (frm.doc.quote_to == "Customer") + { + frm.set_df_property("party_name", "label", "Customer"); + frm.fields_dict.party_name.get_query = null; + } + + if (frm.doc.quote_to == "Lead") + { + frm.set_df_property("party_name", "label", "Lead"); + + frm.fields_dict.party_name.get_query = function() { + return{ query: "erpnext.controllers.queries.lead_query" }; + }; + } + }, + + bom: function (frm) { + if (frm.doc.bom) { + frappe.call({ + method: "get_raw_materials", + doc: frm.doc, + callback: () => { + frm.refresh_field("raw_material_items"); + frm.refresh_field("rm_cost"); + }, + }); + } + }, + bulk_discount_scheme: function (frm) { + if (frm.doc.bulk_discount_scheme) { + frappe.call({ + method: "get_bulk_discount", + doc: frm.doc, + callback: () => { + frm.refresh_field("bulk_discount"); + }, + }); } }, @@ -17,13 +80,27 @@ frappe.ui.form.on('Quoting Sheet', { frm.reload_doc(); }, }); - } + }, + + calculate_total_raw_material_cost: function (frm) { + frappe.call({ + method: "calculate_total_raw_material_cost", + doc: frm.doc, + callback: (res) => { + if (res.message) { + frm.set_value("rm_cost", res.message); + frm.refresh_field("rm_cost"); + } + + } + }); + }, }); frappe.ui.form.on("Quoting Sheet Item", { item_code: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; - if (!row.item_code) {return;} + if (!row.item_code) { return; } frappe.call({ method: "erpnext.selling.doctype.quoting_sheet.quoting_sheet.get_item_details_quoting_sheet", args: { @@ -42,6 +119,7 @@ frappe.ui.form.on("Quoting Sheet Item", { if (row.qty && row.rate) { let amount = row.qty * row.rate; frappe.model.set_value(cdt, cdn, "amount", amount); + frm.trigger("calculate_total_raw_material_cost"); } }, @@ -50,6 +128,7 @@ frappe.ui.form.on("Quoting Sheet Item", { if (row.qty && row.rate) { let amount = row.qty * row.rate; frappe.model.set_value(cdt, cdn, "amount", amount); + frm.trigger("calculate_total_raw_material_cost"); } }, diff --git a/erpnext/selling/doctype/quoting_sheet/quoting_sheet.json b/erpnext/selling/doctype/quoting_sheet/quoting_sheet.json index adf9a3c7c6ad..349f5205d4ab 100644 --- a/erpnext/selling/doctype/quoting_sheet/quoting_sheet.json +++ b/erpnext/selling/doctype/quoting_sheet/quoting_sheet.json @@ -6,24 +6,26 @@ "engine": "InnoDB", "field_order": [ "item_code", - "item_name", - "customer", - "lead", + "bom", + "quote_to", + "party_name", "column_break_4", + "item_name", "currency", - "conversion_rate", "qty", "section_break_8", + "section_break_15", "raw_material_items", "update_rate", "rm_cost", "section_break_11", "packaging_charges", + "profit_margin", "column_break_13", "shipping_cost", - "section_break_15", - "profit_markup", "bulk_discounting_section", + "bulk_discount_scheme", + "bulk_discount", "section_break_16", "total_price", "column_break_17", @@ -75,31 +77,17 @@ "label": "Shipping Cost" }, { - "default": "0", - "fieldname": "profit_markup", - "fieldtype": "Percent", - "label": "Profit Markup Percent" - }, - { - "default": "0", "fieldname": "total_price", "fieldtype": "Currency", "label": "Total Price", "read_only": 1 }, { - "default": "0", "fieldname": "price_per_unit", "fieldtype": "Currency", "label": "Price Per Unit", "read_only": 1 }, - { - "fieldname": "conversion_rate", - "fieldtype": "Float", - "label": "Conversion Rate", - "reqd": 1 - }, { "fieldname": "currency", "fieldtype": "Link", @@ -107,12 +95,6 @@ "options": "Currency", "reqd": 1 }, - { - "fieldname": "customer", - "fieldtype": "Link", - "label": "Customer", - "options": "Customer" - }, { "fieldname": "column_break_4", "fieldtype": "Column Break" @@ -155,12 +137,6 @@ "options": "Quoting Sheet Item", "reqd": 1 }, - { - "fieldname": "lead", - "fieldtype": "Link", - "label": "Lead", - "options": "Lead" - }, { "fieldname": "bulk_discounting_section", "fieldtype": "Section Break", @@ -171,10 +147,46 @@ "fieldname": "update_rate", "fieldtype": "Button", "label": "Update Rate" + }, + { + "fieldname": "bom", + "fieldtype": "Link", + "label": "BOM", + "options": "BOM" + }, + { + "fieldname": "bulk_discount", + "fieldtype": "Table", + "label": "Bulk Discount", + "options": "Bulk Discount" + }, + { + "fieldname": "bulk_discount_scheme", + "fieldtype": "Link", + "label": "Bulk Discount Scheme", + "options": "Bulk Discount Scheme" + }, + { + "default": "0", + "fieldname": "profit_margin", + "fieldtype": "Percent", + "label": "Profit Margin %" + }, + { + "fieldname": "party_name", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "quote_to" + }, + { + "fieldname": "quote_to", + "fieldtype": "Link", + "label": "Quote To", + "options": "DocType" } ], "is_submittable": 1, - "modified": "2021-06-02 23:07:43.385905", + "modified": "2021-07-15 05:44:41.738772", "modified_by": "Administrator", "module": "Selling", "name": "Quoting Sheet", @@ -191,6 +203,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/selling/doctype/quoting_sheet/quoting_sheet.py b/erpnext/selling/doctype/quoting_sheet/quoting_sheet.py index 21960e252a80..7f6b879637b3 100644 --- a/erpnext/selling/doctype/quoting_sheet/quoting_sheet.py +++ b/erpnext/selling/doctype/quoting_sheet/quoting_sheet.py @@ -5,26 +5,87 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt from frappe.model.document import Document class QuotingSheet(Document): def validate(self): - self.calculate_raw_material_cost() + self.calculate_single_raw_material_cost() + self.calculate_total_raw_material_cost() self.calculate_totals() def calculate_totals(self): """ - Calculates total cost and cost per unit of item + Calculates total price and price per unit of item """ + self.total_price = 0 + self.price_per_unit = 0 total_charges = self.rm_cost + self.packaging_charges + self.shipping_cost - self.total_price = total_charges + ((total_charges * int(self.profit_markup))/100) + if not self.bulk_discount: + self.total_price = total_charges / (1 - (self.profit_margin/100)) + self.price_per_unit = flt(self.total_price) / flt(self.qty) + else: + for discount in self.bulk_discount: + if self.qty in range(discount.minimum_qty, discount.maximum_qty+1): + if discount.discount_type == "Percentage": + self.total_price = total_charges / (1 - (self.profit_margin/100)) * (1 - (discount.discount_percentage/100)) + self.price_per_unit = flt(self.total_price) / flt(self.qty) + elif discount.discount_type == "Amount": + self.total_price = total_charges / (1 - (self.profit_margin/100)) - discount.discount_amount + self.price_per_unit = flt(self.total_price) / flt(self.qty) - self.price_per_unit = self.total_price / self.qty - def calculate_raw_material_cost(self): + def calculate_total_raw_material_cost(self): + """ + Calculates total cost of raw materials + """ self.rm_cost = 0 for item in self.raw_material_items: self.rm_cost += item.amount + return self.rm_cost + + def get_raw_materials(self): + """ + set raw materials items against to BOM Item given + """ + self.raw_material_items = [] + raw_materials = frappe.get_all("BOM Item", filters={"parent": self.bom}) + for material in raw_materials: + bom_item = frappe.db.get_value("BOM Item", material.name, ["item_code", "qty", "rate", "uom", "item_name"], as_dict = 1) + if bom_item: + self.append("raw_material_items", { + "item_code": bom_item.get("item_code"), + "qty": bom_item.get("qty"), + "rate": bom_item.get("rate"), + "uom": bom_item.get("uom"), + "item_name": bom_item.get("item_name") + }) + self.calculate_single_raw_material_cost() + self.calculate_total_raw_material_cost() + + def get_bulk_discount(self): + """ + set bulk disount against to bulk discount scheme given + """ + self.bulk_discount = [] + bulk_discounts = frappe.get_all("Bulk Discount", filters={"parent": self.bulk_discount_scheme}) + for discounts in bulk_discounts: + discount_item = frappe.db.get_value("Bulk Discount", discounts.name, ["minimum_qty", "maximum_qty", "discount_type", "discount_percentage", "discount_amount"], as_dict = 1) + if discount_item: + self.append("bulk_discount", { + "minimum_qty": discount_item.get("minimum_qty"), + "maximum_qty": discount_item.get("maximum_qty"), + "discount_type": discount_item.get("discount_type"), + "discount_percentage": discount_item.get("discount_percentage"), + "discount_amount": discount_item.get("discount_amount") + }) + + def calculate_single_raw_material_cost(self): + """ + calculate amount of single raw material cost + """ + for item in self.raw_material_items: + item.amount = flt(item.qty) * flt(item.rate) @frappe.whitelist() diff --git a/erpnext/selling/doctype/quoting_sheet_item/quoting_sheet_item.json b/erpnext/selling/doctype/quoting_sheet_item/quoting_sheet_item.json index 817b1d6faafe..8de00abcb460 100644 --- a/erpnext/selling/doctype/quoting_sheet_item/quoting_sheet_item.json +++ b/erpnext/selling/doctype/quoting_sheet_item/quoting_sheet_item.json @@ -5,7 +5,6 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "customer_provided_item", "item_code", "item_name", "operation", @@ -35,7 +34,8 @@ "qty_consumed_per_unit", "section_break_27", "include_item_in_manufacturing", - "original_item" + "original_item", + "customer_provided_item" ], "fields": [ { @@ -263,7 +263,7 @@ } ], "istable": 1, - "modified": "2021-06-02 23:02:08.899460", + "modified": "2021-07-15 05:27:24.029070", "modified_by": "Administrator", "module": "Selling", "name": "Quoting Sheet Item",