mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-12 19:35:09 +00:00
fix: validate reserved batches
This commit is contained in:
@@ -1183,6 +1183,91 @@ class StockController(AccountsController):
|
||||
self.doctype, self.name, self.docstatus, via_landed_cost_voucher=via_landed_cost_voucher
|
||||
)
|
||||
|
||||
self.validate_reserved_batches()
|
||||
|
||||
def validate_reserved_batches(self):
|
||||
if not frappe.db.get_single_value("Stock Settings", "enable_stock_reservation"):
|
||||
return
|
||||
|
||||
if self.doctype not in ["Delivery Note", "Sales Invoice", "Stock Entry"]:
|
||||
return
|
||||
|
||||
batches = frappe.get_all(
|
||||
"Serial and Batch Entry",
|
||||
filters={
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"docstatus": 1,
|
||||
"batch_no": ("is", "set"),
|
||||
"qty": ("<", 0),
|
||||
},
|
||||
pluck="batch_no",
|
||||
)
|
||||
|
||||
if not batches:
|
||||
return
|
||||
|
||||
field_mapper = {
|
||||
"Sales Invoice": [["Sales Order", "sales_order"]],
|
||||
"Delivery Note": [["Sales Order", "against_sales_order"]],
|
||||
"Stock Entry": [
|
||||
["Work Order", "work_order"],
|
||||
["Subcontracting Inward Order", "subcontracting_inward_order"],
|
||||
],
|
||||
}.get(self.doctype)
|
||||
|
||||
reserved_batches_data = self.get_reserved_batches(batches)
|
||||
items = self.items
|
||||
if self.doctype == "Stock Entry":
|
||||
items = [self]
|
||||
|
||||
for item in items:
|
||||
for field in field_mapper:
|
||||
if not item.get(field[1]):
|
||||
continue
|
||||
|
||||
value = item.get(field[1])
|
||||
for row in reserved_batches_data:
|
||||
if self.doctype in ["Sales Invoice", "Delivery Note"] and row.item_code != item.get(
|
||||
"item_code"
|
||||
):
|
||||
continue
|
||||
|
||||
if row.voucher_no == value:
|
||||
continue
|
||||
|
||||
frappe.throw(
|
||||
_(
|
||||
"The batch {0} is already reserved in {1} {2}. So, cannot proceed with the {3} {4}, which is created against the {5} {6}."
|
||||
).format(
|
||||
frappe.bold(row.batch_no),
|
||||
frappe.bold(row.voucher_type),
|
||||
frappe.bold(row.voucher_no),
|
||||
frappe.bold(self.doctype),
|
||||
frappe.bold(self.name),
|
||||
frappe.bold(field[0]),
|
||||
frappe.bold(value),
|
||||
),
|
||||
title=_("Reserved Batch Conflict"),
|
||||
)
|
||||
|
||||
def get_reserved_batches(self, batches):
|
||||
doctype = frappe.qb.DocType("Stock Reservation Entry")
|
||||
child_doc = frappe.qb.DocType("Serial and Batch Entry")
|
||||
|
||||
return (
|
||||
frappe.qb.from_(doctype)
|
||||
.join(child_doc)
|
||||
.on(doctype.name == child_doc.parent)
|
||||
.select(
|
||||
child_doc.batch_no,
|
||||
doctype.voucher_type,
|
||||
doctype.voucher_no,
|
||||
doctype.item_code,
|
||||
)
|
||||
.where((doctype.docstatus == 1) & (child_doc.batch_no.isin(batches)))
|
||||
).run(as_dict=True)
|
||||
|
||||
def make_gl_entries_on_cancel(self, from_repost=False):
|
||||
if not from_repost:
|
||||
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
|
||||
|
||||
Reference in New Issue
Block a user