diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index 0d68caa7e09..a8b2bd1d782 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -36,38 +36,37 @@ frappe.query_reports["Stock Balance"] = { }, { fieldname: "item_code", - label: __("Item"), - fieldtype: "Link", + label: __("Items"), + fieldtype: "MultiSelectList", width: "80", options: "Item", - get_query: function () { + get_data: function (txt) { let item_group = frappe.query_report.get_filter_value("item_group"); - return { - query: "erpnext.controllers.queries.item_query", - filters: { - ...(item_group && { item_group }), - is_stock_item: 1, - }, + let filters = { + ...(item_group && { item_group }), + is_stock_item: 1, }; + + return frappe.db.get_link_options("Item", txt, filters); }, }, { fieldname: "warehouse", - label: __("Warehouse"), - fieldtype: "Link", + label: __("Warehouses"), + fieldtype: "MultiSelectList", width: "80", options: "Warehouse", - get_query: () => { + get_data: (txt) => { let warehouse_type = frappe.query_report.get_filter_value("warehouse_type"); let company = frappe.query_report.get_filter_value("company"); - return { - filters: { - ...(warehouse_type && { warehouse_type }), - ...(company && { company }), - }, + let filters = { + ...(warehouse_type && { warehouse_type }), + ...(company && { company }), }; + + return frappe.db.get_link_options("Warehouse", txt, filters); }, }, { diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index b6587cd7b37..05a35d64520 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -11,6 +11,7 @@ from frappe.query_builder import Order from frappe.query_builder.functions import Coalesce from frappe.utils import add_days, cint, date_diff, flt, getdate from frappe.utils.nestedset import get_descendants_of +from pypika.terms import ExistsCriterion import erpnext from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions @@ -24,8 +25,8 @@ class StockBalanceFilter(TypedDict): from_date: str to_date: str item_group: str | None - item: str | None - warehouse: str | None + item: list[str] | None + warehouse: list[str] | None warehouse_type: str | None include_uom: str | None # include extra info in converted UOM show_stock_ageing_data: bool @@ -345,8 +346,29 @@ class StockBalanceReport: def apply_warehouse_filters(self, query, sle) -> str: warehouse_table = frappe.qb.DocType("Warehouse") - if self.filters.get("warehouse"): - query = apply_warehouse_filter(query, sle, self.filters) + if warehouses := self.filters.get("warehouse"): + warehouse_range = frappe.get_all( + "Warehouse", + filters={ + "name": ("in", warehouses), + }, + fields=["lft", "rgt"], + as_list=True, + ) + + child_query = frappe.qb.from_(warehouse_table).select(warehouse_table.name) + + range_conditions = [ + (warehouse_table.lft >= lft) & (warehouse_table.rgt <= rgt) for lft, rgt in warehouse_range + ] + + combined_condition = range_conditions[0] + for condition in range_conditions[1:]: + combined_condition = combined_condition | condition + + child_query = child_query.where(combined_condition & (warehouse_table.name == sle.warehouse)) + query = query.where(ExistsCriterion(child_query)) + elif warehouse_type := self.filters.get("warehouse_type"): query = ( query.join(warehouse_table) @@ -361,13 +383,11 @@ class StockBalanceReport: children = get_descendants_of("Item Group", item_group, ignore_permissions=True) query = query.where(item_table.item_group.isin([*children, item_group])) - for field in ["item_code", "brand"]: - if not self.filters.get(field): - continue - elif field == "item_code": - query = query.where(item_table.name == self.filters.get(field)) - else: - query = query.where(item_table[field] == self.filters.get(field)) + if item_codes := self.filters.get("item_code"): + query = query.where(item_table.name.isin(item_codes)) + + if brand := self.filters.get("brand"): + query = query.where(item_table.brand == brand) return query