Merge pull request #34851 from rohitwaghchaure/fixed-too-many-writes-error-in-stock-reco
fix: too many writes error while making backdated stock reconciliation
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 1e4fabe..479fef7 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -859,6 +859,8 @@
def future_sle_exists(args, sl_entries=None):
key = (args.voucher_type, args.voucher_no)
+ if not hasattr(frappe.local, "future_sle"):
+ frappe.local.future_sle = {}
if validate_future_sle_not_exists(args, key, sl_entries):
return False
@@ -892,6 +894,9 @@
)
for d in data:
+ if key not in frappe.local.future_sle:
+ frappe.local.future_sle[key] = frappe._dict({})
+
frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
return len(data)
@@ -903,6 +908,9 @@
item_key = (args.get("item_code"), args.get("warehouse"))
if not sl_entries and hasattr(frappe.local, "future_sle"):
+ if key not in frappe.local.future_sle:
+ return False
+
if not frappe.local.future_sle.get(key) or (
item_key and item_key not in frappe.local.future_sle.get(key)
):
@@ -910,11 +918,8 @@
def get_cached_data(args, key):
- if not hasattr(frappe.local, "future_sle"):
- frappe.local.future_sle = {}
-
if key not in frappe.local.future_sle:
- frappe.local.future_sle[key] = frappe._dict({})
+ return False
if args.get("item_code"):
item_key = (args.get("item_code"), args.get("warehouse"))
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index e304bd1..3fd4cec 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -571,24 +571,33 @@
self._cancel()
def recalculate_current_qty(self, item_code, batch_no):
+ from erpnext.stock.stock_ledger import get_valuation_rate
+
+ sl_entries = []
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(
+ current_qty = get_batch_qty_for_stock_reco(
item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name
)
- qty, val_rate = get_stock_balance(
- item_code,
- row.warehouse,
- self.posting_date,
- self.posting_time,
- with_valuation_rate=True,
+ precesion = row.precision("current_qty")
+ if flt(current_qty, precesion) == flt(row.current_qty, precesion):
+ continue
+
+ val_rate = get_valuation_rate(
+ item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no
)
row.current_valuation_rate = val_rate
+ if not row.current_qty and current_qty:
+ sle = self.get_sle_for_items(row)
+ sle.actual_qty = current_qty * -1
+ sle.valuation_rate = val_rate
+ sl_entries.append(sle)
+ row.current_qty = current_qty
row.db_set(
{
"current_qty": row.current_qty,
@@ -597,6 +606,9 @@
}
)
+ if sl_entries:
+ self.make_sl_entries(sl_entries)
+
def get_batch_qty_for_stock_reco(
item_code, warehouse, batch_no, posting_date, posting_time, voucher_no
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b0a093d..03c04a5 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -545,6 +545,14 @@
self.get_dynamic_incoming_outgoing_rate(sle)
if (
+ sle.voucher_type == "Stock Reconciliation"
+ and sle.batch_no
+ and sle.voucher_detail_no
+ and sle.actual_qty < 0
+ ):
+ self.reset_actual_qty_for_stock_reco(sle)
+
+ if (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
and sle.actual_qty < 0
@@ -605,6 +613,16 @@
if not self.args.get("sle_id"):
self.update_outgoing_rate_on_transaction(sle)
+ def reset_actual_qty_for_stock_reco(self, sle):
+ current_qty = frappe.get_cached_value(
+ "Stock Reconciliation Item", sle.voucher_detail_no, "current_qty"
+ )
+
+ if current_qty:
+ sle.actual_qty = current_qty * -1
+ elif current_qty == 0:
+ sle.is_cancelled = 1
+
def validate_negative_stock(self, sle):
"""
validate negative stock for entries current datetime onwards
@@ -1369,12 +1387,7 @@
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()
doc.repost_future_sle_and_gle()
@@ -1401,34 +1414,50 @@
return stock_reco_qty_shift
-def get_next_stock_reco(args):
+def get_next_stock_reco(kwargs):
"""Returns next nearest stock reconciliaton's details."""
- return frappe.db.sql(
- """
- select
- name, posting_date, posting_time, creation, voucher_no, item_code, batch_no, actual_qty
- from
- `tabStock Ledger Entry`
- where
- item_code = %(item_code)s
- and warehouse = %(warehouse)s
- and voucher_type = 'Stock Reconciliation'
- and voucher_no != %(voucher_no)s
- and is_cancelled = 0
- and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
- or (
- timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
- and creation > %(creation)s
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+
+ query = (
+ frappe.qb.from_(sle)
+ .select(
+ sle.name,
+ sle.posting_date,
+ sle.posting_time,
+ sle.creation,
+ sle.voucher_no,
+ sle.item_code,
+ sle.batch_no,
+ sle.actual_qty,
+ )
+ .where(
+ (sle.item_code == kwargs.get("item_code"))
+ & (sle.warehouse == kwargs.get("warehouse"))
+ & (sle.voucher_type == "Stock Reconciliation")
+ & (sle.voucher_no != kwargs.get("voucher_no"))
+ & (sle.is_cancelled == 0)
+ & (
+ (
+ CombineDatetime(sle.posting_date, sle.posting_time)
+ > CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
+ | (
+ (
+ CombineDatetime(sle.posting_date, sle.posting_time)
+ == CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
+ )
+ & (sle.creation > kwargs.get("creation"))
+ )
)
)
- order by timestamp(posting_date, posting_time) asc, creation asc
- limit 1
- """,
- args,
- as_dict=1,
+ )
)
+ if kwargs.get("batch_no"):
+ query.where(sle.batch_no == kwargs.get("batch_no"))
+
+ return query.run(as_dict=True)
+
def get_datetime_limit_condition(detail):
return f"""