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_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 @@
-
{%= __("BOM Stock Report") %}
-{%= filters.bom %}
-{%= filters.warehouse %}
-
-
-
-
-
- | {%= __("Item") %} |
- {%= __("Description") %} |
- {%= __("Required Qty") %} |
- {%= __("In Stock Qty") %} |
- {%= __("Enough Parts to Build") %} |
-
-
-
- {% for(var i=0, l=data.length; i
- {%= data[i][ __("Item")] %} |
- {%= data[i][ __("Description")] %} |
- {%= data[i][ __("Required Qty")] %} |
- {%= data[i][ __("In Stock Qty")] %} |
- {%= data[i][ __("Enough Parts to Build")] %} |
-
- {% } %}
-
-
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
deleted file mode 100644
index 91d73d0101c..00000000000
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
+++ /dev/null
@@ -1,41 +0,0 @@
-frappe.query_reports["BOM Stock Report"] = {
- filters: [
- {
- fieldname: "bom",
- label: __("BOM"),
- fieldtype: "Link",
- options: "BOM",
- reqd: 1,
- },
- {
- fieldname: "warehouse",
- label: __("Warehouse"),
- fieldtype: "Link",
- options: "Warehouse",
- reqd: 1,
- },
- {
- fieldname: "show_exploded_view",
- label: __("Show exploded view"),
- fieldtype: "Check",
- },
- {
- fieldname: "qty_to_produce",
- label: __("Quantity to Produce"),
- fieldtype: "Int",
- default: "1",
- },
- ],
- formatter: function (value, row, column, data, default_formatter) {
- value = default_formatter(value, row, column, data);
-
- if (column.id == "item") {
- if (data["in_stock_qty"] >= data["required_qty"]) {
- value = `${data["item"]}`;
- } else {
- value = `${data["item"]}`;
- }
- }
- return value;
- },
-};
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.json b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.json
deleted file mode 100644
index c563b87686d..00000000000
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2017-01-10 14:00:50.387244",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "letter_head": "",
- "modified": "2017-06-23 04:46:43.209008",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "BOM Stock Report",
- "owner": "Administrator",
- "query": "SELECT \n\tbom_item.item_code as \"Item:Link/Item:200\",\n\tbom_item.description as \"Description:Data:300\",\n\tbom_item.qty as \"Required Qty:Float:100\",\n\tledger.actual_qty as \"In Stock Qty:Float:100\",\n\tFLOOR(ledger.actual_qty /bom_item.qty) as \"Enough Parts to Build:Int:100\"\nFROM\n\t`tabBOM Item` AS bom_item \n\tLEFT JOIN `tabBin` AS ledger\t\n\t\tON bom_item.item_code = ledger.item_code \n\t\tAND ledger.warehouse = %(warehouse)s\nWHERE\n\tbom_item.parent=%(bom)s\n\nGROUP BY bom_item.item_code",
- "ref_doctype": "BOM",
- "report_name": "BOM Stock Report",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Manufacturing Manager"
- },
- {
- "role": "Manufacturing User"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
deleted file mode 100644
index eeda32c64c7..00000000000
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py
+++ /dev/null
@@ -1,106 +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 Floor, Sum
-from frappe.utils import cint
-
-
-def execute(filters=None):
- if not filters:
- filters = {}
-
- columns = get_columns()
- data = get_bom_stock(filters)
-
- return columns, data
-
-
-def get_columns():
- return [
- _("Item") + ":Link/Item:150",
- _("Item Name") + "::240",
- _("Description") + "::300",
- _("From BOM No") + "::200",
- _("BOM Qty") + ":Float:160",
- _("BOM UOM") + "::160",
- _("Required Qty") + ":Float:120",
- _("In Stock Qty") + ":Float:120",
- _("Enough Parts to Build") + ":Float:200",
- ]
-
-
-def get_bom_stock(filters):
- qty_to_produce = filters.get("qty_to_produce")
- if cint(qty_to_produce) <= 0:
- frappe.throw(_("Quantity to Produce should be greater than zero."))
-
- bom_item_table = "BOM Explosion Item" if filters.get("show_exploded_view") else "BOM Item"
-
- warehouse = filters.get("warehouse")
- warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1)
-
- BOM = frappe.qb.DocType("BOM")
- BOM_ITEM = frappe.qb.DocType(bom_item_table)
- BIN = frappe.qb.DocType("Bin")
- WH = frappe.qb.DocType("Warehouse")
-
- if warehouse_details:
- bin_subquery = (
- frappe.qb.from_(BIN)
- .join(WH)
- .on(BIN.warehouse == WH.name)
- .select(BIN.item_code, Sum(BIN.actual_qty).as_("actual_qty"))
- .where((WH.lft >= warehouse_details.lft) & (WH.rgt <= warehouse_details.rgt))
- .groupby(BIN.item_code)
- )
- else:
- bin_subquery = (
- frappe.qb.from_(BIN)
- .select(BIN.item_code, Sum(BIN.actual_qty).as_("actual_qty"))
- .where(BIN.warehouse == warehouse)
- .groupby(BIN.item_code)
- )
-
- QUERY = (
- frappe.qb.from_(BOM)
- .join(BOM_ITEM)
- .on(BOM.name == BOM_ITEM.parent)
- .left_join(bin_subquery)
- .on(BOM_ITEM.item_code == bin_subquery.item_code)
- .select(
- BOM_ITEM.item_code,
- BOM_ITEM.item_name,
- BOM_ITEM.description,
- BOM.name,
- Sum(BOM_ITEM.stock_qty),
- BOM_ITEM.stock_uom,
- (Sum(BOM_ITEM.stock_qty) * qty_to_produce) / BOM.quantity,
- bin_subquery.actual_qty,
- Floor(bin_subquery.actual_qty / ((Sum(BOM_ITEM.stock_qty) * qty_to_produce) / BOM.quantity)),
- )
- .where((BOM_ITEM.parent == filters.get("bom")) & (BOM_ITEM.parenttype == "BOM"))
- .groupby(BOM_ITEM.item_code)
- .orderby(BOM_ITEM.idx)
- )
-
- if bom_item_table == "BOM Item":
- QUERY = QUERY.select(BOM_ITEM.bom_no, BOM_ITEM.is_phantom_item)
-
- data = QUERY.run(as_list=True)
- return explode_phantom_boms(data, filters) if bom_item_table == "BOM Item" else data
-
-
-def explode_phantom_boms(data, filters):
- expanded = []
- for row in data:
- if row[-1]: # last element is `is_phantom_item`
- phantom_filters = filters.copy()
- phantom_filters["qty_to_produce"] = row[-5]
- phantom_filters["bom"] = row[-2]
- expanded.extend(get_bom_stock(phantom_filters))
- else:
- expanded.append(row)
-
- return expanded
diff --git a/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py
deleted file mode 100644
index 43706fcb4de..00000000000
--- a/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe.exceptions import ValidationError
-from frappe.utils import floor
-
-from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
-from erpnext.manufacturing.report.bom_stock_report.bom_stock_report import (
- get_bom_stock as bom_stock_report,
-)
-from erpnext.stock.doctype.item.test_item import make_item
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
-from erpnext.tests.utils import ERPNextTestSuite
-
-
-class TestBomStockReport(ERPNextTestSuite):
- def setUp(self):
- self.warehouse = "_Test Warehouse - _TC"
- self.fg_item, self.rm_items = create_items()
- make_stock_entry(target=self.warehouse, item_code=self.rm_items[0], qty=20, basic_rate=100)
- make_stock_entry(target=self.warehouse, item_code=self.rm_items[1], qty=40, basic_rate=200)
- self.bom = make_bom(item=self.fg_item, quantity=1, raw_materials=self.rm_items, rm_qty=10)
-
- def test_bom_stock_report(self):
- # Test 1: When `qty_to_produce` is 0.
- filters = frappe._dict(
- {
- "bom": self.bom.name,
- "warehouse": "Stores - _TC",
- "qty_to_produce": 0,
- }
- )
- self.assertRaises(ValidationError, bom_stock_report, filters)
-
- # Test 2: When stock is not available.
- data = bom_stock_report(
- frappe._dict(
- {
- "bom": self.bom.name,
- "warehouse": "Stores - _TC",
- "qty_to_produce": 1,
- }
- )
- )
- expected_data = get_expected_data(self.bom, "Stores - _TC", 1)
- self.assertSetEqual(set(tuple(x) for x in data), set(tuple(x) for x in expected_data))
-
- # Test 3: When stock is available.
- data = bom_stock_report(
- frappe._dict(
- {
- "bom": self.bom.name,
- "warehouse": self.warehouse,
- "qty_to_produce": 1,
- }
- )
- )
- expected_data = get_expected_data(self.bom, self.warehouse, 1)
- 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,
- }
- ).name
- rm_item2 = make_item(
- properties={
- "is_stock_item": 1,
- "standard_rate": 200,
- "opening_stock": 200,
- "last_purchase_rate": 200,
- }
- ).name
-
- return fg_item, [rm_item1, rm_item2]
-
-
-def get_expected_data(bom, warehouse, qty_to_produce, show_exploded_view=False):
- expected_data = []
-
- for item in bom.get("exploded_items") if show_exploded_view else bom.get("items"):
- in_stock_qty = frappe.get_cached_value(
- "Bin", {"item_code": item.item_code, "warehouse": warehouse}, "actual_qty"
- )
-
- expected_data.append(
- [
- item.item_code,
- item.item_name,
- item.description,
- bom.name,
- item.stock_qty,
- item.stock_uom,
- item.stock_qty * qty_to_produce / bom.quantity,
- in_stock_qty,
- floor(in_stock_qty / (item.stock_qty * qty_to_produce / bom.quantity))
- if in_stock_qty
- else None,
- item.bom_no,
- item.is_phantom_item,
- ]
- )
-
- return expected_data