From 3f28fb19aa3f244981eda815b80e3a1fcf6974d5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 8 Oct 2020 14:57:27 +0530 Subject: [PATCH 01/18] chore: validate is_group of child parent account before auto creating an account --- erpnext/accounts/doctype/account/account.py | 11 +++++++++-- erpnext/accounts/doctype/account/test_account.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 164f120067..58674f4c1e 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -162,9 +162,16 @@ class Account(NestedSet): def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name): for company in descendants: + company_bold = frappe.bold(company) + parent_acc_name_bold = frappe.bold(parent_acc_name) if not parent_acc_name_map.get(company): - frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA") - .format(company, parent_acc_name)) + frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA") + .format(company_bold, parent_acc_name_bold), title=_("Account Not Found")) + if (frappe.db.get_value("Account", self.parent_account, "is_group") + and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")): + frappe.throw(_("While creating account for Child Company {0}, parent account {1} is found \ + as a ledger account.

Please convert the parent account in corresponding child company COA to a group account.") + .format(company_bold, parent_acc_name_bold), title=_("Invalid Parent Account")) filters = { "account_name": self.account_name, diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 89bb0184af..d16a8b2193 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -111,6 +111,17 @@ class TestAccount(unittest.TestCase): self.assertEqual(acc_tc_4, "Test Sync Account - _TC4") self.assertEqual(acc_tc_5, "Test Sync Account - _TC5") + def test_add_account_to_a_group(self): + frappe.db.set_value("Account", "Office Rent - _TC3", "is_group", 1) + + acc = frappe.new_doc("Account") + acc.account_name = "Test Group Account" + acc.parent_account = "Office Rent - _TC3" + acc.company = "_Test Company 3" + self.assertRaises(frappe.ValidationError, acc.insert) + + frappe.db.set_value("Account", "Office Rent - _TC3", "is_group", 0) + def _make_test_records(verbose): from frappe.test_runner import make_test_objects From 1dec68e8e4865403d2d646e14fbbbdb6bce5474c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 16 Oct 2020 10:47:29 +0530 Subject: [PATCH 02/18] fix: transalation syntax --- erpnext/accounts/doctype/account/account.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index f23107b073..2605bc155f 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -171,9 +171,10 @@ class Account(NestedSet): .format(company_bold, parent_acc_name_bold), title=_("Account Not Found")) if (frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")): - frappe.throw(_("While creating account for Child Company {0}, parent account {1} is found \ - as a ledger account.

Please convert the parent account in corresponding child company COA to a group account.") - .format(company_bold, parent_acc_name_bold), title=_("Invalid Parent Account")) + msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold) + msg += "

" + msg = _("Please convert the parent account in corresponding child company to a group account.") + frappe.throw(msg), title=_("Invalid Parent Account")) filters = { "account_name": self.account_name, From de505631a6fab5315b5d6a13ebd5a12a045f7ec1 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 16 Oct 2020 11:30:34 +0530 Subject: [PATCH 03/18] fix: codacy --- erpnext/accounts/doctype/account/account.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 2605bc155f..9de4649977 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -101,7 +101,7 @@ class Account(NestedSet): return if not frappe.db.get_value("Account", {'account_name': self.account_name, 'company': ancestors[0]}, 'name'): - frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) + frappe.throw(_("Please add the account to root level Company - {}").format(ancestors[0])) elif self.parent_account: descendants = get_descendants_of('Company', self.company) if not descendants: return @@ -169,12 +169,14 @@ class Account(NestedSet): if not parent_acc_name_map.get(company): frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA") .format(company_bold, parent_acc_name_bold), title=_("Account Not Found")) - if (frappe.db.get_value("Account", self.parent_account, "is_group") + + # validate if parent of child company account to be added is a group + if (frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")): msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold) msg += "

" msg = _("Please convert the parent account in corresponding child company to a group account.") - frappe.throw(msg), title=_("Invalid Parent Account")) + frappe.throw(msg, title=_("Invalid Parent Account")) filters = { "account_name": self.account_name, @@ -317,8 +319,9 @@ def update_account_number(name, account_name, account_number=None, from_descenda allow_child_account_creation = _("Allow Account Creation Against Child Company") message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor)) - message += "
" + _("Renaming it is only allowed via parent company {0}, \ - to avoid mismatch.").format(frappe.bold(ancestor)) + "

" + message += "
" + message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(frappe.bold(ancestor)) + message += "

" message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company)) frappe.throw(message, title=_("Rename Not Allowed")) From 1cfa61628d71d50aa7d210a2293923f9a16c36c3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 14 Sep 2020 21:12:16 +0530 Subject: [PATCH 04/18] fix: incorrect calculation for consumed qty for subcontract item (#23257) * fix: incorrect calculation for consumed qty for subcontract item * added test case --- erpnext/controllers/buying_controller.py | 23 +++-- .../purchase_receipt/test_purchase_receipt.py | 87 +++++++++++++++++++ .../stock/doctype/stock_entry/stock_entry.py | 29 +++++-- .../stock_entry_detail.json | 6 +- 4 files changed, 130 insertions(+), 15 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index e05c70e41c..7fab538990 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -331,7 +331,7 @@ class BuyingController(StockController): if raw_material.batch_nos: batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code, - qty, transferred_batch_qty_map, backflushed_batch_qty_map) + qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order) for batch_data in batches_qty: qty = batch_data['qty'] raw_material.batch_no = batch_data['batch'] @@ -343,6 +343,9 @@ class BuyingController(StockController): rm = self.append('supplied_items', {}) rm.update(raw_material_data) + if not rm.main_item_code: + rm.main_item_code = fg_item_doc.item_code + rm.required_qty = qty rm.consumed_qty = qty @@ -873,7 +876,7 @@ def get_subcontracted_raw_materials_from_se(purchase_order, fg_item): AND se.purpose='Send to Subcontractor' AND se.purchase_order = %s AND IFNULL(sed.t_warehouse, '') != '' - AND sed.subcontracted_item = %s + AND IFNULL(sed.subcontracted_item, '') in ('', %s) GROUP BY sed.item_code, sed.subcontracted_item """ raw_materials = frappe.db.multisql({ @@ -1004,14 +1007,15 @@ def get_transferred_batch_qty_map(purchase_order, fg_item): SELECT sed.batch_no, SUM(sed.qty) AS qty, - sed.item_code + sed.item_code, + sed.subcontracted_item FROM `tabStock Entry` se,`tabStock Entry Detail` sed WHERE se.name = sed.parent AND se.docstatus=1 AND se.purpose='Send to Subcontractor' AND se.purchase_order = %s - AND sed.subcontracted_item = %s + AND ifnull(sed.subcontracted_item, '') in ('', %s) AND sed.batch_no IS NOT NULL GROUP BY sed.batch_no, @@ -1019,8 +1023,10 @@ def get_transferred_batch_qty_map(purchase_order, fg_item): """, (purchase_order, fg_item), as_dict=1) for batch_data in transferred_batches: - transferred_batch_qty_map.setdefault((batch_data.item_code, fg_item), {}) - transferred_batch_qty_map[(batch_data.item_code, fg_item)][batch_data.batch_no] = batch_data.qty + key = ((batch_data.item_code, fg_item) + if batch_data.subcontracted_item else (batch_data.item_code, purchase_order)) + transferred_batch_qty_map.setdefault(key, {}) + transferred_batch_qty_map[key][batch_data.batch_no] = batch_data.qty return transferred_batch_qty_map @@ -1057,9 +1063,12 @@ def get_backflushed_batch_qty_map(purchase_order, fg_item): return backflushed_batch_qty_map -def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batch_qty_map): +def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batch_qty_map, po): # Returns available batches to be backflushed based on requirements transferred_batches = transferred_batch_qty_map.get((item_code, fg_item), {}) + if not transferred_batches: + transferred_batches = transferred_batch_qty_map.get((item_code, po), {}) + backflushed_batches = backflushed_batch_qty_map.get((item_code, fg_item), {}) available_batches = [] diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 12c89066a7..c5f3034ccb 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -717,6 +717,66 @@ class TestPurchaseReceipt(unittest.TestCase): # Allowed to submit for other company's PR self.assertEqual(pr.docstatus, 1) + def test_subcontracted_pr_for_multi_transfer_batches(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry, make_purchase_receipt + from erpnext.buying.doctype.purchase_order.test_purchase_order import (update_backflush_based_on, + create_purchase_order) + + update_backflush_based_on("Material Transferred for Subcontract") + item_code = "_Test Subcontracted FG Item 3" + + make_item('Sub Contracted Raw Material 3', { + 'is_stock_item': 1, + 'is_sub_contracted_item': 1, + 'has_batch_no': 1, + 'create_new_batch': 1 + }) + + create_subcontracted_item(item_code=item_code, has_batch_no=1, + raw_materials=["Sub Contracted Raw Material 3"]) + + order_qty = 500 + po = create_purchase_order(item_code=item_code, qty=order_qty, + is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") + + ste1=make_stock_entry(target="_Test Warehouse - _TC", + item_code = "Sub Contracted Raw Material 3", qty=300, basic_rate=100) + ste2=make_stock_entry(target="_Test Warehouse - _TC", + item_code = "Sub Contracted Raw Material 3", qty=200, basic_rate=100) + + transferred_batch = { + ste1.items[0].batch_no : 300, + ste2.items[0].batch_no : 200 + } + + rm_items = [ + {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 3","item_name":"_Test Item", + "qty":300,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}, + {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 3","item_name":"_Test Item", + "qty":200,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"} + ] + + rm_item_string = json.dumps(rm_items) + se = frappe.get_doc(make_rm_stock_entry(po.name, rm_item_string)) + self.assertEqual(len(se.items), 2) + se.items[0].batch_no = ste1.items[0].batch_no + se.items[1].batch_no = ste2.items[0].batch_no + se.submit() + + supplied_qty = frappe.db.get_value("Purchase Order Item Supplied", + {"parent": po.name, "rm_item_code": "Sub Contracted Raw Material 3"}, "supplied_qty") + + self.assertEqual(supplied_qty, 500.00) + + pr = make_purchase_receipt(po.name) + pr.save() + self.assertEqual(len(pr.supplied_items), 2) + + for row in pr.supplied_items: + self.assertEqual(transferred_batch.get(row.batch_no), row.consumed_qty) + + update_backflush_based_on("BOM") def get_sl_entries(voucher_type, voucher_no): return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference @@ -858,6 +918,33 @@ def make_purchase_receipt(**args): pr.submit() return pr +def create_subcontracted_item(**args): + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + args = frappe._dict(args) + + if not frappe.db.exists('Item', args.item_code): + make_item(args.item_code, { + 'is_stock_item': 1, + 'is_sub_contracted_item': 1, + 'has_batch_no': args.get("has_batch_no") or 0 + }) + + if not args.raw_materials: + if not frappe.db.exists('Item', "Test Extra Item 1"): + make_item("Test Extra Item 1", { + 'is_stock_item': 1, + }) + + if not frappe.db.exists('Item', "Test Extra Item 2"): + make_item("Test Extra Item 2", { + 'is_stock_item': 1, + }) + + args.raw_materials = ['_Test FG Item', 'Test Extra Item 1'] + + if not frappe.db.get_value('BOM', {'item': args.item_code}, 'name'): + make_bom(item = args.item_code, raw_materials = args.get("raw_materials")) test_dependencies = ["BOM", "Item Price", "Location"] test_records = frappe.get_test_records('Purchase Receipt') diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 9ad3694c0a..54e70d442d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -571,8 +571,9 @@ class StockEntry(StockController): qty_allowance = flt(frappe.db.get_single_value("Buying Settings", "over_transfer_allowance")) - if (self.purpose == "Send to Subcontractor" and self.purchase_order and - backflush_raw_materials_based_on == 'BOM'): + if not (self.purpose == "Send to Subcontractor" and self.purchase_order): return + + if (backflush_raw_materials_based_on == 'BOM'): purchase_order = frappe.get_doc("Purchase Order", self.purchase_order) for se_item in self.items: item_code = se_item.original_item or se_item.item_code @@ -609,6 +610,11 @@ class StockEntry(StockController): if flt(total_supplied, precision) > flt(total_allowed, precision): frappe.throw(_("Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3}") .format(se_item.idx, se_item.item_code, total_allowed, self.purchase_order)) + elif backflush_raw_materials_based_on == "Material Transferred for Subcontract": + for row in self.items: + if not row.subcontracted_item: + frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}") + .format(row.idx, frappe.bold(row.item_code))) def validate_bom(self): for d in self.get('items'): @@ -817,6 +823,13 @@ class StockEntry(StockController): ret.get('has_batch_no') and not args.get('batch_no')): args.batch_no = get_batch_no(args['item_code'], args['s_warehouse'], args['qty']) + if self.purpose == "Send to Subcontractor" and self.get("purchase_order") and args.get('item_code'): + subcontract_items = frappe.get_all("Purchase Order Item Supplied", + {"parent": self.purchase_order, "rm_item_code": args.get('item_code')}, "main_item_code") + + if subcontract_items and len(subcontract_items) == 1: + ret["subcontracted_item"] = subcontract_items[0].main_item_code + return ret def set_items_for_stock_in(self): @@ -1288,9 +1301,15 @@ class StockEntry(StockController): #Update Supplied Qty in PO Supplied Items frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos - SET pos.supplied_qty = (SELECT ifnull(sum(transfer_qty), 0) FROM `tabStock Entry Detail` sed - WHERE pos.name = sed.po_detail and sed.docstatus = 1) - WHERE pos.docstatus = 1 and pos.parent = %s""", self.purchase_order) + SET + pos.supplied_qty = IFNULL((SELECT ifnull(sum(transfer_qty), 0) + FROM + `tabStock Entry Detail` sed, `tabStock Entry` se + WHERE + (pos.name = sed.po_detail OR sed.subcontracted_item = pos.main_item_code) + AND sed.docstatus = 1 AND se.name = sed.parent and se.purchase_order = %(po)s + ), 0) + WHERE pos.docstatus = 1 and pos.parent = %(po)s""", {"po": self.purchase_order}) #Update reserved sub contracted quantity in bin based on Supplied Item Details and for d in self.get("items"): diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index ae2e3a134f..79e8f9af8f 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "hash", "creation": "2013-03-29 18:22:12", "doctype": "DocType", @@ -16,6 +15,7 @@ "item_code", "col_break2", "item_name", + "subcontracted_item", "section_break_8", "description", "column_break_10", @@ -57,7 +57,6 @@ "material_request", "material_request_item", "original_item", - "subcontracted_item", "reference_section", "against_stock_entry", "ste_detail", @@ -415,6 +414,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.purpose == 'Send to Subcontractor'", "fieldname": "subcontracted_item", "fieldtype": "Link", "label": "Subcontracted Item", @@ -504,7 +504,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-09-22 17:55:03.384138", + "modified": "2020-09-23 17:55:03.384138", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 4c688b71f36d9306f4878a028e46a5235de25e4d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 15 Sep 2020 19:40:37 +0530 Subject: [PATCH 05/18] fix: consumed qty logic for subcontracted raw materials (#23314) * fix: consumed qty logic for subcontracted raw materials * added test case * fix: sales order workflow test case --- .../purchase_order/test_purchase_order.py | 110 +++++++++++++++--- erpnext/controllers/buying_controller.py | 62 +++++----- .../doctype/sales_order/test_sales_order.py | 1 + erpnext/stock/doctype/batch/test_batch.py | 15 +++ .../purchase_receipt/test_purchase_receipt.py | 2 +- erpnext/stock/doctype/serial_no/serial_no.py | 3 + 6 files changed, 147 insertions(+), 46 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 0cf3d24478..c85d0156b7 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -19,6 +19,8 @@ from erpnext.controllers.accounts_controller import update_child_qty_rate from erpnext.controllers.status_updater import OverAllowanceError from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order +from erpnext.stock.doctype.batch.test_batch import make_new_batch +from erpnext.controllers.buying_controller import get_backflushed_subcontracted_raw_materials class TestPurchaseOrder(unittest.TestCase): def test_make_purchase_receipt(self): @@ -686,7 +688,7 @@ class TestPurchaseOrder(unittest.TestCase): def test_exploded_items_in_subcontracted(self): item_code = "_Test Subcontracted FG Item 1" - make_subcontracted_item(item_code) + make_subcontracted_item(item_code=item_code) po = create_purchase_order(item_code=item_code, qty=1, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=1) @@ -708,7 +710,7 @@ class TestPurchaseOrder(unittest.TestCase): def test_backflush_based_on_stock_entry(self): item_code = "_Test Subcontracted FG Item 1" - make_subcontracted_item(item_code) + make_subcontracted_item(item_code=item_code) make_item('Sub Contracted Raw Material 1', { 'is_stock_item': 1, 'is_sub_contracted_item': 1 @@ -767,6 +769,76 @@ class TestPurchaseOrder(unittest.TestCase): update_backflush_based_on("BOM") + def test_backflushed_based_on_for_multiple_batches(self): + item_code = "_Test Subcontracted FG Item 2" + make_item('Sub Contracted Raw Material 2', { + 'is_stock_item': 1, + 'is_sub_contracted_item': 1 + }) + + make_subcontracted_item(item_code=item_code, has_batch_no=1, create_new_batch=1, + raw_materials=["Sub Contracted Raw Material 2"]) + + update_backflush_based_on("Material Transferred for Subcontract") + + order_qty = 500 + po = create_purchase_order(item_code=item_code, qty=order_qty, + is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") + + make_stock_entry(target="_Test Warehouse - _TC", + item_code = "Sub Contracted Raw Material 2", qty=552, basic_rate=100) + + rm_items = [ + {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 2","item_name":"_Test Item", + "qty":552,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}] + + rm_item_string = json.dumps(rm_items) + se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) + se.submit() + + for batch in ["ABCD1", "ABCD2", "ABCD3", "ABCD4"]: + make_new_batch(batch_id=batch, item_code=item_code) + + pr = make_purchase_receipt(po.name) + + # partial receipt + pr.get('items')[0].qty = 30 + pr.get('items')[0].batch_no = "ABCD1" + + purchase_order = po.name + purchase_order_item = po.items[0].name + + for batch_no, qty in {"ABCD2": 60, "ABCD3": 70, "ABCD4":40}.items(): + pr.append("items", { + "item_code": pr.get('items')[0].item_code, + "item_name": pr.get('items')[0].item_name, + "uom": pr.get('items')[0].uom, + "stock_uom": pr.get('items')[0].stock_uom, + "warehouse": pr.get('items')[0].warehouse, + "conversion_factor": pr.get('items')[0].conversion_factor, + "cost_center": pr.get('items')[0].cost_center, + "rate": pr.get('items')[0].rate, + "qty": qty, + "batch_no": batch_no, + "purchase_order": purchase_order, + "purchase_order_item": purchase_order_item + }) + + pr.submit() + + pr1 = make_purchase_receipt(po.name) + pr1.get('items')[0].qty = 300 + pr1.get('items')[0].batch_no = "ABCD1" + pr1.save() + + pr_key = ("Sub Contracted Raw Material 2", po.name) + consumed_qty = get_backflushed_subcontracted_raw_materials([po.name]).get(pr_key) + + self.assertTrue(pr1.supplied_items[0].consumed_qty > 0) + self.assertTrue(pr1.supplied_items[0].consumed_qty, flt(552.0) - flt(consumed_qty)) + + update_backflush_based_on("BOM") + def test_advance_payment_entry_unlink_against_purchase_order(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry frappe.db.set_value("Accounts Settings", "Accounts Settings", @@ -839,27 +911,33 @@ def make_pr_against_po(po, received_qty=0): pr.submit() return pr -def make_subcontracted_item(item_code): +def make_subcontracted_item(**args): from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom - if not frappe.db.exists('Item', item_code): - make_item(item_code, { + args = frappe._dict(args) + + if not frappe.db.exists('Item', args.item_code): + make_item(args.item_code, { 'is_stock_item': 1, - 'is_sub_contracted_item': 1 + 'is_sub_contracted_item': 1, + 'has_batch_no': args.get("has_batch_no") or 0 }) - if not frappe.db.exists('Item', "Test Extra Item 1"): - make_item("Test Extra Item 1", { - 'is_stock_item': 1, - }) + if not args.raw_materials: + if not frappe.db.exists('Item', "Test Extra Item 1"): + make_item("Test Extra Item 1", { + 'is_stock_item': 1, + }) - if not frappe.db.exists('Item', "Test Extra Item 2"): - make_item("Test Extra Item 2", { - 'is_stock_item': 1, - }) + if not frappe.db.exists('Item', "Test Extra Item 2"): + make_item("Test Extra Item 2", { + 'is_stock_item': 1, + }) - if not frappe.db.get_value('BOM', {'item': item_code}, 'name'): - make_bom(item = item_code, raw_materials = ['_Test FG Item', 'Test Extra Item 1']) + args.raw_materials = ['_Test FG Item', 'Test Extra Item 1'] + + if not frappe.db.get_value('BOM', {'item': args.item_code}, 'name'): + make_bom(item = args.item_code, raw_materials = args.get("raw_materials")) def update_backflush_based_on(based_on): doc = frappe.get_doc('Buying Settings') diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 7fab538990..c3a2560a8c 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _, msgprint from frappe.utils import flt,cint, cstr, getdate - +from six import iteritems from erpnext.accounts.party import get_party_details from erpnext.stock.get_item_details import get_conversion_factor from erpnext.buying.utils import validate_for_items, update_last_purchase_rate @@ -301,7 +301,7 @@ class BuyingController(StockController): backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code) for raw_material in transferred_raw_materials + non_stock_items: - rm_item_key = '{}{}'.format(raw_material.rm_item_code, item.purchase_order) + rm_item_key = (raw_material.rm_item_code, item.purchase_order) raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {}) consumed_qty = raw_material_data.get('qty', 0) @@ -346,6 +346,7 @@ class BuyingController(StockController): if not rm.main_item_code: rm.main_item_code = fg_item_doc.item_code + rm.reference_name = fg_item_doc.name rm.required_qty = qty rm.consumed_qty = qty @@ -893,39 +894,42 @@ def get_subcontracted_raw_materials_from_se(purchase_order, fg_item): return raw_materials def get_backflushed_subcontracted_raw_materials(purchase_orders): - common_query = """ - SELECT - CONCAT(prsi.rm_item_code, pri.purchase_order) AS item_key, - SUM(prsi.consumed_qty) AS qty, - {serial_no_concat_syntax} AS serial_nos, - {batch_no_concat_syntax} AS batch_nos - FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` prsi - WHERE - pr.name = pri.parent - AND pr.name = prsi.parent - AND pri.purchase_order IN %s - AND pri.item_code = prsi.main_item_code - AND pr.docstatus = 1 - GROUP BY prsi.rm_item_code, pri.purchase_order - """ + purchase_receipts = frappe.get_all("Purchase Receipt Item", + fields = ["purchase_order", "item_code", "name", "parent"], + filters={"docstatus": 1, "purchase_order": ("in", list(purchase_orders))}) - backflushed_raw_materials = frappe.db.multisql({ - 'mariadb': common_query.format( - serial_no_concat_syntax="GROUP_CONCAT(prsi.serial_no)", - batch_no_concat_syntax="GROUP_CONCAT(prsi.batch_no)" - ), - 'postgres': common_query.format( - serial_no_concat_syntax="STRING_AGG(prsi.serial_no, ',')", - batch_no_concat_syntax="STRING_AGG(prsi.batch_no, ',')" - ) - }, (purchase_orders, ), as_dict=1) + distinct_purchase_receipts = {} + for pr in purchase_receipts: + key = (pr.purchase_order, pr.item_code, pr.parent) + distinct_purchase_receipts.setdefault(key, []).append(pr.name) backflushed_raw_materials_map = frappe._dict() - for item in backflushed_raw_materials: - backflushed_raw_materials_map.setdefault(item.item_key, item) + for args, references in iteritems(distinct_purchase_receipts): + purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references) + + for data in purchase_receipt_supplied_items: + pr_key = (data.rm_item_code, args[0]) + if pr_key not in backflushed_raw_materials_map: + backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({ + "qty": 0.0, + "serial_no": [], + "batch_no": [] + })) + + row = backflushed_raw_materials_map.get(pr_key) + row.qty += data.consumed_qty + + for field in ["serial_no", "batch_no"]: + if data.get(field): + row[field].append(data.get(field)) return backflushed_raw_materials_map +def get_supplied_items(item_code, purchase_receipt, references): + return frappe.get_all("Purchase Receipt Item Supplied", + fields=["rm_item_code", "consumed_qty", "serial_no", "batch_no"], + filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)}) + def get_asset_item_details(asset_items): asset_items_data = {} for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"], diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 9e25ed0c99..a33d401b57 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -441,6 +441,7 @@ class TestSalesOrder(unittest.TestCase): def test_update_child_qty_rate_with_workflow(self): from frappe.model.workflow import apply_workflow + frappe.set_user("Administrator") workflow = make_sales_order_workflow() so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1) apply_workflow(so, 'Approve') diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 1fce5046f9..c2a3d3c151 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -256,3 +256,18 @@ class TestBatch(unittest.TestCase): batch.insert() return batch + +def make_new_batch(**args): + args = frappe._dict(args) + + try: + batch = frappe.get_doc({ + "doctype": "Batch", + "batch_id": args.batch_id, + "item": args.item_code, + }).insert() + + except frappe.DuplicateEntryError: + batch = frappe.get_doc("Batch", args.batch_id) + + return batch \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c5f3034ccb..7161f4be43 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -174,7 +174,7 @@ class TestPurchaseReceipt(unittest.TestCase): update_backflush_based_on("Material Transferred for Subcontract") item_code = "_Test Subcontracted FG Item 1" - make_subcontracted_item(item_code) + make_subcontracted_item(item_code=item_code) po = create_purchase_order(item_code=item_code, qty=1, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index dec4fe2da8..6b33502e8d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -449,6 +449,9 @@ def get_item_details(item_code): from tabItem where name=%s""", item_code, as_dict=True)[0] def get_serial_nos(serial_no): + if isinstance(serial_no, list): + return serial_no + return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n') if s.strip()] From 538d62b43d2e239be33df63091b2c5ad6a4e26b2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 22 Sep 2020 10:36:52 +0530 Subject: [PATCH 06/18] fix: incorrect consumed qty if raw material with batch --- erpnext/controllers/buying_controller.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index c3a2560a8c..55b4319379 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -298,7 +298,7 @@ class BuyingController(StockController): title=_("Limit Crossed")) transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code) - backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code) + # backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code) for raw_material in transferred_raw_materials + non_stock_items: rm_item_key = (raw_material.rm_item_code, item.purchase_order) @@ -330,6 +330,8 @@ class BuyingController(StockController): set_serial_nos(raw_material, consumed_serial_nos, qty) if raw_material.batch_nos: + backflushed_batch_qty_map = raw_material_data.get('consumed_batch', {}) + batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code, qty, transferred_batch_qty_map, backflushed_batch_qty_map, item.purchase_order) for batch_data in batches_qty: @@ -913,7 +915,8 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders): backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({ "qty": 0.0, "serial_no": [], - "batch_no": [] + "batch_no": [], + "consumed_batch": {} })) row = backflushed_raw_materials_map.get(pr_key) @@ -923,6 +926,12 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders): if data.get(field): row[field].append(data.get(field)) + if data.get("batch_no"): + if data.get("batch_no") in row.consumed_batch: + row.consumed_batch[data.get("batch_no")] += data.consumed_qty + else: + row.consumed_batch[data.get("batch_no")] = data.consumed_qty + return backflushed_raw_materials_map def get_supplied_items(item_code, purchase_receipt, references): @@ -1067,14 +1076,12 @@ def get_backflushed_batch_qty_map(purchase_order, fg_item): return backflushed_batch_qty_map -def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batch_qty_map, po): +def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batches, po): # Returns available batches to be backflushed based on requirements transferred_batches = transferred_batch_qty_map.get((item_code, fg_item), {}) if not transferred_batches: transferred_batches = transferred_batch_qty_map.get((item_code, po), {}) - backflushed_batches = backflushed_batch_qty_map.get((item_code, fg_item), {}) - available_batches = [] for (batch, transferred_qty) in transferred_batches.items(): From 9caac6976c04c84ea58b06da747d09579b80d625 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 13 Oct 2020 14:34:12 +0530 Subject: [PATCH 07/18] fix: incorrect supplied qty error --- .../purchase_order/test_purchase_order.py | 53 +++++++++++++++++++ .../purchase_receipt/test_purchase_receipt.py | 4 +- .../stock/doctype/stock_entry/stock_entry.py | 5 +- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index c85d0156b7..0181ae78b4 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -839,6 +839,59 @@ class TestPurchaseOrder(unittest.TestCase): update_backflush_based_on("BOM") + def test_supplied_qty_against_subcontracted_po(self): + item_code = "_Test Subcontracted FG Item 5" + make_item('Sub Contracted Raw Material 4', { + 'is_stock_item': 1, + 'is_sub_contracted_item': 1 + }) + + make_subcontracted_item(item_code=item_code, raw_materials=["Sub Contracted Raw Material 4"]) + + update_backflush_based_on("Material Transferred for Subcontract") + + order_qty = 250 + po = create_purchase_order(item_code=item_code, qty=order_qty, + is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", do_not_save=True) + + # Add same subcontracted items multiple times + po.append("items", { + "item_code": item_code, + "qty": order_qty, + "schedule_date": add_days(nowdate(), 1), + "warehouse": "_Test Warehouse - _TC" + }) + + po.set_missing_values() + po.submit() + + # Material receipt entry for the raw materials which will be send to supplier + make_stock_entry(target="_Test Warehouse - _TC", + item_code = "Sub Contracted Raw Material 4", qty=500, basic_rate=100) + + rm_items = [ + { + "item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item", + "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[0].name + }, + { + "item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item", + "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[1].name + }, + ] + + # Raw Materials transfer entry from stores to supplier's warehouse + rm_item_string = json.dumps(rm_items) + se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) + se.submit() + + po_doc = frappe.get_doc("Purchase Order", po.name) + for row in po_doc.supplied_items: + # Valid that whether transferred quantity is matching with supplied qty or not in the purchase order + self.assertEqual(row.supplied_qty, 250.0) + + update_backflush_based_on("BOM") + def test_advance_payment_entry_unlink_against_purchase_order(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry frappe.db.set_value("Accounts Settings", "Accounts Settings", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 7161f4be43..a1e01bf7ce 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -752,9 +752,9 @@ class TestPurchaseReceipt(unittest.TestCase): rm_items = [ {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 3","item_name":"_Test Item", - "qty":300,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}, + "qty":300,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[0].name}, {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 3","item_name":"_Test Item", - "qty":200,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"} + "qty":200,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[0].name} ] rm_item_string = json.dumps(rm_items) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 54e70d442d..4583c5174e 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1306,8 +1306,9 @@ class StockEntry(StockController): FROM `tabStock Entry Detail` sed, `tabStock Entry` se WHERE - (pos.name = sed.po_detail OR sed.subcontracted_item = pos.main_item_code) - AND sed.docstatus = 1 AND se.name = sed.parent and se.purchase_order = %(po)s + pos.name = sed.po_detail AND pos.rm_item_code = sed.item_code + AND pos.parent = se.purchase_order AND sed.docstatus = 1 + AND se.name = sed.parent and se.purchase_order = %(po)s ), 0) WHERE pos.docstatus = 1 and pos.parent = %(po)s""", {"po": self.purchase_order}) From 103ecec9d4d82bd324b73f6ed3f7eb02d3f409be Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 26 Oct 2020 11:55:08 +0530 Subject: [PATCH 08/18] fixed translation syntax --- erpnext/controllers/buying_controller.py | 8 ++++---- erpnext/stock/doctype/serial_no/serial_no.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 55b4319379..f376836f7b 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -112,8 +112,8 @@ class BuyingController(StockController): "docstatus": 1 })] if self.is_return and len(not_cancelled_asset): - frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.".format(self.return_against)), - title=_("Not Allowed")) + frappe.throw(_("{} has submitted assets linked to it. You need to cancel the assets to create purchase return.") + .format(self.return_against), title=_("Not Allowed")) def get_asset_items(self): if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: @@ -798,8 +798,8 @@ class BuyingController(StockController): asset.set(field, None) asset.supplier = None if asset.docstatus == 1 and delete_asset: - frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\ - Please cancel the it to continue.').format(frappe.utils.get_link_to_form('Asset', asset.name))) + frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}. Please cancel it to continue.') + .format(frappe.utils.get_link_to_form('Asset', asset.name))) asset.flags.ignore_validate_update_after_submit = True asset.flags.ignore_mandatory = True diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6b33502e8d..295149e238 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -343,10 +343,11 @@ def validate_material_transfer_entry(sle_doc): def validate_so_serial_no(sr, sales_order): if not sr.sales_order or sr.sales_order!= sales_order: - msg = _("Sales Order {0} has reservation for item {1}") - msg += _(", you can only deliver reserved {1} against {0}.") - msg += _(" Serial No {2} cannot be delivered") - frappe.throw(msg.format(sales_order, sr.item_code, sr.name)) + msg = (_("Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}.") + .format(sales_order, sr.item_code)) + + frappe.throw(_("""{0} Serial No {1} cannot be delivered""") + .format(msg, sr.name)) def has_duplicate_serial_no(sn, sle): if (sn.warehouse and not sle.skip_serial_no_validaiton @@ -575,8 +576,8 @@ def get_pos_reserved_serial_nos(filters): pos_transacted_sr_nos = frappe.db.sql("""select item.serial_no as serial_no from `tabPOS Invoice` p, `tabPOS Invoice Item` item - where p.name = item.parent - and p.consolidated_invoice is NULL + where p.name = item.parent + and p.consolidated_invoice is NULL and p.docstatus = 1 and item.docstatus = 1 and item.item_code = %(item_code)s @@ -608,7 +609,7 @@ def fetch_serial_numbers(filters, qty, do_not_include=[]): SELECT sr.name FROM `tabSerial No` sr {batch_join_selection} WHERE sr.name not in ({excluded_sr_nos}) AND - sr.item_code = %(item_code)s AND + sr.item_code = %(item_code)s AND sr.warehouse = %(warehouse)s AND ifnull(sr.sales_invoice,'') = '' AND ifnull(sr.delivery_document_no, '') = '' @@ -623,5 +624,5 @@ def fetch_serial_numbers(filters, qty, do_not_include=[]): batch_join_selection=batch_join_selection, batch_no_condition=batch_no_condition ), filters, as_dict=1) - + return serial_numbers \ No newline at end of file From ea49e30bfdda95c48c2a6407906c9836269cc70c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 26 Oct 2020 15:30:52 +0530 Subject: [PATCH 09/18] fix(cleanup): cleaned up forms --- .../v13_0/rename_issue_doctype_fields.py | 2 +- .../desk_page/quality/quality.json | 27 +++- .../doctype/non_conformance/__init__.py | 0 .../non_conformance/non_conformance.js | 8 ++ .../non_conformance/non_conformance.json | 118 ++++++++++++++++++ .../non_conformance/non_conformance.py | 10 ++ .../non_conformance/test_non_conformance.py | 10 ++ .../doctype/quality_action/quality_action.js | 27 ---- .../quality_action/quality_action.json | 58 +++++---- .../doctype/quality_action/quality_action.py | 3 +- .../quality_action_resolution.json | 37 ++++-- .../quality_feedback/quality_feedback.json | 4 +- .../quality_feedback_template.json | 7 +- .../doctype/quality_goal/quality_goal.json | 13 +- .../quality_goal/quality_goal_dashboard.py | 12 -- .../quality_meeting/quality_meeting.json | 12 +- .../quality_procedure/quality_procedure.json | 63 ++++++++-- .../quality_procedure_dashboard.py | 20 --- .../quality_procedure_process.json | 6 +- .../doctype/quality_review/quality_review.js | 3 - .../quality_review/quality_review.json | 20 ++- .../doctype/quality_review/quality_review.py | 4 +- .../quality_review_objective.json | 17 ++- .../quality_inspection.json | 3 +- 24 files changed, 345 insertions(+), 139 deletions(-) create mode 100644 erpnext/quality_management/doctype/non_conformance/__init__.py create mode 100644 erpnext/quality_management/doctype/non_conformance/non_conformance.js create mode 100644 erpnext/quality_management/doctype/non_conformance/non_conformance.json create mode 100644 erpnext/quality_management/doctype/non_conformance/non_conformance.py create mode 100644 erpnext/quality_management/doctype/non_conformance/test_non_conformance.py delete mode 100644 erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py delete mode 100644 erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py index 5bd6596579..96a63623c0 100644 --- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py +++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py @@ -53,7 +53,7 @@ def execute(): # renamed reports from "Minutes to First Response for Issues" to "First Response Time for Issues". Same for Opportunity for report in ['Minutes to First Response for Issues', 'Minutes to First Response for Opportunity']: if frappe.db.exists('Report', report): - frappe.delete_doc('Report', report) + frappe.delete_doc('Report', report, ignore_permissions=True) def convert_to_seconds(value, unit): diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index 5ee70008dd..ffa35dfd68 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -18,7 +18,7 @@ { "hidden": 0, "label": "Review and Action", - "links": "[\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"description\": \"Non Conformance\",\n \"label\": \"Non Conformance\",\n \"name\": \"Non Conformance\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", @@ -29,11 +29,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Quality", - "modified": "2020-04-01 11:28:51.095012", + "modified": "2020-10-26 15:24:58.058088", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", @@ -55,6 +55,27 @@ "label": "Quality Inspection", "link_to": "Quality Inspection", "type": "DocType" + }, + { + "doc_view": "", + "label": "Quality Review", + "link_to": "Quality Review", + "type": "DocType" + }, + { + "color": "#ff8989", + "doc_view": "", + "format": "{} Open", + "label": "Quality Action", + "link_to": "Quality Action", + "stats_filter": "{\"status\": \"Open\"}", + "type": "DocType" + }, + { + "doc_view": "", + "label": "Non Conformance", + "link_to": "Non Conformance", + "type": "DocType" } ] } \ No newline at end of file diff --git a/erpnext/quality_management/doctype/non_conformance/__init__.py b/erpnext/quality_management/doctype/non_conformance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.js b/erpnext/quality_management/doctype/non_conformance/non_conformance.js new file mode 100644 index 0000000000..e7f5eee623 --- /dev/null +++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.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('Non Conformance', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.json b/erpnext/quality_management/doctype/non_conformance/non_conformance.json new file mode 100644 index 0000000000..bfeb96bcaf --- /dev/null +++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.json @@ -0,0 +1,118 @@ +{ + "actions": [], + "autoname": "format:QA-NC-{#####}", + "creation": "2020-10-21 14:49:50.350136", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "subject", + "procedure", + "process_owner", + "full_name", + "column_break_4", + "status", + "section_break_4", + "details", + "corrective_action", + "preventive_action" + ], + "fields": [ + { + "fieldname": "subject", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Subject", + "reqd": 1 + }, + { + "fieldname": "procedure", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Procedure", + "options": "Quality Procedure", + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Open\nResolved\nCancelled", + "reqd": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "details", + "fieldtype": "Text Editor", + "label": "Details" + }, + { + "fetch_from": "procedure.process_owner", + "fieldname": "process_owner", + "fieldtype": "Data", + "label": "Process Owner", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fetch_from": "process_owner.full_name", + "fieldname": "full_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Full Name" + }, + { + "fieldname": "corrective_action", + "fieldtype": "Text", + "label": "Corrective Action" + }, + { + "fieldname": "preventive_action", + "fieldtype": "Text", + "label": "Preventive Action" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-10-26 15:27:47.247814", + "modified_by": "Administrator", + "module": "Quality Management", + "name": "Non Conformance", + "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": "Employee", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.py b/erpnext/quality_management/doctype/non_conformance/non_conformance.py new file mode 100644 index 0000000000..d4e8cc7a71 --- /dev/null +++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.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 NonConformance(Document): + pass diff --git a/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py new file mode 100644 index 0000000000..54f8b58cfb --- /dev/null +++ b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.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 TestNonConformance(unittest.TestCase): + pass diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.js b/erpnext/quality_management/doctype/quality_action/quality_action.js index 70782477f0..e216a7539c 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.js +++ b/erpnext/quality_management/doctype/quality_action/quality_action.js @@ -2,32 +2,5 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Action', { - onload: function(frm) { - frm.set_value("date", frappe.datetime.get_today()); - frm.refresh(); - }, - document_name: function(frm){ - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: frm.doc.document_type, - name: frm.doc.document_name - }, - callback: function(data){ - frm.fields_dict.resolutions.grid.remove_all(); - let objectives = []; - if(frm.doc.document_type === "Quality Review"){ - for(let i in data.message.reviews) objectives.push(data.message.reviews[i].review); - } else { - for(let j in data.message.parameters) objectives.push(data.message.parameters[j].feedback); - } - for (var objective in objectives){ - frm.add_child("resolutions"); - frm.fields_dict.resolutions.get_value()[objective].problem = objectives[objective]; - } - frm.refresh(); - } - }); - }, }); \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.json b/erpnext/quality_management/doctype/quality_action/quality_action.json index 8835b479cc..02f84be7d0 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.json +++ b/erpnext/quality_management/doctype/quality_action/quality_action.json @@ -1,32 +1,34 @@ { - "autoname": "format:ACTN-{#####}", + "actions": [], + "autoname": "format:QA-ACT-{#####}", "creation": "2018-10-02 11:40:43.666100", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ "corrective_preventive", - "document_type", - "goal", + "review", + "feedback", + "status", "cb_00", "date", - "document_name", + "goal", "procedure", - "status", "sb_00", "resolutions" ], "fields": [ { - "depends_on": "eval:doc.type == 'Quality Review'", "fetch_from": "review.goal", "fieldname": "goal", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Goal", - "options": "Quality Goal", - "read_only": 1 + "options": "Quality Goal" }, { + "default": "Today", "fieldname": "date", "fieldtype": "Date", "in_list_view": 1, @@ -34,34 +36,20 @@ "read_only": 1 }, { - "depends_on": "eval:doc.type == 'Quality Review'", "fieldname": "procedure", "fieldtype": "Link", "label": "Procedure", - "options": "Quality Procedure", - "read_only": 1 + "options": "Quality Procedure" }, { "default": "Open", "fieldname": "status", "fieldtype": "Select", "in_list_view": 1, + "in_standard_filter": 1, "label": "Status", - "options": "Open\nClosed" - }, - { - "fieldname": "document_name", - "fieldtype": "Dynamic Link", - "label": "Document Name", - "options": "document_type" - }, - { - "fieldname": "document_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Document Type", - "options": "Quality Review\nQuality Feedback", - "reqd": 1 + "options": "Open\nCompleted", + "read_only": 1 }, { "default": "Corrective", @@ -86,9 +74,25 @@ "fieldtype": "Table", "label": "Resolutions", "options": "Quality Action Resolution" + }, + { + "fieldname": "review", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Review", + "options": "Quality Review", + "reqd": 1 + }, + { + "fieldname": "feedback", + "fieldtype": "Link", + "label": "Feedback", + "options": "Quality Feedback" } ], - "modified": "2019-05-28 13:10:44.092497", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-10-21 13:01:31.920215", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Action", diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py index 88d4bd844a..d6fa5051ee 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.py +++ b/erpnext/quality_management/doctype/quality_action/quality_action.py @@ -7,4 +7,5 @@ import frappe from frappe.model.document import Document class QualityAction(Document): - pass \ No newline at end of file + def validate(self): + self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed' \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json index a4e6aed86a..993274b549 100644 --- a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json +++ b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json @@ -1,33 +1,54 @@ { + "actions": [], "creation": "2019-05-26 20:36:44.337186", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ "problem", - "sb_00", - "resolution" + "resolution", + "status", + "responsible", + "completion_by" ], "fields": [ { "fieldname": "problem", "fieldtype": "Long Text", "in_list_view": 1, - "label": "Review" - }, - { - "fieldname": "sb_00", - "fieldtype": "Section Break" + "label": "Problem" }, { "fieldname": "resolution", "fieldtype": "Text Editor", "in_list_view": 1, "label": "Resolution" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Open\nCompleted" + }, + { + "fieldname": "responsible", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Responsible", + "options": "User" + }, + { + "fieldname": "completion_by", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion By" } ], + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-05-28 13:09:50.435323", + "links": [], + "modified": "2020-10-21 12:59:25.566682", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Action Resolution", diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json index ab9084fa79..8d4b996de2 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json @@ -1,6 +1,6 @@ { "actions": [], - "autoname": "format:FDBK-{#####}", + "autoname": "format:QA-FB-{#####}", "creation": "2019-05-26 21:23:05.308379", "doctype": "DocType", "editable_grid": 1, @@ -60,7 +60,7 @@ } ], "links": [], - "modified": "2020-07-03 15:50:58.589302", + "modified": "2020-10-21 12:39:20.740854", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback", diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json index bdc9dbab49..49275186dc 100644 --- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json +++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json @@ -1,6 +1,6 @@ { "actions": [], - "autoname": "format:TMPL-{template}", + "autoname": "field:template", "creation": "2019-05-26 21:17:24.283061", "doctype": "DocType", "editable_grid": 1, @@ -17,7 +17,8 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Template", - "reqd": 1 + "reqd": 1, + "unique": 1 }, { "fieldname": "cb_00", @@ -36,7 +37,7 @@ } ], "links": [], - "modified": "2020-07-03 16:06:03.749415", + "modified": "2020-10-21 12:39:02.221128", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback Template", diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.json b/erpnext/quality_management/doctype/quality_goal/quality_goal.json index c32610948e..b64754e9a1 100644 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal.json +++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.json @@ -1,5 +1,6 @@ { - "autoname": "format:GOAL-{goal}", + "actions": [], + "autoname": "field:goal", "creation": "2018-10-02 12:17:41.727541", "doctype": "DocType", "editable_grid": 1, @@ -112,7 +113,15 @@ "read_only": 1 } ], - "modified": "2019-05-28 14:49:12.768863", + "index_web_pages_for_search": 1, + "links": [ + { + "group": "Review", + "link_doctype": "Quality Review", + "link_fieldname": "goal" + } + ], + "modified": "2020-10-21 12:49:38.251228", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Goal", diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py b/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py deleted file mode 100644 index 22af3c0f74..0000000000 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py +++ /dev/null @@ -1,12 +0,0 @@ -from frappe import _ - -def get_data(): - return { - 'fieldname': 'goal', - 'transactions': [ - { - 'label': _('Review'), - 'items': ['Quality Review'] - } - ] - } \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 7691fe3587..480abf09db 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -1,12 +1,11 @@ { "actions": [], - "autoname": "naming_series:", + "autoname": "format:QA-MEET-{YY}-{MM}-{DD}", "creation": "2018-10-15 16:25:41.548432", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "naming_series", "date", "cb_00", "status", @@ -55,16 +54,11 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "Minutes" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Naming Series", - "options": "MTNG-.YYYY.-.MM.-.DD.-" } ], + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-05-19 13:18:59.821740", + "modified": "2020-10-21 13:08:50.367577", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index 1ed921cc76..f588f9aea1 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,19 +1,22 @@ { "actions": [], "allow_rename": 1, - "autoname": "format:PRC-{quality_procedure_name}", + "autoname": "field:quality_procedure_name", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ "quality_procedure_name", + "process_owner", + "process_owner_full_name", + "section_break_3", + "processes", + "sb_00", "parent_quality_procedure", "is_group", - "sb_00", - "processes", - "lft", "rgt", + "lft", "old_parent" ], "fields": [ @@ -34,14 +37,14 @@ "fieldname": "lft", "fieldtype": "Int", "hidden": 1, - "label": "Lft", + "label": "Left Index", "read_only": 1 }, { "fieldname": "rgt", "fieldtype": "Int", "hidden": 1, - "label": "Rgt", + "label": "Right Index", "read_only": 1 }, { @@ -54,7 +57,7 @@ { "fieldname": "sb_00", "fieldtype": "Section Break", - "label": "Processes" + "label": "Parent" }, { "fieldname": "processes", @@ -67,12 +70,52 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Quality Procedure", - "reqd": 1 + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "process_owner", + "fieldtype": "Link", + "label": "Process Owner", + "options": "User" + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fetch_from": "process_owner.full_name", + "fieldname": "process_owner_full_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Process Owner Full Name", + "print_hide": 1 } ], "is_tree": 1, - "links": [], - "modified": "2020-10-13 11:46:07.744194", + "links": [ + { + "group": "Reviews", + "link_doctype": "Quality Review", + "link_fieldname": "procedure" + }, + { + "group": "Goals", + "link_doctype": "Quality Goal", + "link_fieldname": "procedure" + }, + { + "group": "Actions", + "link_doctype": "Quality Action", + "link_fieldname": "procedure" + }, + { + "group": "Actions", + "link_doctype": "Non Conformance", + "link_fieldname": "procedure" + } + ], + "modified": "2020-10-26 15:25:39.316088", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py deleted file mode 100644 index 407028bb82..0000000000 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py +++ /dev/null @@ -1,20 +0,0 @@ -from frappe import _ - -def get_data(): - return { - 'fieldname': 'procedure', - 'transactions': [ - { - 'label': _('Goal'), - 'items': ['Quality Goal'] - }, - { - 'label': _('Review'), - 'items': ['Quality Review'] - }, - { - 'label': _('Action'), - 'items': ['Quality Action'] - } - ], - } \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json index 3925dbb8ac..cf7606e6e5 100644 --- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json +++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json @@ -10,6 +10,7 @@ ], "fields": [ { + "columns": 8, "fieldname": "process_description", "fieldtype": "Text Editor", "in_list_view": 1, @@ -20,13 +21,14 @@ "fieldname": "procedure", "fieldtype": "Link", "in_list_view": 1, - "label": "Child Procedure", + "label": "Sub Produre", "options": "Quality Procedure" } ], + "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-06-17 15:44:38.937915", + "modified": "2020-10-21 12:10:23.790655", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure Process", diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.js b/erpnext/quality_management/doctype/quality_review/quality_review.js index b6245818f5..67371bfc5c 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.js +++ b/erpnext/quality_management/doctype/quality_review/quality_review.js @@ -2,9 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Review', { - onload: function(frm){ - frm.set_value("date", frappe.datetime.get_today()); - }, goal: function(frm) { frappe.call({ "method": "frappe.client.get", diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.json b/erpnext/quality_management/doctype/quality_review/quality_review.json index 76714ced81..31ad341362 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.json +++ b/erpnext/quality_management/doctype/quality_review/quality_review.json @@ -1,6 +1,6 @@ { "actions": [], - "autoname": "format:REV-{#####}", + "autoname": "format:QA-REV-{#####}", "creation": "2018-10-02 11:45:16.301955", "doctype": "DocType", "editable_grid": 1, @@ -18,6 +18,7 @@ ], "fields": [ { + "default": "Today", "fieldname": "date", "fieldtype": "Date", "in_list_view": 1, @@ -50,7 +51,7 @@ "collapsible": 1, "fieldname": "sb_01", "fieldtype": "Section Break", - "label": "Additional Information" + "label": "Notes" }, { "fieldname": "reviews", @@ -63,7 +64,8 @@ "fieldname": "status", "fieldtype": "Select", "label": "Status", - "options": "Open\nClosed" + "options": "Open\nPassed\nFailed", + "read_only": 1 }, { "fieldname": "goal", @@ -74,8 +76,15 @@ "reqd": 1 } ], - "links": [], - "modified": "2020-02-01 10:59:38.933115", + "index_web_pages_for_search": 1, + "links": [ + { + "group": "Review", + "link_doctype": "Quality Action", + "link_fieldname": "review" + } + ], + "modified": "2020-10-21 12:56:47.046172", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Review", @@ -120,5 +129,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "title_field": "goal", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py index 2bc8867ef7..ddf6207a9c 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.py +++ b/erpnext/quality_management/doctype/quality_review/quality_review.py @@ -7,7 +7,9 @@ import frappe from frappe.model.document import Document class QualityReview(Document): - pass + def validate(self): + # if any child item is failed, fail the parent + self.status = 'Failed' if any([d.status=='Failed' for d in self.reviews]) else 'Passed' def review(): day = frappe.utils.getdate().day diff --git a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json index 91f7bc07c7..e31e65e668 100644 --- a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json +++ b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-26 15:17:44.796958", "doctype": "DocType", "editable_grid": 1, @@ -9,10 +10,12 @@ "target", "uom", "sb_00", + "status", "review" ], "fields": [ { + "columns": 3, "fieldname": "objective", "fieldtype": "Text", "in_list_view": 1, @@ -20,6 +23,7 @@ "read_only": 1 }, { + "columns": 2, "fieldname": "target", "fieldtype": "Data", "in_list_view": 1, @@ -27,6 +31,7 @@ "read_only": 1 }, { + "columns": 1, "fetch_from": "target_unit", "fieldname": "uom", "fieldtype": "Link", @@ -49,10 +54,20 @@ { "fieldname": "cb_00", "fieldtype": "Column Break" + }, + { + "columns": 2, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Passed\nFailed" } ], + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-05-26 16:14:12.586128", + "links": [], + "modified": "2020-10-21 12:21:18.460863", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Review Objective", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index 3643174fb4..dd95075e28 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -236,7 +236,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-09-12 16:11:31.910508", + "modified": "2020-10-21 13:03:11.938072", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", @@ -257,7 +257,6 @@ "write": 1 } ], - "quick_entry": 1, "search_fields": "item_code, report_date, reference_name", "show_name_in_global_search": 1, "sort_field": "modified", From fa5fe18b70168c7e6784f042004e2228adf511a5 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 26 Oct 2020 19:27:02 +0530 Subject: [PATCH 10/18] fix error message Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/accounts/doctype/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 9de4649977..c801cfcbba 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -175,7 +175,7 @@ class Account(NestedSet): and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")): msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold) msg += "

" - msg = _("Please convert the parent account in corresponding child company to a group account.") + msg += _("Please convert the parent account in corresponding child company to a group account.") frappe.throw(msg, title=_("Invalid Parent Account")) filters = { From bd987e56bc54b7aebe37cce8dad521ac9be841e2 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Mon, 26 Oct 2020 19:37:33 +0530 Subject: [PATCH 11/18] fix: showing error for wrong filters. (#23726) * fix: showing error for wrong filters. * style: formatting translation --- .../stock_qty_vs_serial_no_count.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py index 55f041c95c..78e95df989 100644 --- a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py +++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py @@ -6,10 +6,17 @@ import frappe from frappe import _ def execute(filters=None): + validate_warehouse(filters) columns = get_columns() data = get_data(filters.warehouse) return columns, data +def validate_warehouse(filters): + company = filters.company + warehouse = filters.warehouse + if not frappe.db.exists("Warehouse", {"name": warehouse, "company": company}): + frappe.throw(_("Warehouse: {0} does not belong to {1}").format(warehouse, company)) + def get_columns(): columns = [ { From cf1428904a0e81b6f0c67926516daa9fd01c0ade Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 27 Oct 2020 17:36:30 +0530 Subject: [PATCH 12/18] fix(refactor): refactor tests, client side code --- .../desk_page/quality/quality.json | 9 +- .../quality_action/quality_action.json | 5 +- .../quality_action/test_quality_action.js | 23 ----- .../quality_action/test_quality_action.py | 39 +-------- .../quality_feedback/quality_feedback.js | 24 +----- .../quality_feedback/quality_feedback.json | 23 ++--- .../quality_feedback/quality_feedback.py | 15 +++- .../quality_feedback/test_quality_feedback.py | 58 ++++--------- .../quality_feedback_parameter.json | 8 +- .../quality_feedback_template.json | 17 ++-- .../test_quality_feedback_template.py | 29 +------ .../doctype/quality_goal/quality_goal.js | 5 +- .../doctype/quality_goal/quality_goal.json | 47 +--------- .../doctype/quality_goal/quality_goal.py | 4 +- .../doctype/quality_goal/test_quality_goal.js | 23 ----- .../doctype/quality_goal/test_quality_goal.py | 50 +++-------- .../quality_meeting/quality_meeting.js | 5 +- .../quality_meeting/quality_meeting.json | 12 +-- .../quality_meeting/test_quality_meeting.js | 23 ----- .../quality_meeting/test_quality_meeting.py | 38 +-------- .../quality_procedure/quality_procedure.py | 63 ++++++-------- .../quality_procedure_tree.js | 2 +- .../test_quality_procedure.js | 23 ----- .../test_quality_procedure.py | 85 +++++++++---------- .../quality_procedure_process.json | 4 +- .../doctype/quality_review/quality_review.py | 30 ++++--- .../quality_review/test_quality_review.js | 23 ----- .../quality_review/test_quality_review.py | 46 +++------- .../quality_review_objective.json | 4 +- 29 files changed, 192 insertions(+), 545 deletions(-) delete mode 100644 erpnext/quality_management/doctype/quality_action/test_quality_action.js delete mode 100644 erpnext/quality_management/doctype/quality_goal/test_quality_goal.js delete mode 100644 erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js delete mode 100644 erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js delete mode 100644 erpnext/quality_management/doctype/quality_review/test_quality_review.js diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json index ffa35dfd68..7a049b21c0 100644 --- a/erpnext/quality_management/desk_page/quality/quality.json +++ b/erpnext/quality_management/desk_page/quality/quality.json @@ -33,7 +33,7 @@ "idx": 0, "is_standard": 1, "label": "Quality", - "modified": "2020-10-26 15:24:58.058088", + "modified": "2020-10-27 16:28:54.138055", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", @@ -47,6 +47,7 @@ "type": "DocType" }, { + "doc_view": "Tree", "label": "Quality Procedure", "link_to": "Quality Procedure", "type": "DocType" @@ -57,9 +58,12 @@ "type": "DocType" }, { + "color": "#ff8989", "doc_view": "", + "format": "{} Open", "label": "Quality Review", "link_to": "Quality Review", + "stats_filter": "{\"status\": \"Open\"}", "type": "DocType" }, { @@ -72,9 +76,12 @@ "type": "DocType" }, { + "color": "#ff8989", "doc_view": "", + "format": "{} Open", "label": "Non Conformance", "link_to": "Non Conformance", + "stats_filter": "{\"status\": \"Open\"}", "type": "DocType" } ] diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.json b/erpnext/quality_management/doctype/quality_action/quality_action.json index 02f84be7d0..0cc2a98cd2 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.json +++ b/erpnext/quality_management/doctype/quality_action/quality_action.json @@ -80,8 +80,7 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Review", - "options": "Quality Review", - "reqd": 1 + "options": "Quality Review" }, { "fieldname": "feedback", @@ -92,7 +91,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-21 13:01:31.920215", + "modified": "2020-10-27 16:21:59.533937", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Action", diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.js b/erpnext/quality_management/doctype/quality_action/test_quality_action.js deleted file mode 100644 index 34a8c86889..0000000000 --- a/erpnext/quality_management/doctype/quality_action/test_quality_action.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Quality Action", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Quality Actions - () => frappe.tests.make('Quality Actions', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.py b/erpnext/quality_management/doctype/quality_action/test_quality_action.py index 51178d6225..24b97ca3a0 100644 --- a/erpnext/quality_management/doctype/quality_action/test_quality_action.py +++ b/erpnext/quality_management/doctype/quality_action/test_quality_action.py @@ -5,42 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure -from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_unit -from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal -from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review class TestQualityAction(unittest.TestCase): - - def test_quality_action(self): - create_procedure() - create_unit() - create_goal() - create_review() - test_create_action = create_action() - test_get_action = get_action() - - self.assertEquals(test_create_action, test_get_action) - -def create_action(): - review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}) - action = frappe.get_doc({ - "doctype": "Quality Action", - "action": "Corrective", - "document_type": "Quality Review", - "document_name": review, - "date": frappe.utils.nowdate(), - "goal": "GOAL-_Test Quality Goal", - "procedure": "PRC-_Test Quality Procedure" - }) - action_exist = frappe.db.exists("Quality Action", {"review": review}) - - if not action_exist: - action.insert() - return action.name - else: - return action_exist - -def get_action(): - review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}) - return frappe.db.exists("Quality Action", {"document_name": review}) \ No newline at end of file + # quality action has no code + pass \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js index dac6ac40a7..6fb326776e 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js @@ -2,31 +2,9 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Feedback', { - refresh: function(frm) { - frm.set_value("date", frappe.datetime.get_today()); - }, - template: function(frm) { if (frm.doc.template) { - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Quality Feedback Template", - name: frm.doc.template - }, - callback: function(data) { - if (data && data.message) { - frm.fields_dict.parameters.grid.remove_all(); - - // fetch parameters from template and autofill - for (let template_parameter of data.message.parameters) { - let row = frm.add_child("parameters"); - row.parameter = template_parameter.parameter; - } - frm.refresh(); - } - } - }); + frm.call('set_parameters'); } } }); diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json index 8d4b996de2..f3bd0ddb2e 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json @@ -6,11 +6,10 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "document_type", "template", "cb_00", + "document_type", "document_name", - "date", "sb_00", "parameters" ], @@ -18,6 +17,7 @@ { "fieldname": "template", "fieldtype": "Link", + "in_list_view": 1, "label": "Template", "options": "Quality Feedback Template", "reqd": 1 @@ -26,13 +26,6 @@ "fieldname": "cb_00", "fieldtype": "Column Break" }, - { - "fieldname": "date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Date", - "read_only": 1 - }, { "fieldname": "sb_00", "fieldtype": "Section Break" @@ -47,6 +40,7 @@ { "fieldname": "document_type", "fieldtype": "Select", + "in_list_view": 1, "label": "Type", "options": "User\nCustomer", "reqd": 1 @@ -54,13 +48,20 @@ { "fieldname": "document_name", "fieldtype": "Dynamic Link", + "in_list_view": 1, "label": "Feedback By", "options": "document_type", "reqd": 1 } ], - "links": [], - "modified": "2020-10-21 12:39:20.740854", + "links": [ + { + "group": "Actions", + "link_doctype": "Quality Action", + "link_fieldname": "feedback" + } + ], + "modified": "2020-10-27 16:20:10.918544", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback", diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py index 9894181004..bf82cc080a 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py @@ -7,4 +7,17 @@ import frappe from frappe.model.document import Document class QualityFeedback(Document): - pass \ No newline at end of file + def set_parameters(self): + if self.template and not getattr(self, 'parameters', []): + for d in frappe.get_doc('Quality Feedback Template', self.template).parameters: + self.append('parameters', dict( + parameter = d.parameter, + rating = 1 + )) + + def validate(self): + if not self.document_name: + self.document_type ='User' + self.document_name = frappe.session.user + self.set_parameters() + diff --git a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py index 3be1eb2791..5a8bd5ce30 100644 --- a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py +++ b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py @@ -5,49 +5,27 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.quality_management.doctype.quality_feedback_template.test_quality_feedback_template import create_template + class TestQualityFeedback(unittest.TestCase): - def test_quality_feedback(self): - create_template() - test_create_feedback = create_feedback() - test_get_feedback = get_feedback() + template = frappe.get_doc(dict( + doctype = 'Quality Feedback Template', + template = 'Test Template', + parameters = [ + dict(parameter='Test Parameter 1'), + dict(parameter='Test Parameter 2') + ] + )).insert() - self.assertEqual(test_create_feedback, test_get_feedback) + feedback = frappe.get_doc(dict( + doctype = 'Quality Feedback', + template = template.name, + document_type = 'User', + document_name = frappe.session.user + )).insert() -def create_feedback(): - create_customer() + self.assertEqual(template.parameters[0].parameter, feedback.parameters[0].parameter) - feedabck = frappe.get_doc({ - "doctype": "Quality Feedback", - "template": "TMPL-_Test Feedback Template", - "document_type": "Customer", - "document_name": "Quality Feedback Customer", - "date": frappe.utils.nowdate(), - "parameters": [ - { - "parameter": "Test Parameter", - "rating": 3, - "feedback": "Test Feedback" - } - ] - }) - - feedback_exists = frappe.db.exists("Quality Feedback", {"template": "TMPL-_Test Feedback Template"}) - - if not feedback_exists: - feedabck.insert() - return feedabck.name - else: - return feedback_exists - -def get_feedback(): - return frappe.db.exists("Quality Feedback", {"template": "TMPL-_Test Feedback Template"}) - -def create_customer(): - if not frappe.db.exists("Customer", {"customer_name": "Quality Feedback Customer"}): - customer = frappe.get_doc({ - "doctype": "Customer", - "customer_name": "Quality Feedback Customer" - }).insert(ignore_permissions=True) \ No newline at end of file + feedback.delete() + template.delete() diff --git a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json index 5bd8920a32..ce5d4cfeee 100644 --- a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json +++ b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-26 21:25:01.715807", "doctype": "DocType", "editable_grid": 1, @@ -39,12 +40,13 @@ "fieldname": "feedback", "fieldtype": "Text Editor", "in_list_view": 1, - "label": "Feedback", - "reqd": 1 + "label": "Feedback" } ], + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-07-13 19:58:08.966141", + "links": [], + "modified": "2020-10-27 17:28:12.033145", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback Parameter", diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json index 49275186dc..169647046d 100644 --- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json +++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json @@ -7,7 +7,6 @@ "engine": "InnoDB", "field_order": [ "template", - "cb_00", "sb_00", "parameters" ], @@ -16,14 +15,10 @@ "fieldname": "template", "fieldtype": "Data", "in_list_view": 1, - "label": "Template", + "label": "Template Name", "reqd": 1, "unique": 1 }, - { - "fieldname": "cb_00", - "fieldtype": "Column Break" - }, { "fieldname": "sb_00", "fieldtype": "Section Break" @@ -36,8 +31,14 @@ "reqd": 1 } ], - "links": [], - "modified": "2020-10-21 12:39:02.221128", + "links": [ + { + "group": "Records", + "link_doctype": "Quality Feedback", + "link_fieldname": "template" + } + ], + "modified": "2020-10-27 16:18:53.579688", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback Template", diff --git a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py index 36dbe137a5..b3eed10383 100644 --- a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py +++ b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py @@ -7,31 +7,4 @@ import frappe import unittest class TestQualityFeedbackTemplate(unittest.TestCase): - - def test_quality_feedback_template(self): - test_create_template = create_template() - test_get_template = get_template() - - self.assertEqual(test_create_template, test_get_template) - -def create_template(): - template = frappe.get_doc({ - "doctype": "Quality Feedback Template", - "template": "_Test Feedback Template", - "parameters": [ - { - "parameter": "Test Parameter" - } - ] - }) - - template_exists = frappe.db.exists("Quality Feedback Template", {"template": "_Test Feedback Template"}) - - if not template_exists: - template.insert() - return template.name - else: - return template_exists - -def get_template(): - return frappe.db.exists("Quality Feedback Template", {"template": "_Test Feedback Template"}) \ No newline at end of file + pass \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.js b/erpnext/quality_management/doctype/quality_goal/quality_goal.js index ff58c5ad21..40cb4d9246 100644 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal.js +++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.js @@ -2,7 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Goal', { - refresh: function(frm) { - frm.doc.created_by = frappe.session.user; - } + // refresh: function(frm) { + // } }); diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.json b/erpnext/quality_management/doctype/quality_goal/quality_goal.json index b64754e9a1..26802550dc 100644 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal.json +++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.json @@ -8,27 +8,14 @@ "field_order": [ "goal", "frequency", - "created_by", "cb_00", "procedure", "weekday", - "quarter", "date", - "sb_00", - "revision", - "cb_01", - "revised_on", "sb_01", "objectives" ], "fields": [ - { - "fieldname": "created_by", - "fieldtype": "Link", - "label": "Created By", - "options": "User", - "read_only": 1 - }, { "default": "None", "fieldname": "frequency", @@ -51,20 +38,6 @@ "label": "Date", "options": "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30" }, - { - "default": "0", - "fieldname": "revision", - "fieldtype": "Int", - "label": "Revision", - "read_only": 1 - }, - { - "fieldname": "revised_on", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Revised On", - "read_only": 1 - }, { "depends_on": "eval:doc.frequency == 'Weekly';", "fieldname": "weekday", @@ -76,15 +49,6 @@ "fieldname": "cb_00", "fieldtype": "Column Break" }, - { - "fieldname": "sb_00", - "fieldtype": "Section Break", - "label": "Revision and Revised On" - }, - { - "fieldname": "cb_01", - "fieldtype": "Column Break" - }, { "fieldname": "sb_01", "fieldtype": "Section Break", @@ -102,15 +66,6 @@ "label": "Goal", "reqd": 1, "unique": 1 - }, - { - "default": "January-April-July-October", - "depends_on": "eval:doc.frequency == 'Quarterly';", - "fieldname": "quarter", - "fieldtype": "Select", - "label": "Quarter", - "options": "January-April-July-October", - "read_only": 1 } ], "index_web_pages_for_search": 1, @@ -121,7 +76,7 @@ "link_fieldname": "goal" } ], - "modified": "2020-10-21 12:49:38.251228", + "modified": "2020-10-27 15:57:59.368605", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Goal", diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.py b/erpnext/quality_management/doctype/quality_goal/quality_goal.py index 4ae015e4a1..f3fe986d53 100644 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal.py +++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.py @@ -8,7 +8,5 @@ import frappe from frappe.model.document import Document class QualityGoal(Document): - def validate(self): - self.revision += 1 - self.revised_on = frappe.utils.today() + pass \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js deleted file mode 100644 index f8afe548a4..0000000000 --- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Quality Goal", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Quality Goal - () => frappe.tests.make('Quality Goal', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py index d77187aaa1..f61d6e581d 100644 --- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py +++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py @@ -8,44 +8,18 @@ import unittest from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure class TestQualityGoal(unittest.TestCase): - def test_quality_goal(self): - create_procedure() - create_unit() - test_create_goal = create_goal() - test_get_goal = get_goal() + # no code, just a basic sanity check + goal = get_quality_goal() + self.assertTrue(goal) + goal.delete() - self.assertEquals(test_create_goal, test_get_goal) - -def create_goal(): - goal = frappe.get_doc({ - "doctype": "Quality Goal", - "goal": "_Test Quality Goal", - "procedure": "PRC-_Test Quality Procedure", - "objectives": [ - { - "objective": "_Test Quality Objective", - "target": "4", - "uom": "_Test UOM" - } +def get_quality_goal(): + return frappe.get_doc(dict( + doctype = 'Quality Goal', + goal = 'Test Quality Module', + frequency = 'Daily', + objectives = [ + dict(objective = 'Check test cases', target='100', uom='Percent') ] - }) - goal_exist = frappe.db.exists("Quality Goal", {"goal": goal.goal}) - if not goal_exist: - goal.insert() - return goal.name - else: - return goal_exist - -def get_goal(): - goal = frappe.db.exists("Quality Goal", "GOAL-_Test Quality Goal") - return goal - -def create_unit(): - unit = frappe.get_doc({ - "doctype": "UOM", - "uom_name": "_Test UOM", - }) - unit_exist = frappe.db.exists("UOM", unit.uom_name) - if not unit_exist: - unit.insert() + )).insert() \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js index 32c7c33fd5..eb7a8c32d7 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js @@ -2,8 +2,5 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Meeting', { - onload: function(frm){ - frm.set_value("date", frappe.datetime.get_today()); - frm.refresh(); - } + }); diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 480abf09db..ead403d453 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -6,22 +6,14 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "date", - "cb_00", "status", + "cb_00", "sb_00", "agenda", "sb_01", "minutes" ], "fields": [ - { - "fieldname": "date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Date", - "read_only": 1 - }, { "default": "Open", "fieldname": "status", @@ -58,7 +50,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-21 13:08:50.367577", + "modified": "2020-10-27 16:36:45.657883", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js deleted file mode 100644 index 196cc85ccb..0000000000 --- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Quality Meeting", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Quality Meeting - () => frappe.tests.make('Quality Meeting', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py index e61b5dfc57..754bccb06e 100644 --- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py +++ b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py @@ -5,41 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review class TestQualityMeeting(unittest.TestCase): - def test_quality_meeting(self): - create_review() - test_create_meeting = create_meeting() - test_get_meeting = get_meeting() - self.assertEquals(test_create_meeting, test_get_meeting) - -def create_meeting(): - meeting = frappe.get_doc({ - "doctype": "Quality Meeting", - "status": "Open", - "date": frappe.utils.nowdate(), - "agenda": [ - { - "agenda": "Test Agenda" - } - ], - "minutes": [ - { - "document_type": "Quality Review", - "document_name": frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}), - "minute": "Test Minute" - } - ] - }) - meeting_exist = frappe.db.exists("Quality Meeting", {"date": frappe.utils.nowdate(), "status": "Open"}) - - if not meeting_exist: - meeting.insert() - return meeting.name - else: - return meeting_exist - -def get_meeting(): - meeting = frappe.db.exists("Quality Meeting", {"date": frappe.utils.nowdate(), "status": "Open"}) - return meeting \ No newline at end of file + # nothing to test + pass \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index 797c26b64c..f0857275ca 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -14,69 +14,58 @@ class QualityProcedure(NestedSet): self.check_for_incorrect_child() def on_update(self): + NestedSet.on_update(self) self.set_parent() def after_insert(self): self.set_parent() - #if Child is Added through Tree View. + + # add child to parent if missing if self.parent_quality_procedure: - parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) - parent_quality_procedure.append("processes", {"procedure": self.name}) - parent_quality_procedure.save() + parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + if not [d for d in parent.processes if d.procedure == self.name]: + parent.append("processes", {"procedure": self.name, "process_description": self.name}) + parent.save() def on_trash(self): - if self.parent_quality_procedure: - doc = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) - for process in doc.processes: - if process.procedure == self.name: - doc.processes.remove(process) - doc.save(ignore_permissions=True) - - flag_is_group = 0 - doc.load_from_db() - - for process in doc.processes: - flag_is_group = 1 if process.procedure else 0 - - doc.is_group = 0 if flag_is_group == 0 else 1 - doc.save(ignore_permissions=True) + # clear from child table (sub procedures) + frappe.db.sql('''update `tabQuality Procedure Process` + set `procedure`='' where `procedure`=%s''', self.name) + NestedSet.on_trash(self, allow_root_deletion=True) def set_parent(self): - rebuild_tree('Quality Procedure', 'parent_quality_procedure') - for process in self.processes: # Set parent for only those children who don't have a parent - parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") - if not parent_quality_procedure and process.procedure: + has_parent = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if not has_parent and process.procedure: frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) def check_for_incorrect_child(self): for process in self.processes: if process.procedure: + self.is_group = 1 # Check if any child process belongs to another parent. parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") if parent_quality_procedure and parent_quality_procedure != self.name: frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))), title=_("Invalid Child Procedure")) - self.is_group = 1 @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): if parent is None or parent == "All Quality Procedures": parent = "" - return frappe.db.sql(""" - select - name as value, - is_group as expandable - from - `tab{doctype}` - where - ifnull(parent_quality_procedure, "")={parent} - """.format( - doctype = doctype, - parent=frappe.db.escape(parent) - ), as_dict=1) + if parent: + parent_procedure = frappe.get_doc('Quality Procedure', parent) + # return the list in order + return [dict( + value=d.procedure, + expandable=frappe.db.get_value('Quality Procedure', d.procedure, 'is_group')) + for d in parent_procedure.processes if d.procedure + ] + else: + return frappe.get_all(doctype, fields=['name as value', 'is_group as expandable'], + filters = dict(parent_quality_procedure = parent), order_by='name asc') @frappe.whitelist() def add_node(): @@ -88,4 +77,4 @@ def add_node(): if args.parent_quality_procedure == 'All Quality Procedures': args.parent_quality_procedure = None - frappe.get_doc(args).insert() \ No newline at end of file + return frappe.get_doc(args).insert() \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index ef48ab6c6e..eeb4cf617c 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -15,7 +15,7 @@ frappe.treeview_settings["Quality Procedure"] = { } }, ], - breadcrumb: "Setup", + breadcrumb: "Quality Management", disable_add_node: true, root_label: "All Quality Procedures", get_tree_root: false, diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js deleted file mode 100644 index 0a187ebfb7..0000000000 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Quality Procedure", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Quality Procedure - () => frappe.tests.make('Quality Procedure', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py index 3289bb5a37..36bdf26acf 100644 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py @@ -6,54 +6,45 @@ from __future__ import unicode_literals import frappe import unittest -class TestQualityProcedure(unittest.TestCase): - def test_quality_procedure(self): - test_create_procedure = create_procedure() - test_create_nested_procedure = create_nested_procedure() - test_get_procedure, test_get_nested_procedure = get_procedure() +from .quality_procedure import add_node - self.assertEquals(test_create_procedure, test_get_procedure.get("name")) - self.assertEquals(test_create_nested_procedure, test_get_nested_procedure.get("name")) +class TestQualityProcedure(unittest.TestCase): + def test_add_node(self): + try: + procedure = frappe.get_doc(dict( + doctype = 'Quality Procedure', + quality_procedure_name = 'Test Procedure 1', + processes = [ + dict(process_description = 'Test Step 1') + ] + )).insert() + + frappe.form_dict = dict(doctype = 'Quality Procedure', quality_procedure_name = 'Test Child 1', + parent_quality_procedure = procedure.name, cmd='test', is_root='false') + node = add_node() + + procedure.reload() + + self.assertEqual(procedure.is_group, 1) + + # child row created + self.assertTrue([d for d in procedure.processes if d.procedure == node.name]) + + node.delete() + procedure.reload() + + # child unset + self.assertFalse([d for d in procedure.processes if d.name == node.name]) + + finally: + procedure.delete() def create_procedure(): - procedure = frappe.get_doc({ - "doctype": "Quality Procedure", - "quality_procedure_name": "_Test Quality Procedure", - "processes": [ - { - "process_description": "_Test Quality Procedure Table", - } + return frappe.get_doc(dict( + doctype = 'Quality Procedure', + quality_procedure_name = 'Test Procedure 1', + is_group = 1, + processes = [ + dict(process_description = 'Test Step 1') ] - }) - - procedure_exist = frappe.db.exists("Quality Procedure", "PRC-_Test Quality Procedure") - - if not procedure_exist: - procedure.insert() - return procedure.name - else: - return procedure_exist - -def create_nested_procedure(): - nested_procedure = frappe.get_doc({ - "doctype": "Quality Procedure", - "quality_procedure_name": "_Test Nested Quality Procedure", - "processes": [ - { - "procedure": "PRC-_Test Quality Procedure" - } - ] - }) - - nested_procedure_exist = frappe.db.exists("Quality Procedure", "PRC-_Test Nested Quality Procedure") - - if not nested_procedure_exist: - nested_procedure.insert() - return nested_procedure.name - else: - return nested_procedure_exist - -def get_procedure(): - procedure = frappe.get_doc("Quality Procedure", "PRC-_Test Quality Procedure") - nested_procedure = frappe.get_doc("Quality Procedure", "PRC-_Test Nested Quality Procedure") - return {"name": procedure.name}, {"name": nested_procedure.name, "parent_quality_procedure": nested_procedure.parent_quality_procedure} \ No newline at end of file + )).insert() \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json index cf7606e6e5..aeca6ff47a 100644 --- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json +++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json @@ -21,14 +21,14 @@ "fieldname": "procedure", "fieldtype": "Link", "in_list_view": 1, - "label": "Sub Produre", + "label": "Sub Procedure", "options": "Quality Procedure" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-10-21 12:10:23.790655", + "modified": "2020-10-27 13:55:11.252945", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure Process", diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py index ddf6207a9c..e3a8b073f0 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.py +++ b/erpnext/quality_management/doctype/quality_review/quality_review.py @@ -8,8 +8,25 @@ from frappe.model.document import Document class QualityReview(Document): def validate(self): + # fetch targets from goal + if not self.reviews: + for d in frappe.get_doc('Quality Goal', self.goal).objectives: + self.append('reviews', dict( + objective = d.objective, + target = d.target, + uom = d.uom + )) + + self.set_status() + + def set_status(self): # if any child item is failed, fail the parent - self.status = 'Failed' if any([d.status=='Failed' for d in self.reviews]) else 'Passed' + if not len(self.reviews or []) or any([d.status=='Open' for d in self.reviews]): + self.status = 'Open' + elif any([d.status=='Failed' for d in self.reviews]): + self.status = 'Failed' + else: + self.status = 'Passed' def review(): day = frappe.utils.getdate().day @@ -26,7 +43,7 @@ def review(): elif goal.frequency == 'Monthly' and goal.date == str(day): create_review(goal.name) - elif goal.frequency == 'Quarterly' and goal.data == str(day) and get_quarter(month): + elif goal.frequency == 'Quarterly' and day==1 and get_quarter(month): create_review(goal.name) def create_review(goal): @@ -38,15 +55,6 @@ def create_review(goal): "date": frappe.utils.getdate() }) - for objective in goal.objectives: - review.append("reviews", - { - "objective": objective.objective, - "target": objective.target, - "uom": objective.uom - } - ) - review.insert(ignore_permissions=True) def get_quarter(month): diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.js b/erpnext/quality_management/doctype/quality_review/test_quality_review.js deleted file mode 100644 index cf910b27af..0000000000 --- a/erpnext/quality_management/doctype/quality_review/test_quality_review.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Performance Monitoring", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Performance Monitoring - () => frappe.tests.make('Performance Monitoring', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.py b/erpnext/quality_management/doctype/quality_review/test_quality_review.py index 8add6db9c9..a7d92da8ac 100644 --- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py +++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py @@ -5,42 +5,18 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure -from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_unit -from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal + +from ..quality_goal.test_quality_goal import get_quality_goal +from .quality_review import review class TestQualityReview(unittest.TestCase): + def test_review_creation(self): + quality_goal = get_quality_goal() + review() - def test_quality_review(self): - create_procedure() - create_unit() - create_goal() - test_create_review = create_review() - test_get_review = get_review() - self.assertEquals(test_create_review, test_get_review) + # check if review exists + quality_review = frappe.get_doc('Quality Review', dict(goal = quality_goal.name)) + self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target) + quality_review.delete() -def create_review(): - review = frappe.get_doc({ - "doctype": "Quality Review", - "goal": "GOAL-_Test Quality Goal", - "procedure": "PRC-_Test Quality Procedure", - "date": frappe.utils.nowdate(), - "reviews": [ - { - "objective": "_Test Quality Objective", - "target": "100", - "uom": "_Test UOM", - "review": "Test Review" - } - ] - }) - review_exist = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}) - if not review_exist: - review.insert(ignore_permissions=True) - return review.name - else: - return review_exist - -def get_review(): - review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}) - return review \ No newline at end of file + quality_goal.delete() \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json index e31e65e668..3a750c21d6 100644 --- a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json +++ b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json @@ -61,13 +61,13 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Status", - "options": "Passed\nFailed" + "options": "Open\nPassed\nFailed" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-10-21 12:21:18.460863", + "modified": "2020-10-27 16:28:20.908637", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Review Objective", From 12a2e51e20ea6f7a4f525a14e921aa56bd8a0894 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 27 Oct 2020 18:38:44 +0530 Subject: [PATCH 13/18] fix(minor): translation string encoding in quality_procedure.py --- .../doctype/quality_procedure/quality_procedure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index f0857275ca..53f4e6c70f 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -47,7 +47,7 @@ class QualityProcedure(NestedSet): # Check if any child process belongs to another parent. parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") if parent_quality_procedure and parent_quality_procedure != self.name: - frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))), + frappe.throw(_("{0} already has a Parent Procedure {1}.").format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure)), title=_("Invalid Child Procedure")) @frappe.whitelist() From 7d9dd3c6281b7c35f7be04b0f61a9ccfc8266e42 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 28 Oct 2020 17:07:10 +0530 Subject: [PATCH 14/18] fix: item wise tax calculation (#23744) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 23705a8779..02b1dc0beb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -651,7 +651,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ let child = frappe.model.add_child(me.frm.doc, "taxes"); child.charge_type = "On Net Total"; child.account_head = tax; - child.rate = rate; + child.rate = 0; } }); } From a51d9e3259c4994025d15a718682a14335097244 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 28 Oct 2020 17:08:47 +0530 Subject: [PATCH 15/18] fix: pos register shows cancelled documents (#23747) --- erpnext/accounts/report/pos_register/pos_register.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py index 0bcde64f7f..52f7fe238e 100644 --- a/erpnext/accounts/report/pos_register/pos_register.py +++ b/erpnext/accounts/report/pos_register/pos_register.py @@ -63,6 +63,7 @@ def get_pos_entries(filters, group_by_field): FROM `tabPOS Invoice` p {from_sales_invoice_payment} WHERE + p.docstatus = 1 and {group_by_mop_condition} {conditions} ORDER BY From 94a630c2551e1b699c2dc5be7b8f966e3ba75bcd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Oct 2020 17:15:14 +0530 Subject: [PATCH 16/18] fix: Do not allow Company as accounting dimension --- .../doctype/accounting_dimension/accounting_dimension.js | 2 +- .../doctype/accounting_dimension/accounting_dimension.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 3c12f85f93..9a6c389339 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -7,7 +7,7 @@ frappe.ui.form.on('Accounting Dimension', { frm.set_query('document_type', () => { let invalid_doctypes = frappe.model.core_doctypes_list; invalid_doctypes.push('Accounting Dimension', 'Project', - 'Cost Center', 'Accounting Dimension Detail'); + 'Cost Center', 'Accounting Dimension Detail', 'Company'); return { filters: { diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 8834385135..f888d9e038 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -19,7 +19,7 @@ class AccountingDimension(Document): def validate(self): if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project', - 'Cost Center', 'Accounting Dimension Detail') : + 'Cost Center', 'Accounting Dimension Detail', 'Company') : msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type) frappe.throw(msg) From c17cdd7c2e16454b7bcd9bbac8a4366ddf32522f Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Thu, 29 Oct 2020 11:10:06 +0530 Subject: [PATCH 17/18] fix: Fix for LMS Sign Up link (#23743) Signed-off-by: Syed Mujeer Hashmi Co-authored-by: Shivam Mishra Co-authored-by: Rucha Mahabal --- erpnext/www/lms/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html index 7ce3521273..7b239acd56 100644 --- a/erpnext/www/lms/index.html +++ b/erpnext/www/lms/index.html @@ -45,7 +45,7 @@

{{ education_settings.description }}

{% if frappe.session.user == 'Guest' %} - {{_('Sign Up')}} + {{_('Sign Up')}} {% endif %}

@@ -62,4 +62,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} From 10f246807699b67315e7b05cce2dc5c9245a27e0 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:02:48 +0530 Subject: [PATCH 18/18] fix: make asset dashboard charts public (#23751) --- .../asset_value_analytics/asset_value_analytics.json | 4 ++-- .../category_wise_asset_value/category_wise_asset_value.json | 4 ++-- .../location_wise_asset_value/location_wise_asset_value.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json index bc2edc9d7d..94debf1243 100644 --- a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json +++ b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json @@ -9,9 +9,9 @@ "filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}", "group_by_type": "Count", "idx": 0, - "is_public": 0, + "is_public": 1, "is_standard": 1, - "modified": "2020-07-23 13:53:33.211371", + "modified": "2020-10-28 23:15:58.432189", "modified_by": "Administrator", "module": "Assets", "name": "Asset Value Analytics", diff --git a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json index e79d2d7372..78611da003 100644 --- a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json +++ b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json @@ -8,9 +8,9 @@ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}", "idx": 0, - "is_public": 0, + "is_public": 1, "is_standard": 1, - "modified": "2020-07-23 13:39:32.429240", + "modified": "2020-10-28 23:16:16.939070", "modified_by": "Administrator", "module": "Assets", "name": "Category-wise Asset Value", diff --git a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json index 481586e7ca..848184cc14 100644 --- a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json +++ b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json @@ -8,9 +8,9 @@ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}", "idx": 0, - "is_public": 0, + "is_public": 1, "is_standard": 1, - "modified": "2020-07-23 13:42:44.912551", + "modified": "2020-10-28 23:16:07.883312", "modified_by": "Administrator", "module": "Assets", "name": "Location-wise Asset Value",