Merge pull request #34851 from rohitwaghchaure/fixed-too-many-writes-error-in-stock-reco

fix: too many writes error while making backdated stock reconciliation
This commit is contained in:
rohitwaghchaure
2023-04-17 12:01:36 +05:30
committed by GitHub
3 changed files with 84 additions and 38 deletions

View File

@@ -859,6 +859,8 @@ def is_reposting_pending():
def future_sle_exists(args, sl_entries=None): def future_sle_exists(args, sl_entries=None):
key = (args.voucher_type, args.voucher_no) 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): if validate_future_sle_not_exists(args, key, sl_entries):
return False return False
@@ -892,6 +894,9 @@ def future_sle_exists(args, sl_entries=None):
) )
for d in data: 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 frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
return len(data) return len(data)
@@ -903,6 +908,9 @@ def validate_future_sle_not_exists(args, key, sl_entries=None):
item_key = (args.get("item_code"), args.get("warehouse")) item_key = (args.get("item_code"), args.get("warehouse"))
if not sl_entries and hasattr(frappe.local, "future_sle"): 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 ( if not frappe.local.future_sle.get(key) or (
item_key and item_key not in frappe.local.future_sle.get(key) item_key and item_key not in frappe.local.future_sle.get(key)
): ):
@@ -910,11 +918,8 @@ def validate_future_sle_not_exists(args, key, sl_entries=None):
def get_cached_data(args, key): 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: if key not in frappe.local.future_sle:
frappe.local.future_sle[key] = frappe._dict({}) return False
if args.get("item_code"): if args.get("item_code"):
item_key = (args.get("item_code"), args.get("warehouse")) item_key = (args.get("item_code"), args.get("warehouse"))

View File

@@ -571,24 +571,33 @@ class StockReconciliation(StockController):
self._cancel() self._cancel()
def recalculate_current_qty(self, item_code, batch_no): 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: for row in self.items:
if not (row.item_code == item_code and row.batch_no == batch_no): if not (row.item_code == item_code and row.batch_no == batch_no):
continue 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 item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name
) )
qty, val_rate = get_stock_balance( precesion = row.precision("current_qty")
item_code, if flt(current_qty, precesion) == flt(row.current_qty, precesion):
row.warehouse, continue
self.posting_date,
self.posting_time, val_rate = get_valuation_rate(
with_valuation_rate=True, item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no
) )
row.current_valuation_rate = val_rate 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( row.db_set(
{ {
"current_qty": row.current_qty, "current_qty": row.current_qty,
@@ -597,6 +606,9 @@ class StockReconciliation(StockController):
} }
) )
if sl_entries:
self.make_sl_entries(sl_entries)
def get_batch_qty_for_stock_reco( def get_batch_qty_for_stock_reco(
item_code, warehouse, batch_no, posting_date, posting_time, voucher_no item_code, warehouse, batch_no, posting_date, posting_time, voucher_no

View File

@@ -544,6 +544,14 @@ class update_entries_after(object):
if not self.args.get("sle_id"): if not self.args.get("sle_id"):
self.get_dynamic_incoming_outgoing_rate(sle) 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 ( if (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no and sle.voucher_detail_no
@@ -605,6 +613,16 @@ class update_entries_after(object):
if not self.args.get("sle_id"): if not self.args.get("sle_id"):
self.update_outgoing_rate_on_transaction(sle) 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): def validate_negative_stock(self, sle):
""" """
validate negative stock for entries current datetime onwards validate negative stock for entries current datetime onwards
@@ -1369,12 +1387,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=False):
def regenerate_sle_for_batch_stock_reco(detail): def regenerate_sle_for_batch_stock_reco(detail):
doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no) 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.recalculate_current_qty(detail.item_code, detail.batch_no)
doc.docstatus = 1
doc.update_stock_ledger()
doc.repost_future_sle_and_gle() doc.repost_future_sle_and_gle()
@@ -1401,34 +1414,50 @@ def get_stock_reco_qty_shift(args):
return stock_reco_qty_shift return stock_reco_qty_shift
def get_next_stock_reco(args): def get_next_stock_reco(kwargs):
"""Returns next nearest stock reconciliaton's details.""" """Returns next nearest stock reconciliaton's details."""
return frappe.db.sql( sle = frappe.qb.DocType("Stock Ledger Entry")
"""
select query = (
name, posting_date, posting_time, creation, voucher_no, item_code, batch_no, actual_qty frappe.qb.from_(sle)
from .select(
`tabStock Ledger Entry` sle.name,
where sle.posting_date,
item_code = %(item_code)s sle.posting_time,
and warehouse = %(warehouse)s sle.creation,
and voucher_type = 'Stock Reconciliation' sle.voucher_no,
and voucher_no != %(voucher_no)s sle.item_code,
and is_cancelled = 0 sle.batch_no,
and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s) sle.actual_qty,
or ( )
timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s) .where(
and creation > %(creation)s (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): def get_datetime_limit_condition(detail):
return f""" return f"""