feat: provision to skip available sub assembly items in the production plan
(cherry picked from commit 64751ec4d9)
This commit is contained in:
committed by
Mergify
parent
1b827f61f7
commit
ce601afc4e
@@ -35,8 +35,12 @@
|
|||||||
"section_break_25",
|
"section_break_25",
|
||||||
"prod_plan_references",
|
"prod_plan_references",
|
||||||
"section_break_24",
|
"section_break_24",
|
||||||
"get_sub_assembly_items",
|
|
||||||
"combine_sub_items",
|
"combine_sub_items",
|
||||||
|
"section_break_ucc4",
|
||||||
|
"skip_available_sub_assembly_item",
|
||||||
|
"column_break_igxl",
|
||||||
|
"get_sub_assembly_items",
|
||||||
|
"section_break_g4ip",
|
||||||
"sub_assembly_items",
|
"sub_assembly_items",
|
||||||
"download_materials_request_plan_section_section",
|
"download_materials_request_plan_section_section",
|
||||||
"download_materials_required",
|
"download_materials_required",
|
||||||
@@ -351,12 +355,12 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "section_break_24",
|
"fieldname": "section_break_24",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hide_border": 1
|
"hide_border": 1,
|
||||||
|
"label": "Sub Assembly Items"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sub_assembly_items",
|
"fieldname": "sub_assembly_items",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Sub Assembly Items",
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Production Plan Sub Assembly Item"
|
"options": "Production Plan Sub Assembly Item"
|
||||||
},
|
},
|
||||||
@@ -392,13 +396,33 @@
|
|||||||
"fieldname": "download_materials_request_plan_section_section",
|
"fieldname": "download_materials_request_plan_section_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Download Materials Request Plan Section"
|
"label": "Download Materials Request Plan Section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "System consider the projected quantity to check available or will be available sub-assembly items ",
|
||||||
|
"fieldname": "skip_available_sub_assembly_item",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Skip Available Sub Assembly Items"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_ucc4",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_g4ip",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_igxl",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"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": "2023-03-31 10:30:48.118932",
|
"modified": "2023-05-22 23:36:31.770517",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan",
|
"name": "Production Plan",
|
||||||
|
|||||||
@@ -718,7 +718,9 @@ class ProductionPlan(Document):
|
|||||||
frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
|
frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
|
||||||
|
|
||||||
bom_data = []
|
bom_data = []
|
||||||
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
|
|
||||||
|
warehouse = row.warehouse if self.skip_available_sub_assembly_item else None
|
||||||
|
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse)
|
||||||
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)
|
sub_assembly_items_store.extend(bom_data)
|
||||||
|
|
||||||
@@ -894,7 +896,9 @@ def download_raw_materials(doc, warehouses=None):
|
|||||||
build_csv_response(item_list, doc.name)
|
build_csv_response(item_list, doc.name)
|
||||||
|
|
||||||
|
|
||||||
def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1):
|
def get_exploded_items(
|
||||||
|
item_details, company, bom_no, include_non_stock_items, planned_qty=1, doc=None
|
||||||
|
):
|
||||||
bei = frappe.qb.DocType("BOM Explosion Item")
|
bei = frappe.qb.DocType("BOM Explosion Item")
|
||||||
bom = frappe.qb.DocType("BOM")
|
bom = frappe.qb.DocType("BOM")
|
||||||
item = frappe.qb.DocType("Item")
|
item = frappe.qb.DocType("Item")
|
||||||
@@ -1271,6 +1275,12 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
|||||||
include_safety_stock = doc.get("include_safety_stock")
|
include_safety_stock = doc.get("include_safety_stock")
|
||||||
|
|
||||||
so_item_details = frappe._dict()
|
so_item_details = frappe._dict()
|
||||||
|
|
||||||
|
sub_assembly_items = {}
|
||||||
|
if doc.get("skip_available_sub_assembly_item"):
|
||||||
|
for d in doc.get("sub_assembly_items"):
|
||||||
|
sub_assembly_items.setdefault((d.get("production_item"), d.get("bom_no")), d.get("qty"))
|
||||||
|
|
||||||
for data in po_items:
|
for data in po_items:
|
||||||
if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
|
if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
|
||||||
data["include_exploded_items"] = 1
|
data["include_exploded_items"] = 1
|
||||||
@@ -1296,10 +1306,24 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
|||||||
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
|
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
|
||||||
|
|
||||||
if bom_no:
|
if bom_no:
|
||||||
if data.get("include_exploded_items") and include_subcontracted_items:
|
if (
|
||||||
|
data.get("include_exploded_items")
|
||||||
|
and doc.get("sub_assembly_items")
|
||||||
|
and doc.get("skip_available_sub_assembly_item")
|
||||||
|
):
|
||||||
|
item_details = get_raw_materials_of_sub_assembly_items(
|
||||||
|
item_details,
|
||||||
|
company,
|
||||||
|
bom_no,
|
||||||
|
include_non_stock_items,
|
||||||
|
sub_assembly_items,
|
||||||
|
planned_qty=planned_qty,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif data.get("include_exploded_items") and include_subcontracted_items:
|
||||||
# fetch exploded items from BOM
|
# fetch exploded items from BOM
|
||||||
item_details = get_exploded_items(
|
item_details = get_exploded_items(
|
||||||
item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty
|
item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty, doc=doc
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
item_details = get_subitems(
|
item_details = get_subitems(
|
||||||
@@ -1456,12 +1480,22 @@ 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, company, warehouse=None, indent=0):
|
||||||
data = get_bom_children(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")
|
||||||
stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
|
stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
|
||||||
|
|
||||||
|
if warehouse:
|
||||||
|
bin_dict = get_bin_details(d, company, for_warehouse=warehouse)
|
||||||
|
|
||||||
|
if bin_dict and bin_dict[0].projected_qty > 0:
|
||||||
|
if bin_dict[0].projected_qty > stock_qty:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
stock_qty = stock_qty - bin_dict[0].projected_qty
|
||||||
|
|
||||||
bom_data.append(
|
bom_data.append(
|
||||||
frappe._dict(
|
frappe._dict(
|
||||||
{
|
{
|
||||||
@@ -1481,7 +1515,7 @@ def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if d.value:
|
if d.value:
|
||||||
get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent + 1)
|
get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1)
|
||||||
|
|
||||||
|
|
||||||
def set_default_warehouses(row, default_warehouses):
|
def set_default_warehouses(row, default_warehouses):
|
||||||
@@ -1519,3 +1553,68 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return reserved_qty_for_production_plan - reserved_qty_for_production
|
return reserved_qty_for_production_plan - reserved_qty_for_production
|
||||||
|
|
||||||
|
|
||||||
|
def get_raw_materials_of_sub_assembly_items(
|
||||||
|
item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
|
||||||
|
):
|
||||||
|
|
||||||
|
bei = frappe.qb.DocType("BOM Item")
|
||||||
|
bom = frappe.qb.DocType("BOM")
|
||||||
|
item = frappe.qb.DocType("Item")
|
||||||
|
item_default = frappe.qb.DocType("Item Default")
|
||||||
|
item_uom = frappe.qb.DocType("UOM Conversion Detail")
|
||||||
|
|
||||||
|
items = (
|
||||||
|
frappe.qb.from_(bei)
|
||||||
|
.join(bom)
|
||||||
|
.on(bom.name == bei.parent)
|
||||||
|
.join(item)
|
||||||
|
.on(item.name == bei.item_code)
|
||||||
|
.left_join(item_default)
|
||||||
|
.on((item_default.parent == item.name) & (item_default.company == company))
|
||||||
|
.left_join(item_uom)
|
||||||
|
.on((item.name == item_uom.parent) & (item_uom.uom == item.purchase_uom))
|
||||||
|
.select(
|
||||||
|
(IfNull(Sum(bei.stock_qty / IfNull(bom.quantity, 1)), 0) * planned_qty).as_("qty"),
|
||||||
|
item.item_name,
|
||||||
|
item.name.as_("item_code"),
|
||||||
|
bei.description,
|
||||||
|
bei.stock_uom,
|
||||||
|
bei.bom_no,
|
||||||
|
item.min_order_qty,
|
||||||
|
bei.source_warehouse,
|
||||||
|
item.default_material_request_type,
|
||||||
|
item.min_order_qty,
|
||||||
|
item_default.default_warehouse,
|
||||||
|
item.purchase_uom,
|
||||||
|
item_uom.conversion_factor,
|
||||||
|
item.safety_stock,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(bei.docstatus == 1)
|
||||||
|
& (bom.name == bom_no)
|
||||||
|
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
|
||||||
|
)
|
||||||
|
.groupby(bei.item_code, bei.stock_uom)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
key = (item.item_code, item.bom_no)
|
||||||
|
if item.bom_no and key in sub_assembly_items:
|
||||||
|
planned_qty = flt(sub_assembly_items[key])
|
||||||
|
get_raw_materials_of_sub_assembly_items(
|
||||||
|
item_details,
|
||||||
|
company,
|
||||||
|
item.bom_no,
|
||||||
|
include_non_stock_items,
|
||||||
|
sub_assembly_items,
|
||||||
|
planned_qty=planned_qty,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if not item.conversion_factor and item.purchase_uom:
|
||||||
|
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
|
||||||
|
|
||||||
|
item_details.setdefault(item.get("item_code"), item)
|
||||||
|
|
||||||
|
return item_details
|
||||||
|
|||||||
@@ -28,7 +28,11 @@
|
|||||||
"uom",
|
"uom",
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
"column_break_22",
|
"column_break_22",
|
||||||
"description"
|
"description",
|
||||||
|
"section_break_4rxf",
|
||||||
|
"actual_qty",
|
||||||
|
"column_break_xfhm",
|
||||||
|
"projected_qty"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -183,12 +187,34 @@
|
|||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Schedule Date"
|
"label": "Schedule Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_4rxf",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "actual_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Actual Qty",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_xfhm",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "projected_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Projected Qty",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-11-28 13:50:15.116082",
|
"modified": "2023-05-22 17:52:34.708879",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan Sub Assembly Item",
|
"name": "Production Plan Sub Assembly Item",
|
||||||
|
|||||||
Reference in New Issue
Block a user