From d6277cdc7f08f14081b7e425f8a901472c4a73cb Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 18 Dec 2020 21:37:19 +0530 Subject: [PATCH 01/11] feat: Value Based and Numeric Quality Inspection - Acceptance Formula is optional - Choose between Value based and Numeric QI - If numeric, select single or multiple readings - Added Min, Max and Mean Values for numeric inspection to avoid formula usage - Deprecated code cleanup in js file --- .../item_quality_inspection_parameter.json | 54 +++++++++- .../quality_inspection/quality_inspection.js | 102 +++++++++--------- .../quality_inspection.json | 4 +- .../quality_inspection/quality_inspection.py | 98 +++++++++++++---- .../quality_inspection_reading.json | 93 ++++++++++++++-- .../quality_inspection_template.py | 4 +- 6 files changed, 268 insertions(+), 87 deletions(-) diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json index 888bc2de47..f450128157 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json @@ -8,8 +8,14 @@ "field_order": [ "specification", "value", + "value_based", + "single_reading", "column_break_3", - "acceptance_formula" + "formula_based_criteria", + "acceptance_formula", + "min_value", + "max_value", + "mean_value" ], "fields": [ { @@ -24,10 +30,11 @@ "width": "200px" }, { + "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, - "label": "Acceptance Criteria", + "label": "Acceptance Criteria Value", "oldfieldname": "value", "oldfieldtype": "Data" }, @@ -36,17 +43,56 @@ "fieldtype": "Column Break" }, { - "description": "Simple Python formula based on numeric Readings.
Example 1: reading_1 > 0.2 and reading_1 < 0.5
\nExample 2: (reading_1 + reading_2) / 2 < 10", + "depends_on": "formula_based_criteria", + "description": "Simple Python formula applied on Reading fields.
Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", "in_list_view": 1, "label": "Acceptance Criteria Formula" + }, + { + "default": "0", + "fieldname": "formula_based_criteria", + "fieldtype": "Check", + "label": "Formula Based Criteria" + }, + { + "default": "0", + "depends_on": "eval:!doc.value_based", + "fieldname": "single_reading", + "fieldtype": "Check", + "label": "Single Reading" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", + "fieldname": "mean_value", + "fieldtype": "Float", + "label": "Mean Value" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "fieldname": "min_value", + "fieldtype": "Float", + "label": "Minimum Value" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "fieldname": "max_value", + "fieldtype": "Float", + "label": "Maximum Value" + }, + { + "default": "0", + "description": "Non-numeric Inspection.", + "fieldname": "value_based", + "fieldtype": "Check", + "label": "Value Based" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-11-16 16:33:42.421842", + "modified": "2020-12-18 21:03:29.828723", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index 376848afaa..f0bf9aed80 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -4,6 +4,54 @@ cur_frm.cscript.refresh = cur_frm.cscript.inspection_type; frappe.ui.form.on("Quality Inspection", { + setup: function(frm) { + frm.set_query("batch_no", function() { + return { + filters: { + "item": frm.doc.item_code + } + } + }); + + // Serial No based on item_code + frm.set_query("item_serial_no", function() { + var filters = {}; + if (frm.doc.item_code) { + filters = { + 'item_code': frm.doc.item_code + } + } + return { filters: filters } + }); + + // item code based on GRN/DN + frm.set_query("item_code", function(doc) { + let doctype = doc.reference_type; + + if (doc.reference_type !== "Job Card") { + doctype = (doc.reference_type == "Stock Entry") ? + "Stock Entry Detail" : doc.reference_type + " Item"; + } + + if (doc.reference_type && doc.reference_name) { + let filters = { + "from": doctype, + "inspection_type": doc.inspection_type + }; + + if (doc.reference_type == doctype) + filters["reference_name"] = doc.reference_name; + else + filters["parent"] = doc.reference_name; + + return { + query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query", + filters: filters + }; + } + }); + }, + item_code: function(frm) { if (frm.doc.item_code) { return frm.call({ @@ -26,55 +74,5 @@ frappe.ui.form.on("Quality Inspection", { } }); } - } -}) - -// item code based on GRN/DN -cur_frm.fields_dict['item_code'].get_query = function(doc, cdt, cdn) { - let doctype = doc.reference_type; - - if (doc.reference_type !== "Job Card") { - doctype = (doc.reference_type == "Stock Entry") ? - "Stock Entry Detail" : doc.reference_type + " Item"; - } - - if (doc.reference_type && doc.reference_name) { - let filters = { - "from": doctype, - "inspection_type": doc.inspection_type - }; - - if (doc.reference_type == doctype) - filters["reference_name"] = doc.reference_name; - else - filters["parent"] = doc.reference_name; - - return { - query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query", - filters: filters - }; - } -}, - -// Serial No based on item_code -cur_frm.fields_dict['item_serial_no'].get_query = function(doc, cdt, cdn) { - var filters = {}; - if (doc.item_code) { - filters = { - 'item_code': doc.item_code - } - } - return { filters: filters } -} - -cur_frm.set_query("batch_no", function(doc) { - return { - filters: { - "item": doc.item_code - } - } -}) - -cur_frm.add_fetch('item_code', 'item_name', 'item_name'); -cur_frm.add_fetch('item_code', 'description', 'description'); - + }, +}) \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index f6d76194d9..edfe7e98b2 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -136,6 +136,7 @@ "width": "50%" }, { + "fetch_from": "item_code.item_name", "fieldname": "item_name", "fieldtype": "Data", "in_global_search": 1, @@ -143,6 +144,7 @@ "read_only": 1 }, { + "fetch_from": "item_code.description", "fieldname": "description", "fieldtype": "Small Text", "label": "Description", @@ -236,7 +238,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-11-19 17:06:05.409963", + "modified": "2020-12-18 19:59:55.710300", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index ae4eb9b995..a7a023bcbf 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -6,7 +6,7 @@ import frappe from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc from frappe import _ -from frappe.utils import flt +from frappe.utils import flt, cint from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \ import get_template_details @@ -16,7 +16,7 @@ class QualityInspection(Document): self.get_item_specification_details() if self.readings: - self.set_status_based_on_acceptance_formula() + self.inspect_and_set_status() def get_item_specification_details(self): if not self.quality_inspection_template: @@ -29,9 +29,7 @@ class QualityInspection(Document): parameters = get_template_details(self.quality_inspection_template) for d in parameters: child = self.append('readings', {}) - child.specification = d.specification - child.value = d.value - child.acceptance_formula = d.acceptance_formula + child.update(d) child.status = "Accepted" def get_quality_inspection_template(self): @@ -76,28 +74,84 @@ class QualityInspection(Document): """.format(parent_doc=self.reference_type, child_doc=doctype), (quality_inspection, self.modified, self.reference_name, self.item_code)) - def set_status_based_on_acceptance_formula(self): + def inspect_and_set_status(self): for reading in self.readings: - if not reading.acceptance_formula: continue + if reading.formula_based_criteria: + self.set_status_based_on_acceptance_formula(reading) + else: + self.set_status_based_on_acceptance_values(reading) + + def set_status_based_on_acceptance_values(self, reading): + if cint(reading.value_based): + result = reading.get("reading_value") == reading.get("value") + else: + # numeric readings + if cint(reading.single_reading): + reading_1 = flt(reading.get("reading_1")) + result = flt(reading.get("min_value")) <= reading_1 <= flt(reading.get("max_value")) + else: + result = self.min_max_criteria_passed(reading) and self.mean_criteria_passed(reading) + + reading.status = "Accepted" if result else "Rejected" + + def min_max_criteria_passed(self, reading): + """Determine whether all readings fall in the acceptable range.""" + for i in range(1, 11): + reading_field = reading.get("reading_" + str(i)) + if reading_field is not None: + result = flt(reading.get("min_value")) <= flt(reading_field) <= flt(reading.get("max_value")) + if not result: return False + return True + + def mean_criteria_passed(self, reading): + """Determine whether mean of all readings is acceptable.""" + if reading.get("mean_value"): + from statistics import mean + readings_list = [] - condition = reading.acceptance_formula - data = {} for i in range(1, 11): - field = "reading_" + str(i) - data[field] = flt(reading.get(field)) or 0 + reading_value = reading.get("reading_" + str(i)) + if reading_value is not None: + readings_list.append(flt(reading_value)) - try: - result = frappe.safe_eval(condition, None, data) - reading.status = "Accepted" if result else "Rejected" - except SyntaxError: - frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx), - title=_("Invalid Formula")) - except NameError as e: - field = frappe.bold(e.args[0].split()[1]) - frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.") - .format(reading.idx, field), - title=_("Invalid Formula")) + actual_mean = mean(readings_list) if readings_list else 0 + return True if actual_mean == reading.get("mean_value") else False + return True # no mean value, nothing to check + + def set_status_based_on_acceptance_formula(self, reading): + if not reading.acceptance_formula: + frappe.throw(_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx), + title=_("Missing Formula")) + + condition = reading.acceptance_formula + data = self.get_formula_evaluation_data(reading) + + try: + result = frappe.safe_eval(condition, None, data) + reading.status = "Accepted" if result else "Rejected" + except NameError as e: + field = frappe.bold(e.args[0].split()[1]) + frappe.throw(_("Row #{0}: {1} is not a valid reading field. Please refer to the field description.") + .format(reading.idx, field), + title=_("Invalid Formula")) + except Exception: + frappe.throw(_("Row #{0}: Acceptance Criteria Formula is incorrect.").format(reading.idx), + title=_("Invalid Formula")) + + def get_formula_evaluation_data(self, reading): + data = {} + if cint(reading.value_based): + data = {"reading_value": reading.get("reading_value")} + else: + # numeric readings + data = {"reading_1": flt(reading.get("reading_1"))} + if not cint(reading.single_reading): + # if multiple numeric readings add all readings to data + for i in range(2, 11): + field = "reading_" + str(i) + data[field] = flt(reading.get(field)) + return data @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index c1976dd1fb..db95fabee0 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -7,21 +7,30 @@ "engine": "InnoDB", "field_order": [ "specification", - "value", "status", + "value", + "value_based", "column_break_4", + "formula_based_criteria", "acceptance_formula", + "min_value", + "max_value", + "mean_value", "section_break_3", + "reading_value", + "section_break_14", + "single_reading", + "section_break_12", "reading_1", "reading_2", "reading_3", - "column_break_10", "reading_4", + "column_break_10", "reading_5", "reading_6", - "column_break_14", "reading_7", "reading_8", + "column_break_14", "reading_9", "reading_10" ], @@ -38,10 +47,11 @@ }, { "columns": 2, + "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, - "label": "Acceptance Criteria", + "label": "Acceptance Criteria Value", "oldfieldname": "value", "oldfieldtype": "Data" }, @@ -56,6 +66,7 @@ }, { "columns": 1, + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_2", "fieldtype": "Data", "in_list_view": 1, @@ -65,6 +76,7 @@ }, { "columns": 1, + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_3", "fieldtype": "Data", "in_list_view": 1, @@ -73,6 +85,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_4", "fieldtype": "Data", "label": "Reading 4", @@ -80,6 +93,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_5", "fieldtype": "Data", "label": "Reading 5", @@ -87,6 +101,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_6", "fieldtype": "Data", "label": "Reading 6", @@ -94,6 +109,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_7", "fieldtype": "Data", "label": "Reading 7", @@ -101,6 +117,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_8", "fieldtype": "Data", "label": "Reading 8", @@ -108,6 +125,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_9", "fieldtype": "Data", "label": "Reading 9", @@ -115,6 +133,7 @@ "oldfieldtype": "Data" }, { + "depends_on": "eval:!doc.single_reading", "fieldname": "reading_10", "fieldtype": "Data", "label": "Reading 10", @@ -133,15 +152,18 @@ "options": "Accepted\nRejected" }, { + "depends_on": "value_based", "fieldname": "section_break_3", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Value Based Inspection" }, { "fieldname": "column_break_4", "fieldtype": "Column Break" }, { - "description": "Simple Python formula based on numeric Readings.
Example 1: reading_1 > 0.2 and reading_1 < 0.5
\nExample 2: (reading_1 + reading_2) / 2 < 10", + "depends_on": "formula_based_criteria", + "description": "Simple Python formula applied on Reading fields.
Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", "label": "Acceptance Criteria Formula" @@ -153,12 +175,69 @@ { "fieldname": "column_break_14", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "formula_based_criteria", + "fieldtype": "Check", + "label": "Formula Based Criteria" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", + "fieldname": "mean_value", + "fieldtype": "Float", + "label": "Mean Value" + }, + { + "default": "0", + "fieldname": "single_reading", + "fieldtype": "Check", + "label": "Single Reading" + }, + { + "depends_on": "eval:!doc.value_based", + "fieldname": "section_break_12", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "description": "Applied on each reading.", + "fieldname": "min_value", + "fieldtype": "Float", + "label": "Minimum Value" + }, + { + "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "description": "Applied on each reading.", + "fieldname": "max_value", + "fieldtype": "Float", + "label": "Maximum Value" + }, + { + "default": "0", + "description": "Non-numeric Inspection.", + "fieldname": "value_based", + "fieldtype": "Check", + "label": "Value Based" + }, + { + "depends_on": "value_based", + "fieldname": "reading_value", + "fieldtype": "Data", + "label": "Reading Value" + }, + { + "depends_on": "eval:!doc.value_based", + "fieldname": "section_break_14", + "fieldtype": "Section Break", + "label": "Numeric Inspection" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-11-16 16:34:29.947856", + "modified": "2020-12-18 21:02:04.865777", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py index e2848469b8..7dd0febc20 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -13,6 +13,8 @@ def get_template_details(template): if not template: return [] return frappe.get_all('Item Quality Inspection Parameter', - fields=["specification", "value", "acceptance_formula"], + fields=["specification", "value", "acceptance_formula", + "value_based", "formula_based_criteria", "single_reading", + "min_value", "max_value", "mean_value"], filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx") \ No newline at end of file From 0c4f97368d05f2277ccca54b583be0a104acf7a9 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Dec 2020 11:44:48 +0530 Subject: [PATCH 02/11] chore: UX improvement - Removed 'single reading' checkbox, unnecessary - Removed 'Mean' field and added computed mean to formula data - Changed 'Value Based' to 'Non-Numeric' - Re-arranged fields --- .../item_quality_inspection_parameter.json | 43 +++++------- .../quality_inspection/quality_inspection.py | 57 +++++++--------- .../quality_inspection_reading.json | 65 +++++-------------- .../quality_inspection_template.py | 3 +- 4 files changed, 58 insertions(+), 110 deletions(-) diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json index f450128157..9b980a1e01 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json @@ -8,14 +8,12 @@ "field_order": [ "specification", "value", - "value_based", - "single_reading", + "non_numeric", "column_break_3", - "formula_based_criteria", - "acceptance_formula", "min_value", "max_value", - "mean_value" + "formula_based_criteria", + "acceptance_formula" ], "fields": [ { @@ -27,10 +25,10 @@ "oldfieldtype": "Data", "print_width": "200px", "reqd": 1, - "width": "200px" + "width": "100px" }, { - "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, @@ -44,10 +42,9 @@ }, { "depends_on": "formula_based_criteria", - "description": "Simple Python formula applied on Reading fields.
Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", + "description": "Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
\nNumeric eg. 2: mean > 3.5 (mean of populated fields)
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", - "in_list_view": 1, "label": "Acceptance Criteria Formula" }, { @@ -57,42 +54,32 @@ "label": "Formula Based Criteria" }, { - "default": "0", - "depends_on": "eval:!doc.value_based", - "fieldname": "single_reading", - "fieldtype": "Check", - "label": "Single Reading" - }, - { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", - "fieldname": "mean_value", - "fieldtype": "Float", - "label": "Mean Value" - }, - { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "fieldname": "min_value", "fieldtype": "Float", + "in_list_view": 1, "label": "Minimum Value" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "fieldname": "max_value", "fieldtype": "Float", + "in_list_view": 1, "label": "Maximum Value" }, { "default": "0", - "description": "Non-numeric Inspection.", - "fieldname": "value_based", + "fieldname": "non_numeric", "fieldtype": "Check", - "label": "Value Based" + "in_list_view": 1, + "label": "Non-Numeric", + "width": "80px" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-18 21:03:29.828723", + "modified": "2020-12-21 11:37:55.387677", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index a7a023bcbf..f582658d87 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -79,46 +79,27 @@ class QualityInspection(Document): if reading.formula_based_criteria: self.set_status_based_on_acceptance_formula(reading) else: + # if not formula based check acceptance values set self.set_status_based_on_acceptance_values(reading) def set_status_based_on_acceptance_values(self, reading): - if cint(reading.value_based): + if cint(reading.non_numeric): result = reading.get("reading_value") == reading.get("value") else: # numeric readings - if cint(reading.single_reading): - reading_1 = flt(reading.get("reading_1")) - result = flt(reading.get("min_value")) <= reading_1 <= flt(reading.get("max_value")) - else: - result = self.min_max_criteria_passed(reading) and self.mean_criteria_passed(reading) + result = self.min_max_criteria_passed(reading) reading.status = "Accepted" if result else "Rejected" def min_max_criteria_passed(self, reading): """Determine whether all readings fall in the acceptable range.""" for i in range(1, 11): - reading_field = reading.get("reading_" + str(i)) - if reading_field is not None: - result = flt(reading.get("min_value")) <= flt(reading_field) <= flt(reading.get("max_value")) + reading_value = reading.get("reading_" + str(i)) + if reading_value is not None and reading_value.strip(): + result = flt(reading.get("min_value")) <= flt(reading_value) <= flt(reading.get("max_value")) if not result: return False return True - def mean_criteria_passed(self, reading): - """Determine whether mean of all readings is acceptable.""" - if reading.get("mean_value"): - from statistics import mean - readings_list = [] - - for i in range(1, 11): - reading_value = reading.get("reading_" + str(i)) - if reading_value is not None: - readings_list.append(flt(reading_value)) - - actual_mean = mean(readings_list) if readings_list else 0 - return True if actual_mean == reading.get("mean_value") else False - - return True # no mean value, nothing to check - def set_status_based_on_acceptance_formula(self, reading): if not reading.acceptance_formula: frappe.throw(_("Row #{0}: Acceptance Criteria Formula is required.").format(reading.idx), @@ -141,18 +122,30 @@ class QualityInspection(Document): def get_formula_evaluation_data(self, reading): data = {} - if cint(reading.value_based): + if cint(reading.non_numeric): data = {"reading_value": reading.get("reading_value")} else: # numeric readings - data = {"reading_1": flt(reading.get("reading_1"))} - if not cint(reading.single_reading): - # if multiple numeric readings add all readings to data - for i in range(2, 11): - field = "reading_" + str(i) - data[field] = flt(reading.get(field)) + for i in range(1, 11): + field = "reading_" + str(i) + data[field] = flt(reading.get(field)) + data["mean"] = self.calculate_mean(reading) + return data + def calculate_mean(self, reading): + """Calculate mean of all non-empty readings.""" + from statistics import mean + readings_list = [] + + for i in range(1, 11): + reading_value = reading.get("reading_" + str(i)) + if reading_value is not None and reading_value.strip(): + readings_list.append(flt(reading_value)) + + actual_mean = mean(readings_list) if readings_list else 0 + return actual_mean + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def item_query(doctype, txt, searchfield, start, page_len, filters): diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index db95fabee0..0792f26d2a 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -9,18 +9,15 @@ "specification", "status", "value", - "value_based", + "non_numeric", "column_break_4", - "formula_based_criteria", - "acceptance_formula", "min_value", "max_value", - "mean_value", + "formula_based_criteria", + "acceptance_formula", "section_break_3", "reading_value", "section_break_14", - "single_reading", - "section_break_12", "reading_1", "reading_2", "reading_3", @@ -47,7 +44,7 @@ }, { "columns": 2, - "depends_on": "eval:(!doc.formula_based_criteria && doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", "fieldname": "value", "fieldtype": "Data", "in_list_view": 1, @@ -66,7 +63,6 @@ }, { "columns": 1, - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_2", "fieldtype": "Data", "in_list_view": 1, @@ -76,7 +72,6 @@ }, { "columns": 1, - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_3", "fieldtype": "Data", "in_list_view": 1, @@ -85,7 +80,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_4", "fieldtype": "Data", "label": "Reading 4", @@ -93,7 +87,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_5", "fieldtype": "Data", "label": "Reading 5", @@ -101,7 +94,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_6", "fieldtype": "Data", "label": "Reading 6", @@ -109,7 +101,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_7", "fieldtype": "Data", "label": "Reading 7", @@ -117,7 +108,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_8", "fieldtype": "Data", "label": "Reading 8", @@ -125,7 +115,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_9", "fieldtype": "Data", "label": "Reading 9", @@ -133,7 +122,6 @@ "oldfieldtype": "Data" }, { - "depends_on": "eval:!doc.single_reading", "fieldname": "reading_10", "fieldtype": "Data", "label": "Reading 10", @@ -152,7 +140,7 @@ "options": "Accepted\nRejected" }, { - "depends_on": "value_based", + "depends_on": "non_numeric", "fieldname": "section_break_3", "fieldtype": "Section Break", "label": "Value Based Inspection" @@ -163,7 +151,7 @@ }, { "depends_on": "formula_based_criteria", - "description": "Simple Python formula applied on Reading fields.
Numeric eg.: reading_1 > 0.2 and reading_1 < 0.5
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", + "description": "Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
\nNumeric eg. 2: mean > 3.5 (mean of populated fields)
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", "fieldname": "acceptance_formula", "fieldtype": "Code", "label": "Acceptance Criteria Formula" @@ -183,61 +171,42 @@ "label": "Formula Based Criteria" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.single_reading && !doc.value_based)", - "fieldname": "mean_value", - "fieldtype": "Float", - "label": "Mean Value" - }, - { - "default": "0", - "fieldname": "single_reading", - "fieldtype": "Check", - "label": "Single Reading" - }, - { - "depends_on": "eval:!doc.value_based", - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hide_border": 1 - }, - { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "description": "Applied on each reading.", "fieldname": "min_value", "fieldtype": "Float", "label": "Minimum Value" }, { - "depends_on": "eval:(!doc.formula_based_criteria && !doc.value_based)", + "depends_on": "eval:(!doc.formula_based_criteria && !doc.non_numeric)", "description": "Applied on each reading.", "fieldname": "max_value", "fieldtype": "Float", "label": "Maximum Value" }, { - "default": "0", - "description": "Non-numeric Inspection.", - "fieldname": "value_based", - "fieldtype": "Check", - "label": "Value Based" - }, - { - "depends_on": "value_based", + "depends_on": "non_numeric", "fieldname": "reading_value", "fieldtype": "Data", "label": "Reading Value" }, { - "depends_on": "eval:!doc.value_based", + "depends_on": "eval:!doc.non_numeric", "fieldname": "section_break_14", "fieldtype": "Section Break", "label": "Numeric Inspection" + }, + { + "default": "0", + "fieldname": "non_numeric", + "fieldtype": "Check", + "label": "Non-Numeric" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-18 21:02:04.865777", + "modified": "2020-12-21 11:36:24.885019", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py index 7dd0febc20..c5a7974a73 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -14,7 +14,6 @@ def get_template_details(template): return frappe.get_all('Item Quality Inspection Parameter', fields=["specification", "value", "acceptance_formula", - "value_based", "formula_based_criteria", "single_reading", - "min_value", "max_value", "mean_value"], + "non_numeric", "formula_based_criteria", "min_value", "max_value"], filters={'parenttype': 'Quality Inspection Template', 'parent': template}, order_by="idx") \ No newline at end of file From 68f91c96400226254875016d0e0b95bdc3816580 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Dec 2020 12:24:45 +0530 Subject: [PATCH 03/11] chore: Added tests for new ux - Test for value based inspection - tweaks in test for formula based inspection - tweaks in create_quality_inspection as status in child row is auto set now --- .../test_quality_inspection.py | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 2c40009426..d0bfb466e0 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -44,24 +44,61 @@ class TestQualityInspection(unittest.TestCase): qa.delete() dn.delete() + def test_value_based_qi_readings(self): + # Test QI based on acceptance values (Non formula) + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + readings = [{ + "specification": "Iron Content", # numeric reading + "min_value": 0.1, + "max_value": 0.9, + "reading_1": "0.4" + }, + { + "specification": "Particle Inspection Needed", # non-numeric reading + "non_numeric": 1, + "value": "Yes", + "reading_value": "Yes" + }] + + qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, + readings=readings, do_not_save=True) + qa.save() + + # status must be auto set as per formula + self.assertEqual(qa.readings[0].status, "Accepted") + self.assertEqual(qa.readings[1].status, "Accepted") + + qa.delete() + dn.delete() + def test_formula_based_qi_readings(self): dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) readings = [{ - "specification": "Iron Content", + "specification": "Iron Content", # numeric reading + "formula_based_criteria": 1, "acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50", - "reading_1": 0.4 + "reading_1": "0.4" }, { - "specification": "Calcium Content", + "specification": "Calcium Content", # numeric reading + "formula_based_criteria": 1, "acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50", - "reading_1": 0.7 + "reading_1": "0.7" }, { - "specification": "Mg Content", - "acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9", - "reading_1": 0.5, - "reading_2": 0.7, + "specification": "Mg Content", # numeric reading + "formula_based_criteria": 1, + "acceptance_formula": "mean < 0.9", + "reading_1": "0.5", + "reading_2": "0.7", "reading_3": "random text" # check if random string input causes issues + }, + { + "specification": "Calcium Content", # non-numeric reading + "formula_based_criteria": 1, + "non_numeric": 1, + "acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')", + "reading_value": "Grade B" }] qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, @@ -72,6 +109,7 @@ class TestQualityInspection(unittest.TestCase): self.assertEqual(qa.readings[0].status, "Accepted") self.assertEqual(qa.readings[1].status, "Rejected") self.assertEqual(qa.readings[2].status, "Accepted") + self.assertEqual(qa.readings[3].status, "Accepted") qa.delete() dn.delete() @@ -86,8 +124,11 @@ def create_quality_inspection(**args): qa.item_code = args.item_code or "_Test Item with QA" qa.sample_size = 1 qa.inspected_by = frappe.session.user + qa.status = args.status or "Accepted" - readings = args.readings or {"specification": "Size", "status": args.status} + readings = args.readings or {"specification": "Size", "min_value": 0, "max_value": 10} + if args.status == "Rejected": + readings["reading_1"] = "12" # status is auto set in child on save if isinstance(readings, list): for entry in readings: From eae31f02cc1a5254292b7c621513e70b91d10b22 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 21 Dec 2020 13:58:44 +0530 Subject: [PATCH 04/11] fix: Sider (missing semi-colons) --- .../doctype/quality_inspection/quality_inspection.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index f0bf9aed80..2ec8a07005 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -10,18 +10,18 @@ frappe.ui.form.on("Quality Inspection", { filters: { "item": frm.doc.item_code } - } + }; }); // Serial No based on item_code frm.set_query("item_serial_no", function() { - var filters = {}; + let filters = {}; if (frm.doc.item_code) { filters = { 'item_code': frm.doc.item_code - } + }; } - return { filters: filters } + return { filters: filters }; }); // item code based on GRN/DN @@ -75,4 +75,4 @@ frappe.ui.form.on("Quality Inspection", { }); } }, -}) \ No newline at end of file +}); \ No newline at end of file From 29a03bd5a1d250479369f8539450414bfbef080c Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Dec 2020 16:59:13 +0530 Subject: [PATCH 05/11] feat: Add 'Manual Inspection' checkbox - fix merge conflict in js file - Dont auto set status if manual inspection is checked - Added 'Manual Inspection' checkbox in QI readings table --- .../doctype/quality_inspection/quality_inspection.js | 1 + .../doctype/quality_inspection/quality_inspection.py | 11 ++++++----- .../quality_inspection_reading.json | 10 +++++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index 544bc2c307..f7565fd505 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -51,6 +51,7 @@ frappe.ui.form.on("Quality Inspection", { }; } }); + }, refresh: function(frm) { // Ignore cancellation of reference doctype on cancel all. diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index f582658d87..9672b62394 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -76,11 +76,12 @@ class QualityInspection(Document): def inspect_and_set_status(self): for reading in self.readings: - if reading.formula_based_criteria: - self.set_status_based_on_acceptance_formula(reading) - else: - # if not formula based check acceptance values set - self.set_status_based_on_acceptance_values(reading) + if not reading.manual_inspection: # dont auto set status if manual + if reading.formula_based_criteria: + self.set_status_based_on_acceptance_formula(reading) + else: + # if not formula based check acceptance values set + self.set_status_based_on_acceptance_values(reading) def set_status_based_on_acceptance_values(self, reading): if cint(reading.non_numeric): diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index 0792f26d2a..264a6ea634 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -10,6 +10,7 @@ "status", "value", "non_numeric", + "manual_inspection", "column_break_4", "min_value", "max_value", @@ -201,12 +202,19 @@ "fieldname": "non_numeric", "fieldtype": "Check", "label": "Non-Numeric" + }, + { + "default": "0", + "description": "Set the status manually.", + "fieldname": "manual_inspection", + "fieldtype": "Check", + "label": "Manual Inspection" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-21 11:36:24.885019", + "modified": "2020-12-28 16:40:47.586382", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", From a69e81a1510d3dc4d3ece2744224023ed3f14a23 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Dec 2020 18:07:22 +0530 Subject: [PATCH 06/11] chore: Made 'Parameter' a link field in QI and QI Template - Added doctype Quality Inspection Parameter - Made 'Parameter' a link field in QI and QI Template - Added patch to create Quality Inspection Parameter records for every parameter in the system --- erpnext/patches.txt | 1 + .../convert_qi_parameter_to_link_field.py | 23 +++++ .../item_quality_inspection_parameter.json | 5 +- .../quality_inspection_parameter/__init__.py | 0 .../quality_inspection_parameter.js | 8 ++ .../quality_inspection_parameter.json | 86 +++++++++++++++++++ .../quality_inspection_parameter.py | 10 +++ .../test_quality_inspection_parameter.py | 10 +++ .../quality_inspection_reading.json | 5 +- 9 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py create mode 100644 erpnext/stock/doctype/quality_inspection_parameter/__init__.py create mode 100644 erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js create mode 100644 erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json create mode 100644 erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py create mode 100644 erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d69dabf15c..621f4173ad 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -741,3 +741,4 @@ erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy erpnext.patches.v13_0.add_po_to_global_search erpnext.patches.v13_0.update_returned_qty_in_pr_dn +erpnext.patches.v13_0.convert_qi_parameter_to_link_field #2345 diff --git a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py new file mode 100644 index 0000000000..289b6a761e --- /dev/null +++ b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py @@ -0,0 +1,23 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc('stock', 'doctype', 'quality_inspection_parameter') + + # get all distinct parameters from QI readigs table + reading_params = frappe.db.get_all("Quality Inspection Reading", fields=["distinct specification"]) + reading_params = [d.specification for d in reading_params] + + # get all distinct parameters from QI Template as some may be unused in QI + template_params = frappe.db.get_all("Item Quality Inspection Parameter", fields=["distinct specification"]) + template_params = [d.specification for d in template_params] + + params = list(set(reading_params + template_params)) + + for parameter in params: + if not frappe.db.exists("Quality Inspection Parameter", parameter): + frappe.get_doc({ + "doctype": "Quality Inspection Parameter", + "parameter": parameter, + "description": parameter + }).insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json index 9b980a1e01..fc06e89f2f 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json @@ -18,11 +18,12 @@ "fields": [ { "fieldname": "specification", - "fieldtype": "Data", + "fieldtype": "Link", "in_list_view": 1, "label": "Parameter", "oldfieldname": "specification", "oldfieldtype": "Data", + "options": "Quality Inspection Parameter", "print_width": "200px", "reqd": 1, "width": "100px" @@ -79,7 +80,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-21 11:37:55.387677", + "modified": "2020-12-28 17:41:04.350225", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection_parameter/__init__.py b/erpnext/stock/doctype/quality_inspection_parameter/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js new file mode 100644 index 0000000000..47c7e11d23 --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Quality Inspection Parameter', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json new file mode 100644 index 0000000000..0b5a9b5b3c --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.json @@ -0,0 +1,86 @@ +{ + "actions": [], + "autoname": "field:parameter", + "creation": "2020-12-28 17:06:00.254129", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parameter", + "description" + ], + "fields": [ + { + "fieldname": "parameter", + "fieldtype": "Data", + "label": "Parameter", + "unique": 1 + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Description" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-12-28 18:06:54.897317", + "modified_by": "Administrator", + "module": "Stock", + "name": "Quality Inspection Parameter", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py new file mode 100644 index 0000000000..86784221a0 --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class QualityInspectionParameter(Document): + pass diff --git a/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py b/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py new file mode 100644 index 0000000000..cefdc0867b --- /dev/null +++ b/erpnext/stock/doctype/quality_inspection_parameter/test_quality_inspection_parameter.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestQualityInspectionParameter(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index 264a6ea634..739845bcda 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -36,11 +36,12 @@ { "columns": 3, "fieldname": "specification", - "fieldtype": "Data", + "fieldtype": "Link", "in_list_view": 1, "label": "Parameter", "oldfieldname": "specification", "oldfieldtype": "Data", + "options": "Quality Inspection Parameter", "reqd": 1 }, { @@ -214,7 +215,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-28 16:40:47.586382", + "modified": "2020-12-28 17:40:47.407210", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", From 7877d5a7c243b0e98be9e1f1362b8dfbd18acd2e Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 4 Jan 2021 11:10:04 +0530 Subject: [PATCH 07/11] fix: Create QI Parameters (links) in test cases --- erpnext/controllers/tests/test_item_variant.py | 3 +++ .../test_quality_inspection.py | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/tests/test_item_variant.py b/erpnext/controllers/tests/test_item_variant.py index c257215e71..813f0a0075 100644 --- a/erpnext/controllers/tests/test_item_variant.py +++ b/erpnext/controllers/tests/test_item_variant.py @@ -6,6 +6,7 @@ import unittest from erpnext.stock.doctype.item.test_item import set_item_variant_settings from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code +from erpnext.stock.doctype.quality_inspection.test_quality_inspection import create_quality_inspection_parameter from six import string_types @@ -56,6 +57,8 @@ def make_quality_inspection_template(): qc = frappe.new_doc("Quality Inspection Template") qc.quality_inspection_template_name = qc_template + + create_quality_inspection_parameter("Moisture") qc.append('item_quality_inspection_parameter', { "specification": "Moisture", "value": "< 5%", diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index d0bfb466e0..8c5a04b3f0 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -126,12 +126,18 @@ def create_quality_inspection(**args): qa.inspected_by = frappe.session.user qa.status = args.status or "Accepted" - readings = args.readings or {"specification": "Size", "min_value": 0, "max_value": 10} + if not args.readings: + create_quality_inspection_parameter("Size") + readings = {"specification": "Size", "min_value": 0, "max_value": 10} + 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"]) qa.append("readings", entry) else: qa.append("readings", readings) @@ -142,3 +148,11 @@ def create_quality_inspection(**args): qa.submit() return qa + +def create_quality_inspection_parameter(parameter): + if not frappe.db.exists("Quality Inspection Parameter", parameter): + frappe.get_doc({ + "doctype": "Quality Inspection Parameter", + "parameter": parameter, + "description": parameter + }).insert() \ No newline at end of file From ff6ee9d4e712ac1d573e010c5a36193abacbcf50 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 8 Jan 2021 09:14:43 +0530 Subject: [PATCH 08/11] fix: Formula field description and Rearrange grid view - Missing closing quote in Formula field description - In grid view of child table in QI, show only input fields --- .../item_quality_inspection_parameter.json | 4 ++-- .../quality_inspection_reading.json | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json index fc06e89f2f..3e81619cfd 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json @@ -43,7 +43,7 @@ }, { "depends_on": "formula_based_criteria", - "description": "Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
\nNumeric eg. 2: mean > 3.5 (mean of populated fields)
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", + "description": "Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
\nNumeric eg. 2: mean > 3.5 (mean of populated fields)
\nValue based eg.: reading_value in (\"A\", \"B\", \"C\")", "fieldname": "acceptance_formula", "fieldtype": "Code", "label": "Acceptance Criteria Formula" @@ -80,7 +80,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-28 17:41:04.350225", + "modified": "2021-01-07 21:32:49.866439", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index 739845bcda..dddb3d517d 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -49,7 +49,6 @@ "depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)", "fieldname": "value", "fieldtype": "Data", - "in_list_view": 1, "label": "Acceptance Criteria Value", "oldfieldname": "value", "oldfieldtype": "Data" @@ -76,7 +75,6 @@ "columns": 1, "fieldname": "reading_3", "fieldtype": "Data", - "in_list_view": 1, "label": "Reading 3", "oldfieldname": "reading_3", "oldfieldtype": "Data" @@ -153,7 +151,7 @@ }, { "depends_on": "formula_based_criteria", - "description": "Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
\nNumeric eg. 2: mean > 3.5 (mean of populated fields)
\nValue based eg.: reading_value in (\"A\", \"B\", \"C)", + "description": "Simple Python formula applied on Reading fields.
Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
\nNumeric eg. 2: mean > 3.5 (mean of populated fields)
\nValue based eg.: reading_value in (\"A\", \"B\", \"C\")", "fieldname": "acceptance_formula", "fieldtype": "Code", "label": "Acceptance Criteria Formula" @@ -190,6 +188,7 @@ "depends_on": "non_numeric", "fieldname": "reading_value", "fieldtype": "Data", + "in_list_view": 1, "label": "Reading Value" }, { @@ -202,6 +201,7 @@ "default": "0", "fieldname": "non_numeric", "fieldtype": "Check", + "in_list_view": 1, "label": "Non-Numeric" }, { @@ -215,7 +215,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-28 17:40:47.407210", + "modified": "2021-01-07 21:56:40.235579", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection Reading", From c4963bfdb24819e2edd4a28c11551190965d9cd1 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 8 Jan 2021 09:56:04 +0530 Subject: [PATCH 09/11] fix: Back Update from QC based on Batch No --- .../quality_inspection/quality_inspection.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index ae4eb9b995..b30d48d547 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -69,11 +69,21 @@ class QualityInspection(Document): doctype = 'Stock Entry Detail' if self.reference_type and self.reference_name: + conditions = "" + if self.batch_no: + conditions += " and t1.batch_no = '%s'"%(self.batch_no) + frappe.db.sql(""" - UPDATE `tab{child_doc}` t1, `tab{parent_doc}` t2 - SET t1.quality_inspection = %s, t2.modified = %s - WHERE t1.parent = %s and t1.item_code = %s and t1.parent = t2.name - """.format(parent_doc=self.reference_type, child_doc=doctype), + UPDATE + `tab{child_doc}` t1, `tab{parent_doc}` t2 + SET + t1.quality_inspection = %s, t2.modified = %s + WHERE + t1.parent = %s + and t1.item_code = %s + and t1.parent = t2.name + {conditions} + """.format(parent_doc=self.reference_type, child_doc=doctype, conditions=conditions), (quality_inspection, self.modified, self.reference_name, self.item_code)) def set_status_based_on_acceptance_formula(self): From a93151502c5f27b8492449d83d95eba25ff2887e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 8 Jan 2021 12:10:26 +0530 Subject: [PATCH 10/11] fix: Components formulated from additional salary not being fetched in Payroll Entry --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 47c9d31bf4..183ad13411 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -577,7 +577,7 @@ class SalarySlip(TransactionBase): 'default_amount': amount if not struct_row.get("is_additional_component") else 0, 'depends_on_payment_days' : struct_row.depends_on_payment_days, 'salary_component' : struct_row.salary_component, - 'abbr' : struct_row.abbr, + 'abbr' : struct_row.abbr or struct_row.get("salary_component_abbr"), 'additional_salary': additional_salary, 'do_not_include_in_total' : struct_row.do_not_include_in_total, 'is_tax_applicable': struct_row.is_tax_applicable, From b7637f49cd543e8a4fdbfc0d840323583d5c3651 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 8 Jan 2021 18:35:49 +0530 Subject: [PATCH 11/11] fix: Remove QI link on cancel wherever same QI name exists --- .../stock/doctype/quality_inspection/quality_inspection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index b30d48d547..2084e3fa54 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -70,9 +70,12 @@ class QualityInspection(Document): if self.reference_type and self.reference_name: conditions = "" - if self.batch_no: + if self.batch_no and self.docstatus == 1: conditions += " and t1.batch_no = '%s'"%(self.batch_no) + if self.docstatus == 2: # if cancel, then remove qi link wherever same name + conditions += " and t1.quality_inspection = '%s'"%(self.name) + frappe.db.sql(""" UPDATE `tab{child_doc}` t1, `tab{parent_doc}` t2