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:
marination 2022-02-24 15:20:18 +05:30
parent 4d8d4acec4
commit 6d72aa377f
4 changed files with 61 additions and 10 deletions

View File

@ -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.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":
frappe.msgprint(_('Please select a BOM'))
return

View File

@ -232,7 +232,7 @@ frappe.ui.form.on('Production Plan', {
});
},
combine_items: function (frm) {
frm.clear_table('prod_plan_references');
frm.clear_table("prod_plan_references");
frappe.call({
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) {
frm.dirty();

View File

@ -36,6 +36,7 @@
"prod_plan_references",
"section_break_24",
"get_sub_assembly_items",
"combine_sub_items",
"sub_assembly_items",
"material_request_planning",
"include_non_stock_items",
@ -340,7 +341,6 @@
{
"fieldname": "prod_plan_references",
"fieldtype": "Table",
"hidden": 1,
"label": "Production Plan Item Reference",
"options": "Production Plan Item Reference"
},
@ -370,16 +370,23 @@
"fieldname": "to_delivery_date",
"fieldtype": "Date",
"label": "To Delivery Date"
},
{
"default": "0",
"fieldname": "combine_sub_items",
"fieldtype": "Check",
"label": "Consolidate Sub Assembly Items"
}
],
"icon": "fa fa-calendar",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2021-09-06 18:35:59.642232",
"modified": "2022-02-23 17:16:10.629378",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{

View File

@ -21,7 +21,8 @@ from frappe.utils import (
)
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.setup.doctype.item_group.item_group import get_item_group_defaults
@ -570,17 +571,28 @@ class ProductionPlan(Document):
@frappe.whitelist()
def get_sub_assembly_items(self, manufacturing_type=None):
"Fetch sub assembly items and optionally combine them."
self.sub_assembly_items = []
sub_assembly_items_store = [] # temporary store to process all subassembly items
for row in self.po_items:
bom_data = []
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)
sub_assembly_items_store.extend(bom_data)
self.sub_assembly_items.sort(key= lambda d: d.bom_level, reverse=True)
for idx, row in enumerate(self.sub_assembly_items, start=1):
row.idx = idx
if self.combine_sub_items:
# Combine subassembly items
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):
"Modify bom_data, set additional details."
for data in bom_data:
data.qty = data.stock_qty
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
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):
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):
data = get_children('BOM', parent = bom_no)
data = get_bom_children(parent=bom_no)
for d in data:
if d.expandable:
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")