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) {
@@ -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) {
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) {
@@ -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();
},
@@ -938,6 +972,7 @@ frappe.ui.form.on("BOM", {
label: __("Raw Materials"),
fieldname: "items",
fieldtype: "Table",
data: [],
reqd: 1,
fields: [
{

View File

@@ -722,12 +722,43 @@ class BOM(WebsiteGenerator):
row.update(get_item_details(row.get("item_code")))
row.operation_row_id = operation_row_id
row.idx = None
row.name = None
self.append("items", row)
item_row = None
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()
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()
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}):
@@ -745,6 +776,9 @@ class BOM(WebsiteGenerator):
row.uom = row.stock_uom
row.operation_row_id = operation_row_id
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)
def traverse_tree(self, bom_list=None):
@@ -946,6 +980,7 @@ class BOM(WebsiteGenerator):
"item_code": d.item_code,
"item_name": d.item_name,
"operation": d.operation,
"is_sub_assembly_item": d.is_sub_assembly_item,
"source_warehouse": d.source_warehouse,
"description": d.description,
"image": d.image,
@@ -978,6 +1013,7 @@ class BOM(WebsiteGenerator):
bom_item.description,
bom_item.source_warehouse,
bom_item.operation,
bom_item.is_sub_assembly_item,
bom_item.stock_uom,
bom_item.stock_qty,
bom_item.rate,
@@ -1008,6 +1044,7 @@ class BOM(WebsiteGenerator):
"rate": flt(d["rate"]),
"include_item_in_manufacturing": d.get("include_item_in_manufacturing", 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",
"amount",
"include_item_in_manufacturing",
"sourced_by_supplier"
"sourced_by_supplier",
"is_sub_assembly_item"
],
"fields": [
{
@@ -165,21 +166,30 @@
"fieldtype": "Check",
"label": "Sourced by Supplier",
"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,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:06:40.935882",
"modified": "2025-08-12 20:02:32.694836",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Explosion Item",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

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

View File

@@ -41,7 +41,8 @@
"include_item_in_manufacturing",
"original_item",
"column_break_33",
"sourced_by_supplier"
"sourced_by_supplier",
"is_sub_assembly_item"
],
"fields": [
{
@@ -300,19 +301,28 @@
"fieldname": "operation_row_id",
"fieldtype": "Int",
"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,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:08:41.079752",
"modified": "2025-08-12 20:01:59.532613",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

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

View File

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

View File

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

View File

@@ -1001,6 +1001,7 @@ class ProductionPlan(Document):
sub_assembly_items_store = [] # temporary store to process all subassembly items
bin_details = frappe._dict()
track_semi_finished_goods = True
for row in self.po_items:
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))
@@ -1011,8 +1012,18 @@ class ProductionPlan(Document):
if not row.bom_no:
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 = []
track_semi_finished_goods = False
get_sub_assembly_items(
[item.production_item for item in sub_assembly_items_store],
bin_details,
@@ -1026,7 +1037,11 @@ class ProductionPlan(Document):
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
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 = (
_(
"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(
(bei.docstatus < 2)
& (bei.is_sub_assembly_item == 0)
& (bom.name == bom_no)
& (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(
(bom.name == bom_no)
& (bom_item.is_sub_assembly_item == 0)
& (bom_item.docstatus < 2)
& (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(
(bei.docstatus == 1)
& (bei.is_sub_assembly_item == 0)
& (bom.name == bom_no)
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
)