diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json index 6c60bbde86..27d7c4175e 100644 --- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json +++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json @@ -6,17 +6,17 @@ "engine": "InnoDB", "field_order": [ "item_code", - "item_name", - "material_request_type", "from_warehouse", "warehouse", - "column_break_4", + "item_name", + "material_request_type", + "actual_qty", + "ordered_qty", "required_bom_qty", + "column_break_4", "quantity", "uom", "projected_qty", - "actual_qty", - "ordered_qty", "reserved_qty_for_production", "safety_stock", "item_details", @@ -28,6 +28,7 @@ ], "fields": [ { + "columns": 2, "fieldname": "item_code", "fieldtype": "Link", "in_list_view": 1, @@ -41,6 +42,7 @@ "label": "Item Name" }, { + "columns": 2, "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -50,10 +52,11 @@ "reqd": 1 }, { + "columns": 1, "fieldname": "material_request_type", "fieldtype": "Select", "in_list_view": 1, - "label": "Material Request Type", + "label": "Type", "options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided" }, { @@ -61,10 +64,11 @@ "fieldtype": "Column Break" }, { + "columns": 1, "fieldname": "quantity", "fieldtype": "Float", "in_list_view": 1, - "label": "Required Quantity", + "label": "Plan to Request Qty", "no_copy": 1, "reqd": 1 }, @@ -75,11 +79,12 @@ "read_only": 1 }, { + "columns": 2, "default": "0", "fieldname": "actual_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Actual Qty", + "label": "Available Qty", "no_copy": 1, "read_only": 1 }, @@ -157,16 +162,18 @@ "read_only": 1 }, { + "columns": 2, "fieldname": "required_bom_qty", "fieldtype": "Float", - "label": "Required Qty as per BOM", + "in_list_view": 1, + "label": "Qty As Per BOM", "no_copy": 1, "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-03-26 12:41:13.013149", + "modified": "2021-08-23 18:17:58.400462", "modified_by": "Administrator", "module": "Manufacturing", "name": "Material Request Plan Item", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 847004fc47..7b4b7c3ca3 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -254,7 +254,7 @@ frappe.ui.form.on('Production Plan', { get_items_for_mr: function(frm) { if (!frm.doc.for_warehouse) { - frappe.throw(__("Select warehouse for material requests")); + frappe.throw(__("To make material requests, 'Make Material Request for Warehouse' field is mandatory")); } if (frm.doc.ignore_existing_ordered_qty) { @@ -265,9 +265,18 @@ frappe.ui.form.on('Production Plan', { title: title, fields: [ { - "fieldtype": "Table MultiSelect", "label": __("Source Warehouses (Optional)"), - "fieldname": "warehouses", "options": "Production Plan Material Request Warehouse", - "description": __("System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase."), + 'label': __('Target Warehouse'), + 'fieldtype': 'Link', + 'fieldname': 'target_warehouse', + 'read_only': true, + 'default': frm.doc.for_warehouse + }, + { + 'label': __('Source Warehouses (Optional)'), + 'fieldtype': 'Table MultiSelect', + 'fieldname': 'warehouses', + 'options': 'Production Plan Material Request Warehouse', + 'description': __('If source warehouse selected then system will create the material request with type Material Transfer from Source to Target warehouse. If not selected then will create the material request with type Purchase for the target warehouse.'), get_query: function () { return { filters: { @@ -342,7 +351,11 @@ frappe.ui.form.on('Production Plan', { frappe.prompt(fields, (row) => { let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials'; - open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses: row.warehouses }); + open_url_post(frappe.request.url, { + cmd: get_template_url, + doc: frm.doc, + warehouses: row.warehouses + }); }, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock')); }, diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 84378956c6..b5ed28802c 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -300,7 +300,7 @@ { "fieldname": "for_warehouse", "fieldtype": "Link", - "label": "Material Request Warehouse", + "label": "Make Material Request for Warehouse", "options": "Warehouse" }, { @@ -364,7 +364,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-28 20:00:33.905114", + "modified": "2021-08-23 17:26:03.799876", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 6b61c6d330..2c77c9c23f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -331,7 +331,7 @@ class ProductionPlan(Document): def get_production_items(self): item_dict = {} for d in self.po_items: - item_details= { + item_details = { "production_item" : d.item_code, "use_multi_level_bom" : d.include_exploded_items, "sales_order" : d.sales_order, @@ -346,8 +346,7 @@ class ProductionPlan(Document): "production_plan" : self.name, "production_plan_item" : d.name, "product_bundle_item" : d.product_bundle_item, - "planned_start_date" : d.planned_start_date, - "make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0) + "planned_start_date" : d.planned_start_date } item_details.update({ @@ -458,6 +457,7 @@ class ProductionPlan(Document): warehouse = get_default_warehouse() wo = frappe.new_doc("Work Order") wo.update(item) + wo.planned_start_date = item.get('planned_start_date') or item.get('schedule_date') if item.get("warehouse"): wo.fg_warehouse = item.get("warehouse") @@ -569,7 +569,10 @@ def download_raw_materials(doc, warehouses=None): 'Reserved Qty for Production', 'Safety Stock', 'Required Qty']] doc.warehouse = None - for d in get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True): + frappe.flags.show_qty_in_stock_uom = 1 + items = get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True) + + for d in items: item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'), d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'), d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')]) @@ -605,9 +608,16 @@ def get_exploded_items(item_details, company, bom_no, include_non_stock_items, p and bom.name=%s and item.is_stock_item in (1, {0}) group by bei.item_code, bei.stock_uom""".format(0 if include_non_stock_items else 1), (planned_qty, company, bom_no), as_dict=1): - item_details.setdefault(d.get('item_code'), d) + if not d.conversion_factor and d.purchase_uom: + d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom) + item_details.setdefault(d.get('item_code'), d) + return item_details +def get_uom_conversion_factor(item_code, uom): + return frappe.db.get_value('UOM Conversion Detail', + {'parent': item_code, 'uom': uom}, 'conversion_factor') + def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_items, include_subcontracted_items, parent_qty, planned_qty=1): items = frappe.db.sql(""" @@ -642,6 +652,9 @@ def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_ite if d.item_code in item_details: item_details[d.item_code].qty = item_details[d.item_code].qty + d.qty else: + if not d.conversion_factor and d.purchase_uom: + d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom) + item_details[d.item_code] = d if data.get('include_exploded_items') and d.default_bom: @@ -669,10 +682,11 @@ def get_material_request_items(row, sales_order, company, row['purchase_uom'] = row['stock_uom'] if row['purchase_uom'] != row['stock_uom']: - if not row['conversion_factor']: + if not (row['conversion_factor'] or frappe.flags.show_qty_in_stock_uom): frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}") .format(row['purchase_uom'], row['stock_uom'], row.item_code)) - required_qty = required_qty / row['conversion_factor'] + + required_qty = required_qty / row['conversion_factor'] if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"): required_qty = ceil(required_qty) @@ -841,10 +855,8 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d elif data.get('item_code'): item_master = frappe.get_doc('Item', data['item_code']).as_dict() purchase_uom = item_master.purchase_uom or item_master.stock_uom - conversion_factor = 0 - for d in item_master.get("uoms"): - if d.uom == purchase_uom: - conversion_factor = d.conversion_factor + conversion_factor = (get_uom_conversion_factor(item_master.name, purchase_uom) + if item_master.purchase_uom else 1.0) item_details[item_master.name] = frappe._dict( {