mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-19 01:25:07 +00:00
feat: update stock ledger report to support multi-select for warehouses and items
(cherry picked from commit f2afd98725)
This commit is contained in:
@@ -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",
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 (
|
||||||
conditions.append(condition)
|
condition := get_item_group_condition(filters.get("item_group"), item)
|
||||||
|
):
|
||||||
|
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,13 +636,31 @@ 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)"
|
|
||||||
|
|
||||||
return ""
|
warehouse_range = frappe.get_all(
|
||||||
|
"Warehouse",
|
||||||
|
filters={
|
||||||
|
"name": ("in", warehouses),
|
||||||
|
},
|
||||||
|
fields=["lft", "rgt"],
|
||||||
|
as_list=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not warehouse_range:
|
||||||
|
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):
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
|
|||||||
"""Create SL entries from SL entry dicts
|
"""Create SL entries from SL entry dicts
|
||||||
|
|
||||||
args:
|
args:
|
||||||
- allow_negative_stock: disable negative stock valiations if true
|
- allow_negative_stock: disable negative stock valiations if true
|
||||||
- via_landed_cost_voucher: landed cost voucher cancels and reposts
|
- via_landed_cost_voucher: landed cost voucher cancels and reposts
|
||||||
entries of purchase document. This flag is used to identify if
|
entries of purchase document. This flag is used to identify if
|
||||||
cancellation and repost is happening via landed cost voucher, in
|
cancellation and repost is happening via landed cost voucher, in
|
||||||
such cases certain validations need to be ignored (like negative
|
such cases certain validations need to be ignored (like negative
|
||||||
stock)
|
stock)
|
||||||
"""
|
"""
|
||||||
from erpnext.controllers.stock_controller import future_sle_exists
|
from erpnext.controllers.stock_controller import future_sle_exists
|
||||||
|
|
||||||
@@ -526,12 +526,12 @@ class update_entries_after:
|
|||||||
|
|
||||||
:param args: args as dict
|
:param args: args as dict
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
"item_code": "ABC",
|
"item_code": "ABC",
|
||||||
"warehouse": "XYZ",
|
"warehouse": "XYZ",
|
||||||
"posting_date": "2012-12-12",
|
"posting_date": "2012-12-12",
|
||||||
"posting_time": "12:00"
|
"posting_time": "12:00"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -599,15 +599,15 @@ class update_entries_after:
|
|||||||
:Data Structure:
|
:Data Structure:
|
||||||
|
|
||||||
self.data = {
|
self.data = {
|
||||||
warehouse1: {
|
warehouse1: {
|
||||||
'previus_sle': {},
|
'previus_sle': {},
|
||||||
'qty_after_transaction': 10,
|
'qty_after_transaction': 10,
|
||||||
'valuation_rate': 100,
|
'valuation_rate': 100,
|
||||||
'stock_value': 1000,
|
'stock_value': 1000,
|
||||||
'prev_stock_value': 1000,
|
'prev_stock_value': 1000,
|
||||||
'stock_queue': '[[10, 100]]',
|
'stock_queue': '[[10, 100]]',
|
||||||
'stock_value_difference': 1000
|
'stock_value_difference': 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -1644,11 +1644,11 @@ 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"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
args["name"] = args.get("sle", None) or ""
|
args["name"] = args.get("sle", None) or ""
|
||||||
@@ -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"):
|
|
||||||
conditions += " and warehouse = %(warehouse)s"
|
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"
|
||||||
|
|
||||||
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(
|
||||||
|
|||||||
Reference in New Issue
Block a user