diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py index f770059131..f55e6405b9 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -179,18 +179,13 @@ def get_stock_reservation_entries_for_voucher( def get_sre_reserved_qty_details_for_item_and_warehouse( - item_code: str | list, warehouse: str | list + item_code_list: list, warehouse_list: list ) -> dict: """Returns a dict like {("item_code", "warehouse"): "reserved_qty", ... }.""" sre_details = {} - if item_code and warehouse: - if isinstance(item_code, str): - item_code = [item_code] - if isinstance(warehouse, str): - warehouse = [warehouse] - + if item_code_list and warehouse_list: sre = frappe.qb.DocType("Stock Reservation Entry") sre_data = ( frappe.qb.from_(sre) @@ -201,8 +196,8 @@ def get_sre_reserved_qty_details_for_item_and_warehouse( ) .where( (sre.docstatus == 1) - & (sre.item_code.isin(item_code)) - & (sre.warehouse.isin(warehouse)) + & (sre.item_code.isin(item_code_list)) + & (sre.warehouse.isin(warehouse_list)) & (sre.status.notin(["Delivered", "Cancelled"])) ) .groupby(sre.item_code, sre.warehouse) @@ -214,6 +209,27 @@ def get_sre_reserved_qty_details_for_item_and_warehouse( return sre_details +def get_sre_reserved_qty_for_item_and_warehouse(item_code: str, warehouse: str) -> float: + """Returns `Reserved Qty` for Item and Warehouse combination.""" + + reserved_qty = 0.0 + + if item_code and warehouse: + sre = frappe.qb.DocType("Stock Reservation Entry") + return ( + frappe.qb.from_(sre) + .select(Sum(sre.reserved_qty - sre.delivered_qty)) + .where( + (sre.docstatus == 1) + & (sre.item_code == item_code) + & (sre.warehouse == warehouse) + & (sre.status.notin(["Delivered", "Cancelled"])) + ) + ).run(as_list=True)[0][0] or 0.0 + + return reserved_qty + + def get_sre_reserved_qty_details_for_voucher( voucher_type: str, voucher_no: str, voucher_detail_no: str = None ) -> dict: diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index c954befdc2..33e7a039d8 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -13,6 +13,9 @@ from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, now, nowdat import erpnext from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty +from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( + get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock, +) from erpnext.stock.utils import ( get_incoming_outgoing_rate_for_cancel, get_or_make_bin, @@ -380,6 +383,7 @@ class update_entries_after(object): self.new_items_found = False self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict()) self.affected_transactions: Set[Tuple[str, str]] = set() + self.reserved_stock = get_reserved_stock(self.args.item_code, self.args.warehouse) self.data = frappe._dict() self.initialize_previous_data(self.args) @@ -610,7 +614,7 @@ class update_entries_after(object): validate negative stock for entries current datetime onwards will not consider cancelled entries """ - diff = self.wh_data.qty_after_transaction + flt(sle.actual_qty) + diff = self.wh_data.qty_after_transaction + flt(sle.actual_qty) - flt(self.reserved_stock) diff = flt(diff, self.flt_precision) # respect system precision if diff < 0 and abs(diff) > 0.0001: @@ -1006,6 +1010,7 @@ class update_entries_after(object): msg_list = [] for warehouse, exceptions in self.exceptions.items(): deficiency = min(e["diff"] for e in exceptions) + msg_prefix = _("As {} units are reserved, ").format(frappe.bold(self.reserved_stock)) if ( exceptions[0]["voucher_type"], @@ -1013,7 +1018,7 @@ class update_entries_after(object): ) in frappe.local.flags.currently_saving: msg = _("{0} units of {1} needed in {2} to complete this transaction.").format( - abs(deficiency), + frappe.bold(abs(deficiency)), frappe.get_desk_link("Item", exceptions[0]["item_code"]), frappe.get_desk_link("Warehouse", warehouse), ) @@ -1021,7 +1026,7 @@ class update_entries_after(object): msg = _( "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction." ).format( - abs(deficiency), + frappe.bold(abs(deficiency)), frappe.get_desk_link("Item", exceptions[0]["item_code"]), frappe.get_desk_link("Warehouse", warehouse), exceptions[0]["posting_date"], @@ -1030,6 +1035,9 @@ class update_entries_after(object): ) if msg: + if self.reserved_stock: + msg = msg_prefix + msg + msg_list.append(msg) if msg_list: