fix: consider reserved batches while cancelling a stock transaction
This commit is contained in:
parent
d9e284366d
commit
e1a87a802d
@ -710,6 +710,39 @@ def get_sre_reserved_serial_nos_details(
|
||||
return frappe._dict(query.run())
|
||||
|
||||
|
||||
def get_sre_reserved_batch_nos_details(
|
||||
item_code: str, warehouse: str, batch_nos: list = None
|
||||
) -> dict:
|
||||
"""Returns a dict of `Batch Qty` reserved in Stock Reservation Entry. The dict is like {batch_no: qty, ...}"""
|
||||
|
||||
sre = frappe.qb.DocType("Stock Reservation Entry")
|
||||
sb_entry = frappe.qb.DocType("Serial and Batch Entry")
|
||||
query = (
|
||||
frappe.qb.from_(sre)
|
||||
.inner_join(sb_entry)
|
||||
.on(sre.name == sb_entry.parent)
|
||||
.select(
|
||||
sb_entry.batch_no,
|
||||
Sum(sb_entry.qty - sb_entry.delivered_qty),
|
||||
)
|
||||
.where(
|
||||
(sre.docstatus == 1)
|
||||
& (sre.item_code == item_code)
|
||||
& (sre.warehouse == warehouse)
|
||||
& ((sre.reserved_qty - sre.delivered_qty) > 0)
|
||||
& (sre.status.notin(["Delivered", "Cancelled"]))
|
||||
& (sre.reservation_based_on == "Serial and Batch")
|
||||
)
|
||||
.groupby(sb_entry.batch_no)
|
||||
.orderby(sb_entry.creation)
|
||||
)
|
||||
|
||||
if batch_nos:
|
||||
query = query.where(sb_entry.batch_no.isin(batch_nos))
|
||||
|
||||
return frappe._dict(query.run())
|
||||
|
||||
|
||||
def get_sre_details_for_voucher(voucher_type: str, voucher_no: str) -> list[dict]:
|
||||
"""Returns a list of SREs for the provided voucher."""
|
||||
|
||||
|
@ -11,11 +11,17 @@ from frappe import _, scrub
|
||||
from frappe.model.meta import get_field_precision
|
||||
from frappe.query_builder import Case
|
||||
from frappe.query_builder.functions import CombineDatetime, Sum
|
||||
from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, parse_json
|
||||
from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, nowtime, parse_json
|
||||
|
||||
import erpnext
|
||||
from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
|
||||
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
|
||||
get_available_batches,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_sre_reserved_batch_nos_details,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock,
|
||||
)
|
||||
@ -1809,6 +1815,9 @@ def validate_reserved_stock(kwargs):
|
||||
serial_nos = kwargs.serial_no.split("\n")
|
||||
validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos)
|
||||
|
||||
elif kwargs.batch_no:
|
||||
validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, [kwargs.batch_no])
|
||||
|
||||
elif kwargs.serial_and_batch_bundle:
|
||||
sbb_entries = frappe.db.get_all(
|
||||
"Serial and Batch Entry",
|
||||
@ -1817,12 +1826,13 @@ def validate_reserved_stock(kwargs):
|
||||
"parent": kwargs.serial_and_batch_bundle,
|
||||
"docstatus": 1,
|
||||
},
|
||||
["batch_no", "serial_no", "qty"],
|
||||
["batch_no", "serial_no"],
|
||||
)
|
||||
serial_nos = [entry.serial_no for entry in sbb_entries if entry.serial_no]
|
||||
|
||||
if serial_nos:
|
||||
if serial_nos := [entry.serial_no for entry in sbb_entries if entry.serial_no]:
|
||||
validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos)
|
||||
elif batch_nos := [entry.batch_no for entry in sbb_entries if entry.batch_no]:
|
||||
validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, batch_nos)
|
||||
|
||||
|
||||
def validate_reserved_serial_nos(item_code, warehouse, serial_nos):
|
||||
@ -1845,6 +1855,36 @@ def validate_reserved_serial_nos(item_code, warehouse, serial_nos):
|
||||
frappe.throw(msg, title=_("Reserved Serial No."))
|
||||
|
||||
|
||||
def validate_reserved_batch_nos(item_code, warehouse, batch_nos):
|
||||
if reserved_batches_map := get_sre_reserved_batch_nos_details(item_code, warehouse, batch_nos):
|
||||
available_batches = get_available_batches(
|
||||
frappe._dict(
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": warehouse,
|
||||
"posting_date": nowdate(),
|
||||
"posting_time": nowtime(),
|
||||
}
|
||||
)
|
||||
)
|
||||
available_batches_map = {row.batch_no: row.qty for row in available_batches}
|
||||
precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
|
||||
for batch_no in batch_nos:
|
||||
diff = flt(
|
||||
available_batches_map.get(batch_no, 0) - reserved_batches_map.get(batch_no, 0), precision
|
||||
)
|
||||
if diff < 0 and abs(diff) > 0.0001:
|
||||
msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format(
|
||||
abs(diff),
|
||||
frappe.get_desk_link("Batch", batch_no),
|
||||
frappe.get_desk_link("Warehouse", warehouse),
|
||||
nowdate(),
|
||||
nowtime(),
|
||||
)
|
||||
frappe.throw(msg, title=_("Reserved Stock for Batch"))
|
||||
|
||||
|
||||
def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool:
|
||||
if cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock", cache=True)):
|
||||
return True
|
||||
|
Loading…
Reference in New Issue
Block a user