diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 93fac84d60a..238b72fa957 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -11,6 +11,7 @@ from frappe.query_builder.functions import Abs, Count from frappe.utils import cint, date_diff, flt, get_datetime from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter from erpnext.stock.valuation import round_off_if_near_zero Filters = frappe._dict @@ -936,9 +937,7 @@ class FIFOSlots: ) ) - for field in ["item_code"]: - if self.filters.get(field): - query = query.where(sle[field] == self.filters.get(field)) + query = self._apply_filter(query, sle, "item_code") if self.filters.get("warehouse"): query = self._get_warehouse_conditions(sle, query) @@ -985,9 +984,7 @@ class FIFOSlots: ) ) - for field in ["item_code"]: - if self.filters.get(field): - query = query.where(sle[field] == self.filters.get(field)) + query = self._apply_filter(query, sle, "item_code") if self.filters.get("warehouse"): query = self._get_warehouse_conditions(sle, query) @@ -1020,27 +1017,25 @@ class FIFOSlots: "has_batch_no", ) - if self.filters.get("item_code"): - item = item.where(item_table.item_code == self.filters.get("item_code")) + item = self._apply_filter(item, item_table, "item_code") if self.filters.get("brand"): item = item.where(item_table.brand == self.filters.get("brand")) return item + def _apply_filter(self, query, table, fieldname: str): + filter_value = self.filters.get(fieldname) + if not filter_value: + return query + + if isinstance(filter_value, list | tuple | set): + return query.where(table[fieldname].isin(filter_value)) + + return query.where(table[fieldname] == filter_value) + def _get_warehouse_conditions(self, sle, sle_query) -> str: - warehouse = frappe.qb.DocType("Warehouse") - lft, rgt = frappe.db.get_value("Warehouse", self.filters.get("warehouse"), ["lft", "rgt"]) - - warehouse_results = ( - frappe.qb.from_(warehouse) - .select("name") - .where((warehouse.lft >= lft) & (warehouse.rgt <= rgt)) - .run() - ) - warehouse_results = [x[0] for x in warehouse_results] - - return sle_query.where(sle.warehouse.isin(warehouse_results)) + return apply_warehouse_filter(sle_query, sle, self.filters) def prepare_stock_reco_voucher_wise_count(self): self.stock_reco_voucher_wise_count = frappe._dict() diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 601524c74e9..1c61306125e 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -129,6 +129,18 @@ class TestStockAgeing(FrappeTestCase): self.assertEqual(queue[0][0], 10.0) self.assertEqual(queue[1][0], 10.0) + def test_item_filter_supports_multi_select_values(self): + bundle = frappe.qb.DocType("Serial and Batch Bundle") + query = frappe.qb.from_(bundle).select(bundle.name) + + filtered_query = FIFOSlots(frappe._dict(item_code=["Item A"]), [])._apply_filter( + query, bundle, "item_code" + ) + + sql = filtered_query.get_sql() + self.assertIn(" IN ", sql) + self.assertNotIn("=[", sql) + def test_basic_stock_reconciliation(self): """ Ledger (same wh): [+30, reco reset >> 50, -10]