fix: Added validation for quality inspection in job card

(cherry picked from commit 46b4cf3add)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.py
#	erpnext/manufacturing/doctype/job_card/test_job_card.py
This commit is contained in:
Nishka Gosalia
2026-01-22 20:34:22 +05:30
committed by Mergify
parent 3cf10fafdf
commit c3523be972
2 changed files with 126 additions and 1 deletions

View File

@@ -24,6 +24,14 @@ from frappe.utils import (
time_diff_in_seconds,
)
<<<<<<< HEAD
=======
from erpnext.controllers.stock_controller import (
QualityInspectionNotSubmittedError,
QualityInspectionRejectedError,
)
from erpnext.manufacturing.doctype.bom.bom import add_additional_cost, get_bom_items_as_dict
>>>>>>> 46b4cf3add (fix: Added validation for quality inspection in job card)
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import (
get_mins_between_operations,
)
@@ -669,6 +677,7 @@ class JobCard(Document):
self.set_process_loss()
def on_submit(self):
self.validate_inspection()
self.validate_transfer_qty()
self.validate_job_card()
self.update_work_order()
@@ -678,6 +687,66 @@ class JobCard(Document):
self.update_work_order()
self.set_transferred_qty()
def validate_inspection(self):
action_submit, action_reject = frappe.get_single_value(
"Stock Settings",
["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.db.get_value(
"BOM", self.semi_fg_bom or self.bom_no, "inspection_required"
)
if bom_inspection_required:
if not self.quality_inspection:
frappe.throw(
_(
"Quality Inspection is required for the item {0} before completing the job card {1}"
).format(get_link_to_form("Item", item), bold(self.name))
)
qa_status, docstatus = frappe.db.get_value(
"Quality Inspection", self.quality_inspection, ["status", "docstatus"]
)
if docstatus != 1:
if action_submit == "Stop":
frappe.throw(
_("Quality Inspection {0} is not submitted for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item),
),
title=_("Inspection Submission"),
exc=QualityInspectionNotSubmittedError,
)
else:
frappe.msgprint(
_("Quality Inspection {0} is not submitted for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item),
),
alert=True,
indicator="orange",
)
elif qa_status == "Rejected":
if action_reject == "Stop":
frappe.throw(
_("Quality Inspection {0} is rejected for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item),
),
title=_("Inspection Rejected"),
exc=QualityInspectionRejectedError,
)
else:
frappe.msgprint(
_("Quality Inspection {0} is rejected for the item: {1}").format(
get_link_to_form("Quality Inspection", self.quality_inspection),
get_link_to_form("Item", item),
),
alert=True,
indicator="orange",
)
def validate_transfer_qty(self):
if not self.is_corrective_job_card and self.items and self.transferred_qty < self.for_quantity:
frappe.throw(

View File

@@ -21,8 +21,9 @@ from erpnext.manufacturing.doctype.job_card.job_card import (
make_stock_entry as make_stock_entry_from_jc,
)
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.manufacturing.doctype.work_order.work_order import WorkOrder
from erpnext.manufacturing.doctype.work_order.work_order import WorkOrder, make_work_order
from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -58,6 +59,50 @@ class TestJobCard(FrappeTestCase):
def tearDown(self):
frappe.db.rollback()
def test_quality_inspection_mandatory_check(self):
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
raw = create_item("Fabric-Raw")
cut_fg = create_item("Cut-Fabric-SFG")
stitch_fg = create_item("Stitched-TShirt-SFG")
final = create_item("Finished-TShirt")
row = {"operation": "Cutting", "workstation": "_Test Workstation 1"}
cutting = make_operation(row)
stitching = make_operation({"operation": "Stitching", "workstation": "_Test Workstation 1"})
ironing = make_operation({"operation": "Ironing", "workstation": "_Test Workstation 1"})
cut_bom = create_semi_fg_bom(cut_fg.name, raw.name, inspection_required=1)
stitch_bom = create_semi_fg_bom(stitch_fg.name, cut_fg.name, inspection_required=0)
final_bom = frappe.new_doc("BOM")
final_bom.item = final.name
final_bom.quantity = 1
final_bom.with_operations = 1
final_bom.track_semi_finished_goods = 1
final_bom.append("items", {"item_code": raw.name, "qty": 1})
final_bom.append(
"operations", {"operation": cutting.name, "workstation": "_Test Workstation 1", "bom_no": cut_bom}
)
final_bom.append(
"operations",
{"operation": stitching.name, "workstation": "_Test Workstation 1", "bom_no": stitch_bom},
)
final_bom.append("operations", {"operation": ironing.name, "workstation": "_Test Workstation 1"})
final_bom.insert()
final_bom.submit()
work_order = make_work_order(final_bom.name, final.name, 1, variant_items=[], use_multi_level_bom=0)
work_order.wip_warehouse = "Work In Progress - WP"
work_order.fg_warehouse = "Finished Goods - WP"
work_order.scrap_warehouse = "All Warehouses - WP"
for operation in work_order.operations:
operation.time_in_mins = 60
work_order.submit()
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)
self.assertRaises(frappe.ValidationError, job_card_doc.submit)
def test_job_card_operations(self):
job_cards = frappe.get_all(
"Job Card", filters={"work_order": self.work_order.name}, fields=["operation_id", "name"]
@@ -750,6 +795,7 @@ def make_wo_with_transfer_against_jc():
return work_order
<<<<<<< HEAD
def make_bom_for_jc_tests():
test_records = frappe.get_test_records("BOM")
bom = frappe.copy_doc(test_records[2])
@@ -758,3 +804,13 @@ def make_bom_for_jc_tests():
bom.items[0].uom = "_Test UOM 1"
bom.items[0].conversion_factor = 5
bom.insert()
=======
def create_semi_fg_bom(semi_fg_item, raw_item, inspection_required):
bom = frappe.new_doc("BOM")
bom.item = semi_fg_item
bom.quantity = 1
bom.inspection_required = inspection_required
bom.append("items", {"item_code": raw_item, "qty": 1})
bom.submit()
return bom.name
>>>>>>> 46b4cf3add (fix: Added validation for quality inspection in job card)