mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-05 22:48:27 +00:00
Merge pull request #25221 from Alchez/dev-quality-inspection-accounts
feat: create Quality Inspections from account and stock documents
This commit is contained in:
@@ -225,7 +225,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
def validate_date_with_fiscal_year(self):
|
def validate_date_with_fiscal_year(self):
|
||||||
if self.meta.get_field("fiscal_year"):
|
if self.meta.get_field("fiscal_year"):
|
||||||
date_field = ""
|
date_field = None
|
||||||
if self.meta.get_field("posting_date"):
|
if self.meta.get_field("posting_date"):
|
||||||
date_field = "posting_date"
|
date_field = "posting_date"
|
||||||
elif self.meta.get_field("transaction_date"):
|
elif self.meta.get_field("transaction_date"):
|
||||||
@@ -1449,6 +1449,7 @@ def validate_and_delete_children(parent, data):
|
|||||||
for d in deleted_children:
|
for d in deleted_children:
|
||||||
update_bin_on_delete(d, parent.doctype)
|
update_bin_on_delete(d, parent.doctype)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||||
def check_doc_permissions(doc, perm_type='create'):
|
def check_doc_permissions(doc, perm_type='create'):
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
import json
|
||||||
import frappe, erpnext
|
|
||||||
from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
|
|
||||||
from frappe import _
|
|
||||||
import frappe.defaults
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from erpnext.accounts.utils import get_fiscal_year, check_if_stock_and_account_balance_synced
|
|
||||||
|
import frappe
|
||||||
|
import frappe.defaults
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import cint, cstr, flt, get_link_to_form, getdate
|
||||||
|
|
||||||
|
import erpnext
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
|
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
|
||||||
|
from erpnext.accounts.utils import check_if_stock_and_account_balance_synced, get_fiscal_year
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
|
||||||
from erpnext.stock import get_warehouse_account_map
|
from erpnext.stock import get_warehouse_account_map
|
||||||
|
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||||
|
|
||||||
|
|
||||||
class QualityInspectionRequiredError(frappe.ValidationError): pass
|
class QualityInspectionRequiredError(frappe.ValidationError): pass
|
||||||
class QualityInspectionRejectedError(frappe.ValidationError): pass
|
class QualityInspectionRejectedError(frappe.ValidationError): pass
|
||||||
@@ -189,7 +193,6 @@ class StockController(AccountsController):
|
|||||||
if hasattr(self, "items"):
|
if hasattr(self, "items"):
|
||||||
item_doclist = self.get("items")
|
item_doclist = self.get("items")
|
||||||
elif self.doctype == "Stock Reconciliation":
|
elif self.doctype == "Stock Reconciliation":
|
||||||
import json
|
|
||||||
item_doclist = []
|
item_doclist = []
|
||||||
data = json.loads(self.reconciliation_json)
|
data = json.loads(self.reconciliation_json)
|
||||||
for row in data[data.index(self.head_row)+1:]:
|
for row in data[data.index(self.head_row)+1:]:
|
||||||
@@ -319,7 +322,7 @@ class StockController(AccountsController):
|
|||||||
return serialized_items
|
return serialized_items
|
||||||
|
|
||||||
def validate_warehouse(self):
|
def validate_warehouse(self):
|
||||||
from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
|
from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company
|
||||||
|
|
||||||
warehouses = list(set([d.warehouse for d in
|
warehouses = list(set([d.warehouse for d in
|
||||||
self.get("items") if getattr(d, "warehouse", None)]))
|
self.get("items") if getattr(d, "warehouse", None)]))
|
||||||
@@ -498,6 +501,39 @@ class StockController(AccountsController):
|
|||||||
check_if_stock_and_account_balance_synced(self.posting_date,
|
check_if_stock_and_account_balance_synced(self.posting_date,
|
||||||
self.company, self.doctype, self.name)
|
self.company, self.doctype, self.name)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_quality_inspections(doctype, docname, items):
|
||||||
|
if isinstance(items, str):
|
||||||
|
items = json.loads(items)
|
||||||
|
|
||||||
|
inspections = []
|
||||||
|
for item in items:
|
||||||
|
if flt(item.get("sample_size")) > flt(item.get("qty")):
|
||||||
|
frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format(
|
||||||
|
item_name=item.get("item_name"),
|
||||||
|
sample_size=item.get("sample_size"),
|
||||||
|
accepted_quantity=item.get("qty")
|
||||||
|
))
|
||||||
|
|
||||||
|
quality_inspection = frappe.get_doc({
|
||||||
|
"doctype": "Quality Inspection",
|
||||||
|
"inspection_type": "Incoming",
|
||||||
|
"inspected_by": frappe.session.user,
|
||||||
|
"reference_type": doctype,
|
||||||
|
"reference_name": docname,
|
||||||
|
"item_code": item.get("item_code"),
|
||||||
|
"description": item.get("description"),
|
||||||
|
"sample_size": flt(item.get("sample_size")),
|
||||||
|
"item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None,
|
||||||
|
"batch_no": item.get("batch_no")
|
||||||
|
}).insert()
|
||||||
|
quality_inspection.save()
|
||||||
|
inspections.append(quality_inspection.name)
|
||||||
|
|
||||||
|
return inspections
|
||||||
|
|
||||||
|
|
||||||
def is_reposting_pending():
|
def is_reposting_pending():
|
||||||
return frappe.db.exists("Repost Item Valuation",
|
return frappe.db.exists("Repost Item Valuation",
|
||||||
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
||||||
|
|||||||
@@ -261,11 +261,19 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
|
if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var me = this;
|
|
||||||
var inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)
|
const me = this;
|
||||||
|
if (!this.frm.is_new() && this.frm.doc.docstatus === 0) {
|
||||||
|
this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
|
||||||
|
me.make_quality_inspection();
|
||||||
|
}, __("Create"));
|
||||||
|
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)
|
||||||
? "Incoming" : "Outgoing";
|
? "Incoming" : "Outgoing";
|
||||||
|
|
||||||
var quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
|
let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection");
|
||||||
quality_inspection_field.get_route_options_for_new_doc = function(row) {
|
quality_inspection_field.get_route_options_for_new_doc = function(row) {
|
||||||
if(me.frm.is_new()) return;
|
if(me.frm.is_new()) return;
|
||||||
return {
|
return {
|
||||||
@@ -280,7 +288,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.frm.set_query("quality_inspection", "items", function(doc, cdt, cdn) {
|
this.frm.set_query("quality_inspection", "items", function(doc, cdt, cdn) {
|
||||||
var d = locals[cdt][cdn];
|
let d = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
@@ -1949,6 +1957,130 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
make_quality_inspection: function () {
|
||||||
|
let data = [];
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
label: "Items",
|
||||||
|
fieldtype: "Table",
|
||||||
|
fieldname: "items",
|
||||||
|
cannot_add_rows: true,
|
||||||
|
in_place_edit: true,
|
||||||
|
data: data,
|
||||||
|
get_data: () => {
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldtype: "Data",
|
||||||
|
fieldname: "docname",
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Read Only",
|
||||||
|
fieldname: "item_code",
|
||||||
|
label: __("Item Code"),
|
||||||
|
in_list_view: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Read Only",
|
||||||
|
fieldname: "item_name",
|
||||||
|
label: __("Item Name"),
|
||||||
|
in_list_view: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Float",
|
||||||
|
fieldname: "qty",
|
||||||
|
label: __("Accepted Quantity"),
|
||||||
|
in_list_view: true,
|
||||||
|
read_only: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Float",
|
||||||
|
fieldname: "sample_size",
|
||||||
|
label: __("Sample Size"),
|
||||||
|
reqd: true,
|
||||||
|
in_list_view: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Data",
|
||||||
|
fieldname: "description",
|
||||||
|
label: __("Description"),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Data",
|
||||||
|
fieldname: "serial_no",
|
||||||
|
label: __("Serial No"),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Data",
|
||||||
|
fieldname: "batch_no",
|
||||||
|
label: __("Batch No"),
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const me = this;
|
||||||
|
const dialog = new frappe.ui.Dialog({
|
||||||
|
title: __("Select Items for Quality Inspection"),
|
||||||
|
fields: fields,
|
||||||
|
primary_action: function () {
|
||||||
|
const data = dialog.get_values();
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.controllers.stock_controller.make_quality_inspections",
|
||||||
|
args: {
|
||||||
|
doctype: me.frm.doc.doctype,
|
||||||
|
docname: me.frm.doc.name,
|
||||||
|
items: data.items
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
callback: function (r) {
|
||||||
|
if (r.message.length > 0) {
|
||||||
|
if (r.message.length === 1) {
|
||||||
|
frappe.set_route("Form", "Quality Inspection", r.message[0]);
|
||||||
|
} else {
|
||||||
|
frappe.route_options = {
|
||||||
|
"reference_type": me.frm.doc.doctype,
|
||||||
|
"reference_name": me.frm.doc.name
|
||||||
|
};
|
||||||
|
frappe.set_route("List", "Quality Inspection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
primary_action_label: __("Create")
|
||||||
|
});
|
||||||
|
|
||||||
|
this.frm.doc.items.forEach(item => {
|
||||||
|
if (!item.quality_inspection) {
|
||||||
|
let dialog_items = dialog.fields_dict.items;
|
||||||
|
dialog_items.df.data.push({
|
||||||
|
"docname": item.name,
|
||||||
|
"item_code": item.item_code,
|
||||||
|
"item_name": item.item_name,
|
||||||
|
"qty": item.qty,
|
||||||
|
"description": item.description,
|
||||||
|
"serial_no": item.serial_no,
|
||||||
|
"batch_no": item.batch_no
|
||||||
|
});
|
||||||
|
dialog_items.grid.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
data = dialog.fields_dict.items.df.data;
|
||||||
|
if (!data.length) {
|
||||||
|
frappe.msgprint(__("All items in this document already have a linked Quality Inspection."));
|
||||||
|
} else {
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
get_method_for_payment: function(){
|
get_method_for_payment: function(){
|
||||||
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
|
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
|
||||||
if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){
|
if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){
|
||||||
|
|||||||
@@ -1,29 +1,45 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
|
||||||
|
from erpnext.controllers.stock_controller import (
|
||||||
|
QualityInspectionNotSubmittedError,
|
||||||
|
QualityInspectionRejectedError,
|
||||||
|
QualityInspectionRequiredError,
|
||||||
|
make_quality_inspections,
|
||||||
|
)
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError
|
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('Quality Inspection')
|
# test_records = frappe.get_test_records('Quality Inspection')
|
||||||
|
|
||||||
|
|
||||||
class TestQualityInspection(unittest.TestCase):
|
class TestQualityInspection(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
create_item("_Test Item with QA")
|
create_item("_Test Item with QA")
|
||||||
frappe.db.set_value("Item", "_Test Item with QA", "inspection_required_before_delivery", 1)
|
frappe.db.set_value(
|
||||||
|
"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
|
||||||
|
)
|
||||||
|
|
||||||
def test_qa_for_delivery(self):
|
def test_qa_for_delivery(self):
|
||||||
make_stock_entry(item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100)
|
make_stock_entry(
|
||||||
|
item_code="_Test Item with QA",
|
||||||
|
target="_Test Warehouse - _TC",
|
||||||
|
qty=1,
|
||||||
|
basic_rate=100
|
||||||
|
)
|
||||||
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)
|
||||||
|
|
||||||
self.assertRaises(QualityInspectionRequiredError, dn.submit)
|
self.assertRaises(QualityInspectionRequiredError, dn.submit)
|
||||||
|
|
||||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
|
qa = create_quality_inspection(
|
||||||
|
reference_type="Delivery Note", reference_name=dn.name, status="Rejected"
|
||||||
|
)
|
||||||
dn.reload()
|
dn.reload()
|
||||||
self.assertRaises(QualityInspectionRejectedError, dn.submit)
|
self.assertRaises(QualityInspectionRejectedError, dn.submit)
|
||||||
|
|
||||||
@@ -38,7 +54,9 @@ class TestQualityInspection(unittest.TestCase):
|
|||||||
|
|
||||||
def test_qa_not_submit(self):
|
def test_qa_not_submit(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)
|
||||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True)
|
qa = create_quality_inspection(
|
||||||
|
reference_type="Delivery Note", reference_name=dn.name, do_not_submit=True
|
||||||
|
)
|
||||||
dn.items[0].quality_inspection = qa.name
|
dn.items[0].quality_inspection = qa.name
|
||||||
self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
|
self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
|
||||||
|
|
||||||
@@ -48,21 +66,28 @@ class TestQualityInspection(unittest.TestCase):
|
|||||||
def test_value_based_qi_readings(self):
|
def test_value_based_qi_readings(self):
|
||||||
# Test QI based on acceptance values (Non formula)
|
# Test QI based on acceptance values (Non formula)
|
||||||
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", # numeric reading
|
{
|
||||||
"min_value": 0.1,
|
"specification": "Iron Content", # numeric reading
|
||||||
"max_value": 0.9,
|
"min_value": 0.1,
|
||||||
"reading_1": "0.4"
|
"max_value": 0.9,
|
||||||
},
|
"reading_1": "0.4"
|
||||||
{
|
},
|
||||||
"specification": "Particle Inspection Needed", # non-numeric reading
|
{
|
||||||
"numeric": 0,
|
"specification": "Particle Inspection Needed", # non-numeric reading
|
||||||
"value": "Yes",
|
"numeric": 0,
|
||||||
"reading_value": "Yes"
|
"value": "Yes",
|
||||||
}]
|
"reading_value": "Yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
qa = create_quality_inspection(
|
||||||
|
reference_type="Delivery Note",
|
||||||
|
reference_name=dn.name,
|
||||||
|
readings=readings,
|
||||||
|
do_not_save=True
|
||||||
|
)
|
||||||
|
|
||||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
|
|
||||||
readings=readings, do_not_save=True)
|
|
||||||
qa.save()
|
qa.save()
|
||||||
|
|
||||||
# status must be auto set as per formula
|
# status must be auto set as per formula
|
||||||
@@ -74,36 +99,43 @@ class TestQualityInspection(unittest.TestCase):
|
|||||||
|
|
||||||
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", # numeric reading
|
{
|
||||||
"formula_based_criteria": 1,
|
"specification": "Iron Content", # numeric reading
|
||||||
"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
|
"formula_based_criteria": 1,
|
||||||
"reading_1": "0.4"
|
"acceptance_formula": "reading_1 > 0.35 and reading_1 < 0.50",
|
||||||
},
|
"reading_1": "0.4"
|
||||||
{
|
},
|
||||||
"specification": "Calcium Content", # numeric reading
|
{
|
||||||
"formula_based_criteria": 1,
|
"specification": "Calcium Content", # numeric reading
|
||||||
"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
|
"formula_based_criteria": 1,
|
||||||
"reading_1": "0.7"
|
"acceptance_formula": "reading_1 > 0.20 and reading_1 < 0.50",
|
||||||
},
|
"reading_1": "0.7"
|
||||||
{
|
},
|
||||||
"specification": "Mg Content", # numeric reading
|
{
|
||||||
"formula_based_criteria": 1,
|
"specification": "Mg Content", # numeric reading
|
||||||
"acceptance_formula": "mean < 0.9",
|
"formula_based_criteria": 1,
|
||||||
"reading_1": "0.5",
|
"acceptance_formula": "mean < 0.9",
|
||||||
"reading_2": "0.7",
|
"reading_1": "0.5",
|
||||||
"reading_3": "random text" # check if random string input causes issues
|
"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,
|
"specification": "Calcium Content", # non-numeric reading
|
||||||
"numeric": 0,
|
"formula_based_criteria": 1,
|
||||||
"acceptance_formula": "reading_value in ('Grade A', 'Grade B', 'Grade C')",
|
"numeric": 0,
|
||||||
"reading_value": "Grade B"
|
"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,
|
||||||
|
readings=readings,
|
||||||
|
do_not_save=True
|
||||||
|
)
|
||||||
|
|
||||||
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name,
|
|
||||||
readings=readings, do_not_save=True)
|
|
||||||
qa.save()
|
qa.save()
|
||||||
|
|
||||||
# status must be auto set as per formula
|
# status must be auto set as per formula
|
||||||
@@ -115,6 +147,19 @@ class TestQualityInspection(unittest.TestCase):
|
|||||||
qa.delete()
|
qa.delete()
|
||||||
dn.delete()
|
dn.delete()
|
||||||
|
|
||||||
|
def test_make_quality_inspections_from_linked_document(self):
|
||||||
|
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
|
||||||
|
for item in dn.items:
|
||||||
|
item.sample_size = item.qty
|
||||||
|
quality_inspections = make_quality_inspections(dn.doctype, dn.name, dn.items)
|
||||||
|
self.assertEqual(len(dn.items), len(quality_inspections))
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
for qi in quality_inspections:
|
||||||
|
frappe.delete_doc("Quality Inspection", qi)
|
||||||
|
dn.delete()
|
||||||
|
|
||||||
|
|
||||||
def create_quality_inspection(**args):
|
def create_quality_inspection(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
qa = frappe.new_doc("Quality Inspection")
|
qa = frappe.new_doc("Quality Inspection")
|
||||||
@@ -134,7 +179,7 @@ def create_quality_inspection(**args):
|
|||||||
readings = args.readings
|
readings = args.readings
|
||||||
|
|
||||||
if args.status == "Rejected":
|
if args.status == "Rejected":
|
||||||
readings["reading_1"] = "12" # status is auto set in child on save
|
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:
|
||||||
@@ -150,10 +195,11 @@ def create_quality_inspection(**args):
|
|||||||
|
|
||||||
return qa
|
return qa
|
||||||
|
|
||||||
|
|
||||||
def create_quality_inspection_parameter(parameter):
|
def create_quality_inspection_parameter(parameter):
|
||||||
if not frappe.db.exists("Quality Inspection Parameter", parameter):
|
if not frappe.db.exists("Quality Inspection Parameter", parameter):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Quality Inspection Parameter",
|
"doctype": "Quality Inspection Parameter",
|
||||||
"parameter": parameter,
|
"parameter": parameter,
|
||||||
"description": parameter
|
"description": parameter
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|||||||
@@ -115,6 +115,14 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!frm.is_new() && frm.doc.docstatus === 0) {
|
||||||
|
frm.add_custom_button(__("Quality Inspection(s)"), () => {
|
||||||
|
let transaction_controller = new erpnext.TransactionController({ frm: frm });
|
||||||
|
transaction_controller.make_quality_inspection();
|
||||||
|
}, __("Create"));
|
||||||
|
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||||
|
}
|
||||||
|
|
||||||
let quality_inspection_field = frm.get_docfield("items", "quality_inspection");
|
let quality_inspection_field = frm.get_docfield("items", "quality_inspection");
|
||||||
quality_inspection_field.get_route_options_for_new_doc = function(row) {
|
quality_inspection_field.get_route_options_for_new_doc = function(row) {
|
||||||
if (frm.is_new()) return;
|
if (frm.is_new()) return;
|
||||||
@@ -155,7 +163,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(!frm.doc.docstatus) {
|
if(!frm.doc.docstatus) {
|
||||||
frm.trigger('validate_purpose_consumption');
|
frm.trigger('validate_purpose_consumption');
|
||||||
frm.add_custom_button(__('Create Material Request'), function() {
|
frm.add_custom_button(__('Material Request'), function() {
|
||||||
frappe.model.with_doctype('Material Request', function() {
|
frappe.model.with_doctype('Material Request', function() {
|
||||||
var mr = frappe.model.get_new_doc('Material Request');
|
var mr = frappe.model.get_new_doc('Material Request');
|
||||||
var items = frm.get_field('items').grid.get_selected_children();
|
var items = frm.get_field('items').grid.get_selected_children();
|
||||||
@@ -178,7 +186,7 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
});
|
});
|
||||||
frappe.set_route('Form', 'Material Request', mr.name);
|
frappe.set_route('Form', 'Material Request', mr.name);
|
||||||
});
|
});
|
||||||
});
|
}, __("Create"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(frm.doc.items) {
|
if(frm.doc.items) {
|
||||||
|
|||||||
Reference in New Issue
Block a user