fix: production plan UX and validation message (#27278)

This commit is contained in:
rohitwaghchaure 2021-09-01 19:16:03 +05:30 committed by GitHub
parent 9506c14d35
commit 2a8cd05b44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 28 deletions

View File

@ -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",

View File

@ -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'));
},

View File

@ -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",

View File

@ -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(
{