diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json index 49d18bb9cd..a5428aa1c6 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.json +++ b/erpnext/manufacturing/doctype/production_order/production_order.json @@ -1314,6 +1314,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_order_item", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Sales Order Item", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1358,7 +1388,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-11-03 05:31:56.636424", + "modified": "2017-11-03 05:31:56.636724", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Order", diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 89ecbe7ec5..fce9d65285 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -35,6 +35,7 @@ class ProductionOrder(Document): validate_bom_no(self.production_item, self.bom_no) self.validate_sales_order() + self.set_default_warehouse() self.validate_warehouse_belongs_to_company() self.calculate_operating_cost() self.validate_qty() @@ -69,6 +70,12 @@ class ProductionOrder(Document): else: frappe.throw(_("Sales Order {0} is not valid").format(self.sales_order)) + def set_default_warehouse(self): + if not self.wip_warehouse: + self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") + if not self.fg_warehouse: + self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse") + def validate_warehouse_belongs_to_company(self): warehouses = [self.fg_warehouse, self.wip_warehouse] for d in self.get("required_items"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1b86bb13ff..fb4e0baade 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -23,7 +23,6 @@ execute:frappe.reload_doc('selling', 'doctype', 'customer') # 2014-01-29 execute:frappe.reload_doc('buying', 'doctype', 'supplier') # 2014-01-29 execute:frappe.reload_doc('accounts', 'doctype', 'asset_category') execute:frappe.reload_doc('accounts', 'doctype', 'pricing_rule') -execute:frappe.reload_doc('stock', 'doctype', 'item_settings') erpnext.patches.v4_0.map_charge_to_taxes_and_charges execute:frappe.reload_doc('support', 'doctype', 'newsletter') # 2014-01-31 execute:frappe.reload_doc('hr', 'doctype', 'employee') # 2014-02-03 diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index cfd40df453..a5e0059d68 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -197,7 +197,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( }); return; } - else if(!r.message.every(function(d) { return !!d.pending_qty })) { + else if(!r.message) { frappe.msgprint({ title: __('Production Order not created'), message: __('Production Order already created for all items with BOM'), @@ -217,6 +217,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( }}, {fieldtype:'Float', fieldname:'pending_qty', reqd: 1, label: __('Qty'), in_list_view:1}, + {fieldtype:'Data', fieldname:'sales_order_item', reqd: 1, + label: __('Sales Order Item'), hidden:1} ], get_data: function() { return r.message diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 747a3c062c..a70fad12f8 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -336,14 +336,16 @@ class SalesOrder(SellingController): bom = get_default_bom_item(i.item_code) if bom: stock_qty = i.qty if i.doctype == 'Packed Item' else i.stock_qty - items.append(dict( - item_code= i.item_code, - bom = bom, - warehouse = i.warehouse, - pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabProduction Order` - where production_item=%s and sales_order=%s''', (i.item_code, self.name))[0][0]) - )) - + pending_qty= stock_qty - flt(frappe.db.sql('''select sum(qty) from `tabProduction Order` + where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2''', (i.item_code, self.name, i.name))[0][0]) + if pending_qty: + items.append(dict( + item_code= i.item_code, + bom = bom, + warehouse = i.warehouse, + pending_qty = pending_qty, + sales_order_item = i.name + )) return items def on_recurring(self, reference_doc, subscription_doc): @@ -769,6 +771,7 @@ def make_production_orders(items, sales_order, company, project=None): qty=i['pending_qty'], company=company, sales_order=sales_order, + sales_order_item=i['sales_order_item'], project=project, fg_warehouse=i['warehouse'] )).insert() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 9c5c82edc4..da600fbbc9 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -10,6 +10,8 @@ from erpnext.selling.doctype.sales_order.sales_order \ import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from frappe.tests.test_permissions import set_user_permission_doctypes +from erpnext.selling.doctype.sales_order.sales_order import make_production_orders +import json class TestSalesOrder(unittest.TestCase): def tearDown(self): @@ -505,6 +507,40 @@ class TestSalesOrder(unittest.TestCase): self.assertEquals(new_so.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate)) + def test_make_production_order(self): + # Make a new Sales Order + so = make_sales_order(**{ + "item_list": [{ + "item_code": "_Test FG Item", + "qty": 10, + "rate":100 + }, + { + "item_code": "_Test FG Item", + "qty": 20, + "rate":200 + }] + }) + + # Raise Production Orders + po_items= [] + so_item_name= {} + for item in so.get_production_order_items(): + po_items.append({ + "warehouse": item.get("warehouse"), + "item_code": item.get("item_code"), + "pending_qty": item.get("pending_qty"), + "sales_order_item": item.get("sales_order_item"), + "bom": item.get("bom") + }) + so_item_name[item.get("sales_order_item")]= item.get("pending_qty") + make_production_orders(json.dumps({"items":po_items}), so.name, so.company) + + # Check if Production Orders were raised + for item in so_item_name: + po_qty = frappe.db.sql("select sum(qty) from `tabProduction Order` where sales_order=%s and sales_order_item=%s", (so.name, item)) + self.assertEquals(po_qty[0][0], so_item_name.get(item)) + def make_sales_order(**args): so = frappe.new_doc("Sales Order") args = frappe._dict(args) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 36ca05839c..929c0eea24 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -633,7 +633,7 @@ class Item(WebsiteGenerator): def validate_stock_exists_for_template_item(self): if self.stock_ledger_created(): - if ((self._doc._doc_before_save + if ((self._doc_before_save and self._doc_before_save.has_variants != self.has_variants) or self.variant_of != self._doc_before_save.variant_of): frappe.throw(_("Cannot change Variant properties after stock transction. You will have to make a new Item to do this.").format(self.name),