From faf251a53e565c53b94610fb0d41fb3c21d34f96 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 30 Nov 2012 14:36:01 +0530 Subject: [PATCH] Production cleanup with linking with sales order --- .../production_control/production_control.py | 44 +- .../production_order/production_order.js | 17 - .../production_order/production_order.py | 8 + .../production_order/production_order.txt | 678 +++++++++--------- .../production_planning_tool.py | 106 ++- .../production_planning_tool.txt | 11 +- selling/doctype/sales_order/sales_order.py | 25 +- stock/doctype/stock_entry/stock_entry.py | 37 +- stock/doctype/stock_entry/stock_entry.txt | 13 +- 9 files changed, 452 insertions(+), 487 deletions(-) diff --git a/production/doctype/production_control/production_control.py b/production/doctype/production_control/production_control.py index ef01cb1066..4cb60309c9 100644 --- a/production/doctype/production_control/production_control.py +++ b/production/doctype/production_control/production_control.py @@ -73,7 +73,7 @@ class DocType: # Raise Production Order - def create_production_order(self,company, pp_items): + def create_production_order(self, items): """Create production order. Called from Production Planning Tool""" default_values = { @@ -82,15 +82,20 @@ class DocType: 'wip_warehouse' : '', 'fg_warehouse' : '', 'status' : 'Draft', - 'company' : company, - 'fiscal_year' : get_defaults()['fiscal_year'] + 'fiscal_year' : get_defaults()['fiscal_year'] } pro_list = [] - for d in pp_items: + for item_so in items: + if item_so[1]: + self.validate_production_order_against_so( + item_so[0], item_so[1], items[item_so].get("qty")) + pro_doc = Document('Production Order') - for key in d.keys(): - pro_doc.fields[key] = d[key] + pro_doc.production_item = item_so[0] + pro_doc.sales_order = item_so[1] + for key in items[item_so]: + pro_doc.fields[key] = items[item_so][key] for key in default_values: pro_doc.fields[key] = default_values[key] @@ -100,7 +105,30 @@ class DocType: return pro_list - + def validate_production_order_against_so(self, item, sales_order, qty, pro_order=None): + # already ordered qty + ordered_qty_against_so = webnotes.conn.sql("""select sum(qty) from `tabProduction Order` + where production_item = %s and sales_order = %s and name != %s""", + (item, sales_order, cstr(pro_order)))[0][0] + # qty including current + total_ordered_qty_against_so = flt(ordered_qty_against_so) + flt(qty) + + # get qty from Sales Order Item table + so_item_qty = webnotes.conn.sql("""select sum(qty) from `tabSales Order Item` + where parent = %s and item_code = %s""", (sales_order, item))[0][0] + # get qty from Packing Item table + dnpi_qty = webnotes.conn.sql("""select sum(qty) from `tabDelivery Note Packing Item` + where parent = %s and parenttype = 'Sales Order' and item_code = %s""", + (sales_order, item))[0][0] + # total qty in SO + so_qty = flt(so_item_qty) + flt(dnpi_qty) + + if total_ordered_qty_against_so > so_qty: + msgprint("""Total production order qty for item: %s against sales order: %s \ + will be %s, which is greater than sales order qty (%s). + Please reduce qty or remove the item.""" % + (item, sales_order, total_ordered_qty_against_so, so_qty), raise_exception=1) + def update_bom(self, bom_no): main_bom_list = self.traverse_bom_tree(bom_no, 1) main_bom_list.reverse() @@ -113,4 +141,4 @@ class DocType: bom_obj.update_flat_bom_engine() bom_obj.doc.docstatus = 1 bom_obj.doc.save() - self.check_bom_list.append(bom) + self.check_bom_list.append(bom) \ No newline at end of file diff --git a/production/doctype/production_order/production_order.js b/production/doctype/production_order/production_order.js index 411f77999b..79497573b1 100644 --- a/production/doctype/production_order/production_order.js +++ b/production/doctype/production_order/production_order.js @@ -18,15 +18,7 @@ cur_frm.cscript.onload = function(doc, dt, dn) { if (!doc.posting_date) doc.transaction_date = dateutil.obj_to_str(new Date()); if (!doc.status) doc.status = 'Draft'; - cfn_set_fields(doc, dt, dn); - - if (doc.origin != "MRP"){ - doc.origin = "Manual"; - set_field_permlevel('production_item', 0); - set_field_permlevel('bom_no', 0); - set_field_permlevel('consider_sa_items',0); - } } // ================================== Refresh ========================================== @@ -48,15 +40,10 @@ var cfn_set_fields = function(doc, dt, dn) { } } - -// ================================================================================================== - cur_frm.cscript.production_item = function(doc, dt, dn) { get_server_fields('get_item_detail',doc.production_item,'',doc,dt,dn,1); } -// Stop PRODUCTION ORDER -// cur_frm.cscript['Stop Production Order'] = function() { var doc = cur_frm.doc; var check = confirm("Do you really want to stop production order: " + doc.name); @@ -65,8 +52,6 @@ cur_frm.cscript['Stop Production Order'] = function() { } } -// Unstop PRODUCTION ORDER -// cur_frm.cscript['Unstop Production Order'] = function() { var doc = cur_frm.doc; var check = confirm("Do really want to unstop production order: " + doc.name); @@ -97,8 +82,6 @@ cur_frm.cscript.make_se = function(doc, process) { loaddoc('Stock Entry', se.name); } - -// ================================================================================================== cur_frm.fields_dict['production_item'].get_query = function(doc) { return 'SELECT DISTINCT `tabItem`.`name`, `tabItem`.`description` FROM `tabItem` WHERE (IFNULL(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` = "0000-00-00" OR `tabItem`.`end_of_life` > NOW()) AND `tabItem`.docstatus != 2 AND `tabItem`.is_pro_applicable = "Yes" AND `tabItem`.%(key)s LIKE "%s" ORDER BY `tabItem`.`name` LIMIT 50'; } diff --git a/production/doctype/production_order/production_order.py b/production/doctype/production_order/production_order.py index 05eaa75230..fd9a614252 100644 --- a/production/doctype/production_order/production_order.py +++ b/production/doctype/production_order/production_order.py @@ -72,6 +72,14 @@ class DocType: msgprint("""Incorrect BOM: %s entered. May be BOM not exists or inactive or not submitted or for some other item.""" % cstr(self.doc.bom_no), raise_exception=1) + + if self.doc.sales_order: + if not webnotes.conn.sql("""select name from `tabSales Order` + where name=%s and docstatus = 1""", self.doc.sales_order): + msgprint("Sales Order: %s is not valid" % self.doc.sales_order, raise_exception=1) + + get_obj("Production Control").validate_production_order_against_so( + self.doc.production_item, self.doc.sales_order, self.doc.qty, self.doc.name) def stop_unstop(self, status): diff --git a/production/doctype/production_order/production_order.txt b/production/doctype/production_order/production_order.txt index c244399278..369efd4d2a 100644 --- a/production/doctype/production_order/production_order.txt +++ b/production/doctype/production_order/production_order.txt @@ -1,358 +1,324 @@ -# DocType, Production Order [ - - # These values are common in all dictionaries - { - 'creation': '2012-05-15 12:14:48', - 'docstatus': 0, - 'modified': '2012-05-28 19:03:56', - 'modified_by': u'Administrator', - 'owner': u'Administrator' - }, - - # These values are common for all DocType - { - '_last_update': u'1325837006', - 'colour': u'White:FFF', - 'default_print_format': u'Standard', - 'doctype': 'DocType', - 'in_create': 0, - 'is_submittable': 1, - 'module': u'Production', - 'name': '__common__', - 'section_style': u'Tabbed', - 'server_code_error': u' ', - 'show_in_menu': 0, - 'version': 1 - }, - - # These values are common for all DocField - { - 'doctype': u'DocField', - 'name': '__common__', - 'parent': u'Production Order', - 'parentfield': u'fields', - 'parenttype': u'DocType' - }, - - # These values are common for all DocPerm - { - 'doctype': u'DocPerm', - 'name': '__common__', - 'parent': u'Production Order', - 'parentfield': u'permissions', - 'parenttype': u'DocType', - 'read': 1 - }, - - # DocType, Production Order - { - 'doctype': 'DocType', - 'name': u'Production Order' - }, - - # DocPerm - { - 'amend': 1, - 'cancel': 1, - 'create': 1, - 'doctype': u'DocPerm', - 'permlevel': 0, - 'role': u'System Manager', - 'submit': 1, - 'write': 1 - }, - - # DocPerm - { - 'doctype': u'DocPerm', - 'permlevel': 1, - 'role': u'All' - }, - - # DocPerm - { - 'amend': 1, - 'cancel': 1, - 'create': 1, - 'doctype': u'DocPerm', - 'permlevel': 0, - 'role': u'Production Manager', - 'submit': 1, - 'write': 1 - }, - - # DocPerm - { - 'amend': 1, - 'cancel': 1, - 'create': 1, - 'doctype': u'DocPerm', - 'permlevel': 0, - 'role': u'Production User', - 'submit': 1, - 'write': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'column_break0', - 'fieldtype': u'Column Break', - 'permlevel': 0, - 'width': u'50%' - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'Item for which this Production Order is raised.', - 'doctype': u'DocField', - 'fieldname': u'production_item', - 'fieldtype': u'Link', - 'in_filter': 1, - 'label': u'Production Item', - 'oldfieldname': u'production_item', - 'oldfieldtype': u'Link', - 'options': u'Item', - 'permlevel': 1, - 'reqd': 1, - 'trigger': u'Client' - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'description', - 'fieldtype': u'Text', - 'label': u'Description', - 'oldfieldname': u'description', - 'oldfieldtype': u'Text', - 'permlevel': 0, - 'width': u'300px' - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'stock_uom', - 'fieldtype': u'Data', - 'label': u'Stock UOM', - 'oldfieldname': u'stock_uom', - 'oldfieldtype': u'Data', - 'permlevel': 1 - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'Bill of Material which was considered for manufacturing the production item.', - 'doctype': u'DocField', - 'fieldname': u'bom_no', - 'fieldtype': u'Link', - 'label': u'BOM No', - 'oldfieldname': u'bom_no', - 'oldfieldtype': u'Link', - 'options': u'BOM', - 'permlevel': 1, - 'reqd': 1, - 'trigger': u'Client' - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'Quantity of item for which Production Order is raised.', - 'doctype': u'DocField', - 'fieldname': u'qty', - 'fieldtype': u'Currency', - 'label': u'Qty', - 'oldfieldname': u'qty', - 'oldfieldtype': u'Currency', - 'permlevel': 0, - 'reqd': 1 - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'The warehouse for finished goods where stock of produced items will be updated.', - 'doctype': u'DocField', - 'fieldname': u'fg_warehouse', - 'fieldtype': u'Link', - 'in_filter': 1, - 'label': u'FG Warehouse', - 'oldfieldname': u'fg_warehouse', - 'oldfieldtype': u'Link', - 'options': u'Warehouse', - 'permlevel': 0, - 'reqd': 1 - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'The work in progress warehouse where raw materials will be operated upon to create finished goods.', - 'doctype': u'DocField', - 'fieldname': u'wip_warehouse', - 'fieldtype': u'Link', - 'in_filter': 1, - 'label': u'WIP Warehouse', - 'oldfieldname': u'wip_warehouse', - 'oldfieldtype': u'Link', - 'options': u'Warehouse', - 'permlevel': 0, - 'reqd': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'amended_from', - 'fieldtype': u'Data', - 'label': u'Amended From', - 'no_copy': 1, - 'oldfieldname': u'amended_from', - 'oldfieldtype': u'Data', - 'permlevel': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'amendment_date', - 'fieldtype': u'Date', - 'label': u'Amendment Date', - 'no_copy': 1, - 'oldfieldname': u'amendment_date', - 'oldfieldtype': u'Date', - 'permlevel': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'column_break1', - 'fieldtype': u'Column Break', - 'oldfieldtype': u'Column Break', - 'permlevel': 0, - 'width': u'50%' - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'The date on which current entry will get or has actually executed.', - 'doctype': u'DocField', - 'fieldname': u'posting_date', - 'fieldtype': u'Date', - 'label': u'Posting Date', - 'oldfieldname': u'posting_date', - 'oldfieldtype': u'Date', - 'permlevel': 0, - 'reqd': 1 - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'Select "Yes" if stock is maintained and tracked for sub-assembly items. Select "No" if you want child items of sub-assembly for material transfer.', - 'doctype': u'DocField', - 'fieldname': u'consider_sa_items', - 'fieldtype': u'Select', - 'in_filter': 1, - 'label': u'Consider SA Items as raw material', - 'oldfieldname': u'consider_sa_items', - 'oldfieldtype': u'Select', - 'options': u'\nYes\nNo', - 'permlevel': 1, - 'reqd': 1 - }, - - # DocField - { - 'description': u'Select name of the project if Production Order need to be created against any project', - 'doctype': u'DocField', - 'fieldname': u'project_name', - 'fieldtype': u'Link', - 'in_filter': 1, - 'label': u'Project Name', - 'oldfieldname': u'project_name', - 'oldfieldtype': u'Link', - 'options': u'Project', - 'permlevel': 0, - 'trigger': u'Client' - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'origin', - 'fieldtype': u'Select', - 'in_filter': 1, - 'label': u'Origin', - 'no_copy': 1, - 'oldfieldname': u'origin', - 'oldfieldtype': u'Select', - 'options': u'Manual\nMRP', - 'permlevel': 1, - 'reqd': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'status', - 'fieldtype': u'Select', - 'in_filter': 1, - 'label': u'Status', - 'no_copy': 1, - 'oldfieldname': u'status', - 'oldfieldtype': u'Select', - 'options': u'\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled', - 'permlevel': 1, - 'reqd': 1, - 'search_index': 1 - }, - - # DocField - { - 'colour': u'White:FFF', - 'description': u'Updated after finished goods are transferred to FG Warehouse through Stock Entry', - 'doctype': u'DocField', - 'fieldname': u'produced_qty', - 'fieldtype': u'Currency', - 'label': u'Produced Qty', - 'no_copy': 1, - 'oldfieldname': u'produced_qty', - 'oldfieldtype': u'Currency', - 'permlevel': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'company', - 'fieldtype': u'Link', - 'label': u'Company', - 'oldfieldname': u'company', - 'oldfieldtype': u'Link', - 'options': u'Company', - 'permlevel': 0, - 'reqd': 1 - }, - - # DocField - { - 'doctype': u'DocField', - 'fieldname': u'fiscal_year', - 'fieldtype': u'Select', - 'in_filter': 1, - 'label': u'Fiscal Year', - 'oldfieldname': u'fiscal_year', - 'oldfieldtype': u'Select', - 'options': u'link:Fiscal Year', - 'permlevel': 0, - 'reqd': 1 - } + { + "owner": "Administrator", + "docstatus": 0, + "creation": "2012-07-03 13:30:03", + "modified_by": "Administrator", + "modified": "2012-11-30 14:28:03" + }, + { + "is_submittable": 1, + "in_create": 0, + "default_print_format": "Standard", + "doctype": "DocType", + "module": "Production", + "name": "__common__" + }, + { + "name": "__common__", + "parent": "Production Order", + "doctype": "DocField", + "parenttype": "DocType", + "parentfield": "fields" + }, + { + "name": "__common__", + "parent": "Production Order", + "read": 1, + "doctype": "DocPerm", + "parenttype": "DocType", + "parentfield": "permissions" + }, + { + "name": "Production Order", + "doctype": "DocType" + }, + { + "doctype": "DocField", + "width": "50%", + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "Item for which this Production Order is raised.", + "oldfieldtype": "Link", + "colour": "White:FFF", + "doctype": "DocField", + "label": "Production Item", + "oldfieldname": "production_item", + "permlevel": 0, + "trigger": "Client", + "fieldname": "production_item", + "fieldtype": "Link", + "reqd": 1, + "in_filter": 1, + "options": "Item" + }, + { + "description": "Bill of Material which was considered for manufacturing the production item.", + "oldfieldtype": "Link", + "colour": "White:FFF", + "doctype": "DocField", + "label": "BOM No", + "oldfieldname": "bom_no", + "permlevel": 0, + "trigger": "Client", + "fieldname": "bom_no", + "fieldtype": "Link", + "reqd": 1, + "options": "BOM" + }, + { + "description": "Quantity of item for which Production Order is raised.", + "oldfieldtype": "Currency", + "colour": "White:FFF", + "doctype": "DocField", + "label": "Qty", + "oldfieldname": "qty", + "fieldname": "qty", + "fieldtype": "Currency", + "reqd": 1, + "permlevel": 0 + }, + { + "description": "The date on which current entry will get or has actually executed.", + "oldfieldtype": "Date", + "colour": "White:FFF", + "doctype": "DocField", + "label": "Posting Date", + "oldfieldname": "posting_date", + "fieldname": "posting_date", + "fieldtype": "Date", + "reqd": 1, + "permlevel": 0 + }, + { + "oldfieldtype": "Column Break", + "doctype": "DocField", + "width": "50%", + "fieldname": "column_break1", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "description": "The warehouse for finished goods where stock of produced items will be updated.", + "oldfieldtype": "Link", + "colour": "White:FFF", + "doctype": "DocField", + "label": "FG Warehouse", + "oldfieldname": "fg_warehouse", + "permlevel": 0, + "fieldname": "fg_warehouse", + "fieldtype": "Link", + "reqd": 1, + "in_filter": 1, + "options": "Warehouse" + }, + { + "description": "The work in progress warehouse where raw materials will be operated upon to create finished goods.", + "oldfieldtype": "Link", + "colour": "White:FFF", + "doctype": "DocField", + "label": "WIP Warehouse", + "oldfieldname": "wip_warehouse", + "permlevel": 0, + "fieldname": "wip_warehouse", + "fieldtype": "Link", + "reqd": 1, + "in_filter": 1, + "options": "Warehouse" + }, + { + "description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.", + "default": "1", + "oldfieldtype": "Select", + "colour": "White:FFF", + "doctype": "DocField", + "label": "Use Multi-Level BOM", + "oldfieldname": "consider_sa_items", + "fieldname": "use_multi_level_bom", + "fieldtype": "Check", + "reqd": 1, + "in_filter": 1, + "permlevel": 0 + }, + { + "description": "Updated after finished goods are transferred to FG Warehouse through Stock Entry", + "no_copy": 1, + "oldfieldtype": "Currency", + "colour": "White:FFF", + "doctype": "DocField", + "label": "Produced Qty", + "oldfieldname": "produced_qty", + "fieldname": "produced_qty", + "fieldtype": "Currency", + "permlevel": 1 + }, + { + "doctype": "DocField", + "label": "More Info", + "fieldname": "more_info", + "fieldtype": "Section Break", + "permlevel": 0 + }, + { + "doctype": "DocField", + "width": "50%", + "fieldname": "column_break2", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "no_copy": 1, + "oldfieldtype": "Select", + "doctype": "DocField", + "label": "Origin", + "oldfieldname": "origin", + "options": "Manual\nMRP", + "fieldname": "origin", + "fieldtype": "Select", + "reqd": 1, + "permlevel": 0, + "in_filter": 1 + }, + { + "no_copy": 1, + "oldfieldtype": "Select", + "doctype": "DocField", + "label": "Status", + "oldfieldname": "status", + "permlevel": 1, + "fieldname": "status", + "fieldtype": "Select", + "search_index": 1, + "reqd": 1, + "options": "\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled", + "in_filter": 1 + }, + { + "doctype": "DocField", + "label": "Sales Order", + "options": "Sales Order", + "fieldname": "sales_order", + "fieldtype": "Link", + "permlevel": 0 + }, + { + "description": "Select name of the project if Production Order need to be created against any project", + "oldfieldtype": "Link", + "label": "Project Name", + "oldfieldname": "project_name", + "trigger": "Client", + "fieldname": "project_name", + "fieldtype": "Link", + "doctype": "DocField", + "options": "Project", + "permlevel": 0, + "in_filter": 1 + }, + { + "oldfieldtype": "Link", + "doctype": "DocField", + "label": "Company", + "oldfieldname": "company", + "options": "Company", + "fieldname": "company", + "fieldtype": "Link", + "reqd": 1, + "permlevel": 0 + }, + { + "oldfieldtype": "Select", + "doctype": "DocField", + "label": "Fiscal Year", + "oldfieldname": "fiscal_year", + "options": "link:Fiscal Year", + "fieldname": "fiscal_year", + "fieldtype": "Select", + "reqd": 1, + "permlevel": 0, + "in_filter": 1 + }, + { + "no_copy": 1, + "oldfieldtype": "Data", + "doctype": "DocField", + "label": "Amended From", + "oldfieldname": "amended_from", + "fieldname": "amended_from", + "fieldtype": "Data", + "permlevel": 1 + }, + { + "no_copy": 1, + "oldfieldtype": "Date", + "doctype": "DocField", + "label": "Amendment Date", + "oldfieldname": "amendment_date", + "fieldname": "amendment_date", + "fieldtype": "Date", + "permlevel": 1 + }, + { + "doctype": "DocField", + "width": "50%", + "fieldname": "column_break3", + "fieldtype": "Column Break", + "permlevel": 0 + }, + { + "oldfieldtype": "Data", + "doctype": "DocField", + "label": "Stock UOM", + "oldfieldname": "stock_uom", + "fieldname": "stock_uom", + "fieldtype": "Data", + "permlevel": 1 + }, + { + "oldfieldtype": "Text", + "doctype": "DocField", + "label": "Production Item Description", + "oldfieldname": "description", + "width": "300px", + "fieldname": "description", + "fieldtype": "Text", + "permlevel": 0 + }, + { + "amend": 1, + "create": 1, + "doctype": "DocPerm", + "submit": 1, + "write": 1, + "role": "System Manager", + "cancel": 1, + "permlevel": 0 + }, + { + "doctype": "DocPerm", + "role": "All", + "permlevel": 1 + }, + { + "amend": 1, + "create": 1, + "doctype": "DocPerm", + "submit": 1, + "write": 1, + "role": "Production Manager", + "cancel": 1, + "permlevel": 0 + }, + { + "amend": 1, + "create": 1, + "doctype": "DocPerm", + "submit": 1, + "write": 1, + "role": "Production User", + "cancel": 1, + "permlevel": 0 + } ] \ No newline at end of file diff --git a/production/doctype/production_planning_tool/production_planning_tool.py b/production/doctype/production_planning_tool/production_planning_tool.py index 10ef652ccd..598df42621 100644 --- a/production/doctype/production_planning_tool/production_planning_tool.py +++ b/production/doctype/production_planning_tool/production_planning_tool.py @@ -170,39 +170,58 @@ class DocType: msgprint("Please Enter Planned Qty for item: %s at row no: %s" % (d.item_code, d.idx), raise_exception=1) - def validate_bom_no(self, d): if not d.bom_no: - msgprint("Please enter bom no for item: %s at row no: %s" % (d.item_code, d.idx), raise_exception=1) + msgprint("Please enter bom no for item: %s at row no: %s" % + (d.item_code, d.idx), raise_exception=1) else: - bom = sql("""select name from `tabBOM` where item = %s and docstatus = 1 - and name = %s and ifnull(is_active, 'No') = 'Yes'""", (d.item_code, d.bom_no), as_dict = 1) + bom = sql("""select name from `tabBOM` where name = %s and item = %s + and docstatus = 1 and ifnull(is_active, 'No') = 'Yes'""", + (d.bom_no, d.item_code), as_dict = 1) if not bom: msgprint("""Incorrect BOM No: %s entered for item: %s at row no: %s - May be BOM is inactive or for other item or does not exists in the system"""% (d.bom_no, d.item_doce, d.idx)) + May be BOM is inactive or for other item or does not exists in the system""" % + (d.bom_no, d.item_doce, d.idx), raise_exception=1) + + def raise_production_order(self): + """It will raise production order (Draft) for all distinct FG items""" + self.validate_company() + self.validate_data() + + items = self.get_distinct_items_and_boms()[1] + pro = get_obj('Production Control').create_production_order(items) + if pro: + msgprint("Following Production Order has been generated:\n" + '\n'.join(pro)) + else : + msgprint("No Production Order generated.") + def get_distinct_items_and_boms(self): + """ Club similar BOM and item for processing""" + item_dict, bom_dict = {}, {} + for d in self.doclist.get({"parentfield": "pp_details"}): + bom_dict[d.bom_no] = bom_dict.get(d.bom_no, 0) + flt(d.planned_qty) + item_dict[(d.item_code, d.sales_order)] = { + "qty" : flt(item_dict.get((d.item_code, d.sales_order), {}).get("qty")) + \ + flt(d.planned_qty), + "bom_no": d.bom_no, + "description": d.description, + "stock_uom": d.stock_uom, + "use_multi_level_bom": self.doc.use_multi_level_bom, + "company": self.doc.company, + } + return bom_dict, item_dict def download_raw_materials(self): """ Create csv data for required raw material to produce finished goods""" - bom_dict = self.get_distinct_bom(action = 'download_rm') + bom_dict = self.get_distinct_items_and_boms()[0] self.get_raw_materials(bom_dict) return self.get_csv() def get_raw_materials(self, bom_dict): """ Get raw materials considering sub-assembly items """ for bom in bom_dict: - if self.doc.use_multi_level_bom == 'No': - # Get all raw materials considering SA items as raw materials, - # so no childs of SA items - fl_bom_items = sql(""" - select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom - from `tabBOM Item` - where parent = '%s' and docstatus < 2 - group by item_code - """ % (flt(bom_dict[bom]), bom)) - - else: + if self.doc.use_multi_level_bom: # get all raw materials with sub assembly childs fl_bom_items = sql(""" select @@ -216,7 +235,16 @@ class DocType: ) a group by item_code,stock_uom """ , (flt(bom_dict[bom]), bom)) - + else: + # Get all raw materials considering SA items as raw materials, + # so no childs of SA items + fl_bom_items = sql(""" + select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom + from `tabBOM Item` + where parent = '%s' and docstatus < 2 + group by item_code + """ % (flt(bom_dict[bom]), bom)) + self.make_items_dict(fl_bom_items) @@ -238,44 +266,4 @@ class DocType: if item_qty: item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty]) - return item_list - - def raise_production_order(self): - """It will raise production order (Draft) for all distinct FG items""" - self.validate_company() - self.validate_data() - - pp_items = self.get_distinct_bom(action = 'raise_pro_order') - pro = get_obj(dt = 'Production Control').create_production_order(self.doc.company, pp_items) - if pro: - for d in getlist(self.doclist, 'pp_details'): - d.is_pro_created = 1 - msgprint("Following Production Order has been generated:\n" + '\n'.join(pro)) - else : - msgprint("No Production Order generated.") - - - def get_distinct_bom(self, action): - """ Club similar BOM and item for processing""" - - bom_dict, item_dict, pp_items = {}, {}, [] - for d in getlist(self.doclist, 'pp_details'): - if action == 'download_rm': - bom_dict[d.bom_no] = bom_dict.get(d.bom_no, 0) + flt(d.planned_qty) - elif not d.is_pro_created: - item_dict[d.item_code] = [ - (flt(item_dict.get(d.item_code, [0])[0]) + flt(d.planned_qty)), - d.bom_no, d.description, d.stock_uom] - - if action == 'raise_pro_order': - for d in item_dict: - pp_items.append({ - 'production_item' : d, - 'qty' : item_dict[d][0], - 'bom_no' : item_dict[d][1], - 'description' : item_dict[d][2], - 'stock_uom' : item_dict[d][3], - 'consider_sa_items' : self.doc.use_multi_level_bom == "Yes" and "No" or "Yes" - }) - - return action == 'download_rm' and bom_dict or pp_items \ No newline at end of file + return item_list \ No newline at end of file diff --git a/production/doctype/production_planning_tool/production_planning_tool.txt b/production/doctype/production_planning_tool/production_planning_tool.txt index 3add84b0c4..42a76f7c30 100644 --- a/production/doctype/production_planning_tool/production_planning_tool.txt +++ b/production/doctype/production_planning_tool/production_planning_tool.txt @@ -4,7 +4,7 @@ "docstatus": 0, "creation": "2012-07-03 13:30:03", "modified_by": "Administrator", - "modified": "2012-11-29 17:52:20" + "modified": "2012-11-30 14:08:55" }, { "read_only": 1, @@ -159,15 +159,14 @@ "options": "clear_item_table" }, { - "description": "If selected as \"No\", all sub-assembly items will be treated as a raw material. Otherwise, BOM for sub-assembly items will be considered for raw materials.", - "default": "Yes", + "description": "If checked, BOM for sub-assembly items will be considered for raw materials. Otherwise, all sub-assembly items will be treated as a raw material.", + "default": "1", "colour": "White:FFF", "doctype": "DocField", - "label": "Use multi-level BOM", + "label": "Use Multi-Level BOM", "reqd": 1, "fieldname": "use_multi_level_bom", - "fieldtype": "Select", - "options": "No\nYes" + "fieldtype": "Check" }, { "doctype": "DocField", diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index a79c84749e..a511d4b3c1 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -337,9 +337,7 @@ class DocType(TransactionBase): #update enquiry self.update_enquiry_status(d.prevdoc_docname, 'Quotation Sent') - - # Submit - # ------- + def on_submit(self): self.check_prev_docstatus() self.update_stock_ledger(update_stock = 1) @@ -347,17 +345,11 @@ class DocType(TransactionBase): update_customer = sql("update `tabCustomer` set last_sales_order = '%s', modified = '%s' where name = '%s'" %(self.doc.name, self.doc.modified, self.doc.customer)) get_obj('Sales Common').check_credit(self,self.doc.grand_total) - # Check for Approving Authority get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.grand_total, self) - #update prevdoc status self.update_prevdoc_status('submit') - # set SO status set(self.doc, 'status', 'Submitted') - -# ON CANCEL -# =============================================================================================== def on_cancel(self): # Cannot cancel stopped SO if self.doc.status == 'Stopped': @@ -366,13 +358,10 @@ class DocType(TransactionBase): self.check_nextdoc_docstatus() self.update_stock_ledger(update_stock = -1) - #update prevdoc status self.update_prevdoc_status('cancel') - # ::::::::: SET SO STATUS :::::::::: set(self.doc, 'status', 'Cancelled') - # CHECK NEXT DOCSTATUS # does not allow to cancel document if DN or RV made against it is SUBMITTED # ---------------------------------------------------------------------------- def check_nextdoc_docstatus(self): @@ -380,18 +369,28 @@ class DocType(TransactionBase): submit_dn = sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.prevdoc_docname = '%s' and t1.docstatus = 1" % (self.doc.name)) if submit_dn: msgprint("Delivery Note : " + cstr(submit_dn[0][0]) + " has been submitted against " + cstr(self.doc.doctype) + ". Please cancel Delivery Note : " + cstr(submit_dn[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1) + # Checks Sales Invoice submit_rv = sql("select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 where t1.name = t2.parent and t2.sales_order = '%s' and t1.docstatus = 1" % (self.doc.name)) if submit_rv: msgprint("Sales Invoice : " + cstr(submit_rv[0][0]) + " has already been submitted against " +cstr(self.doc.doctype)+ ". Please cancel Sales Invoice : "+ cstr(submit_rv[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1) + #check maintenance schedule submit_ms = sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.doc.name) if submit_ms: msgprint("Maintenance Schedule : " + cstr(submit_ms[0][0]) + " has already been submitted against " +cstr(self.doc.doctype)+ ". Please cancel Maintenance Schedule : "+ cstr(submit_ms[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1) + + # check maintenance visit submit_mv = sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.doc.name) if submit_mv: msgprint("Maintenance Visit : " + cstr(submit_mv[0][0]) + " has already been submitted against " +cstr(self.doc.doctype)+ ". Please cancel Maintenance Visit : " + cstr(submit_mv[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1) - + + # check production order + pro_order = sql("""select name from `tabProduction Order` where sales_order = %s and docstatus = 1""", self.doc.name) + if pro_order: + msgprint("""Production Order: %s exists against this sales order. + Please cancel production order first and then cancel this sales order""" % + pro_order[0][0], raise_exception=1) def check_modified_date(self): mod_db = sql("select modified from `tabSales Order` where name = '%s'" % self.doc.name) diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index cc3acfe56c..701f27dca3 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -154,25 +154,13 @@ class DocType(TransactionBase): - def get_raw_materials(self, bom_no, fg_qty, consider_sa_items_as_rm): + def get_raw_materials(self, bom_no, fg_qty, use_multi_level_bom): """ get all items from flat bom except child items of sub-contracted and sub assembly items and sub assembly items itself. """ - if consider_sa_items_as_rm == 'Yes': - # Get all raw materials considering SA items as raw materials, - # so no childs of SA items - fl_bom_sa_items = sql(""" - select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom - from `tabBOM Item` - where parent = '%s' and docstatus < 2 - group by item_code - """ % (fg_qty, bom_no)) - - self.make_items_dict(fl_bom_sa_items) - - else: + if use_multi_level_bom: # get all raw materials with sub assembly childs fl_bom_sa_child_item = sql(""" select @@ -187,6 +175,17 @@ class DocType(TransactionBase): group by item_code,stock_uom """ , (fg_qty, bom_no)) self.make_items_dict(fl_bom_sa_child_item) + else: + # Get all raw materials considering multi level BOM, + # if multi level bom consider childs of Sub-Assembly items + fl_bom_sa_items = sql(""" + select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom + from `tabBOM Item` + where parent = '%s' and docstatus < 2 + group by item_code + """ % (fg_qty, bom_no)) + + self.make_items_dict(fl_bom_sa_items) # Update only qty remaining to be issued for production if self.doc.process == 'Material Transfer': @@ -214,12 +213,8 @@ class DocType(TransactionBase): if self.doc.bom_no: if not self.doc.fg_completed_qty: msgprint("Please enter FG Completed Qty", raise_exception=1) - if not self.doc.consider_sa_items_as_raw_materials: - msgprint("Please confirm whether you want to consider sub assembly item as raw materials", raise_exception=1) - # get items - #------------------ def get_items(self): if self.doc.purpose == 'Production Order': pro_obj = self.doc.production_order and get_obj('Production Order', self.doc.production_order) or '' @@ -227,14 +222,14 @@ class DocType(TransactionBase): bom_no = pro_obj.doc.bom_no fg_qty = (self.doc.process == 'Backflush') and flt(self.doc.fg_completed_qty) or flt(pro_obj.doc.qty) - consider_sa_items_as_rm = pro_obj.doc.consider_sa_items + use_multi_level_bom = pro_obj.doc.use_multi_level_bom elif self.doc.purpose == 'Other': self.validate_bom_no() bom_no = self.doc.bom_no fg_qty = self.doc.fg_completed_qty - consider_sa_items_as_rm = self.doc.consider_sa_items_as_raw_materials + use_multi_level_bom = self.doc.use_multi_level_bom - self.get_raw_materials(bom_no, fg_qty, consider_sa_items_as_rm) + self.get_raw_materials(bom_no, fg_qty, use_multi_level_bom) self.doclist = self.doc.clear_table(self.doclist, 'mtn_details', 1) sw = (self.doc.process == 'Backflush') and cstr(pro_obj.doc.wip_warehouse) or '' diff --git a/stock/doctype/stock_entry/stock_entry.txt b/stock/doctype/stock_entry/stock_entry.txt index f77808e292..24412b88cf 100644 --- a/stock/doctype/stock_entry/stock_entry.txt +++ b/stock/doctype/stock_entry/stock_entry.txt @@ -2,9 +2,9 @@ { "owner": "Administrator", "docstatus": 0, - "creation": "2012-11-02 17:16:56", + "creation": "2012-11-28 11:26:22", "modified_by": "Administrator", - "modified": "2012-11-26 11:51:08" + "modified": "2012-11-30 14:10:02" }, { "is_submittable": 1, @@ -195,14 +195,13 @@ "permlevel": 0 }, { - "description": "Select \"Yes\" if stock is maintained and tracked for sub-assembly items. Select \"No\" if you want child items of sub-assembly for material transfer.", + "description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.", "depends_on": "eval:doc.purpose == 'Other'", "colour": "White:FFF", "doctype": "DocField", - "label": "Consider SA Items as Raw Materials", - "options": "\nNo\nYes", - "fieldname": "consider_sa_items_as_raw_materials", - "fieldtype": "Select", + "label": "Use Multi-Level BOM", + "fieldname": "use_multi_level_bom", + "fieldtype": "Check", "permlevel": 0 }, {