mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-20 13:39:18 +00:00
Merge pull request #49698 from rohitwaghchaure/fixed-serial-no-reservation
fix: serial no reservation issue
This commit is contained in:
@@ -1905,16 +1905,42 @@ def get_available_serial_nos(kwargs):
|
|||||||
if kwargs.warehouse:
|
if kwargs.warehouse:
|
||||||
filters["warehouse"] = kwargs.warehouse
|
filters["warehouse"] = kwargs.warehouse
|
||||||
|
|
||||||
# Since SLEs are not present against Reserved Stock [POS invoices, SRE], need to ignore reserved serial nos.
|
reserved_entries = get_reserved_serial_nos_for_sre(kwargs)
|
||||||
ignore_serial_nos, consider_serial_nos = get_reserved_serial_nos(kwargs)
|
|
||||||
|
|
||||||
if consider_serial_nos:
|
ignore_serial_nos = []
|
||||||
filters["name"] = ("in", consider_serial_nos)
|
if reserved_entries:
|
||||||
|
if kwargs.get("sabb_voucher_type") == "Delivery Note" and kwargs.get("against_sales_order"):
|
||||||
|
reserved_voucher_details = [kwargs.get("against_sales_order")]
|
||||||
|
else:
|
||||||
|
reserved_voucher_details = get_reserved_voucher_details(kwargs)
|
||||||
|
|
||||||
|
# Check if serial nos are reserved for the current voucher then fetch only those serial nos
|
||||||
|
if reserved_serial_nos := get_reserved_serial_nos_for_voucher(
|
||||||
|
kwargs, reserved_entries, reserved_voucher_details
|
||||||
|
):
|
||||||
|
filters["name"] = ("in", reserved_serial_nos)
|
||||||
|
return frappe.get_all(
|
||||||
|
"Serial No",
|
||||||
|
fields=fields,
|
||||||
|
filters=filters,
|
||||||
|
limit=cint(kwargs.qty) or 10000000,
|
||||||
|
order_by=order_by,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if serial nos are reserved for other vouchers then ignore those serial nos
|
||||||
|
elif ignore_reserved_serial_nos := get_other_doc_reserved_serials(
|
||||||
|
kwargs, reserved_entries, reserved_voucher_details
|
||||||
|
):
|
||||||
|
ignore_serial_nos.extend(ignore_reserved_serial_nos)
|
||||||
|
|
||||||
|
if reserved_for_pos := get_reserved_serial_nos_for_pos(kwargs):
|
||||||
|
ignore_serial_nos.extend(reserved_for_pos)
|
||||||
|
|
||||||
# To ignore serial nos in the same record for the draft state
|
# To ignore serial nos in the same record for the draft state
|
||||||
if kwargs.get("ignore_serial_nos"):
|
if kwargs.get("ignore_serial_nos"):
|
||||||
ignore_serial_nos.extend(kwargs.get("ignore_serial_nos"))
|
ignore_serial_nos.extend(kwargs.get("ignore_serial_nos"))
|
||||||
|
|
||||||
|
ignore_serial_nos = list(set(ignore_serial_nos))
|
||||||
if kwargs.get("posting_date"):
|
if kwargs.get("posting_date"):
|
||||||
if kwargs.get("posting_time") is None:
|
if kwargs.get("posting_time") is None:
|
||||||
kwargs.posting_time = nowtime()
|
kwargs.posting_time = nowtime()
|
||||||
@@ -1924,9 +1950,15 @@ def get_available_serial_nos(kwargs):
|
|||||||
if not time_based_serial_nos:
|
if not time_based_serial_nos:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
for sn in ignore_serial_nos:
|
||||||
|
if sn in time_based_serial_nos:
|
||||||
|
time_based_serial_nos.remove(sn)
|
||||||
|
|
||||||
filters["name"] = ("in", time_based_serial_nos)
|
filters["name"] = ("in", time_based_serial_nos)
|
||||||
elif ignore_serial_nos:
|
elif ignore_serial_nos:
|
||||||
filters["name"] = ("not in", ignore_serial_nos)
|
filters["name"] = ("not in", ignore_serial_nos)
|
||||||
|
elif kwargs.get("serial_nos"):
|
||||||
|
filters["name"] = ("in", kwargs.get("serial_nos"))
|
||||||
|
|
||||||
if kwargs.get("batches"):
|
if kwargs.get("batches"):
|
||||||
batches = get_non_expired_batches(kwargs.get("batches"))
|
batches = get_non_expired_batches(kwargs.get("batches"))
|
||||||
@@ -2014,46 +2046,6 @@ def get_bundle_wise_serial_nos(data, kwargs):
|
|||||||
return bundle_wise_serial_nos
|
return bundle_wise_serial_nos
|
||||||
|
|
||||||
|
|
||||||
def get_reserved_serial_nos(kwargs) -> list:
|
|
||||||
"""Returns a list of `Serial No` reserved in POS Invoice and Stock Reservation Entry."""
|
|
||||||
|
|
||||||
ignore_serial_nos = []
|
|
||||||
consider_serial_nos = []
|
|
||||||
|
|
||||||
# Extend the list by serial nos reserved in POS Invoice
|
|
||||||
ignore_serial_nos.extend(get_reserved_serial_nos_for_pos(kwargs))
|
|
||||||
|
|
||||||
reserved_entries = get_reserved_serial_nos_for_sre(kwargs)
|
|
||||||
if not reserved_entries:
|
|
||||||
return ignore_serial_nos, consider_serial_nos
|
|
||||||
|
|
||||||
if kwargs.get("sabb_voucher_type") == "Delivery Note" and kwargs.get("against_sales_order"):
|
|
||||||
reserved_voucher_details = [kwargs.get("against_sales_order")]
|
|
||||||
else:
|
|
||||||
reserved_voucher_details = get_reserved_voucher_details(kwargs)
|
|
||||||
|
|
||||||
serial_nos = []
|
|
||||||
for entry in reserved_entries:
|
|
||||||
if entry.voucher_no in reserved_voucher_details:
|
|
||||||
consider_serial_nos.append(entry.serial_no)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if kwargs.get("serial_nos") and entry.serial_no in kwargs.get("serial_nos"):
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"The Serial No {0} is reserved against the {1} {2} and cannot be used for any other transaction."
|
|
||||||
).format(bold(entry.serial_no), entry.voucher_type, bold(entry.voucher_no)),
|
|
||||||
title=_("Serial No Reserved"),
|
|
||||||
)
|
|
||||||
|
|
||||||
serial_nos.append(entry.serial_no)
|
|
||||||
|
|
||||||
# Extend the list by serial nos reserved via SRE
|
|
||||||
ignore_serial_nos.extend(serial_nos)
|
|
||||||
|
|
||||||
return ignore_serial_nos, consider_serial_nos
|
|
||||||
|
|
||||||
|
|
||||||
def get_reserved_voucher_details(kwargs):
|
def get_reserved_voucher_details(kwargs):
|
||||||
reserved_voucher_details = []
|
reserved_voucher_details = []
|
||||||
|
|
||||||
@@ -2161,6 +2153,38 @@ def get_reserved_serial_nos_for_pos(kwargs):
|
|||||||
return list(ignore_serial_nos_counter - returned_serial_nos_counter)
|
return list(ignore_serial_nos_counter - returned_serial_nos_counter)
|
||||||
|
|
||||||
|
|
||||||
|
def get_reserved_serial_nos_for_voucher(kwargs, reserved_entries, reserved_voucher_details):
|
||||||
|
serial_nos = []
|
||||||
|
if not kwargs.get("pick_reserved_items"):
|
||||||
|
return serial_nos
|
||||||
|
|
||||||
|
for entry in reserved_entries:
|
||||||
|
if entry.voucher_no in reserved_voucher_details:
|
||||||
|
serial_nos.append(entry.serial_no)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if kwargs.get("serial_nos") and entry.serial_no in kwargs.get("serial_nos"):
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"The Serial No {0} is reserved against the {1} {2} and cannot be used for any other transaction."
|
||||||
|
).format(bold(entry.serial_no), entry.voucher_type, bold(entry.voucher_no)),
|
||||||
|
title=_("Serial No Reserved"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return serial_nos
|
||||||
|
|
||||||
|
|
||||||
|
def get_other_doc_reserved_serials(kwargs, reserved_entries, reserved_voucher_details):
|
||||||
|
serial_nos = []
|
||||||
|
for entry in reserved_entries:
|
||||||
|
if entry.voucher_no in reserved_voucher_details:
|
||||||
|
continue
|
||||||
|
|
||||||
|
serial_nos.append(entry.serial_no)
|
||||||
|
|
||||||
|
return serial_nos
|
||||||
|
|
||||||
|
|
||||||
def get_reserved_serial_nos_for_sre(kwargs) -> list:
|
def get_reserved_serial_nos_for_sre(kwargs) -> list:
|
||||||
"""Returns a list of `Serial No` reserved in Stock Reservation Entry."""
|
"""Returns a list of `Serial No` reserved in Stock Reservation Entry."""
|
||||||
|
|
||||||
@@ -2182,6 +2206,7 @@ def get_reserved_serial_nos_for_sre(kwargs) -> list:
|
|||||||
& (sb_entry.delivered_qty < sb_entry.qty)
|
& (sb_entry.delivered_qty < sb_entry.qty)
|
||||||
& (sre.reservation_based_on == "Serial and Batch")
|
& (sre.reservation_based_on == "Serial and Batch")
|
||||||
)
|
)
|
||||||
|
.orderby(sb_entry.idx)
|
||||||
)
|
)
|
||||||
|
|
||||||
if kwargs.warehouse:
|
if kwargs.warehouse:
|
||||||
|
|||||||
@@ -2120,7 +2120,7 @@ class StockEntry(StockController):
|
|||||||
key = (d.item_code, d.s_warehouse)
|
key = (d.item_code, d.s_warehouse)
|
||||||
if details := reservation_entries.get(key):
|
if details := reservation_entries.get(key):
|
||||||
if details.get("serial_no"):
|
if details.get("serial_no"):
|
||||||
d.serial_no = "\n".join(details.get("serial_no"))
|
d.serial_no = "\n".join(details.get("serial_no")[: cint(d.qty)])
|
||||||
|
|
||||||
if batches := details.get("batch_no"):
|
if batches := details.get("batch_no"):
|
||||||
for batch_no, qty in batches.items():
|
for batch_no, qty in batches.items():
|
||||||
@@ -2180,7 +2180,12 @@ class StockEntry(StockController):
|
|||||||
doctype.transferred_qty,
|
doctype.transferred_qty,
|
||||||
doctype.consumed_qty,
|
doctype.consumed_qty,
|
||||||
)
|
)
|
||||||
.where((doctype.docstatus == 1) & (doctype.voucher_no == self.work_order))
|
.where(
|
||||||
|
(doctype.docstatus == 1)
|
||||||
|
& (doctype.voucher_no == self.work_order)
|
||||||
|
& (serial_batch_doc.delivered_qty < serial_batch_doc.qty)
|
||||||
|
)
|
||||||
|
.orderby(serial_batch_doc.idx)
|
||||||
)
|
)
|
||||||
|
|
||||||
return query.run(as_dict=True)
|
return query.run(as_dict=True)
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ def update_stock(ctx, out, doc=None):
|
|||||||
"sabb_voucher_no": doc.get("name") if doc else None,
|
"sabb_voucher_no": doc.get("name") if doc else None,
|
||||||
"sabb_voucher_detail_no": ctx.child_docname,
|
"sabb_voucher_detail_no": ctx.child_docname,
|
||||||
"sabb_voucher_type": ctx.doctype,
|
"sabb_voucher_type": ctx.doctype,
|
||||||
|
"pick_reserved_items": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user