feat: Allowing operation level quality inspection check in BOM (backport #53859) (#54144)

Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
This commit is contained in:
mergify[bot]
2026-04-09 02:02:35 +00:00
committed by GitHub
parent 526c8d0418
commit 233dc7c07b
7 changed files with 42 additions and 13 deletions

View File

@@ -20,6 +20,7 @@
"is_subcontracted", "is_subcontracted",
"is_final_finished_good", "is_final_finished_good",
"set_cost_based_on_bom_qty", "set_cost_based_on_bom_qty",
"quality_inspection_required",
"warehouse_section", "warehouse_section",
"skip_material_transfer", "skip_material_transfer",
"backflush_from_wip_warehouse", "backflush_from_wip_warehouse",
@@ -290,13 +291,20 @@
"fieldname": "backflush_from_wip_warehouse", "fieldname": "backflush_from_wip_warehouse",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Backflush Materials From WIP Warehouse" "label": "Backflush Materials From WIP Warehouse"
},
{
"default": "0",
"depends_on": "eval:parent.inspection_required",
"fieldname": "quality_inspection_required",
"fieldtype": "Check",
"label": "Quality Inspection Required"
} }
], ],
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2026-03-31 17:09:48.771834", "modified": "2026-04-01 17:09:48.771834",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Operation", "name": "BOM Operation",

View File

@@ -35,6 +35,7 @@ class BOMOperation(Document):
parent: DF.Data parent: DF.Data
parentfield: DF.Data parentfield: DF.Data
parenttype: DF.Data parenttype: DF.Data
quality_inspection_required: DF.Check
sequence_id: DF.Int sequence_id: DF.Int
set_cost_based_on_bom_qty: DF.Check set_cost_based_on_bom_qty: DF.Check
skip_material_transfer: DF.Check skip_material_transfer: DF.Check

View File

@@ -788,27 +788,27 @@ class JobCard(Document):
["action_if_quality_inspection_is_not_submitted", "action_if_quality_inspection_is_rejected"], ["action_if_quality_inspection_is_not_submitted", "action_if_quality_inspection_is_rejected"],
) )
item = self.finished_good or self.production_item bom_inspection_required = frappe.get_value("BOM", self.bom_no, "inspection_required")
bom_inspection_required = frappe.db.get_value( operation_inspection_required = frappe.get_value(
"BOM", self.semi_fg_bom or self.bom_no, "inspection_required" "Work Order Operation", self.operation_id, "quality_inspection_required"
) )
if bom_inspection_required: if bom_inspection_required and operation_inspection_required:
if not self.quality_inspection: if not self.quality_inspection:
frappe.throw( frappe.throw(
_( _(
"Quality Inspection is required for the item {0} before completing the job card {1}" "Quality Inspection is required for the item {0} before completing the job card {1}"
).format(get_link_to_form("Item", item), bold(self.name)) ).format(get_link_to_form("Item", self.finished_good), bold(self.name))
) )
qa_status, docstatus = frappe.db.get_value(
qa_status, docstatus = frappe.get_value(
"Quality Inspection", self.quality_inspection, ["status", "docstatus"] "Quality Inspection", self.quality_inspection, ["status", "docstatus"]
) )
if docstatus != 1: if docstatus != 1:
if action_submit == "Stop": if action_submit == "Stop":
frappe.throw( frappe.throw(
_("Quality Inspection {0} is not submitted for the item: {1}").format( _("Quality Inspection {0} is not submitted for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection), get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item), get_link_to_form("Item", self.finished_good),
), ),
title=_("Inspection Submission"), title=_("Inspection Submission"),
exc=QualityInspectionNotSubmittedError, exc=QualityInspectionNotSubmittedError,
@@ -817,7 +817,7 @@ class JobCard(Document):
frappe.msgprint( frappe.msgprint(
_("Quality Inspection {0} is not submitted for the item: {1}").format( _("Quality Inspection {0} is not submitted for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection), get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item), get_link_to_form("Item", self.finished_good),
), ),
alert=True, alert=True,
indicator="orange", indicator="orange",
@@ -827,7 +827,7 @@ class JobCard(Document):
frappe.throw( frappe.throw(
_("Quality Inspection {0} is rejected for the item: {1}").format( _("Quality Inspection {0} is rejected for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection), get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item), get_link_to_form("Item", self.finished_good),
), ),
title=_("Inspection Rejected"), title=_("Inspection Rejected"),
exc=QualityInspectionRejectedError, exc=QualityInspectionRejectedError,
@@ -836,7 +836,7 @@ class JobCard(Document):
frappe.msgprint( frappe.msgprint(
_("Quality Inspection {0} is rejected for the item: {1}").format( _("Quality Inspection {0} is rejected for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection), get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item), get_link_to_form("Item", self.finished_good),
), ),
alert=True, alert=True,
indicator="orange", indicator="orange",

View File

@@ -87,6 +87,7 @@ class TestJobCard(ERPNextTestSuite):
with_operations=1, with_operations=1,
track_semi_finished_goods=1, track_semi_finished_goods=1,
company="_Test Company", company="_Test Company",
inspection_required=1,
) )
final_bom.append("items", {"item_code": raw.name, "qty": 1}) final_bom.append("items", {"item_code": raw.name, "qty": 1})
final_bom.append( final_bom.append(
@@ -97,6 +98,7 @@ class TestJobCard(ERPNextTestSuite):
"bom_no": cut_bom, "bom_no": cut_bom,
"skip_material_transfer": 1, "skip_material_transfer": 1,
"time_in_mins": 60, "time_in_mins": 60,
"quality_inspection_required": 1,
}, },
) )
final_bom.append( final_bom.append(
@@ -133,6 +135,15 @@ class TestJobCard(ERPNextTestSuite):
work_order.submit() work_order.submit()
job_card = frappe.get_all("Job Card", filters={"work_order": work_order.name, "operation": "Cutting"}) job_card = frappe.get_all("Job Card", filters={"work_order": work_order.name, "operation": "Cutting"})
job_card_doc = frappe.get_doc("Job Card", job_card[0].name) job_card_doc = frappe.get_doc("Job Card", job_card[0].name)
job_card_doc.append(
"time_logs",
{
"from_time": "2024-01-01 08:00:00",
"to_time": "2024-01-01 09:00:00",
"time_in_mins": 60,
"completed_qty": 1,
},
)
self.assertRaises(frappe.ValidationError, job_card_doc.submit) self.assertRaises(frappe.ValidationError, job_card_doc.submit)
def test_job_card_operations(self): def test_job_card_operations(self):

View File

@@ -1277,6 +1277,7 @@ class WorkOrder(Document):
"skip_material_transfer", "skip_material_transfer",
"backflush_from_wip_warehouse", "backflush_from_wip_warehouse",
"set_cost_based_on_bom_qty", "set_cost_based_on_bom_qty",
"quality_inspection_required",
], ],
order_by="idx", order_by="idx",
) )

View File

@@ -15,6 +15,7 @@
"workstation_type", "workstation_type",
"workstation", "workstation",
"sequence_id", "sequence_id",
"quality_inspection_required",
"section_break_insy", "section_break_insy",
"bom_no", "bom_no",
"finished_good", "finished_good",
@@ -294,13 +295,19 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Backflush Materials From WIP Warehouse", "label": "Backflush Materials From WIP Warehouse",
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "quality_inspection_required",
"fieldtype": "Check",
"label": "Quality Inspection Required"
} }
], ],
"grid_page_length": 50, "grid_page_length": 50,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-05-15 15:10:06.885440", "modified": "2026-03-30 17:20:08.874381",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order Operation", "name": "Work Order Operation",

View File

@@ -36,6 +36,7 @@ class WorkOrderOperation(Document):
planned_operating_cost: DF.Currency planned_operating_cost: DF.Currency
planned_start_time: DF.Datetime | None planned_start_time: DF.Datetime | None
process_loss_qty: DF.Float process_loss_qty: DF.Float
quality_inspection_required: DF.Check
sequence_id: DF.Int sequence_id: DF.Int
skip_material_transfer: DF.Check skip_material_transfer: DF.Check
source_warehouse: DF.Link | None source_warehouse: DF.Link | None