Supplier Scorecard (#9294)

* Initial start of scorecard docs

* Got basic functionality working

* Fixed doc names and added key functions

* Basic functional version minus Actions

* Hide scorecard docs until functional

* Created supplier scorecard documentation

* Added default variables and standings. Added restrictions for PO + RFQ

* Automatic daily scorecard creation + on save

* Added warning for PO nd RFQs

* Minor fixes for codepy, automatically add variables for criteria, fix hooks.py typo

* Added tests, fixed codacy formatting, small improvements

* Fixed test bug w/ criteria. Codacy cleanup

* Fixed codacy issues. Fixed sticky criteria

* Fixed bug with period search. Remove blank variable child.

* Updated docs, automatically add criteria and standings, clean up period create message

* Uncommented test, set docs to beta

* Fix for nabinhait review

* Fix codacy issue. Fix dict assignment for records
This commit is contained in:
bcornwellmott 2017-07-24 10:12:30 -07:00 committed by Nabin Hait
parent 91b2833708
commit 96381da547
52 changed files with 4811 additions and 2 deletions

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -39,6 +39,8 @@ class PurchaseOrder(BuyingController):
super(PurchaseOrder, self).validate() super(PurchaseOrder, self).validate()
self.set_status() self.set_status()
self.validate_supplier()
validate_for_items(self) validate_for_items(self)
self.check_for_closed_status() 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): def validate_minimum_order_qty(self):
items = list(set([d.item_code for d in self.get("items")])) items = list(set([d.item_code for d in self.get("items")]))

View File

@ -21,6 +21,7 @@ STANDARD_USERS = ("Guest", "Administrator")
class RequestforQuotation(BuyingController): class RequestforQuotation(BuyingController):
def validate(self): def validate(self):
self.validate_duplicate_supplier() self.validate_duplicate_supplier()
self.validate_supplier_list()
validate_for_items(self) validate_for_items(self)
self.update_email_id() self.update_email_id()
@ -29,6 +30,17 @@ class RequestforQuotation(BuyingController):
if len(supplier_list) != len(set(supplier_list)): if len(supplier_list) != len(set(supplier_list)):
frappe.throw(_("Same supplier has been entered multiple times")) 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): def update_email_id(self):
for rfq_supplier in self.suppliers: for rfq_supplier in self.suppliers:
if not rfq_supplier.email_id: if not rfq_supplier.email_id:

View File

@ -322,6 +322,126 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 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_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -850,7 +970,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-06-13 14:29:16.310834", "modified": "2017-07-06 16:40:46.935608",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier", "name": "Supplier",

View File

@ -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");
}
});
};

View File

@ -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
}

View File

@ -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

View File

@ -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']
}
]
}

View File

@ -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,=,''"];
}
},
};

View File

@ -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) )",
}
]

View File

@ -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() {}
});

View File

@ -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
}

View File

@ -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('&lt;','<').replace('&gt;','>')
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

View File

@ -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
},
]

View File

@ -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);
}
});

View File

@ -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
}

View File

@ -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('&lt;','<').replace('&gt;','>')
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('&lt;','<').replace('&gt;','>')
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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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() {
}
});

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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() {
}
});

View File

@ -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
}

View File

@ -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

View File

@ -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"
},
]

View File

@ -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"), "label": _("Other Reports"),
"icon": "fa fa-list", "icon": "fa fa-list",

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -5,3 +5,4 @@ purchase-order
setup setup
articles articles
purchase-taxes purchase-taxes
supplier-scorecard

View File

@ -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.
<img class="screenshot" alt="Purchase Order" src="{{docs_base_url}}/assets/img/buying/supplier-scorecard.png">
#### 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.
<img class="screenshot" alt="Purchase Order" src="{{docs_base_url}}/assets/img/buying/supplier-scorecard-weighing.png">
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.
<img class="screenshot" alt="Purchase Order" src="{{docs_base_url}}/assets/img/buying/supplier-scorecard-standing.png">
#### 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.
<img class="screenshot" alt="Purchase Order" src="{{docs_base_url}}/assets/img/buying/supplier-scorecard-criteria.png">
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}

View File

@ -191,6 +191,7 @@ scheduler_events = {
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries", "erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries",
"erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary", "erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status", "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" "erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history"
] ]
} }

View File

@ -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_3.set_restrict_to_domain_for_module_def
erpnext.patches.v8_1.update_expense_claim_status erpnext.patches.v8_1.update_expense_claim_status
erpnext.patches.v8_3.update_company_total_sales 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_1.set_delivery_date_in_so_item
erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
erpnext.patches.v8_5.remove_quotations_route_in_sidebar erpnext.patches.v8_5.remove_quotations_route_in_sidebar

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -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()

View File

@ -213,6 +213,10 @@ def install(country=None):
records += [{'doctype': 'Lead Source', 'source_name': _(d)} for d in default_lead_sources] 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 from frappe.modules import scrub
for r in records: for r in records:
doc = frappe.new_doc(r.get("doctype")) doc = frappe.new_doc(r.get("doctype"))