From 9f7cfffde1f67bb96cf007d831e2221ca40c7ad8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 10 Dec 2024 15:30:53 +0530 Subject: [PATCH] perf: batchwise balance history report --- .../repost_item_valuation.py | 12 ++--- .../stock_closing_entry.py | 45 ++++++++++--------- .../batch_wise_balance_history.py | 33 +++++++++++++- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 165c883a1fe4..a2d07f28d9f6 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -100,10 +100,10 @@ def validate_period_closing_voucher(self): # Stock Closing Balance closing_stock = self.get_closing_stock_balance() if closing_stock and closing_stock[0].name: - name = get_link_to_form("Stock Closing Balance", closing_stock[0].name) + name = get_link_to_form("Stock Closing Entry", closing_stock[0].stock_closing_entry) to_date = frappe.format(closing_stock[0].to_date, "Date") frappe.throw( - _("Due to stock closing balance {0}, you cannot repost item valuation before {1}").format( + _("Due to stock closing entry {0}, you cannot repost item valuation before {1}").format( name, to_date ) ) @@ -111,16 +111,10 @@ def validate_period_closing_voucher(self): def get_closing_stock_balance(self): filters = { "company": self.company, - "status": "Completed", - "docstatus": 1, "to_date": (">=", self.posting_date), } - for field in ["warehouse", "item_code"]: - if self.get(field): - filters.update({field: ("in", ["", self.get(field)])}) - - return frappe.get_all("Stock Closing Balance", fields=["name", "posting_date"], filters=filters, limit=1) + return frappe.get_all("Stock Closing Balance", fields=["stock_closing_entry", "posting_date"], filters=filters, limit=1) @staticmethod def get_max_period_closing_date(company): diff --git a/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py b/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py index e58ab04d2018..8747aebdca47 100644 --- a/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py +++ b/erpnext/stock/doctype/stock_closing_entry/stock_closing_entry.py @@ -103,8 +103,7 @@ def enqueue_job(self): @frappe.whitelist() def regenerate_closing_balance(self): self.remove_stock_closing() - # self.enqueue_job() - self.create_stock_closing_balance_entries() + self.enqueue_job() def create_stock_closing_balance_entries(self): from erpnext.stock.utils import get_combine_datetime @@ -113,14 +112,14 @@ def create_stock_closing_balance_entries(self): entries = stk_cl_obj.get_stock_closing_entries() - for row in entries: - data = entries[row] + for key in entries: + row = entries[key] - if data.actual_qty == 0.0 and data.stock_value_difference == 0.0: + if row.actual_qty == 0.0 and row.stock_value_difference == 0.0: continue new_doc = frappe.new_doc("Stock Closing Balance") - new_doc.update(data) + new_doc.update(row) new_doc.posting_date = self.to_date new_doc.posting_time = nowtime() new_doc.posting_datetime = get_combine_datetime(self.to_date, new_doc.posting_time) @@ -169,10 +168,10 @@ def get_stock_closing_entries(self): closing_stock = frappe._dict() for row in sl_entries: - keys = self.get_keys(row) - for data in keys: - for fields, values in data.items(): - key = values + dimensions_keys = self.get_keys(row) + for dimension_key in dimensions_keys: + for dimension_fields, dimension_values in dimension_key.items(): + key = dimension_values if key in closing_stock: closing_stock[key].actual_qty += row.sabb_qty or row.actual_qty @@ -187,9 +186,9 @@ def get_stock_closing_entries(self): "Item", row.item_code, ["item_group", "item_name", "stock_uom"], as_dict=1 ) - inventory_dimension_key = "" - if fields not in [("item_code", "warehouse"), ("item_code", "warehouse", "batch_no")]: - inventory_dimension_key = json.dumps(fields) + inventory_dimension_key = None + if dimension_fields not in [("item_code", "warehouse"), ("item_code", "warehouse", "batch_no")]: + inventory_dimension_key = json.dumps(dimension_fields) closing_stock[key] = frappe._dict( { @@ -203,12 +202,14 @@ def get_stock_closing_entries(self): "item_name": item_details.item_name, "stock_uom": item_details.stock_uom, "inventory_dimension_key": inventory_dimension_key, - "batch_no": row.batch_no or row.sabb_batch_no, } ) + if row.sabb_batch_no: + row.batch_no = row.sabb_batch_no + # To update dimensions - for field in fields: + for field in dimension_fields: if row.get(field): closing_stock[key][field] = row.get(field) @@ -330,7 +331,7 @@ def get_keys(self, row): {("item_code", "warehouse", "batch_no"): (row.item_code, row.warehouse, row.sabb_batch_no)} ) - dimensions_has_value = [] + dimension_fields = [] dimension_values = [] for dimension in self.inv_dimensions: if row.get(dimension.fieldname): @@ -344,13 +345,13 @@ def get_keys(self, row): } ) - dimensions_has_value.append(dimension.fieldname) + dimension_fields.append(dimension.fieldname) dimension_values.append(row.get(dimension.fieldname)) - if dimensions_has_value and len(dimensions_has_value) > 1: + if dimension_fields and len(dimension_fields) > 1: keys.append( { - ("item_code", "warehouse", *dimensions_has_value): ( + ("item_code", "warehouse", *dimension_fields): ( row.item_code, row.warehouse, *dimension_values, @@ -360,7 +361,7 @@ def get_keys(self, row): return keys - def get_stock_closing_balance(self, kwargs): + def get_stock_closing_balance(self, kwargs, for_batch=False): if not self.last_closing_balance: return [] @@ -377,4 +378,8 @@ def get_stock_closing_balance(self, kwargs): else: query = query.where(table[key] == value) + if for_batch: + query = query.where(table.batch_no.isnotnull()) + query = query.where(table.inventory_dimension_key.isnull() | (table.inventory_dimension_key == "")) + return query.run(as_dict=True) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index eeb331318ba2..f1632a31572d 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -9,6 +9,7 @@ from erpnext.deprecation_dumpster import deprecated from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter +from erpnext.stock.doctype.stock_closing_entry.stock_closing_entry import StockClosing SLE_COUNT_LIMIT = 10_000 @@ -94,12 +95,36 @@ def get_columns(filters): def get_stock_ledger_entries(filters): - entries = get_stock_ledger_entries_for_batch_no(filters) + entries = [] + stk_cl_obj = StockClosing(filters.company, filters.from_date, filters.from_date) + if stk_cl_obj.last_closing_balance: + entries += get_stock_closing_balance(stk_cl_obj, filters) + print(entries) + filters.start_from = stk_cl_obj.last_closing_balance.to_date + + entries += get_stock_ledger_entries_for_batch_no(filters) entries += get_stock_ledger_entries_for_batch_bundle(filters) + return entries +def get_stock_closing_balance(stk_cl_obj, filters): + query_filters = {} + for field in ["item_code", "warehouse", "company", "batch_no"]: + if filters.get(field): + query_filters[field] = filters.get(field) + + if filters.warehouse_type: + warehouses = frappe.get_all( + "Warehouse", + filters={"warehouse_type": filters.warehouse_type, "is_group": 0}, + pluck="name", + ) + query_filters["warehouse"] = warehouses + + return stk_cl_obj.get_stock_closing_balance(query_filters, for_batch=True) + @deprecated(f"{__name__}.get_stock_ledger_entries_for_batch_no", "unknown", "v16", "No known instructions.") def get_stock_ledger_entries_for_batch_no(filters): if not filters.get("from_date"): @@ -144,6 +169,9 @@ def get_stock_ledger_entries_for_batch_no(filters): if filters.get(field): query = query.where(sle[field] == filters.get(field)) + if filters.start_from: + query = query.where(sle.posting_datetime > get_datetime(filters.start_from)) + return query.run(as_dict=True) or [] @@ -190,6 +218,9 @@ def get_stock_ledger_entries_for_batch_bundle(filters): else: query = query.where(sle[field] == filters.get(field)) + if filters.start_from: + query = query.where(sle.posting_date > getdate(filters.start_from)) + return query.run(as_dict=True) or []