From 3f28fb19aa3f244981eda815b80e3a1fcf6974d5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 8 Oct 2020 14:57:27 +0530 Subject: [PATCH 01/26] 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/26] 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/26] 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 097b32fe673e406e8094d9b0dcf5401526a9d536 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 26 Oct 2020 15:27:37 +0530 Subject: [PATCH 04/26] feat: Close Production Plan --- .../production_plan/production_plan.js | 30 +++++++++++++++++-- .../production_plan/production_plan.json | 5 ++-- .../production_plan/production_plan.py | 9 +++++- .../production_plan/production_plan_list.js | 5 ++-- ...ction_plan_material_request_warehouse.json | 20 ++++--------- 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 1a64bc5e24..b29fd4cf43 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -56,23 +56,35 @@ frappe.ui.form.on('Production Plan', { refresh: function(frm) { if (frm.doc.docstatus === 1) { frm.trigger("show_progress"); + + if (frm.doc.status === "Closed") { + frm.add_custom_button(__("Re-open"), function() { + frm.events.close_open_production_plan(frm, false); + }, __("Status")); + } else { + frm.add_custom_button(__("Close"), function() { + frm.events.close_open_production_plan(frm, true); + }, __("Status")); + } } if (frm.doc.docstatus === 1 && frm.doc.po_items - && frm.doc.status != 'Completed') { + && !in_list(['Closed', 'Completed'], frm.doc.status)) { frm.add_custom_button(__("Work Order"), ()=> { frm.trigger("make_work_order"); }, __('Create')); } if (frm.doc.docstatus === 1 && frm.doc.mr_items - && !in_list(['Material Requested', 'Completed'], frm.doc.status)) { + && !in_list(['Material Requested', 'Completed', 'Closed'], frm.doc.status)) { frm.add_custom_button(__("Material Request"), ()=> { frm.trigger("make_material_request"); }, __('Create')); } - frm.page.set_inner_btn_group_as_primary(__('Create')); + if (frm.doc.status !== "Closed") { + frm.page.set_inner_btn_group_as_primary(__('Create')); + } frm.trigger("material_requirement"); const projected_qty_formula = ` @@ -121,6 +133,18 @@ frappe.ui.form.on('Production Plan', { set_field_options("projected_qty_formula", projected_qty_formula); }, + close_open_production_plan: (frm, close=false) => { + frappe.call({ + method: "set_status", + freeze: true, + doc: frm.doc, + args: {close : close}, + callback: function() { + frm.reload_doc(); + } + }); + }, + make_work_order: function(frm) { frappe.call({ method: "make_work_order", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 90e8b22ed9..850d5aeff8 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -275,7 +275,7 @@ "fieldtype": "Select", "label": "Status", "no_copy": 1, - "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested", + "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested", "print_hide": 1, "read_only": 1 }, @@ -304,9 +304,10 @@ } ], "icon": "fa fa-calendar", + "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-02-03 00:25:25.934202", + "modified": "2020-10-26 13:00:54.335319", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index b6552d5d6b..4cf798d3e2 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -219,13 +219,17 @@ class ProductionPlan(Document): filters = {'docstatus': 0, 'production_plan': ("=", self.name)}): frappe.delete_doc('Work Order', d.name) - def set_status(self): + def set_status(self, close=None): self.status = { 0: 'Draft', 1: 'Submitted', 2: 'Cancelled' }.get(self.docstatus) + if close: + self.db_set('status', 'Closed') + return + if self.total_produced_qty > 0: self.status = "In Process" if self.total_produced_qty == self.total_planned_qty: @@ -235,6 +239,9 @@ class ProductionPlan(Document): self.update_ordered_status() self.update_requested_status() + if close is not None: + self.db_set('status', self.status) + def update_ordered_status(self): update_status = False for d in self.po_items: diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js index d377ef0af7..165b66ff5d 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js @@ -1,6 +1,6 @@ frappe.listview_settings['Production Plan'] = { add_fields: ["status"], - filters: [["status", "!=", "Stopped"]], + filters: [["status", "!=", "Closed"]], get_indicator: function(doc) { if(doc.status==="Submitted") { return [__("Not Started"), "orange", "status,=,Submitted"]; @@ -10,7 +10,8 @@ frappe.listview_settings['Production Plan'] = { "In Process": "orange", "Completed": "green", "Material Requested": "darkgrey", - "Cancelled": "darkgrey" + "Cancelled": "darkgrey", + "Closed": "grey" }[doc.status], "status,=," + doc.status]; } } diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json index 53e33c0265..e72f48943c 100644 --- a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json +++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json @@ -11,30 +11,20 @@ { "fieldname": "warehouse", "fieldtype": "Link", + "in_list_view": 1, "label": "Warehouse", "options": "Warehouse" } ], + "index_web_pages_for_search": 1, + "istable": 1, "links": [], - "modified": "2020-02-02 10:37:16.650836", + "modified": "2020-10-26 12:55:00.778201", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Material Request Warehouse", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], + "permissions": [], "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", From ea49e30bfdda95c48c2a6407906c9836269cc70c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 26 Oct 2020 15:30:52 +0530 Subject: [PATCH 05/26] 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 689b77d1ed39027c2a13b415a1f86a69cafeee07 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 26 Oct 2020 16:28:39 +0530 Subject: [PATCH 06/26] fix: Don't show buttons if status is Completed --- .../production_plan/production_plan.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index b29fd4cf43..b723387a09 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -57,31 +57,31 @@ frappe.ui.form.on('Production Plan', { if (frm.doc.docstatus === 1) { frm.trigger("show_progress"); - if (frm.doc.status === "Closed") { - frm.add_custom_button(__("Re-open"), function() { - frm.events.close_open_production_plan(frm, false); - }, __("Status")); - } else { - frm.add_custom_button(__("Close"), function() { - frm.events.close_open_production_plan(frm, true); - }, __("Status")); + if (frm.doc.status !== "Completed") { + if (frm.doc.po_items && frm.doc.status !== "Closed") { + frm.add_custom_button(__("Work Order"), ()=> { + frm.trigger("make_work_order"); + }, __('Create')); + } + + if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) { + frm.add_custom_button(__("Material Request"), ()=> { + frm.trigger("make_material_request"); + }, __('Create')); + } + + if (frm.doc.status === "Closed") { + frm.add_custom_button(__("Re-open"), function() { + frm.events.close_open_production_plan(frm, false); + }, __("Status")); + } else { + frm.add_custom_button(__("Close"), function() { + frm.events.close_open_production_plan(frm, true); + }, __("Status")); + } } } - if (frm.doc.docstatus === 1 && frm.doc.po_items - && !in_list(['Closed', 'Completed'], frm.doc.status)) { - frm.add_custom_button(__("Work Order"), ()=> { - frm.trigger("make_work_order"); - }, __('Create')); - } - - if (frm.doc.docstatus === 1 && frm.doc.mr_items - && !in_list(['Material Requested', 'Completed', 'Closed'], frm.doc.status)) { - frm.add_custom_button(__("Material Request"), ()=> { - frm.trigger("make_material_request"); - }, __('Create')); - } - if (frm.doc.status !== "Closed") { frm.page.set_inner_btn_group_as_primary(__('Create')); } From fa5fe18b70168c7e6784f042004e2228adf511a5 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 26 Oct 2020 19:27:02 +0530 Subject: [PATCH 07/26] 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 cf1428904a0e81b6f0c67926516daa9fd01c0ade Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 27 Oct 2020 17:36:30 +0530 Subject: [PATCH 08/26] 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 09/26] 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 a248dfb9a5f58fae3219d9bbb7bcc1fa8e5d4bfe Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 16 Jul 2020 17:27:26 +0530 Subject: [PATCH 10/26] fix: multiple pricing rules are not working on selling side --- .../doctype/pricing_rule/pricing_rule.py | 38 ++++++++++--------- .../accounts/doctype/pricing_rule/utils.py | 15 ++++++++ erpnext/controllers/accounts_controller.py | 17 +++++++++ erpnext/controllers/taxes_and_totals.py | 11 ++++-- 4 files changed, 59 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 454776e4e7..149c47673c 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -60,6 +60,15 @@ class PricingRule(Document): if self.price_or_product_discount == 'Price' and not self.rate_or_discount: throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) + if self.apply_discount_on_rate: + if not self.priority: + throw(_("As the field {0} is enabled, the field {1} is mandatory.") + .format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))) + + if self.priority and cint(self.priority) == 1: + throw(_("As the field {0} is enabled, the value of the field {1} should be more than 1.") + .format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))) + def validate_applicable_for_selling_or_buying(self): if not self.selling and not self.buying: throw(_("Atleast one of the Selling or Buying must be selected")) @@ -226,12 +235,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa item_details = frappe._dict({ "doctype": args.doctype, + "has_margin": False, "name": args.name, "parent": args.parent, "parenttype": args.parenttype, - "child_docname": args.get('child_docname'), - "discount_percentage_on_rate": [], - "discount_amount_on_rate": [] + "child_docname": args.get('child_docname') }) if args.ignore_pricing_rule or not args.item_code: @@ -279,6 +287,10 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa else: get_product_discount_rule(pricing_rule, item_details, args, doc) + if not item_details.get("has_margin"): + item_details.margin_type = None + item_details.margin_rate_or_amount = 0.0 + item_details.has_pricing_rule = 1 item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules]) @@ -330,13 +342,11 @@ def get_pricing_rule_details(args, pricing_rule): def apply_price_discount_rule(pricing_rule, item_details, args): item_details.pricing_rule_for = pricing_rule.rate_or_discount - if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency) + if ((pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == args.currency) or (pricing_rule.margin_type == 'Percentage')): item_details.margin_type = pricing_rule.margin_type item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount - else: - item_details.margin_type = None - item_details.margin_rate_or_amount = 0.0 + item_details.has_margin = True if pricing_rule.rate_or_discount == 'Rate': pricing_rule_rate = 0.0 @@ -351,9 +361,9 @@ def apply_price_discount_rule(pricing_rule, item_details, args): if pricing_rule.rate_or_discount != apply_on: continue field = frappe.scrub(apply_on) - if pricing_rule.apply_discount_on_rate: - discount_field = "{0}_on_rate".format(field) - item_details[discount_field].append(pricing_rule.get(field, 0)) + if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"): + # Apply discount on discounted rate + item_details[field] += ((100 - item_details[field]) * (pricing_rule.get(field, 0) / 100)) else: if field not in item_details: item_details.setdefault(field, 0) @@ -361,14 +371,6 @@ def apply_price_discount_rule(pricing_rule, item_details, args): item_details[field] += (pricing_rule.get(field, 0) if pricing_rule else args.get(field, 0)) -def set_discount_amount(rate, item_details): - for field in ['discount_percentage_on_rate', 'discount_amount_on_rate']: - for d in item_details.get(field): - dis_amount = (rate * d / 100 - if field == 'discount_percentage_on_rate' else d) - rate -= dis_amount - item_details.rate = rate - def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules, get_pricing_rule_items) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 25840d4fb7..1a58b1dba1 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -42,6 +42,7 @@ def get_pricing_rules(args, doc=None): if not pricing_rules: return [] if apply_multiple_pricing_rules(pricing_rules): + pricing_rules = sorted_by_priority(pricing_rules) for pricing_rule in pricing_rules: pricing_rule = filter_pricing_rules(args, pricing_rule, doc) if pricing_rule: @@ -53,6 +54,20 @@ def get_pricing_rules(args, doc=None): return rules +def sorted_by_priority(pricing_rules): + # If more than one pricing rules, then sort by priority + pricing_rules_list = [] + pricing_rule_dict = {} + for pricing_rule in pricing_rules: + if not pricing_rule.get("priority"): continue + + pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule) + + for key in sorted(pricing_rule_dict): + pricing_rules_list.append(pricing_rule_dict.get(key)) + + return pricing_rules_list or pricing_rules + def filter_pricing_rule_based_on_condition(pricing_rules, doc=None): filtered_pricing_rules = [] if doc: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 983cfa8c15..6108a614ca 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -263,6 +263,7 @@ class AccountsController(TransactionBase): if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"): parent_dict.update({"customer": parent_dict.get("party_name")}) + self.pricing_rules = [] for item in self.get("items"): if item.get("item_code"): args = parent_dict.copy() @@ -301,6 +302,7 @@ class AccountsController(TransactionBase): if ret.get("pricing_rules"): self.apply_pricing_rule_on_items(item, ret) + self.set_pricing_rule_details(item, ret) if self.doctype == "Purchase Invoice": self.set_expense_account(for_validate) @@ -322,6 +324,9 @@ class AccountsController(TransactionBase): if item.get('discount_amount'): item.rate = item.price_list_rate - item.discount_amount + if item.get("apply_discount_on_discounted_rate") and pricing_rule_args.get("rate"): + item.rate = pricing_rule_args.get("rate") + elif pricing_rule_args.get('free_item_data'): apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data')) @@ -335,6 +340,18 @@ class AccountsController(TransactionBase): frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}") .format(item.idx, frappe.bold(title), frappe.bold(item.item_code))) + def set_pricing_rule_details(self, item_row, args): + pricing_rules = get_applied_pricing_rules(args.get("pricing_rules")) + if not pricing_rules: return + + for pricing_rule in pricing_rules: + self.append("pricing_rules", { + "pricing_rule": pricing_rule, + "item_code": item_row.item_code, + "child_docname": item_row.name, + "rule_applied": True + }) + def set_taxes(self): if not self.meta.get_field("taxes"): return diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 92cfdb7f1a..81d07c1327 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -608,16 +608,19 @@ class calculate_taxes_and_totals(object): base_rate_with_margin = 0.0 if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: + has_margin = False for d in get_applied_pricing_rules(item.pricing_rules): pricing_rule = frappe.get_cached_doc('Pricing Rule', d) - if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\ + if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\ or (pricing_rule.margin_type == 'Percentage'): item.margin_type = pricing_rule.margin_type item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount - else: - item.margin_type = None - item.margin_rate_or_amount = 0.0 + has_margin = True + + if not has_margin: + item.margin_type = None + item.margin_rate_or_amount = 0.0 if item.margin_type and item.margin_rate_or_amount: margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100 From 7d9dd3c6281b7c35f7be04b0f61a9ccfc8266e42 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 28 Oct 2020 17:07:10 +0530 Subject: [PATCH 11/26] 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 12/26] 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 13/26] 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 0fa741b267631d7fbc16fae098905eebb6cb5b80 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 28 Oct 2020 16:51:42 +0530 Subject: [PATCH 14/26] added test cases --- .../doctype/pricing_rule/pricing_rule.json | 6 +-- .../doctype/pricing_rule/test_pricing_rule.py | 48 +++++++++++++++---- .../accounts/doctype/pricing_rule/utils.py | 14 +++--- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index c681c897fc..cc8ed4bc49 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -504,10 +504,10 @@ }, { "default": "0", - "depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules", + "depends_on": "eval:in_list(['Discount Percentage'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules", "fieldname": "apply_discount_on_rate", "fieldtype": "Check", - "label": "Apply Discount on Rate" + "label": "Apply Discount on Discounted Rate" }, { "default": "0", @@ -563,7 +563,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2020-08-26 12:24:44.740734", + "modified": "2020-10-28 16:53:14.416172", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 3555ca895f..22a031c162 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,7 +385,7 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") - + def test_cumulative_pricing_rule(self): frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') test_record = { @@ -429,34 +429,61 @@ class TestPricingRule(unittest.TestCase): details = get_item_details(args) self.assertTrue(details) - + def test_pricing_rule_for_condition(self): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") - + make_pricing_rule(selling=1, margin_type="Percentage", \ condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10) - + # Incorrect Customer and Correct is_return value si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0) si.items[0].price_list_rate = 1000 si.submit() item = si.items[0] self.assertEquals(item.rate, 100) - + # Correct Customer and Incorrect is_return value si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1) si.items[0].price_list_rate = 1000 si.submit() item = si.items[0] self.assertEquals(item.rate, 100) - + # Correct Customer and correct is_return value si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0) si.items[0].price_list_rate = 1000 si.submit() item = si.items[0] self.assertEquals(item.rate, 900) - + + def test_multiple_pricing_rules(self): + make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1, + title="_Test Pricing Rule 1") + make_pricing_rule(discount_percentage=10, selling=1, title="_Test Pricing Rule 2", priority=2, + apply_multiple_pricing_rules=1) + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1) + self.assertEqual(si.items[0].discount_percentage, 30) + si.delete() + + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + + def test_multiple_pricing_rules_with_apply_discount_on_discounted_rate(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + + make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1, + title="_Test Pricing Rule 1") + make_pricing_rule(discount_percentage=10, selling=1, priority=2, + apply_discount_on_rate=1, title="_Test Pricing Rule 2", apply_multiple_pricing_rules=1) + + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1) + self.assertEqual(si.items[0].discount_percentage, 28) + si.delete() + + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + def make_pricing_rule(**args): args = frappe._dict(args) @@ -468,6 +495,7 @@ def make_pricing_rule(**args): "applicable_for": args.applicable_for, "selling": args.selling or 0, "currency": "USD", + "apply_discount_on_rate": args.apply_discount_on_rate or 0, "buying": args.buying or 0, "min_qty": args.min_qty or 0.0, "max_qty": args.max_qty or 0.0, @@ -476,9 +504,13 @@ def make_pricing_rule(**args): "rate": args.rate or 0.0, "margin_type": args.margin_type, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0, - "condition": args.condition or '' + "condition": args.condition or '', + "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 }) + if args.get("priority"): + doc.priority = args.get("priority") + apply_on = doc.apply_on.replace(' ', '_').lower() child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'} doc.append(child_table.get(doc.apply_on), { diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 1a58b1dba1..b003328cc4 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -14,9 +14,8 @@ import frappe from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.get_item_details import get_conversion_factor -from frappe import _, throw -from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today - +from frappe import _, bold +from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money class MultiplePricingRuleConflict(frappe.ValidationError): pass @@ -299,12 +298,13 @@ def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, tr fieldname = field if fieldname: - msg = _("""If you {0} {1} quantities of the item {2}, the scheme {3} - will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description) + msg = (_("If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.") + .format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description))) if fieldname in ['min_amt', 'max_amt']: - msg = _("""If you {0} {1} worth item {2}, the scheme {3} will be applied on the item. - """).format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description) + msg = (_("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.") + .format(type_of_transaction, fmt_money(args.get(fieldname), currency=args.get("currency")), + bold(item_code), bold(args.rule_description))) frappe.msgprint(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 15/26] 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 16/26] 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", From b193731158a21c0d6a76dd28e3a966258225df70 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 29 Oct 2020 13:23:50 +0530 Subject: [PATCH 17/26] fix: pos profile has no attr 'show_only_available_items' (#23758) --- erpnext/accounts/doctype/pos_profile/pos_profile.json | 9 ++++++++- erpnext/selling/page/point_of_sale/point_of_sale.py | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 4e22218c6e..570111acac 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -14,6 +14,7 @@ "column_break_9", "update_stock", "ignore_pricing_rule", + "hide_unavailable_items", "warehouse", "campaign", "company_address", @@ -307,13 +308,19 @@ "fieldtype": "Check", "label": "Update Stock", "read_only": 1 + }, + { + "default": "0", + "fieldname": "hide_unavailable_items", + "fieldtype": "Check", + "label": "Hide Unavailable Items" } ], "icon": "icon-cog", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-20 13:16:50.665081", + "modified": "2020-10-29 13:18:38.795925", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index e5b50d7789..6b9939e8ef 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -14,11 +14,11 @@ from six import string_types def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""): data = dict() result = [] - warehouse, show_only_available_items = "", False + warehouse, hide_unavailable_items = "", False allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock') if not allow_negative_stock: - warehouse, show_only_available_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'show_only_available_items']) + warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items']) if not frappe.db.exists('Item Group', item_group): item_group = get_root_of('Item Group') @@ -48,7 +48,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) bin_join_selection, bin_join_condition = "", "" - if show_only_available_items: + if hide_unavailable_items: bin_join_selection = ", `tabBin` bin" bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0" From d0c2700c8541ee35186c1dea339d824ea3f6a50a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 29 Oct 2020 18:52:16 +0530 Subject: [PATCH 18/26] fix: subscription test case (#23763) Co-authored-by: Marica --- erpnext/accounts/doctype/subscription/test_subscription.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 811fc356cf..c17fccdce0 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -237,7 +237,7 @@ class TestSubscription(unittest.TestCase): subscription.party_type = 'Customer' subscription.party = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) - subscription.start_date = '2018-01-01' + subscription.start_date = add_days(nowdate(), -1000) subscription.insert() subscription.process() # generate first invoice From bf54ea9976fbf4cf8013e2a78251bb433a74066f Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 29 Oct 2020 18:54:36 +0530 Subject: [PATCH 19/26] chore: (Production Plan) Simplify and fix translation in message popup (#23753) --- .../doctype/production_plan/production_plan.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index b6552d5d6b..feac9c8fa2 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -735,10 +735,12 @@ def get_items_for_material_requests(doc, warehouses=None): mr_items = new_mr_items if not mr_items: - frappe.msgprint(_("""As raw materials projected quantity is more than required quantity, - there is no need to create material request for the warehouse {0}. - Still if you want to make material request, - kindly enable Ignore Existing Projected Quantity checkbox""").format(doc.get('for_warehouse'))) + to_enable = frappe.bold(_("Ignore Existing Projected Quantity")) + warehouse = frappe.bold(doc.get('for_warehouse')) + message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "

" + message += _(" If you still want to proceed, please enable {0}.").format(to_enable) + + frappe.msgprint(message, title=_("Note")) return mr_items From 8f6bd851d37a7a456dd1f02a5377e91f9b5b20d5 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 29 Oct 2020 18:55:52 +0530 Subject: [PATCH 20/26] chore: Show Toast alert feedback on Scanning Barcode (#23760) --- erpnext/public/js/controllers/transaction.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 02b1dc0beb..1358a4bd08 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -352,9 +352,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ let show_description = function(idx, exist = null) { if (exist) { - scan_barcode_field.set_new_description(__('Row #{0}: Qty increased by 1', [idx])); + frappe.show_alert({ + message: __('Row #{0}: Qty increased by 1', [idx]), + indicator: 'green' + }); } else { - scan_barcode_field.set_new_description(__('Row #{0}: Item added', [idx])); + frappe.show_alert({ + message: __('Row #{0}: Item added', [idx]), + indicator: 'green' + }); } } @@ -365,7 +371,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }).then(r => { const data = r && r.message; if (!data || Object.keys(data).length === 0) { - scan_barcode_field.set_new_description(__('Cannot find Item with this barcode')); + frappe.show_alert({ + message: __('Cannot find Item with this Barcode'), + indicator: 'red' + }); return; } From 9c67629e281696bf673fc4582ebf59a50b4b05ea Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 29 Oct 2020 19:51:57 +0530 Subject: [PATCH 21/26] fix: Ignore cancelled entries instock balance report (#23757) Co-authored-by: Nabin Hait --- erpnext/stock/report/stock_balance/stock_balance.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 042087a4a7..1339d9b682 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -168,6 +168,7 @@ def get_stock_ledger_entries(filters, items): from `tabStock Ledger Entry` sle force index (posting_sort_index) where sle.docstatus < 2 %s %s + and is_cancelled = 0 order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec (item_conditions_sql, conditions), as_dict=1) From 9db26155ad6fea07e4d03a14eb9e6f33d8b429ae Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 29 Oct 2020 22:39:21 +0530 Subject: [PATCH 22/26] fix: set SLA variance in seconds for Duration fieldtype (#23765) * fix: calculate SLA variance in seconds for Duration fieldtype * fix: sider --- erpnext/support/doctype/issue/issue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 920c13c38d..62b39cced5 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff +from frappe.utils import now_datetime, getdate, get_weekdays, add_to_date, get_time, get_datetime, time_diff_in_seconds from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user @@ -355,13 +355,13 @@ def set_service_level_agreement_variance(issue=None): doc = frappe.get_doc("Issue", issue.name) if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer - variance = round(time_diff_in_hours(doc.response_by, current_time), 2) + variance = round(time_diff_in_seconds(doc.response_by, current_time), 2) frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False) if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False) if not doc.resolution_date: # resolution_date set when issue has been closed - variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2) + variance = round(time_diff_in_seconds(doc.resolution_by, current_time), 2) frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False) if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False) From ca5f5cb367b1522835e85c65bdfd809aa6e45620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Ryckel?= Date: Thu, 29 Oct 2020 20:34:13 +0300 Subject: [PATCH 23/26] Update student_attendance.py (#23761) Co-authored-by: Rucha Mahabal --- .../education/doctype/student_attendance/student_attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py index 595dff9b9d..72a8f55c66 100644 --- a/erpnext/education/doctype/student_attendance/student_attendance.py +++ b/erpnext/education/doctype/student_attendance/student_attendance.py @@ -75,6 +75,6 @@ class StudentAttendance(Document): }) if attendance_record: - record = get_link_to_form('Attendance Record', attendance_record) + record = get_link_to_form('Student Attendance', attendance_record) frappe.throw(_('Student Attendance record {0} already exists against the Student {1}') .format(record, frappe.bold(self.student)), title=_('Duplicate Entry')) From 71913c36a7d97a93111887a751729378ab488ca1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 30 Oct 2020 02:47:39 +0530 Subject: [PATCH 24/26] fix: po_detail field has no value for subcontracted stock entry --- .../buying/doctype/purchase_order/test_purchase_order.py | 6 +++++- erpnext/controllers/buying_controller.py | 6 +++--- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++++++ 3 files changed, 17 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 0181ae78b4..d568ef1ced 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -876,7 +876,7 @@ class TestPurchaseOrder(unittest.TestCase): }, { "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 + "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos" }, ] @@ -885,6 +885,10 @@ class TestPurchaseOrder(unittest.TestCase): se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) se.submit() + # Test po_detail field has value or not + for item_row in se.items: + self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name) + 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 diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index f376836f7b..9ee83e3481 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -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 = (raw_material.rm_item_code, item.purchase_order) + rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order) raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {}) consumed_qty = raw_material_data.get('qty', 0) @@ -910,7 +910,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders): 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]) + pr_key = (data.rm_item_code, data.main_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, @@ -936,7 +936,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders): 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"], + fields=["rm_item_code", "main_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): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4583c5174e..768526705c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -615,6 +615,15 @@ class StockEntry(StockController): 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))) + elif not row.po_detail: + filters = { + "parent": self.purchase_order, "docstatus": 1, + "rm_item_code": row.item_code, "main_item_code": row.subcontracted_item + } + + po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name") + if po_detail: + row.db_set("po_detail", po_detail) def validate_bom(self): for d in self.get('items'): From 32442008dcdf0028584ff8bd9501650eff9189d1 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 30 Oct 2020 15:30:18 +0530 Subject: [PATCH 25/26] fix: asset finance book posting date fix (#23778) --- .../assets/doctype/asset_finance_book/asset_finance_book.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index 79fcb957d4..d422876047 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -55,6 +55,7 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Depreciation Posting Date", + "mandatory_depends_on": "eval:parent.doctype == 'Asset'", "reqd": 1 }, { @@ -86,7 +87,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-09-16 12:11:30.631788", + "modified": "2020-10-30 15:22:29.119868", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", From e1781dc0bb0afab5a2d96712e421aae15e8cbd3f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 30 Oct 2020 15:34:04 +0530 Subject: [PATCH 26/26] fix: patch to update reason for leaving in employee --- erpnext/patches.txt | 1 + .../update_reason_for_resignation_in_employee.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 8b34eaa0a8..5ef53715fb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -732,3 +732,4 @@ erpnext.patches.v13_0.set_youtube_video_id erpnext.patches.v13_0.print_uom_after_quantity_patch erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail +erpnext.patches.v13_0.update_reason_for_resignation_in_employee diff --git a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py new file mode 100644 index 0000000000..792118fbee --- /dev/null +++ b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py @@ -0,0 +1,15 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("hr", "doctype", "employee") + + if frappe.db.has_column("Employee", "reason_for_resignation"): + frappe.db.sql(""" UPDATE `tabEmployee` + SET reason_for_leaving = reason_for_resignation + WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null + """) +