From fd9dc5e9f5bb8da881bd9e7c10190448f269ea3e Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Fri, 12 Feb 2016 14:32:23 +0530 Subject: [PATCH] refactor code in Production Planning Tool --- .../production_plan_item.json | 77 ++++--- .../production_plan_material_request.json | 5 +- .../production_plan_sales_order.json | 16 +- .../production_planning_tool.js | 19 +- .../production_planning_tool.json | 4 +- .../production_planning_tool.py | 207 +++++++++--------- 6 files changed, 173 insertions(+), 155 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json index 6596e234011..6138435ae77 100644 --- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json +++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json @@ -116,6 +116,54 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_6", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Reserved Warehouse in Sales Order / Finished Goods Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Warehouse", + "length": 0, + "no_copy": 0, + "options": "Warehouse", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -160,7 +208,7 @@ "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -194,31 +242,6 @@ "unique": 0, "width": "100px" }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "Reserved Warehouse in Sales Order / Finished Goods Warehouse", - "fieldname": "warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -308,7 +331,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-02-10 07:08:38.461787", + "modified": "2016-02-11 05:08:19.492712", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Item", diff --git a/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.json b/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.json index 0522e7a3473..1a7c89485cd 100644 --- a/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.json +++ b/erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.json @@ -66,7 +66,7 @@ "bold": 0, "collapsible": 0, "fieldname": "material_request_date", - "fieldtype": "Date", + "fieldtype": "Read Only", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, @@ -76,6 +76,7 @@ "no_copy": 0, "oldfieldname": "document_date", "oldfieldtype": "Date", + "options": "material_request.transaction_date", "permlevel": 0, "precision": "", "print_hide": 0, @@ -99,7 +100,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-02-10 05:13:07.529068", + "modified": "2016-02-11 04:21:34.372317", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Material Request", diff --git a/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.json b/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.json index bed893f9210..85bf7d6d60a 100644 --- a/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.json +++ b/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.json @@ -41,7 +41,7 @@ "bold": 0, "collapsible": 0, "fieldname": "sales_order_date", - "fieldtype": "Date", + "fieldtype": "Read Only", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, @@ -51,11 +51,12 @@ "no_copy": 0, "oldfieldname": "document_date", "oldfieldtype": "Date", + "options": "sales_order.transaction_date", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, "print_width": "120px", - "read_only": 1, + "read_only": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -91,7 +92,7 @@ "bold": 0, "collapsible": 0, "fieldname": "customer", - "fieldtype": "Link", + "fieldtype": "Read Only", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, @@ -99,7 +100,7 @@ "label": "Customer", "length": 0, "no_copy": 0, - "options": "Customer", + "options": "sales_order.customer", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -116,8 +117,9 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "default": "", "fieldname": "grand_total", - "fieldtype": "Currency", + "fieldtype": "Read Only", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, @@ -125,7 +127,7 @@ "label": "Grand Total", "length": 0, "no_copy": 0, - "options": "Company:company:default_currency", + "options": "sales_order.base_grand_total", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -148,7 +150,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-02-10 05:13:30.301297", + "modified": "2016-02-11 04:17:42.849873", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Sales Order", diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js index 15c4245fe5e..3fa1160d7e4 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.js @@ -5,24 +5,25 @@ frappe.require("assets/erpnext/js/utils.js"); cur_frm.cscript.onload = function(doc, cdt, cdn) { cur_frm.set_value("company", frappe.defaults.get_user_default("Company")) - cur_frm.set_value("use_multi_level_bom", 1) } cur_frm.cscript.refresh = function(doc) { cur_frm.disable_save(); } -cur_frm.cscript.sales_order = function(doc,cdt,cdn) { - var d = locals[cdt][cdn]; - if (d.sales_order) { - return get_server_fields('get_so_details', d.sales_order, 'sales_orders', doc, cdt, cdn, 1); - } -} - cur_frm.cscript.item_code = function(doc,cdt,cdn) { var d = locals[cdt][cdn]; if (d.item_code) { - return get_server_fields('get_item_details', d.item_code, 'items', doc, cdt, cdn, 1); + frappe.call({ + method: "erpnext.manufacturing.doctype.production_order.production_order.get_item_details", + args: { + "item" : d.item_code + }, + callback: function(r) { + $.extend(d, r.message); + refresh_field("items"); + } + }); } } diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json index 3f047138fd2..19b5fd97845 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json @@ -510,7 +510,7 @@ "label": "Create Production Orders", "length": 0, "no_copy": 0, - "options": "raise_production_order", + "options": "raise_production_orders", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -629,7 +629,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2016-02-10 07:28:50.298538", + "modified": "2016-02-11 06:18:45.077263", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Planning Tool", diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 4be22e40807..bec97d1c03c 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -16,28 +16,8 @@ class ProductionPlanningTool(Document): super(ProductionPlanningTool, self).__init__(arg1, arg2) self.item_dict = {} - def get_so_details(self, so): - """Pull other details from so""" - so = frappe.db.sql("""select transaction_date, customer, base_grand_total - from `tabSales Order` where name = %s""", so, as_dict = 1) - ret = { - 'sales_order_date': so and so[0]['transaction_date'] or '', - 'customer' : so[0]['customer'] or '', - 'grand_total': so[0]['base_grand_total'] - } - return ret - - def get_item_details(self, item_code): - return get_item_details(item_code) - - def clear_so_table(self): - self.set('sales_orders', []) - - def clear_mr_table(self): - self.set('material_requests', []) - - def clear_item_table(self): - self.set('items', []) + def clear_table(self, table_name): + self.set(table_name, []) def validate_company(self): if not self.company: @@ -81,7 +61,7 @@ class ProductionPlanningTool(Document): def add_so_in_table(self, open_so): """ Add sales orders in the table""" - self.clear_so_table() + self.clear_table("sales_orders") so_list = [d.sales_order for d in self.get('sales_orders')] for r in open_so: @@ -125,7 +105,7 @@ class ProductionPlanningTool(Document): def add_mr_in_table(self, pending_mr): """ Add Material Requests in the table""" - self.clear_mr_table() + self.clear_table("material_requests") mr_list = [d.material_request for d in self.get('material_requests')] for r in pending_mr: @@ -199,7 +179,7 @@ class ProductionPlanningTool(Document): def add_items(self, items): - self.clear_item_table() + self.clear_table("items") for p in items: item_details = get_item_details(p['item_code']) pi = self.append('items', {}) @@ -220,97 +200,108 @@ class ProductionPlanningTool(Document): def validate_data(self): self.validate_company() for d in self.get('items'): - validate_bom_no(d.item_code, d.bom_no) + if not d.bom_no: + frappe.throw(_("Please select BOM for Item in Row {0}".format(d.idx))) + else: + validate_bom_no(d.item_code, d.bom_no) + if not flt(d.planned_qty): frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx)) - def raise_production_order(self): + def raise_production_orders(self): """It will raise production order (Draft) for all distinct FG items""" self.validate_data() from erpnext.utilities.transaction_base import validate_uom_is_integer validate_uom_is_integer(self, "stock_uom", "planned_qty") - items = self.get_distinct_items_and_boms()[1] - pro = self.create_production_order(items) - if pro: - pro = ["""%s""" % \ - (p, p) for p in pro] - msgprint(_("{0} created").format(comma_and(pro))) + items = self.get_production_items() + + pro_list = [] + frappe.flags.mute_messages = True + + for key in items: + production_order = self.create_production_order(items[key]) + if production_order: + pro_list.append(production_order) + + frappe.flags.mute_messages = False + + if pro_list: + pro_list = ["""%s""" % \ + (p, p) for p in pro_list] + msgprint(_("{0} created").format(comma_and(pro_list))) else : msgprint(_("No Production Orders created")) - def get_distinct_items_and_boms(self): - """ Club similar BOM and item for processing + def get_production_items(self): + item_dict = {} + for d in self.get("items"): + item_details= { + "production_item" : d.item_code, + "sales_order" : d.sales_order, + "material_request" : d.material_request, + "material_request_item" : d.material_request_item, + "bom_no" : d.bom_no, + "description" : d.description, + "stock_uom" : d.stock_uom, + "company" : self.company, + "wip_warehouse" : "", + "fg_warehouse" : d.warehouse, + "status" : "Draft", + } + + """ Club similar BOM and item for processing in case of Sales Orders """ + if self.plan_using == "Sales Order": + item_details.update({ + "qty":flt(item_dict.get((d.item_code, d.sales_order, d.warehouse),{}) + .get("qty")) + flt(d.planned_qty) + }) + item_dict[(d.item_code, d.sales_order, d.warehouse)] = item_details + + elif self.plan_using == "Material Request": + item_details.update({ + "qty": d.planned_qty + }) + item_dict[(d.item_code, d.material_request_item, d.warehouse)] = item_details + + return item_dict + + def create_production_order(self, item_dict): + """Create production order. Called from Production Planning Tool""" + from erpnext.manufacturing.doctype.production_order.production_order import OverProductionError, get_default_warehouse + warehouse = get_default_warehouse() + pro = frappe.new_doc("Production Order") + pro.update(item_dict) + pro.set_production_order_operations() + if warehouse: + pro.wip_warehouse = warehouse.get('wip_warehouse') + if not pro.fg_warehouse: + pro.fg_warehouse = warehouse.get('fg_warehouse') + + try: + pro.insert() + return pro.name + except OverProductionError: + pass + + def get_so_wise_planned_qty(self): + """ bom_dict { bom_no: ['sales_order', 'qty'] } """ - item_dict, bom_dict = {}, {} + bom_dict = {} for d in self.get("items"): - if d.bom_no: - if self.plan_using == "Sales Order": - bom_dict.setdefault(d.bom_no, []).append([d.sales_order, flt(d.planned_qty)]) - if frappe.db.get_value("Item", d.item_code, "is_pro_applicable"): - item_dict[(d.item_code, d.sales_order, d.warehouse)] = { - "production_item" : d.item_code, - "sales_order" : d.sales_order, - "qty" : flt(item_dict.get((d.item_code, d.sales_order, d.warehouse), - {}).get("qty")) + flt(d.planned_qty), - "bom_no" : d.bom_no, - "description" : d.description, - "stock_uom" : d.stock_uom, - "company" : self.company, - "wip_warehouse" : "", - "fg_warehouse" : d.warehouse, - "status" : "Draft", - } - elif self.plan_using == "Material Request": - bom_dict.setdefault(d.bom_no, []).append([d.material_request_item, flt(d.planned_qty)]) - if frappe.db.get_value("Item", d.item_code, "is_pro_applicable"): - item_dict[(d.item_code, d.material_request_item, d.warehouse)] = { - "production_item" : d.item_code, - "material_request" : d.material_request, - "material_request_item" : d.material_request_item, - "qty" : d.planned_qty, - "bom_no" : d.bom_no, - "description" : d.description, - "stock_uom" : d.stock_uom, - "company" : self.company, - "wip_warehouse" : "", - "fg_warehouse" : d.warehouse, - "status" : "Draft", - } - return bom_dict, item_dict - - def create_production_order(self, items): - """Create production order. Called from Production Planning Tool""" - from erpnext.manufacturing.doctype.production_order.production_order import OverProductionError, get_default_warehouse - warehouse = get_default_warehouse() - pro_list = [] - for key in items: - pro = frappe.new_doc("Production Order") - pro.update(items[key]) - pro.set_production_order_operations() - if warehouse: - pro.wip_warehouse = warehouse.get('wip_warehouse') - if not pro.fg_warehouse: - pro.fg_warehouse = warehouse.get('fg_warehouse') - frappe.flags.mute_messages = True - - try: - pro.insert() - pro_list.append(pro.name) - except OverProductionError: - pass - - frappe.flags.mute_messages = False - return pro_list - + if self.plan_using == "Sales Order": + bom_dict.setdefault(d.bom_no, []).append({d.sales_order: flt(d.planned_qty)}) + elif self.plan_using == "Material Request": + bom_dict.setdefault(d.bom_no, []).append({d.material_request_item: flt(d.planned_qty)}) + def download_raw_materials(self): """ Create csv data for required raw material to produce finished goods""" self.validate_data() - bom_dict = self.get_distinct_items_and_boms()[0] + bom_dict = self.get_so_wise_planned_qty() self.get_raw_materials(bom_dict) return self.get_csv() @@ -389,7 +380,7 @@ class ProductionPlanningTool(Document): if not self.purchase_request_for_warehouse: frappe.throw(_("Please enter Warehouse for which Material Request will be raised")) - bom_dict = self.get_distinct_items_and_boms()[0] + bom_dict = self.get_so_wise_planned_qty() self.get_raw_materials(bom_dict) if self.item_dict: @@ -445,12 +436,12 @@ class ProductionPlanningTool(Document): def insert_purchase_request(self): items_to_be_requested = self.get_requested_items() - purchase_request_list = [] + material_request_list = [] if items_to_be_requested: for item in items_to_be_requested: item_wrapper = frappe.get_doc("Item", item) - pr_doc = frappe.new_doc("Material Request") - pr_doc.update({ + material_request = frappe.new_doc("Material Request") + material_request.update({ "transaction_date": nowdate(), "status": "Draft", "company": self.company, @@ -458,7 +449,7 @@ class ProductionPlanningTool(Document): "material_request_type": "Purchase" }) for sales_order, requested_qty in items_to_be_requested[item].items(): - pr_doc.append("items", { + material_request.append("items", { "doctype": "Material Request Item", "__islocal": 1, "item_code": item, @@ -473,13 +464,13 @@ class ProductionPlanningTool(Document): "sales_order": sales_order if sales_order!="No Sales Order" else None }) - pr_doc.flags.ignore_permissions = 1 - pr_doc.submit() - purchase_request_list.append(pr_doc.name) + material_request.flags.ignore_permissions = 1 + material_request.submit() + material_request_list.append(material_request.name) - if purchase_request_list: - pur_req = ["""%s""" % \ - (p, p) for p in purchase_request_list] - msgprint(_("Material Requests {0} created").format(comma_and(pur_req))) + if material_request_list: + message = ["""%s""" % \ + (p, p) for p in material_request_list] + msgprint(_("Material Requests {0} created").format(comma_and(message))) else: msgprint(_("Nothing to request"))