Merge pull request #49123 from rohitwaghchaure/fixed-sub-assembl-in-pp

fix: do not fetch sub-assembly in PP
This commit is contained in:
rohitwaghchaure
2025-08-12 22:01:34 +05:30
committed by GitHub
9 changed files with 139 additions and 20 deletions

View File

@@ -76,6 +76,8 @@ frappe.ui.form.on("BOM", {
}, },
}; };
}); });
frm.trigger("toggle_fields_for_semi_finished_goods");
}, },
validate: function (frm) { validate: function (frm) {
@@ -87,8 +89,27 @@ frappe.ui.form.on("BOM", {
} }
}, },
track_semi_finished_goods(frm) {
frm.trigger("toggle_fields_for_semi_finished_goods");
},
toggle_fields_for_semi_finished_goods(frm) {
let fields = ["finished_good", "finished_good_qty", "bom_no"];
fields.forEach((field) => {
frm.fields_dict["operations"].grid.update_docfield_property(
field,
"read_only",
!frm.doc.track_semi_finished_goods
);
});
refresh_field("operations");
},
with_operations: function (frm) { with_operations: function (frm) {
frm.set_df_property("fg_based_operating_cost", "hidden", frm.doc.with_operations ? 1 : 0); frm.set_df_property("fg_based_operating_cost", "hidden", frm.doc.with_operations ? 1 : 0);
frm.trigger("toggle_fields_for_semi_finished_goods");
}, },
fg_based_operating_cost: function (frm) { fg_based_operating_cost: function (frm) {
@@ -929,6 +950,19 @@ frappe.ui.form.on("BOM", {
}, },
}); });
let items = frm.doc.items.filter((item) => cint(item.operation_row_id) === cint(row.idx));
if (items?.length) {
items.forEach((item) => {
frm._bom_rm_dialog.fields_dict.items.df.data.push({
item_code: item.item_code,
qty: item.qty,
name: item.name,
});
});
frm._bom_rm_dialog.fields_dict.items.grid.refresh();
}
frm._bom_rm_dialog.show(); frm._bom_rm_dialog.show();
}, },
@@ -938,6 +972,7 @@ frappe.ui.form.on("BOM", {
label: __("Raw Materials"), label: __("Raw Materials"),
fieldname: "items", fieldname: "items",
fieldtype: "Table", fieldtype: "Table",
data: [],
reqd: 1, reqd: 1,
fields: [ fields: [
{ {

View File

@@ -722,12 +722,43 @@ class BOM(WebsiteGenerator):
row.update(get_item_details(row.get("item_code"))) row.update(get_item_details(row.get("item_code")))
row.operation_row_id = operation_row_id row.operation_row_id = operation_row_id
row.idx = None
row.name = None item_row = None
self.append("items", row) if row.name:
item_row = self.get_item_data(row.name)
if item_row:
item_row.update(
{
"item_code": row.get("item_code"),
"qty": row.get("qty"),
}
)
else:
row.idx = None
row.name = None
row.do_not_explode = 1
row.is_sub_assembly_item = self.is_sub_assembly_item(row.item_code)
self.append("items", row)
self.save() self.save()
def is_sub_assembly_item(self, item_code):
if not self.operations:
return False
for row in self.operations:
if row.finished_good == item_code:
return True
return False
def get_item_data(self, name):
for row in self.items:
if row.item_code == name:
return row
@frappe.whitelist() @frappe.whitelist()
def add_materials_from_bom(self, finished_good, bom_no, operation_row_id, qty=None): def add_materials_from_bom(self, finished_good, bom_no, operation_row_id, qty=None):
if not frappe.db.exists("BOM", {"item": finished_good, "name": bom_no, "docstatus": 1}): if not frappe.db.exists("BOM", {"item": finished_good, "name": bom_no, "docstatus": 1}):
@@ -745,6 +776,9 @@ class BOM(WebsiteGenerator):
row.uom = row.stock_uom row.uom = row.stock_uom
row.operation_row_id = operation_row_id row.operation_row_id = operation_row_id
row.idx = None row.idx = None
row.do_not_explode = 1
row.is_sub_assembly_item = self.is_sub_assembly_item(row.item_code)
self.append("items", row) self.append("items", row)
def traverse_tree(self, bom_list=None): def traverse_tree(self, bom_list=None):
@@ -946,6 +980,7 @@ class BOM(WebsiteGenerator):
"item_code": d.item_code, "item_code": d.item_code,
"item_name": d.item_name, "item_name": d.item_name,
"operation": d.operation, "operation": d.operation,
"is_sub_assembly_item": d.is_sub_assembly_item,
"source_warehouse": d.source_warehouse, "source_warehouse": d.source_warehouse,
"description": d.description, "description": d.description,
"image": d.image, "image": d.image,
@@ -978,6 +1013,7 @@ class BOM(WebsiteGenerator):
bom_item.description, bom_item.description,
bom_item.source_warehouse, bom_item.source_warehouse,
bom_item.operation, bom_item.operation,
bom_item.is_sub_assembly_item,
bom_item.stock_uom, bom_item.stock_uom,
bom_item.stock_qty, bom_item.stock_qty,
bom_item.rate, bom_item.rate,
@@ -1008,6 +1044,7 @@ class BOM(WebsiteGenerator):
"rate": flt(d["rate"]), "rate": flt(d["rate"]),
"include_item_in_manufacturing": d.get("include_item_in_manufacturing", 0), "include_item_in_manufacturing": d.get("include_item_in_manufacturing", 0),
"sourced_by_supplier": d.get("sourced_by_supplier", 0), "sourced_by_supplier": d.get("sourced_by_supplier", 0),
"is_sub_assembly_item": d.get("is_sub_assembly_item", 0),
} }
) )
) )

View File

@@ -25,7 +25,8 @@
"stock_uom", "stock_uom",
"amount", "amount",
"include_item_in_manufacturing", "include_item_in_manufacturing",
"sourced_by_supplier" "sourced_by_supplier",
"is_sub_assembly_item"
], ],
"fields": [ "fields": [
{ {
@@ -165,21 +166,30 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Sourced by Supplier", "label": "Sourced by Supplier",
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "is_sub_assembly_item",
"fieldtype": "Check",
"label": "Is Sub Assembly Item",
"no_copy": 1,
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:06:40.935882", "modified": "2025-08-12 20:02:32.694836",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Explosion Item", "name": "BOM Explosion Item",
"naming_rule": "Random", "naming_rule": "Random",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"row_format": "Dynamic",
"sort_field": "creation", "sort_field": "creation",
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -18,6 +18,7 @@ class BOMExplosionItem(Document):
description: DF.TextEditor | None description: DF.TextEditor | None
image: DF.Attach | None image: DF.Attach | None
include_item_in_manufacturing: DF.Check include_item_in_manufacturing: DF.Check
is_sub_assembly_item: DF.Check
item_code: DF.Link | None item_code: DF.Link | None
item_name: DF.Data | None item_name: DF.Data | None
operation: DF.Link | None operation: DF.Link | None

View File

@@ -41,7 +41,8 @@
"include_item_in_manufacturing", "include_item_in_manufacturing",
"original_item", "original_item",
"column_break_33", "column_break_33",
"sourced_by_supplier" "sourced_by_supplier",
"is_sub_assembly_item"
], ],
"fields": [ "fields": [
{ {
@@ -300,19 +301,28 @@
"fieldname": "operation_row_id", "fieldname": "operation_row_id",
"fieldtype": "Int", "fieldtype": "Int",
"label": "Operation ID" "label": "Operation ID"
},
{
"default": "0",
"fieldname": "is_sub_assembly_item",
"fieldtype": "Check",
"label": "Is Sub Assembly Item",
"no_copy": 1,
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:08:41.079752", "modified": "2025-08-12 20:01:59.532613",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Item", "name": "BOM Item",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"row_format": "Dynamic",
"sort_field": "creation", "sort_field": "creation",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": []
} }

View File

@@ -26,6 +26,7 @@ class BOMItem(Document):
image: DF.Attach | None image: DF.Attach | None
include_item_in_manufacturing: DF.Check include_item_in_manufacturing: DF.Check
is_stock_item: DF.Check is_stock_item: DF.Check
is_sub_assembly_item: DF.Check
item_code: DF.Link item_code: DF.Link
item_name: DF.Data | None item_name: DF.Data | None
operation: DF.Link | None operation: DF.Link | None

View File

@@ -43,6 +43,7 @@
], ],
"fields": [ "fields": [
{ {
"columns": 2,
"fieldname": "operation", "fieldname": "operation",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
@@ -53,9 +54,11 @@
"reqd": 1 "reqd": 1
}, },
{ {
"columns": 2,
"depends_on": "eval:!doc.workstation_type", "depends_on": "eval:!doc.workstation_type",
"fieldname": "workstation", "fieldname": "workstation",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1,
"label": "Workstation", "label": "Workstation",
"oldfieldname": "workstation", "oldfieldname": "workstation",
"oldfieldtype": "Link", "oldfieldtype": "Link",
@@ -83,7 +86,7 @@
"precision": "2" "precision": "2"
}, },
{ {
"columns": 1, "columns": 3,
"description": "In minutes", "description": "In minutes",
"fetch_from": "operation.total_operation_time", "fetch_from": "operation.total_operation_time",
"fetch_if_empty": 1, "fetch_if_empty": 1,
@@ -191,7 +194,7 @@
"label": "Set Operating Cost Based On BOM Quantity" "label": "Set Operating Cost Based On BOM Quantity"
}, },
{ {
"columns": 1, "columns": 3,
"fieldname": "workstation_type", "fieldname": "workstation_type",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
@@ -199,26 +202,31 @@
"options": "Workstation Type" "options": "Workstation Type"
}, },
{ {
"columns": 3,
"depends_on": "eval:parent.track_semi_finished_goods === 1",
"fieldname": "finished_good", "fieldname": "finished_good",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Finished Goods / Semi Finished Goods Item", "label": "FG / Semi FG Item",
"options": "Item" "options": "Item"
}, },
{ {
"columns": 1, "columns": 2,
"depends_on": "eval:parent.track_semi_finished_goods === 1",
"fieldname": "bom_no", "fieldname": "bom_no",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1,
"label": "BOM No", "label": "BOM No",
"options": "BOM" "options": "BOM"
}, },
{ {
"columns": 1, "columns": 2,
"default": "1", "default": "1",
"depends_on": "eval:parent.track_semi_finished_goods === 1",
"fieldname": "finished_good_qty", "fieldname": "finished_good_qty",
"fieldtype": "Float", "fieldtype": "Float",
"in_list_view": 1, "in_list_view": 1,
"label": "Finished Goods Qty" "label": "Qty to Produce"
}, },
{ {
"default": "0", "default": "0",
@@ -264,7 +272,7 @@
"label": "Is Subcontracted" "label": "Is Subcontracted"
}, },
{ {
"depends_on": "eval:!doc.bom_no", "depends_on": "eval:!doc.bom_no && parent.track_semi_finished_goods === 1",
"fieldname": "add_raw_materials", "fieldname": "add_raw_materials",
"fieldtype": "Button", "fieldtype": "Button",
"label": "Add Raw Materials" "label": "Add Raw Materials"
@@ -287,7 +295,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-07-31 16:17:47.287117", "modified": "2025-08-12 19:27:20.682797",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Operation", "name": "BOM Operation",

View File

@@ -425,7 +425,6 @@
"fieldname": "sub_assembly_warehouse", "fieldname": "sub_assembly_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Sub Assembly Warehouse", "label": "Sub Assembly Warehouse",
"mandatory_depends_on": "eval:doc.skip_available_sub_assembly_item === 1",
"options": "Warehouse" "options": "Warehouse"
}, },
{ {
@@ -446,7 +445,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-05-09 18:55:45.500257", "modified": "2025-08-12 19:48:09.302503",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan", "name": "Production Plan",

View File

@@ -1001,6 +1001,7 @@ class ProductionPlan(Document):
sub_assembly_items_store = [] # temporary store to process all subassembly items sub_assembly_items_store = [] # temporary store to process all subassembly items
bin_details = frappe._dict() bin_details = frappe._dict()
track_semi_finished_goods = True
for row in self.po_items: for row in self.po_items:
if self.skip_available_sub_assembly_item and not self.sub_assembly_warehouse: if self.skip_available_sub_assembly_item and not self.sub_assembly_warehouse:
frappe.throw(_("Row #{0}: Please select the Sub Assembly Warehouse").format(row.idx)) frappe.throw(_("Row #{0}: Please select the Sub Assembly Warehouse").format(row.idx))
@@ -1011,8 +1012,18 @@ class ProductionPlan(Document):
if not row.bom_no: if not row.bom_no:
frappe.throw(_("Row #{0}: Please select the BOM No in Assembly Items").format(row.idx)) frappe.throw(_("Row #{0}: Please select the BOM No in Assembly Items").format(row.idx))
if frappe.db.get_value("BOM", row.bom_no, "track_semi_finished_goods"):
frappe.msgprint(
_(
"Row #{0}: Since 'Track Semi Finished Goods' is enabled, the BOM {1} cannot be used for Sub Assembly Items"
).format(row.idx, row.bom_no)
)
continue
bom_data = [] bom_data = []
track_semi_finished_goods = False
get_sub_assembly_items( get_sub_assembly_items(
[item.production_item for item in sub_assembly_items_store], [item.production_item for item in sub_assembly_items_store],
bin_details, bin_details,
@@ -1026,7 +1037,11 @@ class ProductionPlan(Document):
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)
if not sub_assembly_items_store and self.skip_available_sub_assembly_item: if (
not track_semi_finished_goods
and not sub_assembly_items_store
and self.skip_available_sub_assembly_item
):
message = ( message = (
_( _(
"As there are sufficient Sub Assembly Items, Work Order is not required for Warehouse {0}." "As there are sufficient Sub Assembly Items, Work Order is not required for Warehouse {0}."
@@ -1253,6 +1268,7 @@ def get_exploded_items(item_details, company, bom_no, include_non_stock_items, p
) )
.where( .where(
(bei.docstatus < 2) (bei.docstatus < 2)
& (bei.is_sub_assembly_item == 0)
& (bom.name == bom_no) & (bom.name == bom_no)
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1) & (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
) )
@@ -1321,6 +1337,7 @@ def get_subitems(
) )
.where( .where(
(bom.name == bom_no) (bom.name == bom_no)
& (bom_item.is_sub_assembly_item == 0)
& (bom_item.docstatus < 2) & (bom_item.docstatus < 2)
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1) & (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
) )
@@ -2003,6 +2020,7 @@ def get_raw_materials_of_sub_assembly_items(
) )
.where( .where(
(bei.docstatus == 1) (bei.docstatus == 1)
& (bei.is_sub_assembly_item == 0)
& (bom.name == bom_no) & (bom.name == bom_no)
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1) & (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
) )