mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-13 06:31:48 +00:00
fix: cap a manufacture entry at the job card's pending production
Entries from operations without their own BOM carry no For Quantity,
so the finished-good reconciliation cannot run for them and a draft
created before other entries were submitted could still over-produce.
Validate every job-card manufacture entry against the job card
directly: finished goods plus process loss must fit in what the job
card still has left to produce after earlier submitted entries.
(cherry picked from commit 94cd27ce5d)
This commit is contained in:
@@ -296,6 +296,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
self.validate_batch()
|
||||
self.validate_inspection()
|
||||
self.validate_fg_completed_qty()
|
||||
self.validate_job_card_pending_production()
|
||||
self.validate_difference_account()
|
||||
self.set_job_card_data()
|
||||
self.validate_job_card_item()
|
||||
@@ -3227,6 +3228,40 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
(flt(self.process_loss_qty) / flt(self.fg_completed_qty)) * 100
|
||||
)
|
||||
|
||||
def validate_job_card_pending_production(self):
|
||||
"""A draft created before other entries were submitted must not book more than the job
|
||||
card still has left; without this, a stale draft over-produces the finished good."""
|
||||
if self.purpose != "Manufacture" or not self.job_card:
|
||||
return
|
||||
|
||||
job_card = frappe.get_doc("Job Card", self.job_card)
|
||||
if job_card.is_corrective_job_card or job_card.is_subcontracted:
|
||||
return
|
||||
|
||||
precision = frappe.get_precision("Stock Entry Detail", "qty")
|
||||
pending_qty = flt(
|
||||
flt(job_card.get_qty_to_produce())
|
||||
- flt(job_card.manufactured_qty)
|
||||
- flt(job_card.get_consumed_process_loss()),
|
||||
precision,
|
||||
)
|
||||
finished_qty = flt(sum(flt(d.transfer_qty) for d in self.items if d.is_finished_item), precision)
|
||||
entry_qty = flt(finished_qty + flt(self.process_loss_qty), precision)
|
||||
|
||||
if entry_qty > pending_qty:
|
||||
uom = job_card.stock_uom
|
||||
frappe.throw(
|
||||
_(
|
||||
"The Job Card {0} has only {1} left to produce, but this entry books {2} ({3} finished goods and {4} process loss). Cancel or update its other manufacture entries first."
|
||||
).format(
|
||||
frappe.bold(self.job_card),
|
||||
frappe.bold(f"{pending_qty} {uom}"),
|
||||
frappe.bold(f"{entry_qty} {uom}"),
|
||||
f"{finished_qty} {uom}",
|
||||
f"{flt(self.process_loss_qty, precision)} {uom}",
|
||||
)
|
||||
)
|
||||
|
||||
def get_pending_process_loss_qty(self):
|
||||
"""Loss this entry should still book: the job card's unbooked loss when the entry
|
||||
belongs to one, else the largest operation loss on the work order (legacy flow)."""
|
||||
|
||||
Reference in New Issue
Block a user