From e43bc38e05349a780e0a4a97812cebea4a90a109 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 19 Apr 2023 12:05:17 +0530 Subject: [PATCH 1/3] refactor: rewrite `get_stock_value_on()` queries in `QB` --- erpnext/stock/utils.py | 49 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index b8c5187b2c..d928dca723 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -9,6 +9,7 @@ import frappe from frappe import _ from frappe.query_builder.functions import CombineDatetime from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime +from pypika.terms import ExistsCriterion import erpnext from erpnext.stock.valuation import FIFOValuation, LIFOValuation @@ -57,39 +58,39 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): if not posting_date: posting_date = nowdate() - values, condition = [posting_date], "" + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select( + sle.item_code, + sle.stock_value, + sle.name, + sle.warehouse, + ) + .where((sle.posting_date <= posting_date) & (sle.is_cancelled == 0)) + .orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=frappe.qb.desc) + .orderby(sle.creation, order=frappe.qb.desc) + ) if warehouse: - lft, rgt, is_group = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt", "is_group"]) if is_group: - values.extend([lft, rgt]) - condition += "and exists (\ - select name from `tabWarehouse` wh where wh.name = sle.warehouse\ - and wh.lft >= %s and wh.rgt <= %s)" - + wh = frappe.qb.DocType("Warehouse") + query = query.where( + ExistsCriterion( + frappe.qb.from_(wh) + .select(wh.name) + .where((wh.name == sle.warehouse) & (wh.lft >= lft) & (wh.rgt <= rgt)) + ) + ) else: - values.append(warehouse) - condition += " AND warehouse = %s" + query = query.where(sle.warehouse == warehouse) if item_code: - values.append(item_code) - condition += " AND item_code = %s" + query = query.where(sle.item_code == item_code) - stock_ledger_entries = frappe.db.sql( - """ - SELECT item_code, stock_value, name, warehouse - FROM `tabStock Ledger Entry` sle - WHERE posting_date <= %s {0} - and is_cancelled = 0 - ORDER BY timestamp(posting_date, posting_time) DESC, creation DESC - """.format( - condition - ), - values, - as_dict=1, - ) + stock_ledger_entries = query.run(as_dict=True) sle_map = {} for sle in stock_ledger_entries: From 9a37ac6c2563f8f6459c5c47a95ef349a9fc10bf Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 21 Apr 2023 13:28:14 +0530 Subject: [PATCH 2/3] refactor: sum up SLE value in query --- erpnext/stock/utils.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index d928dca723..9c2e2c805b 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -7,7 +7,7 @@ from typing import Dict, Optional import frappe from frappe import _ -from frappe.query_builder.functions import CombineDatetime +from frappe.query_builder.functions import CombineDatetime, IfNull, Sum from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime from pypika.terms import ExistsCriterion @@ -61,12 +61,7 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): sle = frappe.qb.DocType("Stock Ledger Entry") query = ( frappe.qb.from_(sle) - .select( - sle.item_code, - sle.stock_value, - sle.name, - sle.warehouse, - ) + .select(IfNull(Sum(sle.stock_value_difference), 0)) .where((sle.posting_date <= posting_date) & (sle.is_cancelled == 0)) .orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=frappe.qb.desc) .orderby(sle.creation, order=frappe.qb.desc) @@ -90,14 +85,7 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): if item_code: query = query.where(sle.item_code == item_code) - stock_ledger_entries = query.run(as_dict=True) - - sle_map = {} - for sle in stock_ledger_entries: - if not (sle.item_code, sle.warehouse) in sle_map: - sle_map[(sle.item_code, sle.warehouse)] = flt(sle.stock_value) - - return sum(sle_map.values()) + return query.run(as_list=True)[0][0] @frappe.whitelist() From e782a054c80656f378da6108bdd91fae99de685e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 25 Apr 2023 13:54:36 +0530 Subject: [PATCH 3/3] refactor: `get_stock_value_on()` to get stock value of multiple warehouses at once --- erpnext/accounts/utils.py | 5 +--- .../incorrect_stock_value_report.py | 2 +- erpnext/stock/utils.py | 28 +++++++++---------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 2ab9ef64b3..015bce5547 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1368,10 +1368,7 @@ def get_stock_and_account_balance(account=None, posting_date=None, company=None) if wh_details.account == account and not wh_details.is_group ] - total_stock_value = 0.0 - for warehouse in related_warehouses: - value = get_stock_value_on(warehouse, posting_date) - total_stock_value += value + total_stock_value = get_stock_value_on(related_warehouses, posting_date) precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency") return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py index df01b14d11..16ff5278e7 100644 --- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py +++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py @@ -84,7 +84,7 @@ def get_data(report_filters): closing_date = add_days(from_date, -1) for key, stock_data in voucher_wise_dict.items(): prev_stock_value = get_stock_value_on( - posting_date=closing_date, item_code=key[0], warehouse=key[1] + posting_date=closing_date, item_code=key[0], warehouses=key[1] ) for data in stock_data: expected_stock_value = prev_stock_value + data.stock_value_difference diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 9c2e2c805b..fb526971ed 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -9,9 +9,9 @@ import frappe from frappe import _ from frappe.query_builder.functions import CombineDatetime, IfNull, Sum from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime -from pypika.terms import ExistsCriterion import erpnext +from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.valuation import FIFOValuation, LIFOValuation BarcodeScanResult = Dict[str, Optional[str]] @@ -54,7 +54,9 @@ def get_stock_value_from_bin(warehouse=None, item_code=None): return stock_value -def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): +def get_stock_value_on( + warehouses: list | str = None, posting_date: str = None, item_code: str = None +) -> float: if not posting_date: posting_date = nowdate() @@ -67,20 +69,16 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): .orderby(sle.creation, order=frappe.qb.desc) ) - if warehouse: - lft, rgt, is_group = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt", "is_group"]) + if warehouses: + if isinstance(warehouses, str): + warehouses = [warehouses] - if is_group: - wh = frappe.qb.DocType("Warehouse") - query = query.where( - ExistsCriterion( - frappe.qb.from_(wh) - .select(wh.name) - .where((wh.name == sle.warehouse) & (wh.lft >= lft) & (wh.rgt <= rgt)) - ) - ) - else: - query = query.where(sle.warehouse == warehouse) + warehouses = set(warehouses) + for wh in list(warehouses): + if frappe.db.get_value("Warehouse", wh, "is_group"): + warehouses.update(get_child_warehouses(wh)) + + query = query.where(sle.warehouse.isin(warehouses)) if item_code: query = query.where(sle.item_code == item_code)