fix: operation status and bom validation

(cherry picked from commit 95baf953a8)
This commit is contained in:
Rohit Waghchaure
2026-02-05 16:40:53 +05:30
committed by Mergify
parent 8c536df5f2
commit 11222653ce
7 changed files with 92 additions and 5 deletions

View File

@@ -580,9 +580,12 @@ frappe.ui.form.on("BOM", {
frappe.ui.form.on("BOM Operation", {
finished_good(frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (row.finished_good === frm.doc.item) {
frappe.model.set_value(row.doctype, row.name, "is_final_finished_good", 1);
}
frappe.model.set_value(
row.doctype,
row.name,
"is_final_finished_good",
row.finished_good === frm.doc.item
);
},
bom_no(frm, cdt, cdn) {

View File

@@ -296,6 +296,55 @@ class BOM(WebsiteGenerator):
self.set_process_loss_qty()
self.validate_scrap_items()
self.set_default_uom()
self.validate_semi_finished_goods()
self.validate_raw_materials_of_operation()
def validate_semi_finished_goods(self):
if not self.track_semi_finished_goods or not self.operations:
return
fg_items = []
for row in self.operations:
if not row.is_final_finished_good:
continue
fg_items.append(row.finished_good)
if not fg_items:
frappe.throw(
_(
"Since you have enabled 'Track Semi Finished Goods', at least one operation must have 'Is Final Finished Good' checked. For that set the FG / Semi FG Item as {0} against an operation."
).format(bold(self.item)),
)
if fg_items and len(fg_items) > 1:
frappe.throw(
_(
"Only one operation can have 'Is Final Finished Good' checked when 'Track Semi Finished Goods' is enabled."
),
)
def validate_raw_materials_of_operation(self):
if not self.track_semi_finished_goods or not self.operations:
return
operation_idx_with_no_rm = {}
for row in self.operations:
if row.bom_no:
continue
operation_idx_with_no_rm[row.idx] = row.operation
for row in self.items:
if row.operation_row_id and row.operation_row_id in operation_idx_with_no_rm:
del operation_idx_with_no_rm[row.operation_row_id]
for idx, row in operation_idx_with_no_rm.items():
frappe.throw(
_("For operation {0} at row {1}, please add raw materials or set a BOM against it.").format(
bold(row.operation), idx
),
)
def set_default_uom(self):
if not self.get("items"):

View File

@@ -166,6 +166,25 @@ class JobCard(Document):
self.validate_work_order()
self.set_employees()
self.validate_semi_finished_goods()
def validate_semi_finished_goods(self):
if not self.track_semi_finished_goods:
return
if self.items and not self.transferred_qty and not self.skip_material_transfer:
frappe.throw(
_(
"Materials needs to be transferred to the work in progress warehouse for the job card {0}"
).format(self.name)
)
if self.docstatus == 1 and not self.total_completed_qty:
frappe.throw(
_(
"Total Completed Qty is required for Job Card {0}, please start and complete the job card before submission"
).format(self.name)
)
def on_update(self):
self.validate_job_card_qty()
@@ -1354,6 +1373,9 @@ class JobCard(Document):
employees=self.employee,
sub_operation=kwargs.get("sub_operation"),
)
if self.docstatus == 1:
self.update_work_order()
else:
self.add_time_logs(completed_qty=kwargs.qty, employees=self.employee)
self.save()

View File

@@ -809,7 +809,7 @@ erpnext.work_order = {
}
}
if (frm.doc.status != "Stopped") {
if (frm.doc.status != "Stopped" && !frm.doc.track_semi_finished_goods) {
// If "Material Consumption is check in Manufacturing Settings, allow Material Consumption
if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) {

View File

@@ -248,6 +248,16 @@ class WorkOrder(Document):
if self.is_new() and frappe.db.get_single_value("Stock Settings", "auto_reserve_stock"):
self.reserve_stock = 1
def before_save(self):
self.set_skip_transfer_for_operations()
def set_skip_transfer_for_operations(self):
if not self.track_semi_finished_goods:
return
for op in self.operations:
op.skip_material_transfer = self.skip_transfer
def validate_operations_sequence(self):
if all([not op.sequence_id for op in self.operations]):
for op in self.operations:

View File

@@ -2006,6 +2006,7 @@ class StockEntry(StockController, SubcontractingInwardController):
else:
job_doc.set_consumed_qty_in_job_card_item(self)
job_doc.set_manufactured_qty()
job_doc.update_work_order()
if self.work_order:
pro_doc = frappe.get_doc("Work Order", self.work_order)

View File

@@ -110,7 +110,9 @@ class ManufactureEntry:
_dict.from_warehouse = self.source_wh.get(item_code) or self.wip_warehouse
_dict.to_warehouse = ""
if backflush_based_on != "BOM":
if backflush_based_on != "BOM" and not frappe.db.get_value(
"Job Card", self.job_card, "skip_material_transfer"
):
calculated_qty = flt(_dict.transferred_qty) - flt(_dict.consumed_qty)
if calculated_qty < 0:
frappe.throw(