From ea2c02738d55d17c2d8161bdae5ce6454f7d4fa9 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 22 Jun 2021 21:35:25 +0530 Subject: [PATCH] fix: Include Stock Reco logic in update_qty_in_future_sle --- erpnext/stock/stock_ledger.py | 75 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 9fe89c3fa5..94bd3077a7 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -215,7 +215,7 @@ class update_entries_after(object): """ self.data.setdefault(args.warehouse, frappe._dict()) warehouse_dict = self.data[args.warehouse] - previous_sle = self.get_previous_sle_of_current_voucher(args) + previous_sle = get_previous_sle_of_current_voucher(args) warehouse_dict.previous_sle = previous_sle for key in ("qty_after_transaction", "valuation_rate", "stock_value"): @@ -227,29 +227,6 @@ class update_entries_after(object): "stock_value_difference": 0.0 }) - def get_previous_sle_of_current_voucher(self, args): - """get stock ledger entries filtered by specific posting datetime conditions""" - - args['time_format'] = '%H:%i:%s' - if not args.get("posting_date"): - args["posting_date"] = "1900-01-01" - if not args.get("posting_time"): - args["posting_time"] = "00:00" - - sle = frappe.db.sql(""" - select *, timestamp(posting_date, posting_time) as "timestamp" - from `tabStock Ledger Entry` - where item_code = %(item_code)s - and warehouse = %(warehouse)s - and is_cancelled = 0 - and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) - order by timestamp(posting_date, posting_time) desc, creation desc - limit 1 - for update""", args, as_dict=1) - - return sle[0] if sle else frappe._dict() - - def build(self): from erpnext.controllers.stock_controller import future_sle_exists @@ -734,6 +711,35 @@ class update_entries_after(object): bin_doc.flags.via_stock_ledger_entry = True bin_doc.save(ignore_permissions=True) + +def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False): + """get stock ledger entries filtered by specific posting datetime conditions""" + + args['time_format'] = '%H:%i:%s' + if not args.get("posting_date"): + args["posting_date"] = "1900-01-01" + if not args.get("posting_time"): + args["posting_time"] = "00:00" + + voucher_condition = "" + if exclude_current_voucher: + voucher_no = args.get("voucher_no") + voucher_condition = f"and voucher_no != '{voucher_no}'" + + sle = frappe.db.sql(""" + select *, timestamp(posting_date, posting_time) as "timestamp" + from `tabStock Ledger Entry` + where item_code = %(item_code)s + and warehouse = %(warehouse)s + and is_cancelled = 0 + {voucher_condition} + and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) + order by timestamp(posting_date, posting_time) desc, creation desc + limit 1 + for update""".format(voucher_condition=voucher_condition), args, as_dict=1) + + return sle[0] if sle else frappe._dict() + def get_previous_sle(args, for_update=False): """ get the last sle on or before the current time-bucket, @@ -862,9 +868,24 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, return valuation_rate def update_qty_in_future_sle(args, allow_negative_stock=None): + """Recalculate Qty after Transaction in future SLEs based on current SLE.""" + qty_shift = args.actual_qty + + # find difference/shift in qty caused by stock reconciliation + if args.voucher_type == "Stock Reconciliation": + last_balance = get_previous_sle_of_current_voucher( + args, + exclude_current_voucher=True + ).get("qty_after_transaction") + if last_balance is not None: + stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance) + else: + stock_reco_qty_shift = args.qty_after_transaction + qty_shift = stock_reco_qty_shift + frappe.db.sql(""" update `tabStock Ledger Entry` - set qty_after_transaction = qty_after_transaction + {qty} + set qty_after_transaction = qty_after_transaction + {qty_shift} where item_code = %(item_code)s and warehouse = %(warehouse)s @@ -876,7 +897,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=None): and creation > %(creation)s ) ) - """.format(qty=args.actual_qty), args) + """.format(qty_shift=qty_shift), args) validate_negative_qty_in_future_sle(args, allow_negative_stock) @@ -884,7 +905,7 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): allow_negative_stock = allow_negative_stock \ or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) - if args.actual_qty < 0 and not allow_negative_stock: + if (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation") and not allow_negative_stock: sle = get_future_sle_with_negative_qty(args) if sle: message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(