feat: Combine Sub-assembly items in Production Plan
- Combine subassembly rows if same Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No.
This commit is contained in:
parent
4d8d4acec4
commit
6d72aa377f
@ -918,7 +918,7 @@ def validate_bom_no(item, bom_no):
|
|||||||
frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
|
frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent=None, is_root=False, **filters):
|
def get_children(parent=None, is_root=False, **filters):
|
||||||
if not parent or parent=="BOM":
|
if not parent or parent=="BOM":
|
||||||
frappe.msgprint(_('Please select a BOM'))
|
frappe.msgprint(_('Please select a BOM'))
|
||||||
return
|
return
|
||||||
|
|||||||
@ -232,7 +232,7 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
combine_items: function (frm) {
|
combine_items: function (frm) {
|
||||||
frm.clear_table('prod_plan_references');
|
frm.clear_table("prod_plan_references");
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "get_items",
|
method: "get_items",
|
||||||
@ -247,6 +247,13 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
combine_sub_items: (frm) => {
|
||||||
|
if (frm.doc.sub_assembly_items.length > 0) {
|
||||||
|
frm.clear_table("sub_assembly_items");
|
||||||
|
frm.trigger("get_sub_assembly_items");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
get_sub_assembly_items: function(frm) {
|
get_sub_assembly_items: function(frm) {
|
||||||
frm.dirty();
|
frm.dirty();
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
"prod_plan_references",
|
"prod_plan_references",
|
||||||
"section_break_24",
|
"section_break_24",
|
||||||
"get_sub_assembly_items",
|
"get_sub_assembly_items",
|
||||||
|
"combine_sub_items",
|
||||||
"sub_assembly_items",
|
"sub_assembly_items",
|
||||||
"material_request_planning",
|
"material_request_planning",
|
||||||
"include_non_stock_items",
|
"include_non_stock_items",
|
||||||
@ -340,7 +341,6 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "prod_plan_references",
|
"fieldname": "prod_plan_references",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 1,
|
|
||||||
"label": "Production Plan Item Reference",
|
"label": "Production Plan Item Reference",
|
||||||
"options": "Production Plan Item Reference"
|
"options": "Production Plan Item Reference"
|
||||||
},
|
},
|
||||||
@ -370,16 +370,23 @@
|
|||||||
"fieldname": "to_delivery_date",
|
"fieldname": "to_delivery_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "To Delivery Date"
|
"label": "To Delivery Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "combine_sub_items",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Consolidate Sub Assembly Items"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-calendar",
|
"icon": "fa fa-calendar",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-06 18:35:59.642232",
|
"modified": "2022-02-23 17:16:10.629378",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan",
|
"name": "Production Plan",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -21,7 +21,8 @@ from frappe.utils import (
|
|||||||
)
|
)
|
||||||
from frappe.utils.csvutils import build_csv_response
|
from frappe.utils.csvutils import build_csv_response
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.bom.bom import get_children, validate_bom_no
|
from erpnext.manufacturing.doctype.bom.bom import get_children as get_bom_children
|
||||||
|
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||||
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
|
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
|
||||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||||
|
|
||||||
@ -570,17 +571,28 @@ class ProductionPlan(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_sub_assembly_items(self, manufacturing_type=None):
|
def get_sub_assembly_items(self, manufacturing_type=None):
|
||||||
|
"Fetch sub assembly items and optionally combine them."
|
||||||
self.sub_assembly_items = []
|
self.sub_assembly_items = []
|
||||||
|
sub_assembly_items_store = [] # temporary store to process all subassembly items
|
||||||
|
|
||||||
for row in self.po_items:
|
for row in self.po_items:
|
||||||
bom_data = []
|
bom_data = []
|
||||||
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
|
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
|
||||||
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
|
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
|
||||||
|
sub_assembly_items_store.extend(bom_data)
|
||||||
|
|
||||||
self.sub_assembly_items.sort(key= lambda d: d.bom_level, reverse=True)
|
if self.combine_sub_items:
|
||||||
for idx, row in enumerate(self.sub_assembly_items, start=1):
|
# Combine subassembly items
|
||||||
row.idx = idx
|
sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
|
||||||
|
|
||||||
|
sub_assembly_items_store.sort(key= lambda d: d.bom_level, reverse=True) # sort by bom level
|
||||||
|
|
||||||
|
for idx, row in enumerate(sub_assembly_items_store):
|
||||||
|
row.idx = idx + 1
|
||||||
|
self.append("sub_assembly_items", row)
|
||||||
|
|
||||||
def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
|
def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
|
||||||
|
"Modify bom_data, set additional details."
|
||||||
for data in bom_data:
|
for data in bom_data:
|
||||||
data.qty = data.stock_qty
|
data.qty = data.stock_qty
|
||||||
data.production_plan_item = row.name
|
data.production_plan_item = row.name
|
||||||
@ -589,7 +601,32 @@ class ProductionPlan(Document):
|
|||||||
data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
|
data.type_of_manufacturing = manufacturing_type or ("Subcontract" if data.is_sub_contracted_item
|
||||||
else "In House")
|
else "In House")
|
||||||
|
|
||||||
self.append("sub_assembly_items", data)
|
def combine_subassembly_items(self, sub_assembly_items_store):
|
||||||
|
"Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No."
|
||||||
|
key_wise_data = {}
|
||||||
|
for row in sub_assembly_items_store:
|
||||||
|
key = (
|
||||||
|
row.get("production_item"), row.get("fg_warehouse"),
|
||||||
|
row.get("bom_no"), row.get("type_of_manufacturing")
|
||||||
|
)
|
||||||
|
if key not in key_wise_data:
|
||||||
|
# intialise (item, wh, bom no, man.g type) wise dict
|
||||||
|
key_wise_data[key] = row
|
||||||
|
continue
|
||||||
|
|
||||||
|
existing_row = key_wise_data[key]
|
||||||
|
if existing_row:
|
||||||
|
# if row with same (item, wh, bom no, man.g type) key, merge
|
||||||
|
existing_row.qty += flt(row.qty)
|
||||||
|
existing_row.stock_qty += flt(row.stock_qty)
|
||||||
|
existing_row.bom_level = max(existing_row.bom_level, row.bom_level)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# add row with key
|
||||||
|
key_wise_data[key] = row
|
||||||
|
|
||||||
|
sub_assembly_items_store = [key_wise_data[key] for key in key_wise_data] # unpack into single level list
|
||||||
|
return sub_assembly_items_store
|
||||||
|
|
||||||
def all_items_completed(self):
|
def all_items_completed(self):
|
||||||
all_items_produced = all(flt(d.planned_qty) - flt(d.produced_qty) < 0.000001
|
all_items_produced = all(flt(d.planned_qty) - flt(d.produced_qty) < 0.000001
|
||||||
@ -1031,7 +1068,7 @@ def get_item_data(item_code):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
|
def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
|
||||||
data = get_children('BOM', parent = bom_no)
|
data = get_bom_children(parent=bom_no)
|
||||||
for d in data:
|
for d in data:
|
||||||
if d.expandable:
|
if d.expandable:
|
||||||
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
|
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user