From c360487cd1fa94174fa5d363833c474b0fbd1edd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 11 Jun 2026 22:23:24 +0530 Subject: [PATCH] fix: converted whitelist non class methods to class methods --- .../doctype/bom_creator/bom_creator.py | 292 +++++++++--------- .../doctype/bom_creator/test_bom_creator.py | 30 +- .../bom_configurator.bundle.js | 17 +- 3 files changed, 162 insertions(+), 177 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py index a41ab1a7160..2eca7519dfb 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py @@ -388,7 +388,7 @@ class BOMCreator(Document): @frappe.whitelist() def edit_bom_creator(self, docname: str, data: str | dict): - frappe.has_permission("BOM Creator", "write", throw=True) + frappe.has_permission("BOM Creator", "write", doc=self, throw=True) if not frappe.db.exists("BOM Creator Item", {"parent": self.name, "name": docname}): frappe.throw(_("BOM Creator Item with name {0} does not exist").format(docname)) @@ -396,15 +396,18 @@ class BOMCreator(Document): if isinstance(data, str): data = frappe.parse_json(data) + updated = False for row in self.items: if row.name == docname: for key, value in data.items(): - if key in BOM_ITEM_FIELDS: + if key in BOM_ITEM_FIELDS and row.get(key) != value: row.set(key, value) + updated = True break - self.set_rate_for_items() - self.save() + if updated: + self.set_rate_for_items() + self.save() frappe.msgprint(_("Updated successfully"), alert=True) @@ -421,6 +424,145 @@ class BOMCreator(Document): def get_default_bom(self, item_code: str): return frappe.get_cached_value("Item", item_code, "default_bom") + @frappe.whitelist() + def add_item(self, **kwargs): + frappe.has_permission("BOM Creator", "write", doc=self, throw=True) + + if isinstance(kwargs, str): + kwargs = frappe.parse_json(kwargs) + + if isinstance(kwargs, dict): + kwargs = frappe._dict(kwargs) + + item_info = get_item_details(kwargs.item_code) + + parent_row_no = "" + if kwargs.fg_reference_id and self.name != kwargs.fg_reference_id: + parent_row_no = get_parent_row_no(self, kwargs.fg_reference_id) + + kwargs.update( + { + "uom": item_info.stock_uom, + "stock_uom": item_info.stock_uom, + "conversion_factor": 1, + } + ) + + if parent_row_no: + kwargs.update({"parent_row_no": parent_row_no}) + + self.append("items", kwargs) + self.save() + + return self + + @frappe.whitelist() + def add_sub_assembly(self, **kwargs): + frappe.has_permission("BOM Creator", "write", doc=self, throw=True) + + if isinstance(kwargs, str): + kwargs = frappe.parse_json(kwargs) + + if isinstance(kwargs, dict): + kwargs = frappe._dict(kwargs) + + bom_item = frappe.parse_json(kwargs.bom_item) + + name = kwargs.fg_reference_id + parent_row_no = "" + + if not kwargs.convert_to_sub_assembly: + item_info = get_item_details(bom_item.item_code) + parent_row_no = get_parent_row_no(self, kwargs.fg_reference_id) + + item_row = self.append( + "items", + { + "item_code": bom_item.item_code, + "qty": bom_item.qty, + "uom": item_info.stock_uom, + "fg_item": kwargs.fg_item, + "conversion_factor": 1, + "parent_row_no": parent_row_no, + "fg_reference_id": name, + "stock_qty": bom_item.qty, + "do_not_explode": 1, + "is_expandable": 1, + "stock_uom": item_info.stock_uom, + "operation": bom_item.operation, + "is_phantom_item": sbool(kwargs.phantom), + }, + ) + + parent_row_no = item_row.idx + name = "" + else: + if sbool(kwargs.phantom): + parent_row = next(item for item in self.items if item.name == kwargs.fg_reference_id) + parent_row.db_set("is_phantom_item", 1) + parent_row_no = get_parent_row_no(self, kwargs.fg_reference_id) + + for row in bom_item.get("items"): + row = frappe._dict(row) + item_info = get_item_details(row.item_code) + self.append( + "items", + { + "item_code": row.item_code, + "qty": row.qty, + "operation": row.operation, + "fg_item": bom_item.item_code, + "uom": item_info.stock_uom, + "fg_reference_id": name, + "parent_row_no": parent_row_no, + "conversion_factor": 1, + "do_not_explode": 1, + "stock_qty": row.qty, + "stock_uom": item_info.stock_uom, + }, + ) + + self.save() + + return self + + @frappe.whitelist() + def delete_node(self, **kwargs): + frappe.has_permission("BOM Creator", "write", doc=self, throw=True) + + if isinstance(kwargs, str): + kwargs = frappe.parse_json(kwargs) + + if isinstance(kwargs, dict): + kwargs = frappe._dict(kwargs) + + updated = False + if kwargs.docname: + row = next((row for row in self.items if row.name == kwargs.docname), None) + if not row: + frappe.throw(_("BOM Creator Item with name {0} does not exist").format(kwargs.docname)) + + row.delete() + updated = True + + items = get_children(parent=kwargs.fg_item, parent_id=self.name) + if items: + for item in items: + updated = True + child_row = next((row for row in self.items if row.name == item.name), None) + if child_row: + child_row.delete() + if item.expandable: + self.delete_node(fg_item=item.value) + + if updated: + self.set_rate_for_items() + self.save() + + return self + + return frappe._dict() + @frappe.whitelist() def get_children(doctype: str | None = None, parent: str | None = None, **kwargs): @@ -459,112 +601,6 @@ def get_children(doctype: str | None = None, parent: str | None = None, **kwargs return frappe.get_all("BOM Creator Item", fields=fields, filters=query_filters, order_by="idx") -@frappe.whitelist() -def add_item(**kwargs): - frappe.has_permission("BOM Creator", "write", throw=True) - - if isinstance(kwargs, str): - kwargs = frappe.parse_json(kwargs) - - if isinstance(kwargs, dict): - kwargs = frappe._dict(kwargs) - - doc = frappe.get_doc("BOM Creator", kwargs.parent) - item_info = get_item_details(kwargs.item_code) - - parent_row_no = "" - if kwargs.fg_reference_id and doc.name != kwargs.fg_reference_id: - parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id) - - kwargs.update( - { - "uom": item_info.stock_uom, - "stock_uom": item_info.stock_uom, - "conversion_factor": 1, - } - ) - - if parent_row_no: - kwargs.update({"parent_row_no": parent_row_no}) - - doc.append("items", kwargs) - doc.save() - - return doc - - -@frappe.whitelist() -def add_sub_assembly(**kwargs): - frappe.has_permission("BOM Creator", "write", throw=True) - - if isinstance(kwargs, str): - kwargs = frappe.parse_json(kwargs) - - if isinstance(kwargs, dict): - kwargs = frappe._dict(kwargs) - - doc = frappe.get_doc("BOM Creator", kwargs.parent) - bom_item = frappe.parse_json(kwargs.bom_item) - - name = kwargs.fg_reference_id - parent_row_no = "" - - if not kwargs.convert_to_sub_assembly: - item_info = get_item_details(bom_item.item_code) - parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id) - - item_row = doc.append( - "items", - { - "item_code": bom_item.item_code, - "qty": bom_item.qty, - "uom": item_info.stock_uom, - "fg_item": kwargs.fg_item, - "conversion_factor": 1, - "parent_row_no": parent_row_no, - "fg_reference_id": name, - "stock_qty": bom_item.qty, - "do_not_explode": 1, - "is_expandable": 1, - "stock_uom": item_info.stock_uom, - "operation": bom_item.operation, - "is_phantom_item": sbool(kwargs.phantom), - }, - ) - - parent_row_no = item_row.idx - name = "" - else: - if sbool(kwargs.phantom): - parent_row = next(item for item in doc.items if item.name == kwargs.fg_reference_id) - parent_row.db_set("is_phantom_item", 1) - parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id) - - for row in bom_item.get("items"): - row = frappe._dict(row) - item_info = get_item_details(row.item_code) - doc.append( - "items", - { - "item_code": row.item_code, - "qty": row.qty, - "operation": row.operation, - "fg_item": bom_item.item_code, - "uom": item_info.stock_uom, - "fg_reference_id": name, - "parent_row_no": parent_row_no, - "conversion_factor": 1, - "do_not_explode": 1, - "stock_qty": row.qty, - "stock_uom": item_info.stock_uom, - }, - ) - - doc.save() - - return doc - - def get_item_details(item_code): return frappe.get_cached_value( "Item", item_code, ["item_name", "description", "image", "stock_uom", "default_bom"], as_dict=1 @@ -582,39 +618,3 @@ def get_parent_row_no(doc, name): frappe.msgprint(_("Parent Row No not found for {0}").format(name), alert=True) return None - - -@frappe.whitelist() -def delete_node(**kwargs): - frappe.has_permission("BOM Creator", "write", throw=True) - - if isinstance(kwargs, str): - kwargs = frappe.parse_json(kwargs) - - if isinstance(kwargs, dict): - kwargs = frappe._dict(kwargs) - - updated = False - if kwargs.docname: - if not frappe.db.exists("BOM Creator Item", {"name": kwargs.docname, "parent": kwargs.parent}): - frappe.throw(_("BOM Creator Item with name {0} does not exist").format(kwargs.docname)) - - frappe.delete_doc("BOM Creator Item", kwargs.docname) - updated = True - - items = get_children(parent=kwargs.fg_item, parent_id=kwargs.parent) - if items: - for item in items: - updated = True - frappe.delete_doc("BOM Creator Item", item.name) - if item.expandable: - delete_node(fg_item=item.value, parent=item.parent_id) - - if updated: - doc = frappe.get_doc("BOM Creator", kwargs.parent) - doc.set_rate_for_items() - doc.save() - - return doc - - return frappe._dict() diff --git a/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py index 3c280f6baae..94a8b0b607b 100644 --- a/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py +++ b/erpnext/manufacturing/doctype/bom_creator/test_bom_creator.py @@ -5,12 +5,6 @@ import random import frappe -from erpnext.manufacturing.doctype.bom_creator.bom_creator import ( - add_item, - add_sub_assembly, - delete_node, - edit_bom_creator, -) from erpnext.stock.doctype.item.test_item import make_item from erpnext.tests.utils import ERPNextTestSuite @@ -40,8 +34,7 @@ class TestBOMCreator(ERPNextTestSuite): conversion_rate=1, ) - add_sub_assembly( - parent=doc.name, + doc.add_sub_assembly( fg_item=final_product, fg_reference_id=doc.name, bom_item={ @@ -95,8 +88,7 @@ class TestBOMCreator(ERPNextTestSuite): conversion_rate=1, ) - add_item( - parent=doc.name, + doc.add_item( fg_item=final_product, fg_reference_id=doc.name, item_code="Pedal Assembly", @@ -139,8 +131,7 @@ class TestBOMCreator(ERPNextTestSuite): conversion_rate=1, ) - add_item( - parent=doc.name, + doc.add_item( fg_item=final_product, fg_reference_id=doc.name, item_code="Pedal Assembly", @@ -150,9 +141,8 @@ class TestBOMCreator(ERPNextTestSuite): doc.reload() self.assertEqual(doc.items[0].is_expandable, 0) - add_sub_assembly( + doc.add_sub_assembly( convert_to_sub_assembly=1, - parent=doc.name, fg_item=final_product, fg_reference_id=doc.items[0].name, bom_item={ @@ -207,8 +197,7 @@ class TestBOMCreator(ERPNextTestSuite): conversion_rate=1, ) - add_item( - parent=doc.name, + doc.add_item( fg_item=final_product, fg_reference_id=doc.name, item_code="Pedal Assembly", @@ -218,9 +207,8 @@ class TestBOMCreator(ERPNextTestSuite): doc.reload() self.assertEqual(doc.items[0].is_expandable, 0) - add_sub_assembly( + doc.add_sub_assembly( convert_to_sub_assembly=1, - parent=doc.name, fg_item=final_product, fg_reference_id=doc.items[0].name, bom_item={ @@ -277,17 +265,15 @@ class TestBOMCreator(ERPNextTestSuite): # Editing a row that does not belong to this BOM Creator must be rejected. self.assertRaises( frappe.ValidationError, - edit_bom_creator, + doc.edit_bom_creator, docname="non-existent-row", data={"qty": 5}, - parent=doc.name, ) # Deleting a row that does not belong to this BOM Creator must be rejected. self.assertRaises( frappe.ValidationError, - delete_node, - parent=doc.name, + doc.delete_node, fg_item=final_product, docname="non-existent-row", ) diff --git a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js index 92960d967bc..07871687006 100644 --- a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js +++ b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js @@ -240,9 +240,9 @@ class BOMConfigurator { } frappe.call({ - method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.add_item", + method: "add_item", + doc: this.frm.doc, args: { - parent: node.data.parent_id, fg_item: node.data.value, item_code: data.item_code, fg_reference_id: node.data.name || this.frm.doc.name, @@ -295,9 +295,9 @@ class BOMConfigurator { } frappe.call({ - method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.add_sub_assembly", + method: "add_sub_assembly", + doc: this.frm.doc, args: { - parent: node.data.parent_id, fg_item: node.data.value, fg_reference_id: node.data.name || this.frm.doc.name, bom_item: bom_item, @@ -442,9 +442,9 @@ class BOMConfigurator { } frappe.call({ - method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.add_sub_assembly", + method: "add_sub_assembly", + doc: this.frm.doc, args: { - parent: node.data.parent_id, fg_item: node.data.value, bom_item: bom_item, fg_reference_id: node.data.name || this.frm.doc.name, @@ -479,9 +479,9 @@ class BOMConfigurator { delete_node(node, view) { frappe.confirm(__("Are you sure you want to delete this Item?"), () => { frappe.call({ - method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.delete_node", + method: "delete_node", + doc: this.frm.doc, args: { - parent: node.data.parent_id, fg_item: node.data.value, doctype: node.data.doctype, docname: node.data.name, @@ -501,7 +501,6 @@ class BOMConfigurator { this.frm.edit_bom_dialog = frappe.prompt( fields, (data) => { - let doctype = node.data.doctype || this.frm.doc.doctype; let docname = node.data.name || this.frm.doc.name; frappe.call({