Merge branch 'develop' into batch-selector
This commit is contained in:
commit
aef71a8abe
@ -6,6 +6,7 @@ import unittest
|
|||||||
|
|
||||||
from erpnext.stock.doctype.item.test_item import set_item_variant_settings
|
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.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
|
from six import string_types
|
||||||
|
|
||||||
@ -56,6 +57,8 @@ def make_quality_inspection_template():
|
|||||||
|
|
||||||
qc = frappe.new_doc("Quality Inspection Template")
|
qc = frappe.new_doc("Quality Inspection Template")
|
||||||
qc.quality_inspection_template_name = qc_template
|
qc.quality_inspection_template_name = qc_template
|
||||||
|
|
||||||
|
create_quality_inspection_parameter("Moisture")
|
||||||
qc.append('item_quality_inspection_parameter', {
|
qc.append('item_quality_inspection_parameter', {
|
||||||
"specification": "Moisture",
|
"specification": "Moisture",
|
||||||
"value": "< 5%",
|
"value": "< 5%",
|
||||||
|
@ -23,8 +23,10 @@ class TestPatientAppointment(unittest.TestCase):
|
|||||||
self.assertEquals(appointment.status, 'Open')
|
self.assertEquals(appointment.status, 'Open')
|
||||||
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
|
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2))
|
||||||
self.assertEquals(appointment.status, 'Scheduled')
|
self.assertEquals(appointment.status, 'Scheduled')
|
||||||
create_encounter(appointment)
|
encounter = create_encounter(appointment)
|
||||||
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
|
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
|
||||||
|
encounter.cancel()
|
||||||
|
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
|
||||||
|
|
||||||
def test_start_encounter(self):
|
def test_start_encounter(self):
|
||||||
patient, medical_department, practitioner = create_healthcare_docs()
|
patient, medical_department, practitioner = create_healthcare_docs()
|
||||||
|
@ -5,10 +5,10 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import getdate, flt
|
from frappe.utils import getdate, flt, nowdate
|
||||||
from erpnext.healthcare.doctype.therapy_type.test_therapy_type import create_therapy_type
|
from erpnext.healthcare.doctype.therapy_type.test_therapy_type import create_therapy_type
|
||||||
from erpnext.healthcare.doctype.therapy_plan.therapy_plan import make_therapy_session, make_sales_invoice
|
from erpnext.healthcare.doctype.therapy_plan.therapy_plan import make_therapy_session, make_sales_invoice
|
||||||
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_patient
|
from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_healthcare_docs, create_patient, create_appointment
|
||||||
|
|
||||||
class TestTherapyPlan(unittest.TestCase):
|
class TestTherapyPlan(unittest.TestCase):
|
||||||
def test_creation_on_encounter_submission(self):
|
def test_creation_on_encounter_submission(self):
|
||||||
@ -28,6 +28,15 @@ class TestTherapyPlan(unittest.TestCase):
|
|||||||
frappe.get_doc(session).submit()
|
frappe.get_doc(session).submit()
|
||||||
self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
|
self.assertEquals(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
|
||||||
|
|
||||||
|
patient, medical_department, practitioner = create_healthcare_docs()
|
||||||
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
|
session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
|
||||||
|
session = frappe.get_doc(session)
|
||||||
|
session.submit()
|
||||||
|
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
|
||||||
|
session.cancel()
|
||||||
|
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Open')
|
||||||
|
|
||||||
def test_therapy_plan_from_template(self):
|
def test_therapy_plan_from_template(self):
|
||||||
patient = create_patient()
|
patient = create_patient()
|
||||||
template = create_therapy_plan_template()
|
template = create_therapy_plan_template()
|
||||||
|
@ -47,7 +47,7 @@ class TherapyPlan(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_therapy_session(therapy_plan, patient, therapy_type, company):
|
def make_therapy_session(therapy_plan, patient, therapy_type, company, appointment=None):
|
||||||
therapy_type = frappe.get_doc('Therapy Type', therapy_type)
|
therapy_type = frappe.get_doc('Therapy Type', therapy_type)
|
||||||
|
|
||||||
therapy_session = frappe.new_doc('Therapy Session')
|
therapy_session = frappe.new_doc('Therapy Session')
|
||||||
@ -58,6 +58,7 @@ def make_therapy_session(therapy_plan, patient, therapy_type, company):
|
|||||||
therapy_session.duration = therapy_type.default_duration
|
therapy_session.duration = therapy_type.default_duration
|
||||||
therapy_session.rate = therapy_type.rate
|
therapy_session.rate = therapy_type.rate
|
||||||
therapy_session.exercises = therapy_type.exercises
|
therapy_session.exercises = therapy_type.exercises
|
||||||
|
therapy_session.appointment = appointment
|
||||||
|
|
||||||
if frappe.flags.in_test:
|
if frappe.flags.in_test:
|
||||||
therapy_session.start_date = today()
|
therapy_session.start_date = today()
|
||||||
|
@ -19,6 +19,15 @@ frappe.ui.form.on('Therapy Session', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query('appointment', function() {
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'status': ['in', ['Open', 'Scheduled']]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
@ -43,7 +43,14 @@ class TherapySession(Document):
|
|||||||
self.update_sessions_count_in_therapy_plan()
|
self.update_sessions_count_in_therapy_plan()
|
||||||
insert_session_medical_record(self)
|
insert_session_medical_record(self)
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
if self.appointment:
|
||||||
|
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
if self.appointment:
|
||||||
|
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
|
||||||
|
|
||||||
self.update_sessions_count_in_therapy_plan(on_cancel=True)
|
self.update_sessions_count_in_therapy_plan(on_cancel=True)
|
||||||
|
|
||||||
def update_sessions_count_in_therapy_plan(self, on_cancel=False):
|
def update_sessions_count_in_therapy_plan(self, on_cancel=False):
|
||||||
|
@ -742,4 +742,5 @@ 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.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
||||||
erpnext.patches.v13_0.add_po_to_global_search
|
erpnext.patches.v13_0.add_po_to_global_search
|
||||||
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||||
erpnext.patches.v13_0.set_company_in_leave_ledger_entry
|
erpnext.patches.v13_0.set_company_in_leave_ledger_entry
|
||||||
|
erpnext.patches.v13_0.convert_qi_parameter_to_link_field
|
||||||
|
23
erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
Normal file
23
erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py
Normal file
@ -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)
|
@ -830,56 +830,49 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t
|
|||||||
frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order."))
|
frappe.throw(_("Please set a Supplier against the Items to be considered in the Purchase Order."))
|
||||||
|
|
||||||
for supplier in suppliers:
|
for supplier in suppliers:
|
||||||
po = frappe.get_list("Purchase Order", filters={"sales_order":source_name, "supplier":supplier, "docstatus": ("<", "2")})
|
doc = get_mapped_doc("Sales Order", source_name, {
|
||||||
if len(po) == 0:
|
"Sales Order": {
|
||||||
doc = get_mapped_doc("Sales Order", source_name, {
|
"doctype": "Purchase Order",
|
||||||
"Sales Order": {
|
"field_no_map": [
|
||||||
"doctype": "Purchase Order",
|
"address_display",
|
||||||
"field_no_map": [
|
"contact_display",
|
||||||
"address_display",
|
"contact_mobile",
|
||||||
"contact_display",
|
"contact_email",
|
||||||
"contact_mobile",
|
"contact_person",
|
||||||
"contact_email",
|
"taxes_and_charges",
|
||||||
"contact_person",
|
"shipping_address",
|
||||||
"taxes_and_charges",
|
"terms"
|
||||||
"shipping_address",
|
],
|
||||||
"terms"
|
"validation": {
|
||||||
],
|
"docstatus": ["=", 1]
|
||||||
"validation": {
|
|
||||||
"docstatus": ["=", 1]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Sales Order Item": {
|
|
||||||
"doctype": "Purchase Order Item",
|
|
||||||
"field_map": [
|
|
||||||
["name", "sales_order_item"],
|
|
||||||
["parent", "sales_order"],
|
|
||||||
["stock_uom", "stock_uom"],
|
|
||||||
["uom", "uom"],
|
|
||||||
["conversion_factor", "conversion_factor"],
|
|
||||||
["delivery_date", "schedule_date"]
|
|
||||||
],
|
|
||||||
"field_no_map": [
|
|
||||||
"rate",
|
|
||||||
"price_list_rate",
|
|
||||||
"item_tax_template",
|
|
||||||
"discount_percentage",
|
|
||||||
"discount_amount",
|
|
||||||
"pricing_rules"
|
|
||||||
],
|
|
||||||
"postprocess": update_item,
|
|
||||||
"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
|
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
},
|
||||||
|
"Sales Order Item": {
|
||||||
|
"doctype": "Purchase Order Item",
|
||||||
|
"field_map": [
|
||||||
|
["name", "sales_order_item"],
|
||||||
|
["parent", "sales_order"],
|
||||||
|
["stock_uom", "stock_uom"],
|
||||||
|
["uom", "uom"],
|
||||||
|
["conversion_factor", "conversion_factor"],
|
||||||
|
["delivery_date", "schedule_date"]
|
||||||
|
],
|
||||||
|
"field_no_map": [
|
||||||
|
"rate",
|
||||||
|
"price_list_rate",
|
||||||
|
"item_tax_template",
|
||||||
|
"discount_percentage",
|
||||||
|
"discount_amount",
|
||||||
|
"pricing_rules"
|
||||||
|
],
|
||||||
|
"postprocess": update_item,
|
||||||
|
"condition": lambda doc: doc.ordered_qty < doc.stock_qty and doc.supplier == supplier and doc.item_code in items_to_map
|
||||||
|
}
|
||||||
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
doc.insert()
|
doc.insert()
|
||||||
else:
|
|
||||||
suppliers =[]
|
|
||||||
if suppliers:
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
return doc
|
return doc
|
||||||
else:
|
|
||||||
frappe.msgprint(_("Purchase Order already created for all Sales Order items"))
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
||||||
@ -1094,4 +1087,4 @@ def update_produced_qty_in_so_item(sales_order, sales_order_item):
|
|||||||
|
|
||||||
if not total_produced_qty and frappe.flags.in_patch: return
|
if not total_produced_qty and frappe.flags.in_patch: return
|
||||||
|
|
||||||
frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
|
frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
|
||||||
|
@ -772,6 +772,59 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
so.cancel()
|
so.cancel()
|
||||||
|
|
||||||
|
def test_drop_shipping_partial_order(self):
|
||||||
|
from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_default_supplier, \
|
||||||
|
update_status as so_update_status
|
||||||
|
|
||||||
|
# make items
|
||||||
|
po_item1 = make_item("_Test Item for Drop Shipping 1", {"is_stock_item": 1, "delivered_by_supplier": 1})
|
||||||
|
po_item2 = make_item("_Test Item for Drop Shipping 2", {"is_stock_item": 1, "delivered_by_supplier": 1})
|
||||||
|
|
||||||
|
so_items = [
|
||||||
|
{
|
||||||
|
"item_code": po_item1.item_code,
|
||||||
|
"warehouse": "",
|
||||||
|
"qty": 2,
|
||||||
|
"rate": 400,
|
||||||
|
"delivered_by_supplier": 1,
|
||||||
|
"supplier": '_Test Supplier'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_code": po_item2.item_code,
|
||||||
|
"warehouse": "",
|
||||||
|
"qty": 2,
|
||||||
|
"rate": 400,
|
||||||
|
"delivered_by_supplier": 1,
|
||||||
|
"supplier": '_Test Supplier'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
# create so and po
|
||||||
|
so = make_sales_order(item_list=so_items, do_not_submit=True)
|
||||||
|
so.submit()
|
||||||
|
|
||||||
|
# create po for only one item
|
||||||
|
po1 = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[0]])
|
||||||
|
po1.submit()
|
||||||
|
|
||||||
|
self.assertEqual(so.customer, po1.customer)
|
||||||
|
self.assertEqual(po1.items[0].sales_order, so.name)
|
||||||
|
self.assertEqual(po1.items[0].item_code, po_item1.item_code)
|
||||||
|
#test po item length
|
||||||
|
self.assertEqual(len(po1.items), 1)
|
||||||
|
|
||||||
|
# create po for remaining item
|
||||||
|
po2 = make_purchase_order_for_default_supplier(so.name, selected_items=[so_items[1]])
|
||||||
|
po2.submit()
|
||||||
|
|
||||||
|
# teardown
|
||||||
|
so_update_status("Draft", so.name)
|
||||||
|
|
||||||
|
po1.cancel()
|
||||||
|
po2.cancel()
|
||||||
|
so.load_from_db()
|
||||||
|
so.cancel()
|
||||||
|
|
||||||
def test_reserved_qty_for_closing_so(self):
|
def test_reserved_qty_for_closing_so(self):
|
||||||
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
|
||||||
fields=["reserved_qty"])
|
fields=["reserved_qty"])
|
||||||
|
@ -8,26 +8,32 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"specification",
|
"specification",
|
||||||
"value",
|
"value",
|
||||||
|
"non_numeric",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
|
"min_value",
|
||||||
|
"max_value",
|
||||||
|
"formula_based_criteria",
|
||||||
"acceptance_formula"
|
"acceptance_formula"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "specification",
|
"fieldname": "specification",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Parameter",
|
"label": "Parameter",
|
||||||
"oldfieldname": "specification",
|
"oldfieldname": "specification",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
|
"options": "Quality Inspection Parameter",
|
||||||
"print_width": "200px",
|
"print_width": "200px",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"width": "200px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)",
|
||||||
"fieldname": "value",
|
"fieldname": "value",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Acceptance Criteria",
|
"label": "Acceptance Criteria Value",
|
||||||
"oldfieldname": "value",
|
"oldfieldname": "value",
|
||||||
"oldfieldtype": "Data"
|
"oldfieldtype": "Data"
|
||||||
},
|
},
|
||||||
@ -36,17 +42,45 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 < 10</b>",
|
"depends_on": "formula_based_criteria",
|
||||||
|
"description": "Simple Python formula applied on Reading fields.<br> Numeric eg. 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nNumeric eg. 2: <b>mean > 3.5</b> (mean of populated fields)<br>\nValue based eg.: <b>reading_value in (\"A\", \"B\", \"C\")</b>",
|
||||||
"fieldname": "acceptance_formula",
|
"fieldname": "acceptance_formula",
|
||||||
"fieldtype": "Code",
|
"fieldtype": "Code",
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Acceptance Criteria Formula"
|
"label": "Acceptance Criteria Formula"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "formula_based_criteria",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Formula Based Criteria"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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.non_numeric)",
|
||||||
|
"fieldname": "max_value",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Maximum Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "non_numeric",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Non-Numeric",
|
||||||
|
"width": "80px"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-16 16:33:42.421842",
|
"modified": "2021-01-07 21:32:49.866439",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item Quality Inspection Parameter",
|
"name": "Item Quality Inspection Parameter",
|
||||||
|
@ -4,6 +4,55 @@
|
|||||||
cur_frm.cscript.refresh = cur_frm.cscript.inspection_type;
|
cur_frm.cscript.refresh = cur_frm.cscript.inspection_type;
|
||||||
|
|
||||||
frappe.ui.form.on("Quality Inspection", {
|
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() {
|
||||||
|
let 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
// Ignore cancellation of reference doctype on cancel all.
|
// Ignore cancellation of reference doctype on cancel all.
|
||||||
frm.ignore_doctypes_on_cancel_all = [frm.doc.reference_type];
|
frm.ignore_doctypes_on_cancel_all = [frm.doc.reference_type];
|
||||||
@ -31,55 +80,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');
|
|
||||||
|
|
@ -136,6 +136,7 @@
|
|||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.item_name",
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
@ -143,6 +144,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.description",
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
@ -236,7 +238,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-19 17:06:05.409963",
|
"modified": "2020-12-18 19:59:55.710300",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Quality Inspection",
|
"name": "Quality Inspection",
|
||||||
|
@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, cint
|
||||||
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
|
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template \
|
||||||
import get_template_details
|
import get_template_details
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ class QualityInspection(Document):
|
|||||||
self.get_item_specification_details()
|
self.get_item_specification_details()
|
||||||
|
|
||||||
if self.readings:
|
if self.readings:
|
||||||
self.set_status_based_on_acceptance_formula()
|
self.inspect_and_set_status()
|
||||||
|
|
||||||
def get_item_specification_details(self):
|
def get_item_specification_details(self):
|
||||||
if not self.quality_inspection_template:
|
if not self.quality_inspection_template:
|
||||||
@ -29,9 +29,7 @@ class QualityInspection(Document):
|
|||||||
parameters = get_template_details(self.quality_inspection_template)
|
parameters = get_template_details(self.quality_inspection_template)
|
||||||
for d in parameters:
|
for d in parameters:
|
||||||
child = self.append('readings', {})
|
child = self.append('readings', {})
|
||||||
child.specification = d.specification
|
child.update(d)
|
||||||
child.value = d.value
|
|
||||||
child.acceptance_formula = d.acceptance_formula
|
|
||||||
child.status = "Accepted"
|
child.status = "Accepted"
|
||||||
|
|
||||||
def get_quality_inspection_template(self):
|
def get_quality_inspection_template(self):
|
||||||
@ -89,28 +87,78 @@ class QualityInspection(Document):
|
|||||||
""".format(parent_doc=self.reference_type, child_doc=doctype, conditions=conditions),
|
""".format(parent_doc=self.reference_type, child_doc=doctype, conditions=conditions),
|
||||||
(quality_inspection, self.modified, self.reference_name, self.item_code))
|
(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:
|
for reading in self.readings:
|
||||||
if not reading.acceptance_formula: continue
|
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)
|
||||||
|
|
||||||
condition = reading.acceptance_formula
|
def set_status_based_on_acceptance_values(self, reading):
|
||||||
data = {}
|
if cint(reading.non_numeric):
|
||||||
|
result = reading.get("reading_value") == reading.get("value")
|
||||||
|
else:
|
||||||
|
# numeric readings
|
||||||
|
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_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 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.non_numeric):
|
||||||
|
data = {"reading_value": reading.get("reading_value")}
|
||||||
|
else:
|
||||||
|
# numeric readings
|
||||||
for i in range(1, 11):
|
for i in range(1, 11):
|
||||||
field = "reading_" + str(i)
|
field = "reading_" + str(i)
|
||||||
data[field] = flt(reading.get(field)) or 0
|
data[field] = flt(reading.get(field))
|
||||||
|
data["mean"] = self.calculate_mean(reading)
|
||||||
|
|
||||||
try:
|
return data
|
||||||
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"))
|
|
||||||
|
|
||||||
|
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.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
|
@ -44,24 +44,61 @@ class TestQualityInspection(unittest.TestCase):
|
|||||||
qa.delete()
|
qa.delete()
|
||||||
dn.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):
|
def test_formula_based_qi_readings(self):
|
||||||
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
|
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
|
||||||
readings = [{
|
readings = [{
|
||||||
"specification": "Iron Content",
|
"specification": "Iron Content", # numeric reading
|
||||||
|
"formula_based_criteria": 1,
|
||||||
"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
|
"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",
|
"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
|
||||||
"reading_1": 0.7
|
"reading_1": "0.7"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"specification": "Mg Content",
|
"specification": "Mg Content", # numeric reading
|
||||||
"acceptance_formula": "(reading_1 + reading_2 + reading_3) / 3 < 0.9",
|
"formula_based_criteria": 1,
|
||||||
"reading_1": 0.5,
|
"acceptance_formula": "mean < 0.9",
|
||||||
"reading_2": 0.7,
|
"reading_1": "0.5",
|
||||||
|
"reading_2": "0.7",
|
||||||
"reading_3": "random text" # check if random string input causes issues
|
"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,
|
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[0].status, "Accepted")
|
||||||
self.assertEqual(qa.readings[1].status, "Rejected")
|
self.assertEqual(qa.readings[1].status, "Rejected")
|
||||||
self.assertEqual(qa.readings[2].status, "Accepted")
|
self.assertEqual(qa.readings[2].status, "Accepted")
|
||||||
|
self.assertEqual(qa.readings[3].status, "Accepted")
|
||||||
|
|
||||||
qa.delete()
|
qa.delete()
|
||||||
dn.delete()
|
dn.delete()
|
||||||
@ -86,11 +124,20 @@ def create_quality_inspection(**args):
|
|||||||
qa.item_code = args.item_code or "_Test Item with QA"
|
qa.item_code = args.item_code or "_Test Item with QA"
|
||||||
qa.sample_size = 1
|
qa.sample_size = 1
|
||||||
qa.inspected_by = frappe.session.user
|
qa.inspected_by = frappe.session.user
|
||||||
|
qa.status = args.status or "Accepted"
|
||||||
|
|
||||||
readings = args.readings or {"specification": "Size", "status": args.status}
|
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):
|
if isinstance(readings, list):
|
||||||
for entry in readings:
|
for entry in readings:
|
||||||
|
create_quality_inspection_parameter(entry["specification"])
|
||||||
qa.append("readings", entry)
|
qa.append("readings", entry)
|
||||||
else:
|
else:
|
||||||
qa.append("readings", readings)
|
qa.append("readings", readings)
|
||||||
@ -101,3 +148,11 @@ def create_quality_inspection(**args):
|
|||||||
qa.submit()
|
qa.submit()
|
||||||
|
|
||||||
return qa
|
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()
|
@ -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) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
@ -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
|
||||||
|
}
|
@ -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
|
@ -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
|
@ -7,21 +7,28 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"specification",
|
"specification",
|
||||||
"value",
|
|
||||||
"status",
|
"status",
|
||||||
|
"value",
|
||||||
|
"non_numeric",
|
||||||
|
"manual_inspection",
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
|
"min_value",
|
||||||
|
"max_value",
|
||||||
|
"formula_based_criteria",
|
||||||
"acceptance_formula",
|
"acceptance_formula",
|
||||||
"section_break_3",
|
"section_break_3",
|
||||||
|
"reading_value",
|
||||||
|
"section_break_14",
|
||||||
"reading_1",
|
"reading_1",
|
||||||
"reading_2",
|
"reading_2",
|
||||||
"reading_3",
|
"reading_3",
|
||||||
"column_break_10",
|
|
||||||
"reading_4",
|
"reading_4",
|
||||||
|
"column_break_10",
|
||||||
"reading_5",
|
"reading_5",
|
||||||
"reading_6",
|
"reading_6",
|
||||||
"column_break_14",
|
|
||||||
"reading_7",
|
"reading_7",
|
||||||
"reading_8",
|
"reading_8",
|
||||||
|
"column_break_14",
|
||||||
"reading_9",
|
"reading_9",
|
||||||
"reading_10"
|
"reading_10"
|
||||||
],
|
],
|
||||||
@ -29,19 +36,20 @@
|
|||||||
{
|
{
|
||||||
"columns": 3,
|
"columns": 3,
|
||||||
"fieldname": "specification",
|
"fieldname": "specification",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Parameter",
|
"label": "Parameter",
|
||||||
"oldfieldname": "specification",
|
"oldfieldname": "specification",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
|
"options": "Quality Inspection Parameter",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
|
"depends_on": "eval:(!doc.formula_based_criteria && doc.non_numeric)",
|
||||||
"fieldname": "value",
|
"fieldname": "value",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"label": "Acceptance Criteria Value",
|
||||||
"label": "Acceptance Criteria",
|
|
||||||
"oldfieldname": "value",
|
"oldfieldname": "value",
|
||||||
"oldfieldtype": "Data"
|
"oldfieldtype": "Data"
|
||||||
},
|
},
|
||||||
@ -67,7 +75,6 @@
|
|||||||
"columns": 1,
|
"columns": 1,
|
||||||
"fieldname": "reading_3",
|
"fieldname": "reading_3",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Reading 3",
|
"label": "Reading 3",
|
||||||
"oldfieldname": "reading_3",
|
"oldfieldname": "reading_3",
|
||||||
"oldfieldtype": "Data"
|
"oldfieldtype": "Data"
|
||||||
@ -133,15 +140,18 @@
|
|||||||
"options": "Accepted\nRejected"
|
"options": "Accepted\nRejected"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "non_numeric",
|
||||||
"fieldname": "section_break_3",
|
"fieldname": "section_break_3",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Value Based Inspection"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Simple Python formula based on numeric Readings.<br> Example 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nExample 2: <b>(reading_1 + reading_2) / 2 < 10</b>",
|
"depends_on": "formula_based_criteria",
|
||||||
|
"description": "Simple Python formula applied on Reading fields.<br> Numeric eg. 1: <b>reading_1 > 0.2 and reading_1 < 0.5</b><br>\nNumeric eg. 2: <b>mean > 3.5</b> (mean of populated fields)<br>\nValue based eg.: <b>reading_value in (\"A\", \"B\", \"C\")</b>",
|
||||||
"fieldname": "acceptance_formula",
|
"fieldname": "acceptance_formula",
|
||||||
"fieldtype": "Code",
|
"fieldtype": "Code",
|
||||||
"label": "Acceptance Criteria Formula"
|
"label": "Acceptance Criteria Formula"
|
||||||
@ -153,12 +163,59 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_14",
|
"fieldname": "column_break_14",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "formula_based_criteria",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Formula Based Criteria"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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.non_numeric)",
|
||||||
|
"description": "Applied on each reading.",
|
||||||
|
"fieldname": "max_value",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Maximum Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "non_numeric",
|
||||||
|
"fieldname": "reading_value",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Reading Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:!doc.non_numeric",
|
||||||
|
"fieldname": "section_break_14",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Numeric Inspection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "non_numeric",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Non-Numeric"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Set the status manually.",
|
||||||
|
"fieldname": "manual_inspection",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Manual Inspection"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-16 16:34:29.947856",
|
"modified": "2021-01-07 21:56:40.235579",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Quality Inspection Reading",
|
"name": "Quality Inspection Reading",
|
||||||
|
@ -13,6 +13,7 @@ def get_template_details(template):
|
|||||||
if not template: return []
|
if not template: return []
|
||||||
|
|
||||||
return frappe.get_all('Item Quality Inspection Parameter',
|
return frappe.get_all('Item Quality Inspection Parameter',
|
||||||
fields=["specification", "value", "acceptance_formula"],
|
fields=["specification", "value", "acceptance_formula",
|
||||||
|
"non_numeric", "formula_based_criteria", "min_value", "max_value"],
|
||||||
filters={'parenttype': 'Quality Inspection Template', 'parent': template},
|
filters={'parenttype': 'Quality Inspection Template', 'parent': template},
|
||||||
order_by="idx")
|
order_by="idx")
|
Loading…
Reference in New Issue
Block a user