feat: update stock ledger report to support multi-select for warehouses and items

(cherry picked from commit f2afd98725)
This commit is contained in:
Karm Soni
2025-07-03 18:04:05 +05:30
committed by Mergify
parent 72e8ce0449
commit ecf9e6e748
3 changed files with 98 additions and 57 deletions

View File

@@ -27,25 +27,24 @@ frappe.query_reports["Stock Ledger"] = {
}, },
{ {
fieldname: "warehouse", fieldname: "warehouse",
label: __("Warehouse"), label: __("Warehouses"),
fieldtype: "Link", fieldtype: "MultiSelectList",
options: "Warehouse", options: "Warehouse",
get_query: function () { get_data: function (txt) {
const company = frappe.query_report.get_filter_value("company"); const company = frappe.query_report.get_filter_value("company");
return {
filters: { company: company }, return frappe.db.get_link_options("Warehouse", txt, {
}; company: company,
});
}, },
}, },
{ {
fieldname: "item_code", fieldname: "item_code",
label: __("Item"), label: __("Items"),
fieldtype: "Link", fieldtype: "MultiSelectList",
options: "Item", options: "Item",
get_query: function () { get_data: function (txt) {
return { return frappe.db.get_link_options("Item", txt, {});
query: "erpnext.controllers.queries.item_query",
};
}, },
}, },
{ {

View File

@@ -456,19 +456,23 @@ def get_items(filters):
query = frappe.qb.from_(item).select(item.name) query = frappe.qb.from_(item).select(item.name)
conditions = [] conditions = []
if item_code := filters.get("item_code"): if item_codes := filters.get("item_code"):
conditions.append(item.name == item_code) conditions.append(item.name.isin(item_codes))
else: else:
if brand := filters.get("brand"): if brand := filters.get("brand"):
conditions.append(item.brand == brand) conditions.append(item.brand == brand)
if item_group := filters.get("item_group"):
if condition := get_item_group_condition(item_group, item): if filters.get("item_group") and (
condition := get_item_group_condition(filters.get("item_group"), item)
):
conditions.append(condition) conditions.append(condition)
items = [] items = []
if conditions: if conditions:
for condition in conditions: for condition in conditions:
query = query.where(condition) query = query.where(condition)
items = [r[0] for r in query.run()] items = [r[0] for r in query.run()]
return items return items
@@ -505,6 +509,7 @@ def get_item_details(items, sl_entries, include_uom):
return item_details return item_details
# TODO: THIS IS NOT USED
def get_sle_conditions(filters): def get_sle_conditions(filters):
conditions = [] conditions = []
if filters.get("warehouse"): if filters.get("warehouse"):
@@ -535,8 +540,8 @@ def get_opening_balance_from_batch(filters, columns, sl_entries):
} }
for fields in ["item_code", "warehouse"]: for fields in ["item_code", "warehouse"]:
if filters.get(fields): value = filters.get(fields)
query_filters[fields] = filters.get(fields) query_filters[fields] = ("in", value)
opening_data = frappe.get_all( opening_data = frappe.get_all(
"Stock Ledger Entry", "Stock Ledger Entry",
@@ -567,8 +572,16 @@ def get_opening_balance_from_batch(filters, columns, sl_entries):
) )
for field in ["item_code", "warehouse", "company"]: for field in ["item_code", "warehouse", "company"]:
if filters.get(field): value = filters.get(field)
query = query.where(table[field] == filters.get(field))
if not value:
continue
if isinstance(value, list | tuple):
query = query.where(table[field].isin(value))
else:
query = query.where(table[field] == value)
bundle_data = query.run(as_dict=True) bundle_data = query.run(as_dict=True)
@@ -623,14 +636,32 @@ def get_opening_balance(filters, columns, sl_entries):
return row return row
def get_warehouse_condition(warehouse): def get_warehouse_condition(warehouses):
warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1) if isinstance(warehouses, str):
if warehouse_details: warehouses = [warehouses]
return f" exists (select name from `tabWarehouse` wh \
where wh.lft >= {warehouse_details.lft} and wh.rgt <= {warehouse_details.rgt} and warehouse = wh.name)"
warehouse_range = frappe.get_all(
"Warehouse",
filters={
"name": ("in", warehouses),
},
fields=["lft", "rgt"],
as_list=True,
)
if not warehouse_range:
return "" return ""
alias = "wh"
condtions = []
for lft, rgt in warehouse_range:
condtions.append(f"({alias}.lft >= {lft} and {alias}.rgt <= {rgt})")
condtions = " or ".join(condtions)
return f" exists (select name from `tabWarehouse` {alias} \
where ({condtions}) and warehouse = {alias}.name)"
def get_item_group_condition(item_group, item_table=None): def get_item_group_condition(item_group, item_table=None):
item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1) item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1)

View File

@@ -1644,8 +1644,8 @@ def get_previous_sle(args, for_update=False, extra_cond=None):
is called from various transaction like stock entry, reco etc is called from various transaction like stock entry, reco etc
args = { args = {
"item_code": "ABC", "item_code": "ABC" or ["ABC", "XYZ"],
"warehouse": "XYZ", "warehouse": "XYZ" or ["XYZ", "ABC"],
"posting_date": "2012-12-12", "posting_date": "2012-12-12",
"posting_time": "12:00", "posting_time": "12:00",
"sle": "name of reference Stock Ledger Entry" "sle": "name of reference Stock Ledger Entry"
@@ -1670,8 +1670,20 @@ def get_stock_ledger_entries(
): ):
"""get stock ledger entries filtered by specific posting datetime conditions""" """get stock ledger entries filtered by specific posting datetime conditions"""
conditions = f" and posting_datetime {operator} %(posting_datetime)s" conditions = f" and posting_datetime {operator} %(posting_datetime)s"
if previous_sle.get("warehouse"):
if item_code := previous_sle.get("item_code"):
if isinstance(item_code, list | tuple):
conditions += " and item_code in %(item_code)s"
else:
conditions += " and item_code = %(item_code)s"
if warehouse := previous_sle.get("warehouse"):
if isinstance(warehouse, list | tuple):
conditions += " and warehouse in %(warehouse)s"
else:
conditions += " and warehouse = %(warehouse)s" conditions += " and warehouse = %(warehouse)s"
elif previous_sle.get("warehouse_condition"): elif previous_sle.get("warehouse_condition"):
conditions += " and " + previous_sle.get("warehouse_condition") conditions += " and " + previous_sle.get("warehouse_condition")
@@ -1714,8 +1726,7 @@ def get_stock_ledger_entries(
""" """
select *, posting_datetime as "timestamp" select *, posting_datetime as "timestamp"
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where item_code = %(item_code)s where is_cancelled = 0
and is_cancelled = 0
{conditions} {conditions}
order by posting_datetime {order}, creation {order} order by posting_datetime {order}, creation {order}
{limit} {for_update}""".format( {limit} {for_update}""".format(