diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 04d1a3a5e2..482b103d1e 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -5,6 +5,7 @@ from typing import Optional import frappe from frappe import _, bold, msgprint +from frappe.query_builder.functions import Sum from frappe.utils import cint, cstr, flt import erpnext @@ -569,6 +570,54 @@ class StockReconciliation(StockController): else: self._cancel() + def recalculate_current_qty(self, item_code, batch_no): + for row in self.items: + if not (row.item_code == item_code and row.batch_no == batch_no): + continue + + row.current_qty = get_batch_qty_for_stock_reco(item_code, row.warehouse, batch_no) + + qty, val_rate = get_stock_balance( + item_code, + row.warehouse, + self.posting_date, + self.posting_time, + with_valuation_rate=True, + ) + + row.current_valuation_rate = val_rate + + row.db_set( + { + "current_qty": row.current_qty, + "current_valuation_rate": row.current_valuation_rate, + "current_amount": flt(row.current_qty * row.current_valuation_rate), + } + ) + + +def get_batch_qty_for_stock_reco(item_code, warehouse, batch_no): + ledger = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(ledger) + .select( + Sum(ledger.actual_qty).as_("batch_qty"), + ) + .where( + (ledger.item_code == item_code) + & (ledger.warehouse == warehouse) + & (ledger.docstatus == 1) + & (ledger.is_cancelled == 0) + & (ledger.batch_no == batch_no) + ) + .groupby(ledger.batch_no) + ) + + sle = query.run(as_dict=True) + + return flt(sle[0].batch_qty) if sle else 0 + @frappe.whitelist() def get_items( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 08fc6fbd42..c954befdc2 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1337,6 +1337,9 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): next_stock_reco_detail = get_next_stock_reco(args) if next_stock_reco_detail: detail = next_stock_reco_detail[0] + if detail.batch_no: + regenerate_sle_for_batch_stock_reco(detail) + # add condition to update SLEs before this date & time datetime_limit_condition = get_datetime_limit_condition(detail) @@ -1364,6 +1367,16 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): validate_negative_qty_in_future_sle(args, allow_negative_stock) +def regenerate_sle_for_batch_stock_reco(detail): + doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no) + doc.docstatus = 2 + doc.update_stock_ledger() + + doc.recalculate_current_qty(detail.item_code, detail.batch_no) + doc.docstatus = 1 + doc.update_stock_ledger() + + def get_stock_reco_qty_shift(args): stock_reco_qty_shift = 0 if args.get("is_cancelled"): @@ -1393,7 +1406,7 @@ def get_next_stock_reco(args): return frappe.db.sql( """ select - name, posting_date, posting_time, creation, voucher_no + name, posting_date, posting_time, creation, voucher_no, item_code, batch_no, actual_qty from `tabStock Ledger Entry` where