diff --git a/erpnext/manufacturing/report/bom_stock_calculated/__init__.py b/erpnext/manufacturing/report/bom_stock_calculated/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js deleted file mode 100644 index 76a95127853..00000000000 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2016, Epoch Consulting and contributors -// For license information, please see license.txt - -frappe.query_reports["BOM Stock Calculated"] = { - filters: [ - { - fieldname: "bom", - label: __("BOM"), - fieldtype: "Link", - options: "BOM", - reqd: 1, - }, - { - fieldname: "warehouse", - label: __("Warehouse"), - fieldtype: "Link", - options: "Warehouse", - }, - { - fieldname: "qty_to_make", - label: __("Quantity to Make"), - fieldtype: "Float", - default: "1.0", - reqd: 1, - }, - { - fieldname: "show_exploded_view", - label: __("Show exploded view"), - fieldtype: "Check", - default: false, - }, - ], -}; diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.json b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.json deleted file mode 100644 index 73421cebf0e..00000000000 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2018-05-17 12:40:31.355049", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "letter_head": "", - "modified": "2018-06-18 13:33:18.103220", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "BOM Stock Calculated", - "owner": "Administrator", - "ref_doctype": "BOM", - "report_name": "BOM Stock Calculated", - "report_type": "Script Report", - "roles": [ - { - "role": "Manufacturing Manager" - }, - { - "role": "Manufacturing User" - } - ] -} diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py deleted file mode 100644 index 4b5df4df4b2..00000000000 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.query_builder.functions import IfNull, Sum -from frappe.utils.data import comma_and -from pypika.terms import ExistsCriterion - - -def execute(filters=None): - columns = get_columns() - data = [] - - bom_data = get_bom_data(filters) - qty_to_make = filters.get("qty_to_make") - manufacture_details = get_manufacturer_records() - - for row in bom_data: - required_qty = qty_to_make * row.qty_per_unit - last_purchase_rate = frappe.db.get_value("Item", row.item_code, "last_purchase_rate") - - data.append(get_report_data(last_purchase_rate, required_qty, row, manufacture_details)) - - return columns, data - - -def get_report_data(last_purchase_rate, required_qty, row, manufacture_details): - qty_per_unit = row.qty_per_unit if row.qty_per_unit > 0 else 0 - difference_qty = row.actual_qty - required_qty - return [ - row.item_code, - row.description, - row.from_bom_no, - comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer", []), add_quotes=False), - comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer_part", []), add_quotes=False), - qty_per_unit, - row.actual_qty, - required_qty, - difference_qty, - last_purchase_rate, - ] - - -def get_columns(): - return [ - { - "fieldname": "item", - "label": _("Item"), - "fieldtype": "Link", - "options": "Item", - "width": 120, - }, - { - "fieldname": "description", - "label": _("Description"), - "fieldtype": "Data", - "width": 150, - }, - { - "fieldname": "from_bom_no", - "label": _("From BOM No"), - "fieldtype": "Link", - "options": "BOM", - "width": 150, - }, - { - "fieldname": "manufacturer", - "label": _("Manufacturer"), - "fieldtype": "Data", - "width": 120, - }, - { - "fieldname": "manufacturer_part_number", - "label": _("Manufacturer Part Number"), - "fieldtype": "Data", - "width": 150, - }, - { - "fieldname": "qty_per_unit", - "label": _("Qty Per Unit"), - "fieldtype": "Float", - "width": 110, - }, - { - "fieldname": "available_qty", - "label": _("Available Qty"), - "fieldtype": "Float", - "width": 120, - }, - { - "fieldname": "required_qty", - "label": _("Required Qty"), - "fieldtype": "Float", - "width": 120, - }, - { - "fieldname": "difference_qty", - "label": _("Difference Qty"), - "fieldtype": "Float", - "width": 130, - }, - { - "fieldname": "last_purchase_rate", - "label": _("Last Purchase Rate"), - "fieldtype": "Float", - "width": 160, - }, - ] - - -def get_bom_data(filters): - bom_item_table = "BOM Explosion Item" if filters.get("show_exploded_view") else "BOM Item" - - bom_item = frappe.qb.DocType(bom_item_table) - bin = frappe.qb.DocType("Bin") - - query = ( - frappe.qb.from_(bom_item) - .left_join(bin) - .on(bom_item.item_code == bin.item_code) - .select( - bom_item.item_code, - bom_item.description, - bom_item.parent.as_("from_bom_no"), - bom_item.qty_consumed_per_unit.as_("qty_per_unit"), - IfNull(Sum(bin.actual_qty), 0).as_("actual_qty"), - ) - .where((bom_item.parent == filters.get("bom")) & (bom_item.parenttype == "BOM")) - .groupby(bom_item.item_code) - .orderby(bom_item.idx) - ) - - if filters.get("warehouse"): - warehouse_details = frappe.db.get_value( - "Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1 - ) - - if warehouse_details: - wh = frappe.qb.DocType("Warehouse") - query = query.where( - ExistsCriterion( - frappe.qb.from_(wh) - .select(wh.name) - .where( - (wh.lft >= warehouse_details.lft) - & (wh.rgt <= warehouse_details.rgt) - & (bin.warehouse == wh.name) - ) - ) - ) - else: - query = query.where(bin.warehouse == filters.get("warehouse")) - - if bom_item_table == "BOM Item": - query = query.select(bom_item.bom_no, bom_item.is_phantom_item) - - data = query.run(as_dict=True) - return explode_phantom_boms(data, filters) if bom_item_table == "BOM Item" else data - - -def explode_phantom_boms(data, filters): - original_bom = filters.get("bom") - replacements = [] - - for idx, item in enumerate(data): - if not item.is_phantom_item: - continue - - filters["bom"] = item.bom_no - children = get_bom_data(filters) - filters["bom"] = original_bom - - for child in children: - child.qty_per_unit = (child.qty_per_unit or 0) * (item.qty_per_unit or 0) - - replacements.append((idx, children)) - - for idx, children in reversed(replacements): - data.pop(idx) - data[idx:idx] = children - - filters["bom"] = original_bom - return data - - -def get_manufacturer_records(): - details = frappe.get_all( - "Item Manufacturer", fields=["manufacturer", "manufacturer_part_no", "item_code"] - ) - - manufacture_details = frappe._dict() - for detail in details: - dic = manufacture_details.setdefault(detail.get("item_code"), {}) - dic.setdefault("manufacturer", []).append(detail.get("manufacturer")) - dic.setdefault("manufacturer_part", []).append(detail.get("manufacturer_part_no")) - - return manufacture_details diff --git a/erpnext/manufacturing/report/bom_stock_calculated/test_bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/test_bom_stock_calculated.py deleted file mode 100644 index 499629c2719..00000000000 --- a/erpnext/manufacturing/report/bom_stock_calculated/test_bom_stock_calculated.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom -from erpnext.manufacturing.report.bom_stock_calculated.bom_stock_calculated import ( - execute as bom_stock_calculated_report, -) -from erpnext.stock.doctype.item.test_item import make_item -from erpnext.tests.utils import ERPNextTestSuite - - -class TestBOMStockCalculated(ERPNextTestSuite): - def setUp(self): - self.fg_item, self.rm_items = create_items() - self.boms = create_boms(self.fg_item, self.rm_items) - - def test_bom_stock_calculated(self): - qty_to_make = 10 - - # Case 1: When Item(s) Qty and Stock Qty are equal. - data = bom_stock_calculated_report( - filters={ - "qty_to_make": qty_to_make, - "bom": self.boms[0].name, - } - )[1] - expected_data = get_expected_data(self.boms[0], qty_to_make) - self.assertSetEqual(set(tuple(x) for x in data), set(tuple(x) for x in expected_data)) - - # Case 2: When Item(s) Qty and Stock Qty are different and BOM Qty is 1. - data = bom_stock_calculated_report( - filters={ - "qty_to_make": qty_to_make, - "bom": self.boms[1].name, - } - )[1] - expected_data = get_expected_data(self.boms[1], qty_to_make) - self.assertSetEqual(set(tuple(x) for x in data), set(tuple(x) for x in expected_data)) - - # Case 3: When Item(s) Qty and Stock Qty are different and BOM Qty is greater than 1. - data = bom_stock_calculated_report( - filters={ - "qty_to_make": qty_to_make, - "bom": self.boms[2].name, - } - )[1] - expected_data = get_expected_data(self.boms[2], qty_to_make) - self.assertSetEqual(set(tuple(x) for x in data), set(tuple(x) for x in expected_data)) - - -def create_items(): - fg_item = make_item(properties={"is_stock_item": 1}).name - rm_item1 = make_item( - properties={ - "is_stock_item": 1, - "standard_rate": 100, - "opening_stock": 100, - "last_purchase_rate": 100, - "item_defaults": [{"company": "_Test Company", "default_warehouse": "Stores - _TC"}], - } - ).name - rm_item2 = make_item( - properties={ - "is_stock_item": 1, - "standard_rate": 200, - "opening_stock": 200, - "last_purchase_rate": 200, - "item_defaults": [{"company": "_Test Company", "default_warehouse": "Stores - _TC"}], - } - ).name - - return fg_item, [rm_item1, rm_item2] - - -def create_boms(fg_item, rm_items): - def update_bom_items(bom, uom, conversion_factor): - for item in bom.items: - item.uom = uom - item.conversion_factor = conversion_factor - - return bom - - bom1 = make_bom(item=fg_item, quantity=1, raw_materials=rm_items, rm_qty=10) - - bom2 = make_bom(item=fg_item, quantity=1, raw_materials=rm_items, rm_qty=10, do_not_submit=True) - bom2 = update_bom_items(bom2, "Box", 10) - bom2.save() - bom2.submit() - - bom3 = make_bom(item=fg_item, quantity=2, raw_materials=rm_items, rm_qty=10, do_not_submit=True) - bom3 = update_bom_items(bom3, "Box", 10) - bom3.save() - bom3.submit() - - return [bom1, bom2, bom3] - - -def get_expected_data(bom, qty_to_make): - expected_data = [] - - for idx in range(len(bom.items)): - expected_data.append( - [ - bom.items[idx].item_code, - bom.items[idx].item_code, - bom.name, - "", - "", - float(bom.items[idx].stock_qty / bom.quantity), - float(100 * (idx + 1)), - float(qty_to_make * (bom.items[idx].stock_qty / bom.quantity)), - float((100 * (idx + 1)) - (qty_to_make * (bom.items[idx].stock_qty / bom.quantity))), - float(100 * (idx + 1)), - ] - ) - - return expected_data diff --git a/erpnext/manufacturing/report/bom_stock_report/__init__.py b/erpnext/manufacturing/report/bom_stock_report/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html deleted file mode 100644 index 2ae8848cc03..00000000000 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html +++ /dev/null @@ -1,27 +0,0 @@ -
| {%= __("Item") %} | -{%= __("Description") %} | -{%= __("Required Qty") %} | -{%= __("In Stock Qty") %} | -{%= __("Enough Parts to Build") %} | -{%= data[i][ __("Item")] %} | -{%= data[i][ __("Description")] %} | -{%= data[i][ __("Required Qty")] %} | -{%= data[i][ __("In Stock Qty")] %} | -{%= data[i][ __("Enough Parts to Build")] %} | - - {% } %} -
|---|