From cb722669ad14658bdadac3fd612fcd15b1fd2eef Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 15:52:41 +0530 Subject: [PATCH 1/8] Changed the BOM scrap item to BOM Secondary item --- .../custom/{bom_scrap_item.json => bom_secondary_item.json} | 6 +++--- beam/beam/handling_unit.py | 4 +++- beam/beam/overrides/stock_entry.py | 4 ++-- beam/tests/setup.py | 2 +- beam/tests/test_handling_unit.py | 4 +++- 5 files changed, 12 insertions(+), 8 deletions(-) rename beam/beam/custom/{bom_scrap_item.json => bom_secondary_item.json} (88%) diff --git a/beam/beam/custom/bom_scrap_item.json b/beam/beam/custom/bom_secondary_item.json similarity index 88% rename from beam/beam/custom/bom_scrap_item.json rename to beam/beam/custom/bom_secondary_item.json index 4037dd3e..205b26d6 100644 --- a/beam/beam/custom/bom_scrap_item.json +++ b/beam/beam/custom/bom_secondary_item.json @@ -7,7 +7,7 @@ "collapsible": 0, "columns": 0, "default": null, - "dt": "BOM Scrap Item", + "dt": "BOM Secondary Item", "fetch_if_empty": 0, "fieldname": "create_handling_unit", "fieldtype": "Check", @@ -28,7 +28,7 @@ "label": "Create Handling Unit", "length": 0, "module": "BEAM", - "name": "BOM Scrap Item-create_handling_unit", + "name": "BOM Secondary Item-create_handling_unit", "no_copy": 0, "non_negative": 0, "permlevel": 0, @@ -43,7 +43,7 @@ "unique": 0 } ], - "doctype": "BOM Scrap Item", + "doctype": "BOM Secondary Item", "property_setters": [], "sync_on_migrate": 1 } diff --git a/beam/beam/handling_unit.py b/beam/beam/handling_unit.py index b73182fe..591daf16 100644 --- a/beam/beam/handling_unit.py +++ b/beam/beam/handling_unit.py @@ -56,7 +56,9 @@ def generate_handling_units(doc, method=None): if doc.doctype == "Stock Entry" and doc.purpose == "Manufacture" and row.is_scrap_item: create_handling_unit = frappe.get_value( - "BOM Scrap Item", {"item_code": row.item_code, "parent": doc.bom_no}, "create_handling_unit" + "BOM Secondary Item", + {"item_code": row.item_code, "parent": doc.bom_no, "type": "Scrap"}, + "create_handling_unit", ) if bool(create_handling_unit): handling_unit = frappe.new_doc("Handling Unit") diff --git a/beam/beam/overrides/stock_entry.py b/beam/beam/overrides/stock_entry.py index b4627ff5..ca320a30 100644 --- a/beam/beam/overrides/stock_entry.py +++ b/beam/beam/overrides/stock_entry.py @@ -173,8 +173,8 @@ def validate_items_with_handling_unit(doc, method=None): if not frappe.get_value("Item", row.item_code, "enable_handling_unit"): continue elif row.is_scrap_item and not frappe.get_value( - "BOM Scrap Item", - {"item_code": row.item_code, "parent": doc.get("bom_no")}, + "BOM Secondary Item", + {"item_code": row.item_code, "parent": doc.get("bom_no"), "type": "Scrap"}, "create_handling_unit", ): continue diff --git a/beam/tests/setup.py b/beam/tests/setup.py index 4cf7dacb..70cc62c8 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -355,7 +355,7 @@ def create_boms(settings): b.append("operations", {**operation, "hour_rate": 15.00}) if bom.get("scrap_items"): for scrap_item in bom.get("scrap_items"): - b.append("scrap_items", {**scrap_item}) + b.append("secondary_items", {**scrap_item, "type": "Scrap"}) b.save() b.submit() diff --git a/beam/tests/test_handling_unit.py b/beam/tests/test_handling_unit.py index 76b5d9b0..d323501d 100644 --- a/beam/tests/test_handling_unit.py +++ b/beam/tests/test_handling_unit.py @@ -330,7 +330,9 @@ def test_stock_entry_for_manufacture(): assert row.transfer_qty == sle.actual_qty assert row.item_code == sle.item_code create_handling_unit = frappe.get_value( - "BOM Scrap Item", {"item_code": row.item_code, "parent": _se.bom_no}, "create_handling_unit" + "BOM Secondary Item", + {"item_code": row.item_code, "parent": _se.bom_no, "type": "Scrap"}, + "create_handling_unit", ) if create_handling_unit: assert row.handling_unit == sle.handling_unit From c826484e49e3b0c07a667a3b5aa6fa2c7f72e946 Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 16:09:06 +0530 Subject: [PATCH 2/8] Added the sub assembly warehouse --- beam/tests/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/beam/tests/setup.py b/beam/tests/setup.py index 70cc62c8..d5ce2738 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -477,6 +477,7 @@ def create_production_plan(settings, prod_plan_from_doc): pp.get_mr_items() for item in pp.po_items: item.planned_start_date = settings.day + pp.sub_assembly_warehouse = "Kitchen - APC" pp.get_sub_assembly_items() for item in pp.sub_assembly_items: item.schedule_date = settings.day From 007d6eb2173097b2d72cfb75f283588494b50cd6 Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 16:25:42 +0530 Subject: [PATCH 3/8] Updated poetry lock file --- poetry.lock | 48 +++--------------------------------------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/poetry.lock b/poetry.lock index a18c40cd..cd7d3b31 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.0 and should not be changed by hand. [[package]] name = "annotated-types" @@ -54,9 +54,7 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] trio = ["trio (>=0.32.0)"] @@ -478,9 +476,6 @@ files = [ {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -542,7 +537,6 @@ files = [ [package.dependencies] cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\""} -typing-extensions = {version = ">=4.13.2", markers = "python_full_version < \"3.11.0\""} [package.extras] ssh = ["bcrypt (>=3.1.5)"] @@ -576,22 +570,6 @@ dev = ["pre-commit (>=2.16.0) ; python_version >= \"3.9\"", "pydoctor (>=25.4.0) docs = ["pydoctor (>=25.4.0)"] test = ["pytest"] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "h11" version = "0.16.0" @@ -1118,7 +1096,6 @@ files = [ librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" pathspec = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = {version = ">=4.6.0", markers = "python_version < \"3.15\""} [package.extras] @@ -1426,7 +1403,6 @@ files = [ [package.dependencies] cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} -typing_extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] crypto = ["cryptography (>=3.4.0)"] @@ -1487,11 +1463,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -1684,19 +1658,6 @@ files = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -markers = "python_full_version <= \"3.11.0a6\"" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tqdm" version = "4.68.3" @@ -1868,9 +1829,6 @@ files = [ {file = "vulture-2.16.tar.gz", hash = "sha256:f8d9f6e2af03011664a3c6c240c9765b3f392917d3135fddca6d6a68d359f717"}, ] -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - [[package]] name = "zebra-zpl" version = "0.1.0" @@ -1889,5 +1847,5 @@ resolved_reference = "45ffc60638814df575d9fe11c7504b1a533e4ecb" [metadata] lock-version = "2.1" -python-versions = ">=3.10,<3.15" -content-hash = "c69661febdefcd14d5a01f31684a13a66b28ef20980ab2b3d2c9bb1b6e2f8b0a" +python-versions = ">=3.14,<3.15" +content-hash = "5b84327d44d5c770614c223d28894257dacd4a6f5973dce17d90104e9b504bf8" From dc3cce890bc98e357a1696374cd5ec6588ecbba6 Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 16:43:03 +0530 Subject: [PATCH 4/8] Fixed test fails --- beam/beam/handling_unit.py | 6 +++--- beam/beam/overrides/stock_entry.py | 4 ++-- beam/beam/scan/__init__.py | 2 +- beam/tests/test_handling_unit.py | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/beam/beam/handling_unit.py b/beam/beam/handling_unit.py index 591daf16..e398904f 100644 --- a/beam/beam/handling_unit.py +++ b/beam/beam/handling_unit.py @@ -54,7 +54,7 @@ def generate_handling_units(doc, method=None): handling_unit.save() row.handling_unit = handling_unit.name - if doc.doctype == "Stock Entry" and doc.purpose == "Manufacture" and row.is_scrap_item: + if doc.doctype == "Stock Entry" and doc.purpose == "Manufacture" and row.type == "Scrap": create_handling_unit = frappe.get_value( "BOM Secondary Item", {"item_code": row.item_code, "parent": doc.bom_no, "type": "Scrap"}, @@ -70,7 +70,7 @@ def generate_handling_units(doc, method=None): continue if doc.doctype == "Stock Entry" and not ( - any([row.is_finished_item, doc.purpose == "Material Receipt", row.is_scrap_item]) + any([row.is_finished_item, doc.purpose == "Material Receipt", row.type == "Scrap"]) ): continue @@ -121,7 +121,7 @@ def validate_handling_unit_overconsumption(doc, method=None): if ( abs(hu.stock_qty - row.get(qty_field)) > 0.0 and (hu.stock_qty - row.get(qty_field) > precision_denominator) - and not row.is_scrap_item + and row.type != "Scrap" ): error = True else: # incoming and transfer / same warehouse diff --git a/beam/beam/overrides/stock_entry.py b/beam/beam/overrides/stock_entry.py index ca320a30..e7d8a736 100644 --- a/beam/beam/overrides/stock_entry.py +++ b/beam/beam/overrides/stock_entry.py @@ -172,7 +172,7 @@ def validate_items_with_handling_unit(doc, method=None): for row in doc.items: if not frappe.get_value("Item", row.item_code, "enable_handling_unit"): continue - elif row.is_scrap_item and not frappe.get_value( + elif row.type == "Scrap" and not frappe.get_value( "BOM Secondary Item", {"item_code": row.item_code, "parent": doc.get("bom_no"), "type": "Scrap"}, "create_handling_unit", @@ -180,7 +180,7 @@ def validate_items_with_handling_unit(doc, method=None): continue elif ( doc.stock_entry_type in ("Repack", "Manufacture") - and not (row.t_warehouse or row.is_finished_item or row.is_scrap_item) + and not (row.t_warehouse or row.is_finished_item or row.type == "Scrap") and not row.handling_unit ): frappe.throw(frappe._(f"Row #{row.idx}: Handling Unit is missing for item {row.item_code}")) diff --git a/beam/beam/scan/__init__.py b/beam/beam/scan/__init__.py index 7ee59668..9c810dd6 100644 --- a/beam/beam/scan/__init__.py +++ b/beam/beam/scan/__init__.py @@ -99,7 +99,7 @@ def get_handling_unit(handling_unit: str, parent_doctype: str | None = None) -> filters={"handling_unit": handling_unit, "is_cancelled": 0}, fields=[ "item_code", - "SUM(actual_qty) AS stock_qty", + {"SUM": "actual_qty", "as": "stock_qty"}, "company", "handling_unit", "voucher_no", diff --git a/beam/tests/test_handling_unit.py b/beam/tests/test_handling_unit.py index d323501d..278098a0 100644 --- a/beam/tests/test_handling_unit.py +++ b/beam/tests/test_handling_unit.py @@ -294,7 +294,7 @@ def test_stock_entry_for_manufacture(): ): continue if ( - row.is_finished_item or row.is_scrap_item + row.is_finished_item or row.type == "Scrap" ): # finished and scrap items' handling units will be generated and wouldn't be scanned continue hu = frappe.get_value( @@ -321,12 +321,12 @@ def test_stock_entry_for_manufacture(): sle = frappe.get_doc( "Stock Ledger Entry", {"voucher_detail_no": row.name, "handling_unit": row.handling_unit} ) - if not row.is_finished_item and not row.is_scrap_item: + if not row.is_finished_item and row.type != "Scrap": assert row.transfer_qty == -(sle.actual_qty) assert row.item_code == sle.item_code assert row.s_warehouse == sle.warehouse # source/ warehouse assert sle.handling_unit == row.handling_unit - elif row.is_scrap_item: + elif row.type == "Scrap": assert row.transfer_qty == sle.actual_qty assert row.item_code == sle.item_code create_handling_unit = frappe.get_value( From f04f8415873ae92e0553dbed1c1b8841530f657b Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 17:12:38 +0530 Subject: [PATCH 5/8] Fixed test fail --- beam/tests/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beam/tests/setup.py b/beam/tests/setup.py index d5ce2738..9298dad4 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -477,7 +477,7 @@ def create_production_plan(settings, prod_plan_from_doc): pp.get_mr_items() for item in pp.po_items: item.planned_start_date = settings.day - pp.sub_assembly_warehouse = "Kitchen - APC" + pp.skip_available_sub_assembly_item = 0 pp.get_sub_assembly_items() for item in pp.sub_assembly_items: item.schedule_date = settings.day From 3bda3f7bd0ec834dc033805f9c8fe6d9baf6e02d Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 17:52:51 +0530 Subject: [PATCH 6/8] Fixed warehouse issue --- beam/tests/setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/beam/tests/setup.py b/beam/tests/setup.py index 9298dad4..609f7503 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -548,6 +548,8 @@ def create_production_plan(settings, prod_plan_from_doc): for wo in wos: wo = frappe.get_doc("Work Order", wo) wo.wip_warehouse = "Kitchen - APC" + if not wo.fg_warehouse: + wo.fg_warehouse = "Kitchen - APC" wo.actual_start_date = wo.planned_start_date = start_time wo.required_items = sorted(wo.required_items, key=lambda x: x.get("item_code")) for idx, w in enumerate(wo.required_items, start=1): From 1877739845f0f84e2265c1021406d01f22bfd35d Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 18:22:02 +0530 Subject: [PATCH 7/8] Fixed CI fail --- beam/tests/setup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/beam/tests/setup.py b/beam/tests/setup.py index 609f7503..08ab415e 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -485,7 +485,14 @@ def create_production_plan(settings, prod_plan_from_doc): raw_materials = get_items_for_material_requests( pp.as_dict(), warehouses=None, get_parent_warehouse_data=None ) + combined_raw_materials = {} for row in raw_materials: + item_code = row.get("item_code") + if item_code in combined_raw_materials: + combined_raw_materials[item_code]["quantity"] += row.get("quantity") + else: + combined_raw_materials[item_code] = row + for row in combined_raw_materials.values(): pp.append( "mr_items", { From 4fd32d976a2af715022814db40ad6a543e1398fe Mon Sep 17 00:00:00 2001 From: Ishwarya Date: Wed, 22 Jul 2026 20:26:10 +0530 Subject: [PATCH 8/8] Fixed setup fail issues --- beam/tests/setup.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/beam/tests/setup.py b/beam/tests/setup.py index 08ab415e..0bc2fd51 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -505,9 +505,23 @@ def create_production_plan(settings, prod_plan_from_doc): pp.save() pp.submit() - pp.make_material_request() - mr = frappe.get_last_doc("Material Request") + mr = frappe.new_doc("Material Request") + mr.company = settings.company + mr.material_request_type = "Purchase" mr.schedule_date = mr.transaction_date = settings.day + for row in combined_raw_materials.values(): + mr.append( + "items", + { + "item_code": row.get("item_code"), + "qty": row.get("quantity"), + "uom": row.get("uom") or row.get("stock_uom"), + "warehouse": frappe.get_value( + "Item Default", {"parent": row.get("item_code")}, "default_warehouse" + ), + "schedule_date": settings.day, + }, + ) mr.save() mr.submit()