mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-18 17:08:42 +00:00
fix: reinstate duplicate entry check for manufacture entries
The stock_entry.py split (#54466) dropped check_duplicate_entry_for_work_order and DuplicateEntryForWorkOrderError with no replacement. The Work Order still throws StockOverProductionError when submitted entries exceed the planned qty, but nothing blocks saving another Manufacture entry, draft or submitted, once existing entries already cover the full work order qty. Restore the validation in the manufacture purpose handler, gated to work orders without track_semi_finished_goods, matching the pre-split behaviour.
This commit is contained in:
@@ -21,6 +21,10 @@ from .serial_batch import create_serial_and_batch_bundle
|
||||
from .stock_entry_base import BaseStockEntry
|
||||
|
||||
|
||||
class DuplicateEntryForWorkOrderError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class OperationsNotCompleteError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
@@ -279,6 +283,7 @@ class ManufactureStockEntry(BaseManufactureStockEntry):
|
||||
self.validate_warehouse()
|
||||
self.validate_raw_materials_exists()
|
||||
self.check_if_operations_completed()
|
||||
self.check_duplicate_entry_for_work_order()
|
||||
self.validate_component_and_quantities()
|
||||
self.validate_finished_good_serial_batch_for_work_order()
|
||||
|
||||
@@ -430,6 +435,46 @@ class ManufactureStockEntry(BaseManufactureStockEntry):
|
||||
OperationsNotCompleteError,
|
||||
)
|
||||
|
||||
def check_duplicate_entry_for_work_order(self):
|
||||
"""Block another manufacture entry once existing entries already cover the full work order qty."""
|
||||
if not self.wo_doc or self.wo_doc.track_semi_finished_goods:
|
||||
return
|
||||
|
||||
other_entries = frappe.get_all(
|
||||
"Stock Entry",
|
||||
filters={
|
||||
"work_order": self.doc.work_order,
|
||||
"purpose": self.doc.purpose,
|
||||
"docstatus": ["!=", 2],
|
||||
"name": ["!=", self.doc.name],
|
||||
},
|
||||
pluck="name",
|
||||
)
|
||||
if not other_entries:
|
||||
return
|
||||
|
||||
if self.get_fg_qty_already_entered(other_entries) >= flt(self.wo_doc.qty):
|
||||
frappe.throw(
|
||||
_("Stock Entries already created for Work Order {0}: {1}").format(
|
||||
self.doc.work_order, ", ".join(other_entries)
|
||||
),
|
||||
DuplicateEntryForWorkOrderError,
|
||||
)
|
||||
|
||||
def get_fg_qty_already_entered(self, other_entries):
|
||||
child = frappe.qb.DocType("Stock Entry Detail")
|
||||
qty = (
|
||||
frappe.qb.from_(child)
|
||||
.select(Sum(child.transfer_qty))
|
||||
.where(
|
||||
child.parent.isin(other_entries)
|
||||
& (child.item_code == self.wo_doc.production_item)
|
||||
& (child.s_warehouse.isnull() | (child.s_warehouse == ""))
|
||||
)
|
||||
.run()
|
||||
)[0][0]
|
||||
return flt(qty)
|
||||
|
||||
def add_items(self):
|
||||
self.add_raw_materials()
|
||||
self.set_process_loss_qty()
|
||||
|
||||
@@ -38,6 +38,7 @@ from erpnext.stock.utils import get_incoming_rate
|
||||
|
||||
from .services.disassemble import DisassembleStockEntry
|
||||
from .services.manufacturing import (
|
||||
DuplicateEntryForWorkOrderError,
|
||||
ManufactureStockEntry,
|
||||
MaterialConsumptionForManufactureStockEntry,
|
||||
OperationsNotCompleteError,
|
||||
|
||||
Reference in New Issue
Block a user