Merge pull request #26133 from marination/qi-rejection
feat: Optionally allow rejected quality inspection on submission
This commit is contained in:
commit
f7748e4109
@ -356,42 +356,68 @@ class StockController(AccountsController):
|
||||
}, update_modified)
|
||||
|
||||
def validate_inspection(self):
|
||||
'''Checks if quality inspection is set for Items that require inspection.
|
||||
On submit, throw an exception'''
|
||||
inspection_required_fieldname = None
|
||||
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
inspection_required_fieldname = "inspection_required_before_purchase"
|
||||
elif self.doctype in ["Delivery Note", "Sales Invoice"]:
|
||||
inspection_required_fieldname = "inspection_required_before_delivery"
|
||||
"""Checks if quality inspection is set/ is valid for Items that require inspection."""
|
||||
inspection_fieldname_map = {
|
||||
"Purchase Receipt": "inspection_required_before_purchase",
|
||||
"Purchase Invoice": "inspection_required_before_purchase",
|
||||
"Sales Invoice": "inspection_required_before_delivery",
|
||||
"Delivery Note": "inspection_required_before_delivery"
|
||||
}
|
||||
inspection_required_fieldname = inspection_fieldname_map.get(self.doctype)
|
||||
|
||||
# return if inspection is not required on document level
|
||||
if ((not inspection_required_fieldname and self.doctype != "Stock Entry") or
|
||||
(self.doctype == "Stock Entry" and not self.inspection_required) or
|
||||
(self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock)):
|
||||
return
|
||||
|
||||
for d in self.get('items'):
|
||||
qa_required = False
|
||||
if (inspection_required_fieldname and not d.quality_inspection and
|
||||
frappe.db.get_value("Item", d.item_code, inspection_required_fieldname)):
|
||||
qa_required = True
|
||||
elif self.doctype == "Stock Entry" and not d.quality_inspection and d.t_warehouse:
|
||||
qa_required = True
|
||||
if self.docstatus == 1 and d.quality_inspection:
|
||||
qa_doc = frappe.get_doc("Quality Inspection", d.quality_inspection)
|
||||
if qa_doc.docstatus == 0:
|
||||
link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection)
|
||||
frappe.throw(_("Quality Inspection: {0} is not submitted for the item: {1} in row {2}").format(link, d.item_code, d.idx), QualityInspectionNotSubmittedError)
|
||||
for row in self.get('items'):
|
||||
qi_required = False
|
||||
if (inspection_required_fieldname and frappe.db.get_value("Item", row.item_code, inspection_required_fieldname)):
|
||||
qi_required = True
|
||||
elif self.doctype == "Stock Entry" and row.t_warehouse:
|
||||
qi_required = True # inward stock needs inspection
|
||||
|
||||
if qa_doc.status != 'Accepted':
|
||||
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
|
||||
.format(d.idx, d.item_code), QualityInspectionRejectedError)
|
||||
elif qa_required :
|
||||
action = frappe.get_doc('Stock Settings').action_if_quality_inspection_is_not_submitted
|
||||
if self.docstatus==1 and action == 'Stop':
|
||||
frappe.throw(_("Quality Inspection required for Item {0} to submit").format(frappe.bold(d.item_code)),
|
||||
exc=QualityInspectionRequiredError)
|
||||
else:
|
||||
frappe.msgprint(_("Create Quality Inspection for Item {0}").format(frappe.bold(d.item_code)))
|
||||
if qi_required: # validate row only if inspection is required on item level
|
||||
self.validate_qi_presence(row)
|
||||
if self.docstatus == 1:
|
||||
self.validate_qi_submission(row)
|
||||
self.validate_qi_rejection(row)
|
||||
|
||||
def validate_qi_presence(self, row):
|
||||
"""Check if QI is present on row level. Warn on save and stop on submit if missing."""
|
||||
if not row.quality_inspection:
|
||||
msg = f"Row #{row.idx}: Quality Inspection is required for Item {frappe.bold(row.item_code)}"
|
||||
if self.docstatus == 1:
|
||||
frappe.throw(_(msg), title=_("Inspection Required"), exc=QualityInspectionRequiredError)
|
||||
else:
|
||||
frappe.msgprint(_(msg), title=_("Inspection Required"), indicator="blue")
|
||||
|
||||
def validate_qi_submission(self, row):
|
||||
"""Check if QI is submitted on row level, during submission"""
|
||||
action = frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_not_submitted")
|
||||
qa_docstatus = frappe.db.get_value("Quality Inspection", row.quality_inspection, "docstatus")
|
||||
|
||||
if not qa_docstatus == 1:
|
||||
link = frappe.utils.get_link_to_form('Quality Inspection', row.quality_inspection)
|
||||
msg = f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
|
||||
if action == "Stop":
|
||||
frappe.throw(_(msg), title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
|
||||
else:
|
||||
frappe.msgprint(_(msg), alert=True, indicator="orange")
|
||||
|
||||
def validate_qi_rejection(self, row):
|
||||
"""Check if QI is rejected on row level, during submission"""
|
||||
action = frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_rejected")
|
||||
qa_status = frappe.db.get_value("Quality Inspection", row.quality_inspection, "status")
|
||||
|
||||
if qa_status == "Rejected":
|
||||
link = frappe.utils.get_link_to_form('Quality Inspection', row.quality_inspection)
|
||||
msg = f"Row #{row.idx}: Quality Inspection {link} was rejected for item {row.item_code}"
|
||||
if action == "Stop":
|
||||
frappe.throw(_(msg), title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
|
||||
else:
|
||||
frappe.msgprint(_(msg), alert=True, indicator="orange")
|
||||
|
||||
def update_blanket_order(self):
|
||||
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))
|
||||
|
@ -14,7 +14,7 @@ from erpnext.controllers.stock_controller import (
|
||||
)
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
# test_records = frappe.get_test_records('Quality Inspection')
|
||||
|
||||
@ -159,6 +159,47 @@ class TestQualityInspection(unittest.TestCase):
|
||||
frappe.delete_doc("Quality Inspection", qi)
|
||||
dn.delete()
|
||||
|
||||
def test_rejected_qi_validation(self):
|
||||
"""Test if rejected QI blocks Stock Entry as per Stock Settings."""
|
||||
se = make_stock_entry(
|
||||
item_code="_Test Item with QA",
|
||||
target="_Test Warehouse - _TC",
|
||||
qty=1,
|
||||
basic_rate=100,
|
||||
inspection_required=True,
|
||||
do_not_submit=True
|
||||
)
|
||||
|
||||
readings = [
|
||||
{
|
||||
"specification": "Iron Content",
|
||||
"min_value": 0.1,
|
||||
"max_value": 0.9,
|
||||
"reading_1": "0.4"
|
||||
}
|
||||
]
|
||||
|
||||
qa = create_quality_inspection(
|
||||
reference_type="Stock Entry",
|
||||
reference_name=se.name,
|
||||
readings=readings,
|
||||
status="Rejected"
|
||||
)
|
||||
|
||||
frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
|
||||
se.reload()
|
||||
self.assertRaises(QualityInspectionRejectedError, se.submit) # when blocked in Stock settings, block rejected QI
|
||||
|
||||
frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Warn")
|
||||
se.reload()
|
||||
se.submit() # when allowed in Stock settings, allow rejected QI
|
||||
|
||||
# teardown
|
||||
qa.reload()
|
||||
qa.cancel()
|
||||
se.reload()
|
||||
se.cancel()
|
||||
frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
|
||||
|
||||
def create_quality_inspection(**args):
|
||||
args = frappe._dict(args)
|
||||
@ -175,12 +216,11 @@ def create_quality_inspection(**args):
|
||||
if not args.readings:
|
||||
create_quality_inspection_parameter("Size")
|
||||
readings = {"specification": "Size", "min_value": 0, "max_value": 10}
|
||||
if args.status == "Rejected":
|
||||
readings["reading_1"] = "12" # status is auto set in child on save
|
||||
else:
|
||||
readings = args.readings
|
||||
|
||||
if args.status == "Rejected":
|
||||
readings["reading_1"] = "12" # status is auto set in child on save
|
||||
|
||||
if isinstance(readings, list):
|
||||
for entry in readings:
|
||||
create_quality_inspection_parameter(entry["specification"])
|
||||
|
@ -45,6 +45,8 @@ def make_stock_entry(**args):
|
||||
s.posting_date = args.posting_date
|
||||
if args.posting_time:
|
||||
s.posting_time = args.posting_time
|
||||
if args.inspection_required:
|
||||
s.inspection_required = args.inspection_required
|
||||
|
||||
# map names
|
||||
if args.from_warehouse:
|
||||
|
@ -307,6 +307,7 @@
|
||||
"fieldname": "quality_inspection",
|
||||
"fieldtype": "Link",
|
||||
"label": "Quality Inspection",
|
||||
"no_copy": 1,
|
||||
"options": "Quality Inspection"
|
||||
},
|
||||
{
|
||||
@ -548,7 +549,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-22 20:08:23.799715",
|
||||
"modified": "2021-06-21 16:03:18.834880",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry Detail",
|
||||
|
@ -23,7 +23,10 @@
|
||||
"allow_negative_stock",
|
||||
"show_barcode_field",
|
||||
"clean_description_html",
|
||||
"quality_inspection_settings_section",
|
||||
"action_if_quality_inspection_is_not_submitted",
|
||||
"column_break_21",
|
||||
"action_if_quality_inspection_is_rejected",
|
||||
"section_break_7",
|
||||
"automatically_set_serial_nos_based_on_fifo",
|
||||
"set_qty_in_transactions_based_on_serial_no_input",
|
||||
@ -264,6 +267,22 @@
|
||||
{
|
||||
"fieldname": "column_break_31",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "quality_inspection_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Quality Inspection Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "Stop",
|
||||
"fieldname": "action_if_quality_inspection_is_rejected",
|
||||
"fieldtype": "Select",
|
||||
"label": "Action If Quality Inspection Is Rejected",
|
||||
"options": "Stop\nWarn"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@ -271,7 +290,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-30 17:27:42.709231",
|
||||
"modified": "2021-07-10 16:17:42.159829",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Settings",
|
||||
|
Loading…
x
Reference in New Issue
Block a user