diff --git a/erpnext/buying/__init__.py b/erpnext/buying/__init__.py index e69de29bb2..baffc48825 100644 --- a/erpnext/buying/__init__.py +++ b/erpnext/buying/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 7e5020a08d..26c8c61167 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -39,6 +39,8 @@ class PurchaseOrder(BuyingController): super(PurchaseOrder, self).validate() self.set_status() + + self.validate_supplier() validate_for_items(self) self.check_for_closed_status() @@ -65,6 +67,17 @@ class PurchaseOrder(BuyingController): } }) + def validate_supplier(self): + prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos') + if prevent_po: + standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status') + frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.").format(self.supplier, standing)) + + warn_po = frappe.db.get_value("Supplier", self.supplier, 'warn_pos') + if warn_po: + standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status') + frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution.").format(self.supplier, standing), title=_("Caution"), indicator='orange') + def validate_minimum_order_qty(self): items = list(set([d.item_code for d in self.get("items")])) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 9109239e93..666a1c6e8a 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -21,6 +21,7 @@ STANDARD_USERS = ("Guest", "Administrator") class RequestforQuotation(BuyingController): def validate(self): self.validate_duplicate_supplier() + self.validate_supplier_list() validate_for_items(self) self.update_email_id() @@ -29,6 +30,17 @@ class RequestforQuotation(BuyingController): if len(supplier_list) != len(set(supplier_list)): frappe.throw(_("Same supplier has been entered multiple times")) + def validate_supplier_list(self): + for d in self.suppliers: + prevent_rfqs = frappe.db.get_value("Supplier", d.supplier, 'prevent_rfqs') + if prevent_rfqs: + standing = frappe.db.get_value("Supplier Scorecard",d.supplier, 'status') + frappe.throw(_("RFQs are not allowed for {0} due to a scorecard standing of {1}").format(d.supplier, standing)) + warn_rfqs = frappe.db.get_value("Supplier", d.supplier, 'warn_rfqs') + if warn_rfqs: + standing = frappe.db.get_value("Supplier Scorecard",d.supplier, 'status') + frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and RFQs to this supplier should be issued with caution.").format(d.supplier, standing), title=_("Caution"), indicator='orange') + def update_email_id(self): for rfq_supplier in self.suppliers: if not rfq_supplier.email_id: diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 58a1fc7464..711e05d913 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -322,6 +322,126 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn POs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent POs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -850,7 +970,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-13 14:29:16.310834", + "modified": "2017-07-06 16:40:46.935608", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/buying/doctype/supplier_scorecard/__init__.py b/erpnext/buying/doctype/supplier_scorecard/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js new file mode 100644 index 0000000000..a3a14147f2 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js @@ -0,0 +1,146 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* global frappe, refresh_field */ + +frappe.ui.form.on("Supplier Scorecard", { + + onload: function(frm) { + + if (frm.doc.indicator_color !== "") { + frm.set_indicator_formatter("status", function(doc) { + return doc.indicator_color.toLowerCase(); + }); + } + if (frm.doc.__unsaved == 1) { + loadAllCriteria(frm); + loadAllStandings(frm); + } + + }, + refresh: function(frm) { + if (frm.dashboard.hasOwnProperty('heatmap')) { + frm.dashboard.heatmap.setLegend([0,20,40,60,80,101],["#991600","#169900"]); + } + } + +}); + +frappe.ui.form.on("Supplier Scorecard Scoring Standing", { + + standing_name: function(frm, cdt, cdn) { + if (frm.doc.standing_name != undefined) { + var d = frappe.get_doc(cdt, cdn); + return frm.call({ + method: "erpnext.buying.doctype.supplier_scorecard_standing.supplier_scorecard_standing.get_scoring_standing", + child: d, + args: { + standing_name: d.standing_name + } + }); + } + } +}); + +frappe.ui.form.on("Supplier Scorecard Scoring Variable", { + + variable_label: function(frm, cdt, cdn) { + if (frm.doc.variable_label != undefined) { + var d = frappe.get_doc(cdt, cdn); + return frm.call({ + method: "erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable.get_scoring_variable", + child: d, + args: { + variable_label: d.variable_label + } + }); + } + } +}); + +frappe.ui.form.on("Supplier Scorecard Scoring Criteria", { + + criteria_name: function(frm, cdt, cdn) { + if (frm.doc.criteria_name != undefined) { + var d = frappe.get_doc(cdt, cdn); + frm.call({ + method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_variables", + args: { + criteria_name: d.criteria_name + }, + callback: function(r) { + for (var i = 0; i < r.message.length; i++) + { + var exists = false; + for (var j = 0; j < frm.doc.variables.length; j++) + { + if(!frm.doc.variables[j].hasOwnProperty("variable_label")) { + frm.get_field("variables").grid.grid_rows[j].remove(); + } + else if(frm.doc.variables[j].variable_label === r.message[i]) { + exists = true; + } + } + if (!exists){ + var new_row = frm.add_child("variables"); + new_row.variable_label = r.message[i]; + frm.script_manager.trigger("variable_label", new_row.doctype, new_row.name); + } + + } + refresh_field("variables"); + } + }); + return frm.call({ + method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_scoring_criteria", + child: d, + args: { + criteria_name: d.criteria_name + } + }); + } + } +}); + +var loadAllCriteria = function(frm) { + frappe.call({ + method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_criteria_list", + callback: function(r) { + for (var j = 0; j < frm.doc.criteria.length; j++) + { + if(!frm.doc.criteria[j].hasOwnProperty("criteria_name")) { + frm.get_field("criteria").grid.grid_rows[j].remove(); + } + } + for (var i = 0; i < r.message.length; i++) + { + var new_row = frm.add_child("criteria"); + new_row.criteria_name = r.message[i].name; + frm.script_manager.trigger("criteria_name", new_row.doctype, new_row.name); + } + refresh_field("criteria"); + } + }); +}; +var loadAllStandings = function(frm) { + frappe.call({ + method: "erpnext.buying.doctype.supplier_scorecard_standing.supplier_scorecard_standing.get_standings_list", + callback: function(r) { + for (var j = 0; j < frm.doc.standings.length; j++) + { + if(!frm.doc.standings[j].hasOwnProperty("standing_name")) { + frm.get_field("standings").grid.grid_rows[j].remove(); + } + } + for (var i = 0; i < r.message.length; i++) + { + var new_row = frm.add_child("standings"); + new_row.standing_name = r.message[i].name; + frm.script_manager.trigger("standing_name", new_row.doctype, new_row.name); + } + refresh_field("standings"); + } + }); +}; + + diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json new file mode 100644 index 0000000000..d7f24c9082 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json @@ -0,0 +1,701 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:supplier", + "beta": 1, + "creation": "2017-05-29 01:40:54.786555", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "supplier", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Supplier", + "length": 0, + "no_copy": 0, + "options": "Supplier", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "supplier_score", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Supplier Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "indicator_color", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Indicator Color", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "status", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Status", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Per Month", + "fieldname": "period", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Evaluation Period", + "length": 0, + "no_copy": 0, + "options": "Per Month\nPer Week\nPer Year", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "scoring_setup", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Scoring Setup", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )", + "description": "Scorecard variables can be used, as well as:\n{total_score} (the total score from that period),\n{period_number} (the number of periods to present day)\n", + "fieldname": "weighting_function", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Weighting Function", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "standings", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Scoring Standings", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Scoring Standing", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "criteria_setup", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Criteria Setup", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "criteria", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Scoring Criteria", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Scoring Criteria", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "variables", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Supplier Variables", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Scoring Variable", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.status != 'Unknown'", + "columns": 0, + "fieldname": "scorecard_actions", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Scorecard Actions", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn for new Request for Quotations", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn for new Purchase Orders", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent POs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_supplier", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Notify Supplier", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_employee", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Notify Employee", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-07-12 07:33:11.874949", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py new file mode 100644 index 0000000000..e13d22ab57 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import throw, _ +from frappe.model.document import Document +import time +from datetime import timedelta +from frappe.utils import nowdate, get_last_day, getdate, add_days, add_years +from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import make_supplier_scorecard + +class SupplierScorecard(Document): + + def validate(self): + self.validate_standings() + self.validate_criteria_weights() + self.calculate_total_score() + self.update_standing() + + def on_update(self): + score = make_all_scorecards(self.name) + if score > 0: + self.save() + + def validate_standings(self): + # Check that there are no overlapping scores and check that there are no missing scores + score = 0 + for c1 in self.standings: + for c2 in self.standings: + if c1 != c2: + if (c1.max_grade > c2.min_grade and c1.min_grade < c2.max_grade): + throw(_('Overlap in scoring between {0} and {1}').format(c1.standing_name,c2.standing_name)) + if c2.min_grade == score: + score = c2.max_grade + if score < 100: + throw(_('Unable to find score starting at {0}. You need to have standing scores covering 0 to 100').format(score)) + + def validate_criteria_weights(self): + + weight = 0 + for c in self.criteria: + weight += c.weight + + if weight != 100: + throw(_('Criteria weights must add up to 100%')) + + def calculate_total_score(self): + scorecards = frappe.db.sql(""" + SELECT + scp.name + FROM + `tabSupplier Scorecard Period` scp + WHERE + scp.scorecard = %(sc)s + ORDER BY + scp.end_date DESC""", + {"sc": self.name}, as_dict=1) + + period = 0 + total_score = 0 + total_max_score = 0 + for scp in scorecards: + my_sc = frappe.get_doc('Supplier Scorecard Period', scp.name) + my_scp_weight = self.weighting_function + my_scp_weight = my_scp_weight.replace('{period_number}', str(period)) + + my_scp_maxweight = my_scp_weight.replace('{total_score}', '100') + my_scp_weight = my_scp_weight.replace('{total_score}', str(my_sc.total_score)) + + max_score = my_sc.calculate_weighted_score(my_scp_maxweight) + score = my_sc.calculate_weighted_score(my_scp_weight) + + total_score += score + total_max_score += max_score + period += 1 + if total_max_score > 0: + self.supplier_score = round(100.0 * (total_score / total_max_score) ,1) + else: + self.supplier_score = 100 + + def update_standing(self): + # Get the setup document + + for standing in self.standings: + if (not standing.min_grade or (standing.min_grade <= self.supplier_score)) and \ + (not standing.max_grade or (standing.max_grade > self.supplier_score)): + self.status = standing.standing_name + self.indicator_color = standing.standing_color + self.notify_supplier = standing.notify_supplier + self.notify_employee = standing.notify_employee + self.employee_link = standing.employee_link + + #Update supplier standing info + for fieldname in ('prevent_pos', 'prevent_rfqs','warn_rfqs','warn_pos'): + self.set(fieldname, standing.get(fieldname)) + frappe.db.set_value("Supplier", self.supplier, fieldname, self.get(fieldname)) + + +@frappe.whitelist() +def get_timeline_data(doctype, name): + # Get a list of all the associated scorecards + scs = frappe.get_doc(doctype, name) + out = {} + timeline_data = {} + scorecards = frappe.db.sql(""" + SELECT + sc.name + FROM + `tabSupplier Scorecard Period` sc + WHERE + sc.scorecard = %(scs)s""", + {"scs": scs.name}, as_dict=1) + + for sc in scorecards: + start_date, end_date, total_score = frappe.db.get_value('Supplier Scorecard Period', sc.name, ['start_date', 'end_date', 'total_score']) + for single_date in daterange(start_date, end_date): + timeline_data[time.mktime(single_date.timetuple())] = total_score + + out['timeline_data'] = timeline_data + return out + +def daterange(start_date, end_date): + for n in range(int ((end_date - start_date).days)+1): + yield start_date + timedelta(n) + +def refresh_scorecards(): + scorecards = frappe.db.sql(""" + SELECT + sc.name + FROM + `tabSupplier Scorecard` sc""", + {}, as_dict=1) + for sc in scorecards: + # Check to see if any new scorecard periods are created + if make_all_scorecards(sc.name) > 0: + # Save the scorecard to update the score and standings + sc.save() + + +@frappe.whitelist() +def make_all_scorecards(docname): + + sc = frappe.get_doc('Supplier Scorecard', docname) + supplier = frappe.get_doc('Supplier',sc.supplier) + + start_date = getdate(supplier.creation) + end_date = get_scorecard_date(sc.period, start_date) + todays = getdate(nowdate()) + + scp_count = 0 + first_start_date = todays + last_end_date = todays + + while (start_date < todays) and (end_date <= todays): + # check to make sure there is no scorecard period already created + scorecards = frappe.db.sql(""" + SELECT + scp.name + FROM + `tabSupplier Scorecard Period` scp + WHERE + scp.scorecard = %(sc)s + AND ( + (scp.start_date > %(end_date)s + AND scp.end_date < %(start_date)s) + OR + (scp.start_date < %(end_date)s + AND scp.end_date > %(start_date)s)) + ORDER BY + scp.end_date DESC""", + {"sc": docname, "start_date": start_date, "end_date": end_date, "supplier": supplier}, as_dict=1) + if len(scorecards) == 0: + period_card = make_supplier_scorecard(docname, None) + period_card.start_date = start_date + period_card.end_date = end_date + period_card.save() + scp_count = scp_count + 1 + if start_date < first_start_date: + first_start_date = start_date + last_end_date = end_date + + start_date = getdate(add_days(end_date,1)) + end_date = get_scorecard_date(sc.period, start_date) + if scp_count > 0: + frappe.msgprint(_("Created {0} scorecards for {1} between: ").format(scp_count, sc.supplier) + str(first_start_date) + " - " + str(last_end_date)) + return scp_count + +def get_scorecard_date(period, start_date): + if period == 'Per Week': + end_date = getdate(add_days(start_date,7)) + elif period == 'Per Month': + end_date = get_last_day(start_date) + elif period == 'Per Year': + end_date = add_days(add_years(start_date,1), -1) + return end_date + +def make_default_records(): + install_variable_docs = [ + {"param_name": "total_accepted_items", "variable_label": "Total Accepted Items", \ + "path": "get_total_accepted_items"}, + {"param_name": "total_accepted_amount", "variable_label": "Total Accepted Amount", \ + "path": "get_total_accepted_amount"}, + {"param_name": "total_rejected_items", "variable_label": "Total Rejected Items", \ + "path": "get_total_rejected_items"}, + {"param_name": "total_rejected_amount", "variable_label": "Total Rejected Amount", \ + "path": "get_total_rejected_amount"}, + {"param_name": "total_received_items", "variable_label": "Total Received Items", \ + "path": "get_total_received_items"}, + {"param_name": "total_received_amount", "variable_label": "Total Received Amount", \ + "path": "get_total_received_amount"}, + {"param_name": "rfq_response_days", "variable_label": "RFQ Response Days", \ + "path": "get_rfq_response_days"}, + {"param_name": "sq_total_items", "variable_label": "SQ Total Items", \ + "path": "get_sq_total_items"}, + {"param_name": "sq_total_number", "variable_label": "SQ Total Number", \ + "path": "get_sq_total_number"}, + {"param_name": "rfq_total_number", "variable_label": "RFQ Total Number", \ + "path": "get_rfq_total_number"}, + {"param_name": "rfq_total_items", "variable_label": "RFQ Total Items", \ + "path": "get_rfq_total_items"}, + {"param_name": "tot_item_days", "variable_label": "Total Item Days", \ + "path": "get_item_workdays"}, + {"param_name": "on_time_shipment_num", "variable_label": "# of On Time Shipments", "path": \ + "get_on_time_shipments"}, + {"param_name": "cost_of_delayed_shipments", "variable_label": "Cost of Delayed Shipments", \ + "path": "get_cost_of_delayed_shipments"}, + {"param_name": "cost_of_on_time_shipments", "variable_label": "Cost of On Time Shipments", \ + "path": "get_cost_of_on_time_shipments"}, + {"param_name": "total_working_days", "variable_label": "Total Working Days", \ + "path": "get_total_workdays"}, + {"param_name": "tot_cost_shipments", "variable_label": "Total Cost of Shipments", \ + "path": "get_total_cost_of_shipments"}, + {"param_name": "tot_days_late", "variable_label": "Total Days Late", \ + "path": "get_total_days_late"}, + {"param_name": "total_shipments", "variable_label": "Total Shipments", \ + "path": "get_total_shipments"} + ] + install_standing_docs = [ + {"min_grade": 0.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 30.0, "prevent_pos": 1, \ + "standing_color": "Red", "notify_employee": 0, "standing_name": "Very Poor"}, + {"min_grade": 30.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 50.0, "prevent_pos": 0, \ + "standing_color": "Red", "notify_employee": 0, "standing_name": "Poor"}, + {"min_grade": 50.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 80.0, "prevent_pos": 0, \ + "standing_color": "Green", "notify_employee": 0, "standing_name": "Average"}, + {"min_grade": 80.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 100.0, "prevent_pos": 0, \ + "standing_color": "Blue", "notify_employee": 0, "standing_name": "Excellent"}, + ] + + for d in install_variable_docs: + try: + d['doctype'] = "Supplier Scorecard Variable" + frappe.get_doc(d).insert() + except frappe.NameError: + pass + for d in install_standing_docs: + try: + d['doctype'] = "Supplier Scorecard Standing" + frappe.get_doc(d).insert() + except frappe.NameError: + pass diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py new file mode 100644 index 0000000000..ff7f119253 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py @@ -0,0 +1,15 @@ +from frappe import _ + +def get_data(): + return { + 'heatmap': True, + 'heatmap_message': _('This covers all scorecards tied to this Setup'), + 'fieldname': 'supplier', + 'method' : 'erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.get_timeline_data', + 'transactions': [ + { + 'label': _('Scorecards'), + 'items': ['Supplier Scorecard Period'] + } + ] + } \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js new file mode 100644 index 0000000000..c50916e4fa --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js @@ -0,0 +1,17 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* global frappe, __ */ + +frappe.listview_settings["Supplier Scorecard"] = { + add_fields: ["indicator_color", "status"], + get_indicator: function(doc) { + + if (doc.indicator_color) { + return [__(doc.status), doc.indicator_color.toLowerCase(), "status,=," + doc.status]; + } else { + return [__("Unknown"), "darkgrey", "status,=,''"]; + } + }, + +}; diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py new file mode 100644 index 0000000000..d64d3f683f --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestSupplierScorecard(unittest.TestCase): + + def test_create_scorecard(self): + delete_test_scorecards() + my_doc = make_supplier_scorecard() + doc = my_doc.insert() + self.assertEqual(doc.name, valid_scorecard[0].get("supplier")) + + def test_criteria_weight(self): + delete_test_scorecards() + my_doc = make_supplier_scorecard() + for d in my_doc.criteria: + d.weight = 0 + self.assertRaises(frappe.ValidationError,my_doc.insert) + + def test_missing_variable(self): + delete_test_scorecards() + my_doc = make_supplier_scorecard() + del my_doc.variables + self.assertRaises(frappe.ValidationError,my_doc.insert) + +def make_supplier_scorecard(): + my_doc = frappe.get_doc(valid_scorecard[0]) + + # Make sure the criteria exist (making them) + for d in valid_scorecard[0].get("criteria"): + if not frappe.db.exists("Supplier Scorecard Criteria", d.get("criteria_name")): + d["doctype"] = "Supplier Scorecard Criteria" + d["name"] = d.get("criteria_name") + my_criteria = frappe.get_doc(d) + my_criteria.insert() + return my_doc + + +def delete_test_scorecards(): + my_doc = make_supplier_scorecard() + if frappe.db.exists("Supplier Scorecard", my_doc.name): + # Delete all the periods, then delete the scorecard + frappe.db.sql("""delete from `tabSupplier Scorecard Period` where scorecard = %(scorecard)s""", {'scorecard': my_doc.name}) + frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'""") + frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'""") + frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'""") + frappe.delete_doc(my_doc.doctype, my_doc.name) + +valid_scorecard = [ + { + "standings":[ + { + "min_grade":0.0,"name":"Very Poor", + "prevent_rfqs":1, + "notify_supplier":0, + "doctype":"Supplier Scorecard Standing", + "max_grade":30.0, + "prevent_pos":1, + "warn_pos":0, + "warn_rfqs":0, + "standing_color":"Red", + "notify_employee":0, + "standing_name":"Very Poor", + "parenttype":"Supplier Scorecard", + "parentfield":"standings" + }, + { + "min_grade":30.0, + "name":"Poor", + "prevent_rfqs":1, + "notify_supplier":0, + "doctype":"Supplier Scorecard Standing", + "max_grade":50.0, + "prevent_pos":0, + "warn_pos":0, + "warn_rfqs":0, + "standing_color":"Red", + "notify_employee":0, + "standing_name":"Poor", + "parenttype":"Supplier Scorecard", + "parentfield":"standings" + }, + { + "min_grade":50.0, + "name":"Average", + "prevent_rfqs":0, + "notify_supplier":0, + "doctype":"Supplier Scorecard Standing", + "max_grade":80.0, + "prevent_pos":0, + "warn_pos":0, + "warn_rfqs":0, + "standing_color":"Green", + "notify_employee":0, + "standing_name":"Average", + "parenttype":"Supplier Scorecard", + "parentfield":"standings" + }, + { + "min_grade":80.0, + "name":"Excellent", + "prevent_rfqs":0, + "notify_supplier":0, + "doctype":"Supplier Scorecard Standing", + "max_grade":100.0, + "prevent_pos":0, + "warn_pos":0, + "warn_rfqs":0, + "standing_color":"Blue", + "notify_employee":0, + "standing_name":"Excellent", + "parenttype":"Supplier Scorecard", + "parentfield":"standings" + } + ], + "prevent_pos":0, + "variables": [ + { + "param_name":"cost_of_on_time_shipments", + "doctype":"Supplier Scorecard Scoring Variable", + "parenttype":"Supplier Scorecard", + "variable_label":"Cost of On Time Shipments", + "path":"get_cost_of_on_time_shipments", + "parentfield":"variables" + }, + { + "param_name":"tot_cost_shipments", + "doctype":"Supplier Scorecard Scoring Variable", + "parenttype":"Supplier Scorecard", + "variable_label":"Total Cost of Shipments", + "path":"get_total_cost_of_shipments", + "parentfield":"variables" + }, + { + "param_name":"tot_days_late", + "doctype":"Supplier Scorecard Scoring Variable", + "parenttype":"Supplier Scorecard", + "variable_label":"Total Days Late", + "path":"get_total_days_late", + "parentfield":"variables" + }, + { + "param_name":"total_working_days", + "doctype":"Supplier Scorecard Scoring Variable", + "parenttype":"Supplier Scorecard", + "variable_label":"Total Working Days", + "path":"get_total_workdays", + "parentfield":"variables" + }, + { + "param_name":"on_time_shipment_num", + "doctype":"Supplier Scorecard Scoring Variable", + "parenttype":"Supplier Scorecard", + "variable_label":"# of On Time Shipments", + "path":"get_on_time_shipments", + "parentfield":"variables" + }, + { + "param_name":"total_shipments", + "doctype":"Supplier Scorecard Scoring Variable", + "parenttype":"Supplier Scorecard", + "variable_label":"Total Shipments", + "path":"get_total_shipments", + "parentfield":"variables" + } + ], + "period":"Per Month", + "doctype":"Supplier Scorecard", + "warn_pos":0, + "warn_rfqs":0, + "notify_supplier":0, + "criteria":[ + { + "weight":100.0, + "doctype":"Supplier Scorecard Scoring Criteria", + "formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100 ", + "criteria_name":"Delivery", + "max_score":100.0, + } + ], + "supplier":"_Test Supplier", + "name":"_Test Supplier", + "weighting_function":"{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )", + } +] + diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/__init__.py b/erpnext/buying/doctype/supplier_scorecard_criteria/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js new file mode 100644 index 0000000000..9f8a2dee81 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* global frappe */ + +frappe.ui.form.on("Supplier Scorecard Criteria", { + refresh: function() {} +}); diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json new file mode 100644 index 0000000000..229c386120 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json @@ -0,0 +1,184 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:criteria_name", + "beta": 1, + "creation": "2017-05-29 01:32:43.064891", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "criteria_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Criteria Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "weight", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Criteria Weight", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "100", + "fieldname": "max_score", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Max Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "formula", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Criteria Formula", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-07-17 10:30:47.458285", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Criteria", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py new file mode 100644 index 0000000000..8514022b78 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +import re +from frappe.model.document import Document + +class InvalidFormulaVariable(frappe.ValidationError): pass + +class SupplierScorecardCriteria(Document): + def validate(self): + self.validate_variables() + self.validate_formula() + + def validate_variables(self): + # make sure all the variables exist + _get_variables(self) + + def validate_formula(self): + # evaluate the formula with 0's to make sure it is valid + test_formula = self.formula.replace("\r", "").replace("\n", "") + + regex = r"\{(.*?)\}" + + mylist = re.finditer(regex, test_formula, re.MULTILINE | re.DOTALL) + for dummy1, match in enumerate(mylist): + for dummy2 in range(0, len(match.groups())): + test_formula = test_formula.replace('{' + match.group(1) + '}', "0") + + test_formula = test_formula.replace('<','<').replace('>','>') + try: + frappe.safe_eval(test_formula, None, {'max':max, 'min': min}) + except Exception: + frappe.throw(_("Error evaluating the criteria formula")) + + + +@frappe.whitelist() +def get_scoring_criteria(criteria_name): + criteria = frappe.get_doc("Supplier Scorecard Criteria", criteria_name) + + return criteria + + +@frappe.whitelist() +def get_criteria_list(): + criteria = frappe.db.sql(""" + SELECT + scs.name + FROM + `tabSupplier Scorecard Criteria` scs""", + {}, as_dict=1) + + return criteria + +@frappe.whitelist() +def get_variables(criteria_name): + criteria = frappe.get_doc("Supplier Scorecard Criteria", criteria_name) + return _get_variables(criteria) + +def _get_variables(criteria): + my_variables = [] + regex = r"\{(.*?)\}" + + mylist = re.finditer(regex, criteria.formula, re.MULTILINE | re.DOTALL) + for dummy1, match in enumerate(mylist): + for dummy2 in range(0, len(match.groups())): + try: + #var = frappe.get_doc("Supplier Scorecard Variable", {'param_name' : d}) + var = frappe.db.sql(""" + SELECT + scv.name + FROM + `tabSupplier Scorecard Variable` scv + WHERE + param_name=%(param)s""", + {'param':match.group(1)},)[0][0] + my_variables.append(var) + except Exception: + # Ignore the ones where the variable can't be found + frappe.throw(_('Unable to find variable: ') + str(match.group(1)), InvalidFormulaVariable) + #pass + + + #frappe.msgprint(str(my_variables)) + return my_variables diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py new file mode 100644 index 0000000000..4eef4b4e03 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestSupplierScorecardCriteria(unittest.TestCase): + def test_variables_exist(self): + delete_test_scorecards() + for d in test_good_criteria: + frappe.get_doc(d).insert() + + self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[0]).insert) + + def test_formula_validate(self): + delete_test_scorecards() + self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[1]).insert) + self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[2]).insert) + +def delete_test_scorecards(): + # Delete all the periods so we can delete all the criteria + frappe.db.sql("""delete from `tabSupplier Scorecard Period`""") + frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'""") + frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'""") + frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'""") + + for d in test_good_criteria: + if frappe.db.exists("Supplier Scorecard Criteria", d.get("name")): + # Delete all the periods, then delete the scorecard + frappe.delete_doc(d.get("doctype"), d.get("name")) + + for d in test_bad_criteria: + if frappe.db.exists("Supplier Scorecard Criteria", d.get("name")): + # Delete all the periods, then delete the scorecard + frappe.delete_doc(d.get("doctype"), d.get("name")) + +test_good_criteria = [ + { + "name":"Delivery", + "weight":40.0, + "doctype":"Supplier Scorecard Criteria", + "formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100", + "criteria_name":"Delivery", + "max_score":100.0 + }, +] + +test_bad_criteria = [ + { + "name":"Fake Criteria 1", + "weight":40.0, + "doctype":"Supplier Scorecard Criteria", + "formula":"(({fake_variable} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100", # Invalid variable name + "criteria_name":"Fake Criteria 1", + "max_score":100.0 + }, + { + "name":"Fake Criteria 2", + "weight":40.0, + "doctype":"Supplier Scorecard Criteria", + "formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}))* 100", # Force 0 divided by 0 + "criteria_name":"Fake Criteria 2", + "max_score":100.0 + }, + { + "name":"Fake Criteria 3", + "weight":40.0, + "doctype":"Supplier Scorecard Criteria", + "formula":"(({cost_of_on_time_shipments} {cost_of_on_time_shipments} / {tot_cost_shipments}))* 100", # Two variables beside eachother + "criteria_name":"Fake Criteria 3", + "max_score":100.0 + }, +] \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_period/__init__.py b/erpnext/buying/doctype/supplier_scorecard_period/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js new file mode 100644 index 0000000000..c51e8ab2df --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js @@ -0,0 +1,14 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* global frappe */ + + +frappe.ui.form.on("Supplier Scorecard Period", { + onload: function(frm) { + frm.get_field("variables").grid.toggle_display("value", true); + frm.get_field("criteria").grid.toggle_display("score", true); + + + } +}); diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json new file mode 100644 index 0000000000..0cf651454b --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json @@ -0,0 +1,397 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 1, + "creation": "2017-05-30 00:38:18.773013", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "supplier", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Supplier", + "length": 0, + "no_copy": 0, + "options": "Supplier", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "naming_series", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Naming Series", + "length": 0, + "no_copy": 0, + "options": "SSC-", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_score", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Period Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "start_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Start Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "end_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "End Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "section_break_11", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Calculations", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "criteria", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Criteria", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Scoring Criteria", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "variables", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Variables", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Scoring Variable", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "fieldname": "sec_ref", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Reference", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "scorecard", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Supplier Scorecard Setup", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 1, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-07-12 07:33:26.130861", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Period", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py new file mode 100644 index 0000000000..90b65bd35a --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import throw, _ +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc +import erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable as variable_functions + +class SupplierScorecardPeriod(Document): + + def validate(self): + self.validate_criteria_weights() + self.calculate_variables() + self.calculate_criteria() + self.calculate_score() + + def validate_criteria_weights(self): + + weight = 0 + for c in self.criteria: + weight += c.weight + + if weight != 100: + throw(_('Criteria weights must add up to 100%')) + + def calculate_variables(self): + for var in self.variables: + + if '.' in var.path: + method_to_call = import_string_path(var.path) + var.value = method_to_call(self) + else: + method_to_call = getattr(variable_functions, var.path) + var.value = method_to_call(self) + + + + def calculate_criteria(self): + #Get the criteria + for crit in self.criteria: + + #me = "" + my_eval_statement = crit.formula.replace("\r", "").replace("\n", "") + #for let in my_eval_statement: + # me += let.encode('hex') + " " + #frappe.msgprint(me) + + for var in self.variables: + if var.value: + if var.param_name in my_eval_statement: + my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', "{:.2f}".format(var.value)) + else: + if var.param_name in my_eval_statement: + my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', '0.0') + + #frappe.msgprint(my_eval_statement ) + + my_eval_statement = my_eval_statement.replace('<','<').replace('>','>') + + try: + crit.score = min(crit.max_score, max( 0 ,frappe.safe_eval(my_eval_statement, None, {'max':max, 'min': min}))) + except Exception: + frappe.throw(_("Could not solve criteria score function for {0}. Make sure the formula is valid.".format(crit.criteria_name)),frappe.ValidationError) + crit.score = 0 + + def calculate_score(self): + myscore = 0 + for crit in self.criteria: + myscore += crit.score * crit.weight/100.0 + self.total_score = myscore + + def calculate_weighted_score(self, weighing_function): + my_eval_statement = weighing_function.replace("\r", "").replace("\n", "") + + for var in self.variables: + if var.value: + if var.param_name in my_eval_statement: + my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', "{:.2f}".format(var.value)) + else: + if var.param_name in my_eval_statement: + my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', '0.0') + + my_eval_statement = my_eval_statement.replace('<','<').replace('>','>') + + try: + weighed_score = frappe.safe_eval(my_eval_statement, None, {'max':max, 'min': min}) + except Exception: + frappe.throw(_("Could not solve weighted score function. Make sure the formula is valid."),frappe.ValidationError) + weighed_score = 0 + return weighed_score + + + +def import_string_path(path): + components = path.split('.') + mod = __import__(components[0]) + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +def post_process(source, target): + pass + + +@frappe.whitelist() +def make_supplier_scorecard(source_name, target_doc=None): + #def update_item(obj, target, source_parent): + # target.qty = flt(obj.qty) - flt(obj.received_qty) + # target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor) + # target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate) + # target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * \ + # flt(obj.rate) * flt(source_parent.conversion_rate) + + doc = get_mapped_doc("Supplier Scorecard", source_name, { + "Supplier Scorecard": { + "doctype": "Supplier Scorecard Period" + }, + "Supplier Scorecard Scoring Variable": { + "doctype": "Supplier Scorecard Scoring Variable", + "add_if_empty": True + }, + "Supplier Scorecard Scoring Constraint": { + "doctype": "Supplier Scorecard Scoring Constraint", + "add_if_empty": True + } + }, target_doc, post_process) + + return doc + diff --git a/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py new file mode 100644 index 0000000000..8baa3185ba --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestSupplierScorecardPeriod(unittest.TestCase): + pass diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/__init__.py b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json new file mode 100644 index 0000000000..567738a6d0 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json @@ -0,0 +1,280 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-05-29 01:32:17.988454", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 3, + "fieldname": "criteria_name", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Criteria Name", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Criteria", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "weight", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Criteria Weight", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "100", + "fieldname": "max_score", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Max Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "formula", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Criteria Formula", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "score", + "fieldtype": "Percent", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Score", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-07-12 07:33:41.532361", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Scoring Criteria", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py new file mode 100644 index 0000000000..b64abed8a6 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SupplierScorecardScoringCriteria(Document): + pass diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/__init__.py b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json new file mode 100644 index 0000000000..1fc04bb120 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json @@ -0,0 +1,491 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-05-29 01:36:22.697234", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 3, + "fieldname": "standing_name", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Standing Name", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Standing", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "standing_color", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Color", + "length": 0, + "no_copy": 0, + "options": "Blue\nPurple\nGreen\nYellow\nOrange\nRed", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "min_grade", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Min Grade", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "max_grade", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Max Grade", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "actions", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Actions", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn Purchase Orders", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent Purchase Orders", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_10", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_supplier", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Notify Supplier", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_employee", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Notify Employee", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_link", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Employee ", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-07-12 07:33:20.615684", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Scoring Standing", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py new file mode 100644 index 0000000000..e8ad79f33d --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SupplierScorecardScoringStanding(Document): + pass diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/__init__.py b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json new file mode 100644 index 0000000000..f0e043e47a --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json @@ -0,0 +1,222 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 1, + "creation": "2017-05-29 01:30:06.105240", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 3, + "fieldname": "variable_label", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Variable Name", + "length": 0, + "no_copy": 0, + "options": "Supplier Scorecard Variable", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_custom", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Custom?", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "param_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Parameter Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "path", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Path", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 2, + "fieldname": "value", + "fieldtype": "Float", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Value", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-07-12 07:33:36.671502", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Scoring Variable", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py new file mode 100644 index 0000000000..58a8a99a09 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class SupplierScorecardScoringVariable(Document): + pass diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/__init__.py b/erpnext/buying/doctype/supplier_scorecard_standing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js new file mode 100644 index 0000000000..dccfcc34bb --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js @@ -0,0 +1,10 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* global frappe */ + +frappe.ui.form.on("Supplier Scorecard Standing", { + refresh: function() { + + } +}); diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json new file mode 100644 index 0000000000..b61b4edd72 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json @@ -0,0 +1,424 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:standing_name", + "beta": 1, + "creation": "2017-05-29 01:36:47.893639", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "standing_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Standing Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "standing_color", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Color", + "length": 0, + "no_copy": 0, + "options": "Blue\nPurple\nGreen\nYellow\nOrange\nRed", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "min_grade", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Min Grade", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "max_grade", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Max Grade", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_5", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "warn_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Warn Purchase Orders", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_rfqs", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent RFQs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "prevent_pos", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Prevent Purchase Orders", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_supplier", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Notify Supplier", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "notify_employee", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Notify Other", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee_link", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Other", + "length": 0, + "no_copy": 0, + "options": "Employee", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-07-12 07:33:16.560273", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Standing", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py new file mode 100644 index 0000000000..1ba5d06c53 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, 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 SupplierScorecardStanding(Document): + pass + + +@frappe.whitelist() +def get_scoring_standing(standing_name): + standing = frappe.get_doc("Supplier Scorecard Standing", standing_name) + + return standing + + +@frappe.whitelist() +def get_standings_list(): + standings = frappe.db.sql(""" + SELECT + scs.name + FROM + `tabSupplier Scorecard Standing` scs""", + {}, as_dict=1) + + return standings \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py new file mode 100644 index 0000000000..4d96651313 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestSupplierScorecardStanding(unittest.TestCase): + pass diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/__init__.py b/erpnext/buying/doctype/supplier_scorecard_variable/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js new file mode 100644 index 0000000000..2d74fdd190 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js @@ -0,0 +1,10 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +/* global frappe */ + +frappe.ui.form.on("Supplier Scorecard Variable", { + refresh: function() { + + } +}); diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json new file mode 100644 index 0000000000..d24484025c --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json @@ -0,0 +1,242 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:variable_label", + "beta": 1, + "creation": "2017-05-29 01:30:34.688389", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "variable_label", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Variable Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 1 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_custom", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Custom?", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "param_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Parameter Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "path", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Path", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_5", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-07-12 07:33:31.395262", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Scorecard Variable", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py new file mode 100644 index 0000000000..17c911a000 --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py @@ -0,0 +1,503 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import sys +from frappe import _ +from frappe.model.document import Document +from frappe.utils import getdate + +class VariablePathNotFound(frappe.ValidationError): pass + +class SupplierScorecardVariable(Document): + def validate(self): + self.validate_path_exists() + + def validate_path_exists(self): + if '.' in self.path: + try: + from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import import_string_path + import_string_path(self.path) + except AttributeError: + frappe.throw(_("Could not find path for " + self.path), VariablePathNotFound) + + else: + if not hasattr(sys.modules[__name__], self.path): + frappe.throw(_("Could not find path for " + self.path), VariablePathNotFound) + + +@frappe.whitelist() +def get_scoring_variable(variable_label): + variable = frappe.get_doc("Supplier Scorecard Variable", variable_label) + + return variable + +def get_total_workdays(scorecard): + """ Gets the number of days in this period""" + delta = getdate(scorecard.end_date) - getdate(scorecard.start_date) + return delta.days + +def get_item_workdays(scorecard): + """ Gets the number of days in this period""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + total_item_days = frappe.db.sql(""" + SELECT + SUM(DATEDIFF( %(end_date)s, po_item.schedule_date) * (po_item.qty)) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Order` po + WHERE + po.supplier = %(supplier)s + AND po_item.received_qty < po_item.qty + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.parent = po.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not total_item_days: + total_item_days = 0 + return total_item_days + + + +def get_total_cost_of_shipments(scorecard): + """ Gets the total cost of all shipments in the period (based on Purchase Orders)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(po_item.base_amount) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Order` po + WHERE + po.supplier = %(supplier)s + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.docstatus = 1 + AND po_item.parent = po.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if data: + return data + else: + return 0 + +def get_cost_of_delayed_shipments(scorecard): + """ Gets the total cost of all delayed shipments in the period (based on Purchase Receipts - POs)""" + return get_total_cost_of_shipments(scorecard) - get_cost_of_on_time_shipments(scorecard) + +def get_cost_of_on_time_shipments(scorecard): + """ Gets the total cost of all on_time shipments in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + + total_delivered_on_time_costs = frappe.db.sql(""" + SELECT + SUM(pr_item.base_amount) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Receipt Item` pr_item, + `tabPurchase Order` po, + `tabPurchase Receipt` pr + WHERE + po.supplier = %(supplier)s + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.schedule_date >= pr.posting_date + AND pr_item.docstatus = 1 + AND pr_item.purchase_order_item = po_item.name + AND po_item.parent = po.name + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if total_delivered_on_time_costs: + return total_delivered_on_time_costs + else: + return 0 + + +def get_total_days_late(scorecard): + """ Gets the number of item days late in the period (based on Purchase Receipts vs POs)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + total_delivered_late_days = frappe.db.sql(""" + SELECT + SUM(DATEDIFF(pr.posting_date,po_item.schedule_date)* pr_item.qty) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Receipt Item` pr_item, + `tabPurchase Order` po, + `tabPurchase Receipt` pr + WHERE + po.supplier = %(supplier)s + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.schedule_date < pr.posting_date + AND pr_item.docstatus = 1 + AND pr_item.purchase_order_item = po_item.name + AND po_item.parent = po.name + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + if not total_delivered_late_days: + total_delivered_late_days = 0 + + total_missed_late_days = frappe.db.sql(""" + SELECT + SUM(DATEDIFF( %(end_date)s, po_item.schedule_date) * (po_item.qty - po_item.received_qty)) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Order` po + WHERE + po.supplier = %(supplier)s + AND po_item.received_qty < po_item.qty + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.parent = po.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not total_missed_late_days: + total_missed_late_days = 0 + return total_missed_late_days + total_delivered_late_days + +def get_on_time_shipments(scorecard): + """ Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)""" + + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + total_items_delivered_on_time = frappe.db.sql(""" + SELECT + COUNT(pr_item.qty) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Receipt Item` pr_item, + `tabPurchase Order` po, + `tabPurchase Receipt` pr + WHERE + po.supplier = %(supplier)s + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.schedule_date <= pr.posting_date + AND po_item.qty = pr_item.qty + AND pr_item.docstatus = 1 + AND pr_item.purchase_order_item = po_item.name + AND po_item.parent = po.name + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not total_items_delivered_on_time: + total_items_delivered_on_time = 0 + return total_items_delivered_on_time + +def get_late_shipments(scorecard): + """ Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)""" + return get_total_shipments(scorecard) - get_on_time_shipments(scorecard) + +def get_total_received(scorecard): + """ Gets the total number of received shipments in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + COUNT(pr_item.base_amount) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_received_amount(scorecard): + """ Gets the total amount (in company currency) received in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(pr_item.received_qty * pr_item.base_rate) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_received_items(scorecard): + """ Gets the total number of received shipments in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(pr_item.received_qty) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_rejected_amount(scorecard): + """ Gets the total amount (in company currency) rejected in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(pr_item.rejected_qty * pr_item.base_rate) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_rejected_items(scorecard): + """ Gets the total number of rejected items in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(pr_item.rejected_qty) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_accepted_amount(scorecard): + """ Gets the total amount (in company currency) accepted in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(pr_item.qty * pr_item.base_rate) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_accepted_items(scorecard): + """ Gets the total number of rejected items in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + SUM(pr_item.qty) + FROM + `tabPurchase Receipt Item` pr_item, + `tabPurchase Receipt` pr + WHERE + pr.supplier = %(supplier)s + AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s + AND pr_item.docstatus = 1 + AND pr_item.parent = pr.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_total_shipments(scorecard): + """ Gets the total number of ordered shipments to arrive in the period (based on Purchase Receipts)""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + COUNT(po_item.base_amount) + FROM + `tabPurchase Order Item` po_item, + `tabPurchase Order` po + WHERE + po.supplier = %(supplier)s + AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s + AND po_item.docstatus = 1 + AND po_item.parent = po.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_rfq_total_number(scorecard): + """ Gets the total number of RFQs sent to supplier""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + COUNT(rfq.name) as total_rfqs + FROM + `tabRequest for Quotation Item` rfq_item, + `tabRequest for Quotation Supplier` rfq_sup, + `tabRequest for Quotation` rfq + WHERE + rfq_sup.supplier = %(supplier)s + AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s + AND rfq_item.docstatus = 1 + AND rfq_item.parent = rfq.name + AND rfq_sup.parent = rfq.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + + if not data: + data = 0 + return data + +def get_rfq_total_items(scorecard): + """ Gets the total number of RFQ items sent to supplier""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + COUNT(rfq_item.name) as total_rfqs + FROM + `tabRequest for Quotation Item` rfq_item, + `tabRequest for Quotation Supplier` rfq_sup, + `tabRequest for Quotation` rfq + WHERE + rfq_sup.supplier = %(supplier)s + AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s + AND rfq_item.docstatus = 1 + AND rfq_item.parent = rfq.name + AND rfq_sup.parent = rfq.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + if not data: + data = 0 + return data + + +def get_sq_total_number(scorecard): + """ Gets the total number of RFQ items sent to supplier""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + COUNT(sq.name) as total_sqs + FROM + `tabRequest for Quotation Item` rfq_item, + `tabSupplier Quotation Item` sq_item, + `tabRequest for Quotation Supplier` rfq_sup, + `tabRequest for Quotation` rfq, + `tabSupplier Quotation` sq + WHERE + rfq_sup.supplier = %(supplier)s + AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s + AND sq_item.request_for_quotation_item = rfq_item.name + AND sq_item.docstatus = 1 + AND rfq_item.docstatus = 1 + AND sq.supplier = %(supplier)s + AND sq_item.parent = sq.name + AND rfq_item.parent = rfq.name + AND rfq_sup.parent = rfq.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + if not data: + data = 0 + return data + +def get_sq_total_items(scorecard): + """ Gets the total number of RFQ items sent to supplier""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + + # Look up all PO Items with delivery dates between our dates + data = frappe.db.sql(""" + SELECT + COUNT(sq_item.name) as total_sqs + FROM + `tabRequest for Quotation Item` rfq_item, + `tabSupplier Quotation Item` sq_item, + `tabSupplier Quotation` sq, + `tabRequest for Quotation Supplier` rfq_sup, + `tabRequest for Quotation` rfq + WHERE + rfq_sup.supplier = %(supplier)s + AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s + AND sq_item.request_for_quotation_item = rfq_item.name + AND sq_item.docstatus = 1 + AND sq.supplier = %(supplier)s + AND sq_item.parent = sq.name + AND rfq_item.docstatus = 1 + AND rfq_item.parent = rfq.name + AND rfq_sup.parent = rfq.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + if not data: + data = 0 + return data + +def get_rfq_response_days(scorecard): + """ Gets the total number of days it has taken a supplier to respond to rfqs in the period""" + supplier = frappe.get_doc('Supplier', scorecard.supplier) + total_sq_days = frappe.db.sql(""" + SELECT + SUM(DATEDIFF(sq.transaction_date, rfq.transaction_date)) + FROM + `tabRequest for Quotation Item` rfq_item, + `tabSupplier Quotation Item` sq_item, + `tabSupplier Quotation` sq, + `tabRequest for Quotation Supplier` rfq_sup, + `tabRequest for Quotation` rfq + WHERE + rfq_sup.supplier = %(supplier)s + AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s + AND sq_item.request_for_quotation_item = rfq_item.name + AND sq_item.docstatus = 1 + AND sq.supplier = %(supplier)s + AND sq_item.parent = sq.name + AND rfq_item.docstatus = 1 + AND rfq_item.parent = rfq.name + AND rfq_sup.parent = rfq.name""", + {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0] + if not total_sq_days: + total_sq_days = 0 + + + return total_sq_days \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py new file mode 100644 index 0000000000..45a2c6250a --- /dev/null +++ b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import VariablePathNotFound + + +class TestSupplierScorecardVariable(unittest.TestCase): + def test_variable_exist(self): + for d in test_existing_variables: + my_doc = frappe.get_doc("Supplier Scorecard Variable", d.get("name")) + self.assertEquals(my_doc.param_name, d.get('param_name')) + self.assertEquals(my_doc.variable_label, d.get('variable_label')) + self.assertEquals(my_doc.path, d.get('path')) + + def test_path_exists(self): + for d in test_good_variables: + if frappe.db.exists(d): + frappe.delete_doc(d.get("doctype"), d.get("name")) + frappe.get_doc(d).insert() + + for d in test_bad_variables: + self.assertRaises(VariablePathNotFound,frappe.get_doc(d).insert) + +test_existing_variables = [ + { + "param_name":"total_accepted_items", + "name":"Total Accepted Items", + "doctype":"Supplier Scorecard Variable", + "variable_label":"Total Accepted Items", + "path":"get_total_accepted_items" + }, +] + +test_good_variables = [ + { + "param_name":"good_variable1", + "name":"Good Variable 1", + "doctype":"Supplier Scorecard Variable", + "variable_label":"Good Variable 1", + "path":"get_total_accepted_items" + }, +] + +test_bad_variables = [ + { + "param_name":"fake_variable1", + "name":"Fake Variable 1", + "doctype":"Supplier Scorecard Variable", + "variable_label":"Fake Variable 1", + "path":"get_fake_variable1" + }, +] \ No newline at end of file diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 990ca7a8ba..ba29125ca0 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -141,6 +141,32 @@ def get_data(): }, ] }, + { + "label": _("Supplier Scorecard"), + "items": [ + { + "type": "doctype", + "name": "Supplier Scorecard", + "description": _("All Supplier scorecards."), + }, + { + "type": "doctype", + "name": "Supplier Scorecard Variable", + "description": _("Templates of supplier scorecard variables.") + }, + { + "type": "doctype", + "name": "Supplier Scorecard Criteria", + "description": _("Templates of supplier scorecard criteria."), + }, + { + "type": "doctype", + "name": "Supplier Scorecard Standing", + "description": _("Templates of supplier standings."), + }, + + ] + }, { "label": _("Other Reports"), "icon": "fa fa-list", diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard-criteria.png b/erpnext/docs/assets/img/buying/supplier-scorecard-criteria.png new file mode 100644 index 0000000000..0bc73c81ee Binary files /dev/null and b/erpnext/docs/assets/img/buying/supplier-scorecard-criteria.png differ diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard-standing.png b/erpnext/docs/assets/img/buying/supplier-scorecard-standing.png new file mode 100644 index 0000000000..8c507cb77f Binary files /dev/null and b/erpnext/docs/assets/img/buying/supplier-scorecard-standing.png differ diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard-weighing.png b/erpnext/docs/assets/img/buying/supplier-scorecard-weighing.png new file mode 100644 index 0000000000..d32e69e2fc Binary files /dev/null and b/erpnext/docs/assets/img/buying/supplier-scorecard-weighing.png differ diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard.png b/erpnext/docs/assets/img/buying/supplier-scorecard.png new file mode 100644 index 0000000000..1f8de1736d Binary files /dev/null and b/erpnext/docs/assets/img/buying/supplier-scorecard.png differ diff --git a/erpnext/docs/user/manual/en/buying/index.txt b/erpnext/docs/user/manual/en/buying/index.txt index 25c8797e18..4bd75f1047 100644 --- a/erpnext/docs/user/manual/en/buying/index.txt +++ b/erpnext/docs/user/manual/en/buying/index.txt @@ -5,3 +5,4 @@ purchase-order setup articles purchase-taxes +supplier-scorecard \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/buying/supplier-scorecard.md b/erpnext/docs/user/manual/en/buying/supplier-scorecard.md new file mode 100644 index 0000000000..cecdf9cd21 --- /dev/null +++ b/erpnext/docs/user/manual/en/buying/supplier-scorecard.md @@ -0,0 +1,76 @@ +A Supplier Scorecard is an evaluation tool used to assess the performance of +suppliers. Supplier scorecards can be used to keep track of item quality, +delivery and responsiveness of suppliers across long periods of time. This data +is typically used to help in purchasing decisions. + +A Supplier Scorecard is manually created for each supplier. + +In ERPNext, you can create a supplier scorecard by going to: + +> Buying > Documents > Supplier Scorecard > New Supplier Scorecard + +### Create Supplier Scorecard +A supplier scorecard is created for each supplier individually. Only one supplier scorecard can be created for each +supplier. +Purchase Order + +#### Final Score and Standings +The supplier scorecard consists of a set evaluation periods, during which the performance of a supplier is +evaluated. This period can be daily, monthly or yearly. The current score is calculated from the score of each evaluation +period based on the weighting function. The default formula is linearly weight over the previous 12 scoring periods. +Purchase Order +This formula is customizable. + +The supplier standing is used to quickly sort suppliers based on their performance. These are customizable for each supplier. +The scorecard standing of a supplier can also be used to restrict suppliers from being included in Request for Quotations or +being issued Purchase Orders. +Purchase Order + +#### Evaluation Criteria and Variables +A supplier can be evaluated on several individual evaluation criteria, including (but not limited to) quotation response time, +delivered item quality, and delivery timeliness. These criteria are weighed to determine the final period score. +Purchase Order +The method for calculating each criteria is determined through the criteria formula field, which can use a number of pre-established variables. +The value of each of these variables is calculated over the scoring period for each supplier. Examples of such variables include: + - The total number of items received from the supplier + - The total number of accepted items from the supplier + - The total number of rejected items from the supplier + - The total number of deliveries from the supplier + - The total amount (in dollars) received from a supplier +Additional variables can be added through server-side customizations. + +The criteria formula should be customized to evaluate the suppliers in each criteria in a way that best fits the Company requirements. + +##### Evaluation Formulas +The evaluation formula uses the pre-established or custom variables to evaluate an aspect of supplier performance over the scoring period. Formulas can use the following mathematical functions: + +* addition: + +* subtraction: - +* multiplication: * +* division: / +* min: min(x,y) +* max: max(x,y) +* if/else: (x) if (formula) else (y) +* less than: < +* greated than: > +* variables: {variable_name} + +It is crucial that the formula be solvable for all variable values. This is most often an issue if the value resolves to 0. For example: +``` +{total_accepted_items} / {total_received_items} +``` + +This example would resolve to 0 / 0 in periods where there are no received items, and therefore should have a check to protect in this case: +``` +({total_accepted_items} / {total_received_items}) if {total_received_items} > 0 else 1. +``` + +### Evaluating the Supplier +An evaluation is generated for each Supplier Scorecard Period by clicking the "Generate Missing Scorecard Periods" button. The supplier +current score can be seen, as well as a visual graphic showing the performance of the supplier over time. Any actions against the supplier +are also noted here, including warnings when create RFQs and POs or locking out those features for this supplier altogether. + + + + +{next} diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 5f0b19ebd7..860aac2ee7 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -191,6 +191,7 @@ scheduler_events = { "erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries", "erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary", "erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status", + "erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.refresh_scorecards", "erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history" ] } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 834ed2fdf4..00f395fe8c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -422,6 +422,7 @@ erpnext.patches.v8_1.add_indexes_in_transaction_doctypes erpnext.patches.v8_3.set_restrict_to_domain_for_module_def erpnext.patches.v8_1.update_expense_claim_status erpnext.patches.v8_3.update_company_total_sales +erpnext.patches.v8_4.make_scorecard_records erpnext.patches.v8_1.set_delivery_date_in_so_item erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs -erpnext.patches.v8_5.remove_quotations_route_in_sidebar \ No newline at end of file +erpnext.patches.v8_5.remove_quotations_route_in_sidebar diff --git a/erpnext/patches/v8_4/__init__.py b/erpnext/patches/v8_4/__init__.py new file mode 100644 index 0000000000..baffc48825 --- /dev/null +++ b/erpnext/patches/v8_4/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/patches/v8_4/make_scorecard_records.py b/erpnext/patches/v8_4/make_scorecard_records.py new file mode 100644 index 0000000000..37789d711a --- /dev/null +++ b/erpnext/patches/v8_4/make_scorecard_records.py @@ -0,0 +1,9 @@ +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records +def execute(): + + make_default_records() \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/install_fixtures.py b/erpnext/setup/setup_wizard/install_fixtures.py index 43baf2f4fb..1301d33856 100644 --- a/erpnext/setup/setup_wizard/install_fixtures.py +++ b/erpnext/setup/setup_wizard/install_fixtures.py @@ -213,6 +213,10 @@ def install(country=None): records += [{'doctype': 'Lead Source', 'source_name': _(d)} for d in default_lead_sources] + # Records for the Supplier Scorecard + from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records + make_default_records() + from frappe.modules import scrub for r in records: doc = frappe.new_doc(r.get("doctype"))