Merge branch 'master' into develop
diff --git a/erpnext/tests/ui/accounts/test_account.js b/erpnext/accounts/doctype/account/test_account.js
similarity index 77%
rename from erpnext/tests/ui/accounts/test_account.js
rename to erpnext/accounts/doctype/account/test_account.js
index 6d7709b..7b23ef0 100644
--- a/erpnext/tests/ui/accounts/test_account.js
+++ b/erpnext/accounts/doctype/account/test_account.js
@@ -5,16 +5,16 @@
let done = assert.async();
frappe.run_serially([
() => frappe.set_route('Tree', 'Account'),
- () => frappe.tests.click_button('Expand All'),
- () => frappe.tests.click_link('Debtors'),
- () => frappe.tests.click_button('Edit'),
+ () => frappe.click_button('Expand All'),
+ () => frappe.click_link('Debtors'),
+ () => frappe.click_button('Edit'),
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.doc.root_type=='Asset');
assert.ok(cur_frm.doc.report_type=='Balance Sheet');
assert.ok(cur_frm.doc.account_type=='Receivable');
},
- () => frappe.tests.click_button('Ledger'),
+ () => frappe.click_button('Ledger'),
() => frappe.timeout(1),
() => {
// check if general ledger report shown
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index ba4cc83..9047a4e 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -43,8 +43,26 @@
$.each(frm.doc.accounts || [], function(i, row) {
erpnext.journal_entry.set_exchange_rate(frm, row.doctype, row.name);
})
+ },
+
+ company: function(frm) {
+ frappe.call({
+ method: "frappe.client.get_value",
+ args: {
+ doctype: "Company",
+ filters: {"name": frm.doc.company},
+ fieldname: "cost_center"
+ },
+ callback: function(r){
+ if(r.message){
+ $.each(frm.doc.accounts || [], function(i, jvd) {
+ frappe.model.set_value(jvd.doctype, jvd.name, "cost_center", r.message.cost_center);
+ });
+ }
+ }
+ });
}
-})
+});
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
onload: function() {
@@ -129,10 +147,11 @@
// account filter
frappe.model.validate_missing(jvd, "account");
-
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
- } else {
+ }
+
+ if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
// party_type and party mandatory
frappe.model.validate_missing(jvd, "party_type");
frappe.model.validate_missing(jvd, "party");
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index bf509de..005abe6 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -284,12 +284,14 @@
if not credit_days_based_on:
if party_type == "Customer":
credit_days_based_on, credit_days = \
- frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
- or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
+ frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"])
else:
credit_days_based_on, credit_days = \
- frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])\
- or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"] )
+ frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])
+
+ if not credit_days_based_on:
+ credit_days_based_on, credit_days = \
+ frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
return credit_days_based_on, credit_days
diff --git a/erpnext/buying/__init__.py b/erpnext/buying/__init__.py
index e69de29..baffc48 100644
--- a/erpnext/buying/__init__.py
+++ b/erpnext/buying/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 7e5020a..26c8c61 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -39,6 +39,8 @@
super(PurchaseOrder, self).validate()
self.set_status()
+
+ self.validate_supplier()
validate_for_items(self)
self.check_for_closed_status()
@@ -65,6 +67,17 @@
}
})
+ def validate_supplier(self):
+ prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
+ if prevent_po:
+ standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status')
+ frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.").format(self.supplier, standing))
+
+ warn_po = frappe.db.get_value("Supplier", self.supplier, 'warn_pos')
+ if warn_po:
+ standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status')
+ frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution.").format(self.supplier, standing), title=_("Caution"), indicator='orange')
+
def validate_minimum_order_qty(self):
items = list(set([d.item_code for d in self.get("items")]))
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index ce759aa..558e072 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -44,6 +44,9 @@
freeze: true,
args: {
rfq_name: frm.doc.name
+ },
+ callback: function(r){
+ frm.reload_doc();
}
});
});
@@ -51,6 +54,91 @@
},
+ get_suppliers_button: function (frm) {
+ var doc = frm.doc;
+ var dialog = new frappe.ui.Dialog({
+ title: __("Get Suppliers"),
+ fields: [
+ { "fieldtype": "Select", "label": __("Get Suppliers By"),
+ "fieldname": "search_type",
+ "options": "Tag\nSupplier Type", "reqd": 1 },
+ { "fieldtype": "Link", "label": __("Supplier Type"),
+ "fieldname": "supplier_type",
+ "options": "Supplier Type", "reqd": 0,
+ "depends_on": "eval:doc.search_type == 'Supplier Type'"},
+ { "fieldtype": "Data", "label": __("Tag"),
+ "fieldname": "tag", "reqd": 0,
+ "depends_on": "eval:doc.search_type == 'Tag'" },
+ { "fieldtype": "Button", "label": __("Add All Suppliers"),
+ "fieldname": "add_suppliers", "cssClass": "btn-primary"},
+ ]
+ });
+
+ dialog.fields_dict.add_suppliers.$input.click(function() {
+ var args = dialog.get_values();
+ if(!args) return;
+ dialog.hide();
+
+ //Remove blanks
+ for (var j = 0; j < frm.doc.suppliers.length; j++) {
+ if(!frm.doc.suppliers[j].hasOwnProperty("supplier")) {
+ frm.get_field("suppliers").grid.grid_rows[j].remove();
+ }
+ }
+
+ function load_suppliers(r) {
+ if(r.message) {
+ for (var i = 0; i < r.message.length; i++) {
+ var exists = false;
+ if (r.message[i].constructor === Array){
+ var supplier = r.message[i][0];
+ } else {
+ var supplier = r.message[i].name;
+ }
+
+ for (var j = 0; j < doc.suppliers.length;j++) {
+ if (supplier === doc.suppliers[j].supplier) {
+ exists = true;
+ }
+ }
+ if(!exists) {
+ var d = frm.add_child('suppliers');
+ d.supplier = supplier;
+ frm.script_manager.trigger("supplier", d.doctype, d.name);
+ }
+ }
+ }
+ frm.refresh_field("suppliers");
+ }
+
+ if (args.search_type === "Tag" && args.tag) {
+ return frappe.call({
+ type: "GET",
+ method: "frappe.desk.tags.get_tagged_docs",
+ args: {
+ "doctype": "Supplier",
+ "tag": args.tag
+ },
+ callback: load_suppliers
+ });
+ } else if (args.supplier_type) {
+ return frappe.call({
+ method: "frappe.client.get_list",
+ args: {
+ doctype: "Supplier",
+ order_by: "name",
+ fields: ["name"],
+ filters: [["Supplier", "supplier_type", "=", args.supplier_type]]
+
+ },
+ callback: load_suppliers
+ });
+ }
+ });
+ dialog.show();
+
+ },
+
make_suppplier_quotation: function(frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
index ac345d9..44068ce 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
@@ -25,7 +25,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
@@ -59,7 +59,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
@@ -156,7 +156,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Date",
"length": 0,
@@ -242,6 +242,36 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "get_suppliers_button",
+ "fieldtype": "Button",
+ "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": "Get Suppliers",
+ "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": "items_section",
"fieldtype": "Section Break",
"hidden": 0,
@@ -406,7 +436,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Message for Supplier",
"length": 0,
@@ -786,7 +816,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-06-13 14:29:13.171291",
+ "modified": "2017-07-21 14:06:46.309322",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation",
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index 9109239..e9603fb 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -21,6 +21,7 @@
class RequestforQuotation(BuyingController):
def validate(self):
self.validate_duplicate_supplier()
+ self.validate_supplier_list()
validate_for_items(self)
self.update_email_id()
@@ -29,6 +30,17 @@
if len(supplier_list) != len(set(supplier_list)):
frappe.throw(_("Same supplier has been entered multiple times"))
+ def validate_supplier_list(self):
+ for d in self.suppliers:
+ prevent_rfqs = frappe.db.get_value("Supplier", d.supplier, 'prevent_rfqs')
+ if prevent_rfqs:
+ standing = frappe.db.get_value("Supplier Scorecard",d.supplier, 'status')
+ frappe.throw(_("RFQs are not allowed for {0} due to a scorecard standing of {1}").format(d.supplier, standing))
+ warn_rfqs = frappe.db.get_value("Supplier", d.supplier, 'warn_rfqs')
+ if warn_rfqs:
+ standing = frappe.db.get_value("Supplier Scorecard",d.supplier, 'status')
+ frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and RFQs to this supplier should be issued with caution.").format(d.supplier, standing), title=_("Caution"), indicator='orange')
+
def update_email_id(self):
for rfq_supplier in self.suppliers:
if not rfq_supplier.email_id:
@@ -40,6 +52,8 @@
def on_submit(self):
frappe.db.set(self, 'status', 'Submitted')
+ for supplier in self.suppliers:
+ supplier.email_sent = 0
def on_cancel(self):
frappe.db.set(self, 'status', 'Cancelled')
@@ -54,6 +68,8 @@
self.update_supplier_part_no(rfq_supplier)
self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link())
+ rfq_supplier.email_sent = 1
+ rfq_supplier.save()
def get_link(self):
# RFQ link for supplier portal
@@ -84,7 +100,10 @@
else:
contact = frappe.new_doc("Contact")
contact.first_name = rfq_supplier.supplier_name or rfq_supplier.supplier
- contact.supplier = rfq_supplier.supplier
+ contact.append('links', {
+ 'link_doctype': 'Supplier',
+ 'link_name': rfq_supplier.supplier
+ })
if not contact.email_id and not contact.user:
contact.email_id = user.name
diff --git a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
index 7bc0def..a7c5a37 100644
--- a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
+++ b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -12,6 +13,7 @@
"engine": "InnoDB",
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -42,6 +44,39 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "depends_on": "eval:doc.docstatus >= 1",
+ "fieldname": "email_sent",
+ "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": "Email Sent",
+ "length": 0,
+ "no_copy": 1,
+ "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,
@@ -72,6 +107,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -102,6 +138,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -130,6 +167,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -160,6 +198,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -190,6 +229,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -219,17 +259,17 @@
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
- "in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2017-02-17 16:42:57.254211",
+ "modified": "2017-07-24 06:52:19.542717",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation Supplier",
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 58a1fc7..711e05d 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -326,6 +326,126 @@
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn POs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent POs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "section_break_7",
@@ -850,7 +970,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-06-13 14:29:16.310834",
+ "modified": "2017-07-06 16:40:46.935608",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
diff --git a/erpnext/buying/doctype/supplier_scorecard/__init__.py b/erpnext/buying/doctype/supplier_scorecard/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
new file mode 100644
index 0000000..a3a1414
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js
@@ -0,0 +1,146 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+/* global frappe, refresh_field */
+
+frappe.ui.form.on("Supplier Scorecard", {
+
+ onload: function(frm) {
+
+ if (frm.doc.indicator_color !== "") {
+ frm.set_indicator_formatter("status", function(doc) {
+ return doc.indicator_color.toLowerCase();
+ });
+ }
+ if (frm.doc.__unsaved == 1) {
+ loadAllCriteria(frm);
+ loadAllStandings(frm);
+ }
+
+ },
+ refresh: function(frm) {
+ if (frm.dashboard.hasOwnProperty('heatmap')) {
+ frm.dashboard.heatmap.setLegend([0,20,40,60,80,101],["#991600","#169900"]);
+ }
+ }
+
+});
+
+frappe.ui.form.on("Supplier Scorecard Scoring Standing", {
+
+ standing_name: function(frm, cdt, cdn) {
+ if (frm.doc.standing_name != undefined) {
+ var d = frappe.get_doc(cdt, cdn);
+ return frm.call({
+ method: "erpnext.buying.doctype.supplier_scorecard_standing.supplier_scorecard_standing.get_scoring_standing",
+ child: d,
+ args: {
+ standing_name: d.standing_name
+ }
+ });
+ }
+ }
+});
+
+frappe.ui.form.on("Supplier Scorecard Scoring Variable", {
+
+ variable_label: function(frm, cdt, cdn) {
+ if (frm.doc.variable_label != undefined) {
+ var d = frappe.get_doc(cdt, cdn);
+ return frm.call({
+ method: "erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable.get_scoring_variable",
+ child: d,
+ args: {
+ variable_label: d.variable_label
+ }
+ });
+ }
+ }
+});
+
+frappe.ui.form.on("Supplier Scorecard Scoring Criteria", {
+
+ criteria_name: function(frm, cdt, cdn) {
+ if (frm.doc.criteria_name != undefined) {
+ var d = frappe.get_doc(cdt, cdn);
+ frm.call({
+ method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_variables",
+ args: {
+ criteria_name: d.criteria_name
+ },
+ callback: function(r) {
+ for (var i = 0; i < r.message.length; i++)
+ {
+ var exists = false;
+ for (var j = 0; j < frm.doc.variables.length; j++)
+ {
+ if(!frm.doc.variables[j].hasOwnProperty("variable_label")) {
+ frm.get_field("variables").grid.grid_rows[j].remove();
+ }
+ else if(frm.doc.variables[j].variable_label === r.message[i]) {
+ exists = true;
+ }
+ }
+ if (!exists){
+ var new_row = frm.add_child("variables");
+ new_row.variable_label = r.message[i];
+ frm.script_manager.trigger("variable_label", new_row.doctype, new_row.name);
+ }
+
+ }
+ refresh_field("variables");
+ }
+ });
+ return frm.call({
+ method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_scoring_criteria",
+ child: d,
+ args: {
+ criteria_name: d.criteria_name
+ }
+ });
+ }
+ }
+});
+
+var loadAllCriteria = function(frm) {
+ frappe.call({
+ method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_criteria_list",
+ callback: function(r) {
+ for (var j = 0; j < frm.doc.criteria.length; j++)
+ {
+ if(!frm.doc.criteria[j].hasOwnProperty("criteria_name")) {
+ frm.get_field("criteria").grid.grid_rows[j].remove();
+ }
+ }
+ for (var i = 0; i < r.message.length; i++)
+ {
+ var new_row = frm.add_child("criteria");
+ new_row.criteria_name = r.message[i].name;
+ frm.script_manager.trigger("criteria_name", new_row.doctype, new_row.name);
+ }
+ refresh_field("criteria");
+ }
+ });
+};
+var loadAllStandings = function(frm) {
+ frappe.call({
+ method: "erpnext.buying.doctype.supplier_scorecard_standing.supplier_scorecard_standing.get_standings_list",
+ callback: function(r) {
+ for (var j = 0; j < frm.doc.standings.length; j++)
+ {
+ if(!frm.doc.standings[j].hasOwnProperty("standing_name")) {
+ frm.get_field("standings").grid.grid_rows[j].remove();
+ }
+ }
+ for (var i = 0; i < r.message.length; i++)
+ {
+ var new_row = frm.add_child("standings");
+ new_row.standing_name = r.message[i].name;
+ frm.script_manager.trigger("standing_name", new_row.doctype, new_row.name);
+ }
+ refresh_field("standings");
+ }
+ });
+};
+
+
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json
new file mode 100644
index 0000000..d7f24c9
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.json
@@ -0,0 +1,701 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:supplier",
+ "beta": 1,
+ "creation": "2017-05-29 01:40:54.786555",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "supplier_score",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Supplier Score",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "indicator_color",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Indicator Color",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "status",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Per Month",
+ "fieldname": "period",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Evaluation Period",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Per Month\nPer Week\nPer Year",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "scoring_setup",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Scoring Setup",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )",
+ "description": "Scorecard variables can be used, as well as:\n{total_score} (the total score from that period),\n{period_number} (the number of periods to present day)\n",
+ "fieldname": "weighting_function",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Weighting Function",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "standings",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Scoring Standings",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Scoring Standing",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "criteria_setup",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Criteria Setup",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "criteria",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Scoring Criteria",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Scoring Criteria",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "variables",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Supplier Variables",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Scoring Variable",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "eval: doc.status != 'Unknown'",
+ "columns": 0,
+ "fieldname": "scorecard_actions",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Scorecard Actions",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn for new Request for Quotations",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn for new Purchase Orders",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent POs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "notify_supplier",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Notify Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "notify_employee",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Notify Employee",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:11.874949",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 0,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
new file mode 100644
index 0000000..e13d22a
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py
@@ -0,0 +1,262 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import throw, _
+from frappe.model.document import Document
+import time
+from datetime import timedelta
+from frappe.utils import nowdate, get_last_day, getdate, add_days, add_years
+from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import make_supplier_scorecard
+
+class SupplierScorecard(Document):
+
+ def validate(self):
+ self.validate_standings()
+ self.validate_criteria_weights()
+ self.calculate_total_score()
+ self.update_standing()
+
+ def on_update(self):
+ score = make_all_scorecards(self.name)
+ if score > 0:
+ self.save()
+
+ def validate_standings(self):
+ # Check that there are no overlapping scores and check that there are no missing scores
+ score = 0
+ for c1 in self.standings:
+ for c2 in self.standings:
+ if c1 != c2:
+ if (c1.max_grade > c2.min_grade and c1.min_grade < c2.max_grade):
+ throw(_('Overlap in scoring between {0} and {1}').format(c1.standing_name,c2.standing_name))
+ if c2.min_grade == score:
+ score = c2.max_grade
+ if score < 100:
+ throw(_('Unable to find score starting at {0}. You need to have standing scores covering 0 to 100').format(score))
+
+ def validate_criteria_weights(self):
+
+ weight = 0
+ for c in self.criteria:
+ weight += c.weight
+
+ if weight != 100:
+ throw(_('Criteria weights must add up to 100%'))
+
+ def calculate_total_score(self):
+ scorecards = frappe.db.sql("""
+ SELECT
+ scp.name
+ FROM
+ `tabSupplier Scorecard Period` scp
+ WHERE
+ scp.scorecard = %(sc)s
+ ORDER BY
+ scp.end_date DESC""",
+ {"sc": self.name}, as_dict=1)
+
+ period = 0
+ total_score = 0
+ total_max_score = 0
+ for scp in scorecards:
+ my_sc = frappe.get_doc('Supplier Scorecard Period', scp.name)
+ my_scp_weight = self.weighting_function
+ my_scp_weight = my_scp_weight.replace('{period_number}', str(period))
+
+ my_scp_maxweight = my_scp_weight.replace('{total_score}', '100')
+ my_scp_weight = my_scp_weight.replace('{total_score}', str(my_sc.total_score))
+
+ max_score = my_sc.calculate_weighted_score(my_scp_maxweight)
+ score = my_sc.calculate_weighted_score(my_scp_weight)
+
+ total_score += score
+ total_max_score += max_score
+ period += 1
+ if total_max_score > 0:
+ self.supplier_score = round(100.0 * (total_score / total_max_score) ,1)
+ else:
+ self.supplier_score = 100
+
+ def update_standing(self):
+ # Get the setup document
+
+ for standing in self.standings:
+ if (not standing.min_grade or (standing.min_grade <= self.supplier_score)) and \
+ (not standing.max_grade or (standing.max_grade > self.supplier_score)):
+ self.status = standing.standing_name
+ self.indicator_color = standing.standing_color
+ self.notify_supplier = standing.notify_supplier
+ self.notify_employee = standing.notify_employee
+ self.employee_link = standing.employee_link
+
+ #Update supplier standing info
+ for fieldname in ('prevent_pos', 'prevent_rfqs','warn_rfqs','warn_pos'):
+ self.set(fieldname, standing.get(fieldname))
+ frappe.db.set_value("Supplier", self.supplier, fieldname, self.get(fieldname))
+
+
+@frappe.whitelist()
+def get_timeline_data(doctype, name):
+ # Get a list of all the associated scorecards
+ scs = frappe.get_doc(doctype, name)
+ out = {}
+ timeline_data = {}
+ scorecards = frappe.db.sql("""
+ SELECT
+ sc.name
+ FROM
+ `tabSupplier Scorecard Period` sc
+ WHERE
+ sc.scorecard = %(scs)s""",
+ {"scs": scs.name}, as_dict=1)
+
+ for sc in scorecards:
+ start_date, end_date, total_score = frappe.db.get_value('Supplier Scorecard Period', sc.name, ['start_date', 'end_date', 'total_score'])
+ for single_date in daterange(start_date, end_date):
+ timeline_data[time.mktime(single_date.timetuple())] = total_score
+
+ out['timeline_data'] = timeline_data
+ return out
+
+def daterange(start_date, end_date):
+ for n in range(int ((end_date - start_date).days)+1):
+ yield start_date + timedelta(n)
+
+def refresh_scorecards():
+ scorecards = frappe.db.sql("""
+ SELECT
+ sc.name
+ FROM
+ `tabSupplier Scorecard` sc""",
+ {}, as_dict=1)
+ for sc in scorecards:
+ # Check to see if any new scorecard periods are created
+ if make_all_scorecards(sc.name) > 0:
+ # Save the scorecard to update the score and standings
+ sc.save()
+
+
+@frappe.whitelist()
+def make_all_scorecards(docname):
+
+ sc = frappe.get_doc('Supplier Scorecard', docname)
+ supplier = frappe.get_doc('Supplier',sc.supplier)
+
+ start_date = getdate(supplier.creation)
+ end_date = get_scorecard_date(sc.period, start_date)
+ todays = getdate(nowdate())
+
+ scp_count = 0
+ first_start_date = todays
+ last_end_date = todays
+
+ while (start_date < todays) and (end_date <= todays):
+ # check to make sure there is no scorecard period already created
+ scorecards = frappe.db.sql("""
+ SELECT
+ scp.name
+ FROM
+ `tabSupplier Scorecard Period` scp
+ WHERE
+ scp.scorecard = %(sc)s
+ AND (
+ (scp.start_date > %(end_date)s
+ AND scp.end_date < %(start_date)s)
+ OR
+ (scp.start_date < %(end_date)s
+ AND scp.end_date > %(start_date)s))
+ ORDER BY
+ scp.end_date DESC""",
+ {"sc": docname, "start_date": start_date, "end_date": end_date, "supplier": supplier}, as_dict=1)
+ if len(scorecards) == 0:
+ period_card = make_supplier_scorecard(docname, None)
+ period_card.start_date = start_date
+ period_card.end_date = end_date
+ period_card.save()
+ scp_count = scp_count + 1
+ if start_date < first_start_date:
+ first_start_date = start_date
+ last_end_date = end_date
+
+ start_date = getdate(add_days(end_date,1))
+ end_date = get_scorecard_date(sc.period, start_date)
+ if scp_count > 0:
+ frappe.msgprint(_("Created {0} scorecards for {1} between: ").format(scp_count, sc.supplier) + str(first_start_date) + " - " + str(last_end_date))
+ return scp_count
+
+def get_scorecard_date(period, start_date):
+ if period == 'Per Week':
+ end_date = getdate(add_days(start_date,7))
+ elif period == 'Per Month':
+ end_date = get_last_day(start_date)
+ elif period == 'Per Year':
+ end_date = add_days(add_years(start_date,1), -1)
+ return end_date
+
+def make_default_records():
+ install_variable_docs = [
+ {"param_name": "total_accepted_items", "variable_label": "Total Accepted Items", \
+ "path": "get_total_accepted_items"},
+ {"param_name": "total_accepted_amount", "variable_label": "Total Accepted Amount", \
+ "path": "get_total_accepted_amount"},
+ {"param_name": "total_rejected_items", "variable_label": "Total Rejected Items", \
+ "path": "get_total_rejected_items"},
+ {"param_name": "total_rejected_amount", "variable_label": "Total Rejected Amount", \
+ "path": "get_total_rejected_amount"},
+ {"param_name": "total_received_items", "variable_label": "Total Received Items", \
+ "path": "get_total_received_items"},
+ {"param_name": "total_received_amount", "variable_label": "Total Received Amount", \
+ "path": "get_total_received_amount"},
+ {"param_name": "rfq_response_days", "variable_label": "RFQ Response Days", \
+ "path": "get_rfq_response_days"},
+ {"param_name": "sq_total_items", "variable_label": "SQ Total Items", \
+ "path": "get_sq_total_items"},
+ {"param_name": "sq_total_number", "variable_label": "SQ Total Number", \
+ "path": "get_sq_total_number"},
+ {"param_name": "rfq_total_number", "variable_label": "RFQ Total Number", \
+ "path": "get_rfq_total_number"},
+ {"param_name": "rfq_total_items", "variable_label": "RFQ Total Items", \
+ "path": "get_rfq_total_items"},
+ {"param_name": "tot_item_days", "variable_label": "Total Item Days", \
+ "path": "get_item_workdays"},
+ {"param_name": "on_time_shipment_num", "variable_label": "# of On Time Shipments", "path": \
+ "get_on_time_shipments"},
+ {"param_name": "cost_of_delayed_shipments", "variable_label": "Cost of Delayed Shipments", \
+ "path": "get_cost_of_delayed_shipments"},
+ {"param_name": "cost_of_on_time_shipments", "variable_label": "Cost of On Time Shipments", \
+ "path": "get_cost_of_on_time_shipments"},
+ {"param_name": "total_working_days", "variable_label": "Total Working Days", \
+ "path": "get_total_workdays"},
+ {"param_name": "tot_cost_shipments", "variable_label": "Total Cost of Shipments", \
+ "path": "get_total_cost_of_shipments"},
+ {"param_name": "tot_days_late", "variable_label": "Total Days Late", \
+ "path": "get_total_days_late"},
+ {"param_name": "total_shipments", "variable_label": "Total Shipments", \
+ "path": "get_total_shipments"}
+ ]
+ install_standing_docs = [
+ {"min_grade": 0.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 30.0, "prevent_pos": 1, \
+ "standing_color": "Red", "notify_employee": 0, "standing_name": "Very Poor"},
+ {"min_grade": 30.0, "prevent_rfqs": 1, "notify_supplier": 0, "max_grade": 50.0, "prevent_pos": 0, \
+ "standing_color": "Red", "notify_employee": 0, "standing_name": "Poor"},
+ {"min_grade": 50.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 80.0, "prevent_pos": 0, \
+ "standing_color": "Green", "notify_employee": 0, "standing_name": "Average"},
+ {"min_grade": 80.0, "prevent_rfqs": 0, "notify_supplier": 0, "max_grade": 100.0, "prevent_pos": 0, \
+ "standing_color": "Blue", "notify_employee": 0, "standing_name": "Excellent"},
+ ]
+
+ for d in install_variable_docs:
+ try:
+ d['doctype'] = "Supplier Scorecard Variable"
+ frappe.get_doc(d).insert()
+ except frappe.NameError:
+ pass
+ for d in install_standing_docs:
+ try:
+ d['doctype'] = "Supplier Scorecard Standing"
+ frappe.get_doc(d).insert()
+ except frappe.NameError:
+ pass
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
new file mode 100644
index 0000000..ff7f119
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py
@@ -0,0 +1,15 @@
+from frappe import _
+
+def get_data():
+ return {
+ 'heatmap': True,
+ 'heatmap_message': _('This covers all scorecards tied to this Setup'),
+ 'fieldname': 'supplier',
+ 'method' : 'erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.get_timeline_data',
+ 'transactions': [
+ {
+ 'label': _('Scorecards'),
+ 'items': ['Supplier Scorecard Period']
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js
new file mode 100644
index 0000000..c50916e
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+/* global frappe, __ */
+
+frappe.listview_settings["Supplier Scorecard"] = {
+ add_fields: ["indicator_color", "status"],
+ get_indicator: function(doc) {
+
+ if (doc.indicator_color) {
+ return [__(doc.status), doc.indicator_color.toLowerCase(), "status,=," + doc.status];
+ } else {
+ return [__("Unknown"), "darkgrey", "status,=,''"];
+ }
+ },
+
+};
diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
new file mode 100644
index 0000000..d64d3f6
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestSupplierScorecard(unittest.TestCase):
+
+ def test_create_scorecard(self):
+ delete_test_scorecards()
+ my_doc = make_supplier_scorecard()
+ doc = my_doc.insert()
+ self.assertEqual(doc.name, valid_scorecard[0].get("supplier"))
+
+ def test_criteria_weight(self):
+ delete_test_scorecards()
+ my_doc = make_supplier_scorecard()
+ for d in my_doc.criteria:
+ d.weight = 0
+ self.assertRaises(frappe.ValidationError,my_doc.insert)
+
+ def test_missing_variable(self):
+ delete_test_scorecards()
+ my_doc = make_supplier_scorecard()
+ del my_doc.variables
+ self.assertRaises(frappe.ValidationError,my_doc.insert)
+
+def make_supplier_scorecard():
+ my_doc = frappe.get_doc(valid_scorecard[0])
+
+ # Make sure the criteria exist (making them)
+ for d in valid_scorecard[0].get("criteria"):
+ if not frappe.db.exists("Supplier Scorecard Criteria", d.get("criteria_name")):
+ d["doctype"] = "Supplier Scorecard Criteria"
+ d["name"] = d.get("criteria_name")
+ my_criteria = frappe.get_doc(d)
+ my_criteria.insert()
+ return my_doc
+
+
+def delete_test_scorecards():
+ my_doc = make_supplier_scorecard()
+ if frappe.db.exists("Supplier Scorecard", my_doc.name):
+ # Delete all the periods, then delete the scorecard
+ frappe.db.sql("""delete from `tabSupplier Scorecard Period` where scorecard = %(scorecard)s""", {'scorecard': my_doc.name})
+ frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'""")
+ frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'""")
+ frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'""")
+ frappe.delete_doc(my_doc.doctype, my_doc.name)
+
+valid_scorecard = [
+ {
+ "standings":[
+ {
+ "min_grade":0.0,"name":"Very Poor",
+ "prevent_rfqs":1,
+ "notify_supplier":0,
+ "doctype":"Supplier Scorecard Standing",
+ "max_grade":30.0,
+ "prevent_pos":1,
+ "warn_pos":0,
+ "warn_rfqs":0,
+ "standing_color":"Red",
+ "notify_employee":0,
+ "standing_name":"Very Poor",
+ "parenttype":"Supplier Scorecard",
+ "parentfield":"standings"
+ },
+ {
+ "min_grade":30.0,
+ "name":"Poor",
+ "prevent_rfqs":1,
+ "notify_supplier":0,
+ "doctype":"Supplier Scorecard Standing",
+ "max_grade":50.0,
+ "prevent_pos":0,
+ "warn_pos":0,
+ "warn_rfqs":0,
+ "standing_color":"Red",
+ "notify_employee":0,
+ "standing_name":"Poor",
+ "parenttype":"Supplier Scorecard",
+ "parentfield":"standings"
+ },
+ {
+ "min_grade":50.0,
+ "name":"Average",
+ "prevent_rfqs":0,
+ "notify_supplier":0,
+ "doctype":"Supplier Scorecard Standing",
+ "max_grade":80.0,
+ "prevent_pos":0,
+ "warn_pos":0,
+ "warn_rfqs":0,
+ "standing_color":"Green",
+ "notify_employee":0,
+ "standing_name":"Average",
+ "parenttype":"Supplier Scorecard",
+ "parentfield":"standings"
+ },
+ {
+ "min_grade":80.0,
+ "name":"Excellent",
+ "prevent_rfqs":0,
+ "notify_supplier":0,
+ "doctype":"Supplier Scorecard Standing",
+ "max_grade":100.0,
+ "prevent_pos":0,
+ "warn_pos":0,
+ "warn_rfqs":0,
+ "standing_color":"Blue",
+ "notify_employee":0,
+ "standing_name":"Excellent",
+ "parenttype":"Supplier Scorecard",
+ "parentfield":"standings"
+ }
+ ],
+ "prevent_pos":0,
+ "variables": [
+ {
+ "param_name":"cost_of_on_time_shipments",
+ "doctype":"Supplier Scorecard Scoring Variable",
+ "parenttype":"Supplier Scorecard",
+ "variable_label":"Cost of On Time Shipments",
+ "path":"get_cost_of_on_time_shipments",
+ "parentfield":"variables"
+ },
+ {
+ "param_name":"tot_cost_shipments",
+ "doctype":"Supplier Scorecard Scoring Variable",
+ "parenttype":"Supplier Scorecard",
+ "variable_label":"Total Cost of Shipments",
+ "path":"get_total_cost_of_shipments",
+ "parentfield":"variables"
+ },
+ {
+ "param_name":"tot_days_late",
+ "doctype":"Supplier Scorecard Scoring Variable",
+ "parenttype":"Supplier Scorecard",
+ "variable_label":"Total Days Late",
+ "path":"get_total_days_late",
+ "parentfield":"variables"
+ },
+ {
+ "param_name":"total_working_days",
+ "doctype":"Supplier Scorecard Scoring Variable",
+ "parenttype":"Supplier Scorecard",
+ "variable_label":"Total Working Days",
+ "path":"get_total_workdays",
+ "parentfield":"variables"
+ },
+ {
+ "param_name":"on_time_shipment_num",
+ "doctype":"Supplier Scorecard Scoring Variable",
+ "parenttype":"Supplier Scorecard",
+ "variable_label":"# of On Time Shipments",
+ "path":"get_on_time_shipments",
+ "parentfield":"variables"
+ },
+ {
+ "param_name":"total_shipments",
+ "doctype":"Supplier Scorecard Scoring Variable",
+ "parenttype":"Supplier Scorecard",
+ "variable_label":"Total Shipments",
+ "path":"get_total_shipments",
+ "parentfield":"variables"
+ }
+ ],
+ "period":"Per Month",
+ "doctype":"Supplier Scorecard",
+ "warn_pos":0,
+ "warn_rfqs":0,
+ "notify_supplier":0,
+ "criteria":[
+ {
+ "weight":100.0,
+ "doctype":"Supplier Scorecard Scoring Criteria",
+ "formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100 ",
+ "criteria_name":"Delivery",
+ "max_score":100.0,
+ }
+ ],
+ "supplier":"_Test Supplier",
+ "name":"_Test Supplier",
+ "weighting_function":"{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )",
+ }
+]
+
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/__init__.py b/erpnext/buying/doctype/supplier_scorecard_criteria/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js
new file mode 100644
index 0000000..9f8a2de
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+/* global frappe */
+
+frappe.ui.form.on("Supplier Scorecard Criteria", {
+ refresh: function() {}
+});
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json
new file mode 100644
index 0000000..229c386
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.json
@@ -0,0 +1,184 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:criteria_name",
+ "beta": 1,
+ "creation": "2017-05-29 01:32:43.064891",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "criteria_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Criteria Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "weight",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Criteria Weight",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "100",
+ "fieldname": "max_score",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Max Score",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "formula",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Criteria Formula",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-07-17 10:30:47.458285",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Criteria",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
new file mode 100644
index 0000000..8514022
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+import re
+from frappe.model.document import Document
+
+class InvalidFormulaVariable(frappe.ValidationError): pass
+
+class SupplierScorecardCriteria(Document):
+ def validate(self):
+ self.validate_variables()
+ self.validate_formula()
+
+ def validate_variables(self):
+ # make sure all the variables exist
+ _get_variables(self)
+
+ def validate_formula(self):
+ # evaluate the formula with 0's to make sure it is valid
+ test_formula = self.formula.replace("\r", "").replace("\n", "")
+
+ regex = r"\{(.*?)\}"
+
+ mylist = re.finditer(regex, test_formula, re.MULTILINE | re.DOTALL)
+ for dummy1, match in enumerate(mylist):
+ for dummy2 in range(0, len(match.groups())):
+ test_formula = test_formula.replace('{' + match.group(1) + '}', "0")
+
+ test_formula = test_formula.replace('<','<').replace('>','>')
+ try:
+ frappe.safe_eval(test_formula, None, {'max':max, 'min': min})
+ except Exception:
+ frappe.throw(_("Error evaluating the criteria formula"))
+
+
+
+@frappe.whitelist()
+def get_scoring_criteria(criteria_name):
+ criteria = frappe.get_doc("Supplier Scorecard Criteria", criteria_name)
+
+ return criteria
+
+
+@frappe.whitelist()
+def get_criteria_list():
+ criteria = frappe.db.sql("""
+ SELECT
+ scs.name
+ FROM
+ `tabSupplier Scorecard Criteria` scs""",
+ {}, as_dict=1)
+
+ return criteria
+
+@frappe.whitelist()
+def get_variables(criteria_name):
+ criteria = frappe.get_doc("Supplier Scorecard Criteria", criteria_name)
+ return _get_variables(criteria)
+
+def _get_variables(criteria):
+ my_variables = []
+ regex = r"\{(.*?)\}"
+
+ mylist = re.finditer(regex, criteria.formula, re.MULTILINE | re.DOTALL)
+ for dummy1, match in enumerate(mylist):
+ for dummy2 in range(0, len(match.groups())):
+ try:
+ #var = frappe.get_doc("Supplier Scorecard Variable", {'param_name' : d})
+ var = frappe.db.sql("""
+ SELECT
+ scv.name
+ FROM
+ `tabSupplier Scorecard Variable` scv
+ WHERE
+ param_name=%(param)s""",
+ {'param':match.group(1)},)[0][0]
+ my_variables.append(var)
+ except Exception:
+ # Ignore the ones where the variable can't be found
+ frappe.throw(_('Unable to find variable: ') + str(match.group(1)), InvalidFormulaVariable)
+ #pass
+
+
+ #frappe.msgprint(str(my_variables))
+ return my_variables
diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
new file mode 100644
index 0000000..4eef4b4
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestSupplierScorecardCriteria(unittest.TestCase):
+ def test_variables_exist(self):
+ delete_test_scorecards()
+ for d in test_good_criteria:
+ frappe.get_doc(d).insert()
+
+ self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[0]).insert)
+
+ def test_formula_validate(self):
+ delete_test_scorecards()
+ self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[1]).insert)
+ self.assertRaises(frappe.ValidationError,frappe.get_doc(test_bad_criteria[2]).insert)
+
+def delete_test_scorecards():
+ # Delete all the periods so we can delete all the criteria
+ frappe.db.sql("""delete from `tabSupplier Scorecard Period`""")
+ frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Criteria` where parenttype = 'Supplier Scorecard Period'""")
+ frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Standing` where parenttype = 'Supplier Scorecard Period'""")
+ frappe.db.sql("""delete from `tabSupplier Scorecard Scoring Variable` where parenttype = 'Supplier Scorecard Period'""")
+
+ for d in test_good_criteria:
+ if frappe.db.exists("Supplier Scorecard Criteria", d.get("name")):
+ # Delete all the periods, then delete the scorecard
+ frappe.delete_doc(d.get("doctype"), d.get("name"))
+
+ for d in test_bad_criteria:
+ if frappe.db.exists("Supplier Scorecard Criteria", d.get("name")):
+ # Delete all the periods, then delete the scorecard
+ frappe.delete_doc(d.get("doctype"), d.get("name"))
+
+test_good_criteria = [
+ {
+ "name":"Delivery",
+ "weight":40.0,
+ "doctype":"Supplier Scorecard Criteria",
+ "formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100",
+ "criteria_name":"Delivery",
+ "max_score":100.0
+ },
+]
+
+test_bad_criteria = [
+ {
+ "name":"Fake Criteria 1",
+ "weight":40.0,
+ "doctype":"Supplier Scorecard Criteria",
+ "formula":"(({fake_variable} / {tot_cost_shipments}) if {tot_cost_shipments} > 0 else 1 )* 100", # Invalid variable name
+ "criteria_name":"Fake Criteria 1",
+ "max_score":100.0
+ },
+ {
+ "name":"Fake Criteria 2",
+ "weight":40.0,
+ "doctype":"Supplier Scorecard Criteria",
+ "formula":"(({cost_of_on_time_shipments} / {tot_cost_shipments}))* 100", # Force 0 divided by 0
+ "criteria_name":"Fake Criteria 2",
+ "max_score":100.0
+ },
+ {
+ "name":"Fake Criteria 3",
+ "weight":40.0,
+ "doctype":"Supplier Scorecard Criteria",
+ "formula":"(({cost_of_on_time_shipments} {cost_of_on_time_shipments} / {tot_cost_shipments}))* 100", # Two variables beside eachother
+ "criteria_name":"Fake Criteria 3",
+ "max_score":100.0
+ },
+]
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/__init__.py b/erpnext/buying/doctype/supplier_scorecard_period/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_period/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js
new file mode 100644
index 0000000..c51e8ab
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+/* global frappe */
+
+
+frappe.ui.form.on("Supplier Scorecard Period", {
+ onload: function(frm) {
+ frm.get_field("variables").grid.toggle_display("value", true);
+ frm.get_field("criteria").grid.toggle_display("score", true);
+
+
+ }
+});
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
new file mode 100644
index 0000000..0cf6514
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
@@ -0,0 +1,397 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "naming_series:",
+ "beta": 1,
+ "creation": "2017-05-30 00:38:18.773013",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Naming Series",
+ "length": 0,
+ "no_copy": 0,
+ "options": "SSC-",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_score",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Period Score",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "start_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Start Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "end_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "End Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Calculations",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "criteria",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Criteria",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Scoring Criteria",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "variables",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Variables",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Scoring Variable",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "sec_ref",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "scorecard",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Supplier Scorecard Setup",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 1,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:26.130861",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Period",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 0,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
new file mode 100644
index 0000000..90b65bd
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import throw, _
+from frappe.model.document import Document
+from frappe.model.mapper import get_mapped_doc
+import erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable as variable_functions
+
+class SupplierScorecardPeriod(Document):
+
+ def validate(self):
+ self.validate_criteria_weights()
+ self.calculate_variables()
+ self.calculate_criteria()
+ self.calculate_score()
+
+ def validate_criteria_weights(self):
+
+ weight = 0
+ for c in self.criteria:
+ weight += c.weight
+
+ if weight != 100:
+ throw(_('Criteria weights must add up to 100%'))
+
+ def calculate_variables(self):
+ for var in self.variables:
+
+ if '.' in var.path:
+ method_to_call = import_string_path(var.path)
+ var.value = method_to_call(self)
+ else:
+ method_to_call = getattr(variable_functions, var.path)
+ var.value = method_to_call(self)
+
+
+
+ def calculate_criteria(self):
+ #Get the criteria
+ for crit in self.criteria:
+
+ #me = ""
+ my_eval_statement = crit.formula.replace("\r", "").replace("\n", "")
+ #for let in my_eval_statement:
+ # me += let.encode('hex') + " "
+ #frappe.msgprint(me)
+
+ for var in self.variables:
+ if var.value:
+ if var.param_name in my_eval_statement:
+ my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', "{:.2f}".format(var.value))
+ else:
+ if var.param_name in my_eval_statement:
+ my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', '0.0')
+
+ #frappe.msgprint(my_eval_statement )
+
+ my_eval_statement = my_eval_statement.replace('<','<').replace('>','>')
+
+ try:
+ crit.score = min(crit.max_score, max( 0 ,frappe.safe_eval(my_eval_statement, None, {'max':max, 'min': min})))
+ except Exception:
+ frappe.throw(_("Could not solve criteria score function for {0}. Make sure the formula is valid.".format(crit.criteria_name)),frappe.ValidationError)
+ crit.score = 0
+
+ def calculate_score(self):
+ myscore = 0
+ for crit in self.criteria:
+ myscore += crit.score * crit.weight/100.0
+ self.total_score = myscore
+
+ def calculate_weighted_score(self, weighing_function):
+ my_eval_statement = weighing_function.replace("\r", "").replace("\n", "")
+
+ for var in self.variables:
+ if var.value:
+ if var.param_name in my_eval_statement:
+ my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', "{:.2f}".format(var.value))
+ else:
+ if var.param_name in my_eval_statement:
+ my_eval_statement = my_eval_statement.replace('{' + var.param_name + '}', '0.0')
+
+ my_eval_statement = my_eval_statement.replace('<','<').replace('>','>')
+
+ try:
+ weighed_score = frappe.safe_eval(my_eval_statement, None, {'max':max, 'min': min})
+ except Exception:
+ frappe.throw(_("Could not solve weighted score function. Make sure the formula is valid."),frappe.ValidationError)
+ weighed_score = 0
+ return weighed_score
+
+
+
+def import_string_path(path):
+ components = path.split('.')
+ mod = __import__(components[0])
+ for comp in components[1:]:
+ mod = getattr(mod, comp)
+ return mod
+
+
+def post_process(source, target):
+ pass
+
+
+@frappe.whitelist()
+def make_supplier_scorecard(source_name, target_doc=None):
+ #def update_item(obj, target, source_parent):
+ # target.qty = flt(obj.qty) - flt(obj.received_qty)
+ # target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor)
+ # target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate)
+ # target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * \
+ # flt(obj.rate) * flt(source_parent.conversion_rate)
+
+ doc = get_mapped_doc("Supplier Scorecard", source_name, {
+ "Supplier Scorecard": {
+ "doctype": "Supplier Scorecard Period"
+ },
+ "Supplier Scorecard Scoring Variable": {
+ "doctype": "Supplier Scorecard Scoring Variable",
+ "add_if_empty": True
+ },
+ "Supplier Scorecard Scoring Constraint": {
+ "doctype": "Supplier Scorecard Scoring Constraint",
+ "add_if_empty": True
+ }
+ }, target_doc, post_process)
+
+ return doc
+
diff --git a/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py
new file mode 100644
index 0000000..8baa318
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_period/test_supplier_scorecard_period.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+class TestSupplierScorecardPeriod(unittest.TestCase):
+ pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/__init__.py b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json
new file mode 100644
index 0000000..567738a
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.json
@@ -0,0 +1,280 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 1,
+ "creation": "2017-05-29 01:32:17.988454",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 3,
+ "fieldname": "criteria_name",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Criteria Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Criteria",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_2",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "weight",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Criteria Weight",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "100",
+ "fieldname": "max_score",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Max Score",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "formula",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Criteria Formula",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "score",
+ "fieldtype": "Percent",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Score",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:41.532361",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Scoring Criteria",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py
new file mode 100644
index 0000000..b64abed
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_criteria/supplier_scorecard_scoring_criteria.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+class SupplierScorecardScoringCriteria(Document):
+ pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/__init__.py b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json
new file mode 100644
index 0000000..1fc04bb
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.json
@@ -0,0 +1,491 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 1,
+ "creation": "2017-05-29 01:36:22.697234",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 3,
+ "fieldname": "standing_name",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Standing Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Standing",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "standing_color",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Color",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Blue\nPurple\nGreen\nYellow\nOrange\nRed",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "min_grade",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Min Grade",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "max_grade",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Max Grade",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "actions",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Actions",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn Purchase Orders",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent Purchase Orders",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "notify_supplier",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Notify Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "notify_employee",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Notify Employee",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee_link",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee ",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:20.615684",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Scoring Standing",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py
new file mode 100644
index 0000000..e8ad79f
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_standing/supplier_scorecard_scoring_standing.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+class SupplierScorecardScoringStanding(Document):
+ pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/__init__.py b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json
new file mode 100644
index 0000000..f0e043e
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.json
@@ -0,0 +1,222 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 1,
+ "creation": "2017-05-29 01:30:06.105240",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 3,
+ "fieldname": "variable_label",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Variable Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier Scorecard Variable",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "is_custom",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Custom?",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "param_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Parameter Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "path",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Path",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "value",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Value",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:36.671502",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Scoring Variable",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py
new file mode 100644
index 0000000..58a8a99
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_scoring_variable/supplier_scorecard_scoring_variable.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+class SupplierScorecardScoringVariable(Document):
+ pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/__init__.py b/erpnext/buying/doctype/supplier_scorecard_standing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js
new file mode 100644
index 0000000..dccfcc3
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+/* global frappe */
+
+frappe.ui.form.on("Supplier Scorecard Standing", {
+ refresh: function() {
+
+ }
+});
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json
new file mode 100644
index 0000000..b61b4ed
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.json
@@ -0,0 +1,424 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:standing_name",
+ "beta": 1,
+ "creation": "2017-05-29 01:36:47.893639",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "standing_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Standing Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "standing_color",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Color",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Blue\nPurple\nGreen\nYellow\nOrange\nRed",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "min_grade",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Min Grade",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "max_grade",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Max Grade",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "warn_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Warn Purchase Orders",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_rfqs",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent RFQs",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "prevent_pos",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Prevent Purchase Orders",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "notify_supplier",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Notify Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "notify_employee",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Notify Other",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee_link",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Other",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:16.560273",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Standing",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 0,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
new file mode 100644
index 0000000..1ba5d06
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class SupplierScorecardStanding(Document):
+ pass
+
+
+@frappe.whitelist()
+def get_scoring_standing(standing_name):
+ standing = frappe.get_doc("Supplier Scorecard Standing", standing_name)
+
+ return standing
+
+
+@frappe.whitelist()
+def get_standings_list():
+ standings = frappe.db.sql("""
+ SELECT
+ scs.name
+ FROM
+ `tabSupplier Scorecard Standing` scs""",
+ {}, as_dict=1)
+
+ return standings
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py
new file mode 100644
index 0000000..4d96651
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_standing/test_supplier_scorecard_standing.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+class TestSupplierScorecardStanding(unittest.TestCase):
+ pass
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/__init__.py b/erpnext/buying/doctype/supplier_scorecard_variable/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/__init__.py
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js
new file mode 100644
index 0000000..2d74fdd
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+/* global frappe */
+
+frappe.ui.form.on("Supplier Scorecard Variable", {
+ refresh: function() {
+
+ }
+});
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json
new file mode 100644
index 0000000..d244840
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.json
@@ -0,0 +1,242 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:variable_label",
+ "beta": 1,
+ "creation": "2017-05-29 01:30:34.688389",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "variable_label",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Variable Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "is_custom",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Custom?",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "param_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Parameter Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "path",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Path",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-07-12 07:33:31.395262",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Scorecard Variable",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
new file mode 100644
index 0000000..17c911a
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py
@@ -0,0 +1,503 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import sys
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import getdate
+
+class VariablePathNotFound(frappe.ValidationError): pass
+
+class SupplierScorecardVariable(Document):
+ def validate(self):
+ self.validate_path_exists()
+
+ def validate_path_exists(self):
+ if '.' in self.path:
+ try:
+ from erpnext.buying.doctype.supplier_scorecard_period.supplier_scorecard_period import import_string_path
+ import_string_path(self.path)
+ except AttributeError:
+ frappe.throw(_("Could not find path for " + self.path), VariablePathNotFound)
+
+ else:
+ if not hasattr(sys.modules[__name__], self.path):
+ frappe.throw(_("Could not find path for " + self.path), VariablePathNotFound)
+
+
+@frappe.whitelist()
+def get_scoring_variable(variable_label):
+ variable = frappe.get_doc("Supplier Scorecard Variable", variable_label)
+
+ return variable
+
+def get_total_workdays(scorecard):
+ """ Gets the number of days in this period"""
+ delta = getdate(scorecard.end_date) - getdate(scorecard.start_date)
+ return delta.days
+
+def get_item_workdays(scorecard):
+ """ Gets the number of days in this period"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+ total_item_days = frappe.db.sql("""
+ SELECT
+ SUM(DATEDIFF( %(end_date)s, po_item.schedule_date) * (po_item.qty))
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Order` po
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.received_qty < po_item.qty
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.parent = po.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not total_item_days:
+ total_item_days = 0
+ return total_item_days
+
+
+
+def get_total_cost_of_shipments(scorecard):
+ """ Gets the total cost of all shipments in the period (based on Purchase Orders)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(po_item.base_amount)
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Order` po
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.docstatus = 1
+ AND po_item.parent = po.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if data:
+ return data
+ else:
+ return 0
+
+def get_cost_of_delayed_shipments(scorecard):
+ """ Gets the total cost of all delayed shipments in the period (based on Purchase Receipts - POs)"""
+ return get_total_cost_of_shipments(scorecard) - get_cost_of_on_time_shipments(scorecard)
+
+def get_cost_of_on_time_shipments(scorecard):
+ """ Gets the total cost of all on_time shipments in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+
+ total_delivered_on_time_costs = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.base_amount)
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Order` po,
+ `tabPurchase Receipt` pr
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.schedule_date >= pr.posting_date
+ AND pr_item.docstatus = 1
+ AND pr_item.purchase_order_item = po_item.name
+ AND po_item.parent = po.name
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if total_delivered_on_time_costs:
+ return total_delivered_on_time_costs
+ else:
+ return 0
+
+
+def get_total_days_late(scorecard):
+ """ Gets the number of item days late in the period (based on Purchase Receipts vs POs)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+ total_delivered_late_days = frappe.db.sql("""
+ SELECT
+ SUM(DATEDIFF(pr.posting_date,po_item.schedule_date)* pr_item.qty)
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Order` po,
+ `tabPurchase Receipt` pr
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.schedule_date < pr.posting_date
+ AND pr_item.docstatus = 1
+ AND pr_item.purchase_order_item = po_item.name
+ AND po_item.parent = po.name
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+ if not total_delivered_late_days:
+ total_delivered_late_days = 0
+
+ total_missed_late_days = frappe.db.sql("""
+ SELECT
+ SUM(DATEDIFF( %(end_date)s, po_item.schedule_date) * (po_item.qty - po_item.received_qty))
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Order` po
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.received_qty < po_item.qty
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.parent = po.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not total_missed_late_days:
+ total_missed_late_days = 0
+ return total_missed_late_days + total_delivered_late_days
+
+def get_on_time_shipments(scorecard):
+ """ Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)"""
+
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ total_items_delivered_on_time = frappe.db.sql("""
+ SELECT
+ COUNT(pr_item.qty)
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Order` po,
+ `tabPurchase Receipt` pr
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.schedule_date <= pr.posting_date
+ AND po_item.qty = pr_item.qty
+ AND pr_item.docstatus = 1
+ AND pr_item.purchase_order_item = po_item.name
+ AND po_item.parent = po.name
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not total_items_delivered_on_time:
+ total_items_delivered_on_time = 0
+ return total_items_delivered_on_time
+
+def get_late_shipments(scorecard):
+ """ Gets the number of late shipments (counting each item) in the period (based on Purchase Receipts vs POs)"""
+ return get_total_shipments(scorecard) - get_on_time_shipments(scorecard)
+
+def get_total_received(scorecard):
+ """ Gets the total number of received shipments in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ COUNT(pr_item.base_amount)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_received_amount(scorecard):
+ """ Gets the total amount (in company currency) received in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.received_qty * pr_item.base_rate)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_received_items(scorecard):
+ """ Gets the total number of received shipments in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.received_qty)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_rejected_amount(scorecard):
+ """ Gets the total amount (in company currency) rejected in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.rejected_qty * pr_item.base_rate)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_rejected_items(scorecard):
+ """ Gets the total number of rejected items in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.rejected_qty)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_accepted_amount(scorecard):
+ """ Gets the total amount (in company currency) accepted in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.qty * pr_item.base_rate)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_accepted_items(scorecard):
+ """ Gets the total number of rejected items in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ SUM(pr_item.qty)
+ FROM
+ `tabPurchase Receipt Item` pr_item,
+ `tabPurchase Receipt` pr
+ WHERE
+ pr.supplier = %(supplier)s
+ AND pr.posting_date BETWEEN %(start_date)s AND %(end_date)s
+ AND pr_item.docstatus = 1
+ AND pr_item.parent = pr.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_total_shipments(scorecard):
+ """ Gets the total number of ordered shipments to arrive in the period (based on Purchase Receipts)"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ COUNT(po_item.base_amount)
+ FROM
+ `tabPurchase Order Item` po_item,
+ `tabPurchase Order` po
+ WHERE
+ po.supplier = %(supplier)s
+ AND po_item.schedule_date BETWEEN %(start_date)s AND %(end_date)s
+ AND po_item.docstatus = 1
+ AND po_item.parent = po.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_rfq_total_number(scorecard):
+ """ Gets the total number of RFQs sent to supplier"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ COUNT(rfq.name) as total_rfqs
+ FROM
+ `tabRequest for Quotation Item` rfq_item,
+ `tabRequest for Quotation Supplier` rfq_sup,
+ `tabRequest for Quotation` rfq
+ WHERE
+ rfq_sup.supplier = %(supplier)s
+ AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s
+ AND rfq_item.docstatus = 1
+ AND rfq_item.parent = rfq.name
+ AND rfq_sup.parent = rfq.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+
+ if not data:
+ data = 0
+ return data
+
+def get_rfq_total_items(scorecard):
+ """ Gets the total number of RFQ items sent to supplier"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ COUNT(rfq_item.name) as total_rfqs
+ FROM
+ `tabRequest for Quotation Item` rfq_item,
+ `tabRequest for Quotation Supplier` rfq_sup,
+ `tabRequest for Quotation` rfq
+ WHERE
+ rfq_sup.supplier = %(supplier)s
+ AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s
+ AND rfq_item.docstatus = 1
+ AND rfq_item.parent = rfq.name
+ AND rfq_sup.parent = rfq.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+ if not data:
+ data = 0
+ return data
+
+
+def get_sq_total_number(scorecard):
+ """ Gets the total number of RFQ items sent to supplier"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ COUNT(sq.name) as total_sqs
+ FROM
+ `tabRequest for Quotation Item` rfq_item,
+ `tabSupplier Quotation Item` sq_item,
+ `tabRequest for Quotation Supplier` rfq_sup,
+ `tabRequest for Quotation` rfq,
+ `tabSupplier Quotation` sq
+ WHERE
+ rfq_sup.supplier = %(supplier)s
+ AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s
+ AND sq_item.request_for_quotation_item = rfq_item.name
+ AND sq_item.docstatus = 1
+ AND rfq_item.docstatus = 1
+ AND sq.supplier = %(supplier)s
+ AND sq_item.parent = sq.name
+ AND rfq_item.parent = rfq.name
+ AND rfq_sup.parent = rfq.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+ if not data:
+ data = 0
+ return data
+
+def get_sq_total_items(scorecard):
+ """ Gets the total number of RFQ items sent to supplier"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+
+ # Look up all PO Items with delivery dates between our dates
+ data = frappe.db.sql("""
+ SELECT
+ COUNT(sq_item.name) as total_sqs
+ FROM
+ `tabRequest for Quotation Item` rfq_item,
+ `tabSupplier Quotation Item` sq_item,
+ `tabSupplier Quotation` sq,
+ `tabRequest for Quotation Supplier` rfq_sup,
+ `tabRequest for Quotation` rfq
+ WHERE
+ rfq_sup.supplier = %(supplier)s
+ AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s
+ AND sq_item.request_for_quotation_item = rfq_item.name
+ AND sq_item.docstatus = 1
+ AND sq.supplier = %(supplier)s
+ AND sq_item.parent = sq.name
+ AND rfq_item.docstatus = 1
+ AND rfq_item.parent = rfq.name
+ AND rfq_sup.parent = rfq.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+ if not data:
+ data = 0
+ return data
+
+def get_rfq_response_days(scorecard):
+ """ Gets the total number of days it has taken a supplier to respond to rfqs in the period"""
+ supplier = frappe.get_doc('Supplier', scorecard.supplier)
+ total_sq_days = frappe.db.sql("""
+ SELECT
+ SUM(DATEDIFF(sq.transaction_date, rfq.transaction_date))
+ FROM
+ `tabRequest for Quotation Item` rfq_item,
+ `tabSupplier Quotation Item` sq_item,
+ `tabSupplier Quotation` sq,
+ `tabRequest for Quotation Supplier` rfq_sup,
+ `tabRequest for Quotation` rfq
+ WHERE
+ rfq_sup.supplier = %(supplier)s
+ AND rfq.transaction_date BETWEEN %(start_date)s AND %(end_date)s
+ AND sq_item.request_for_quotation_item = rfq_item.name
+ AND sq_item.docstatus = 1
+ AND sq.supplier = %(supplier)s
+ AND sq_item.parent = sq.name
+ AND rfq_item.docstatus = 1
+ AND rfq_item.parent = rfq.name
+ AND rfq_sup.parent = rfq.name""",
+ {"supplier": supplier.name, "start_date": scorecard.start_date, "end_date": scorecard.end_date}, as_dict=0)[0][0]
+ if not total_sq_days:
+ total_sq_days = 0
+
+
+ return total_sq_days
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
new file mode 100644
index 0000000..45a2c62
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+from erpnext.buying.doctype.supplier_scorecard_variable.supplier_scorecard_variable import VariablePathNotFound
+
+
+class TestSupplierScorecardVariable(unittest.TestCase):
+ def test_variable_exist(self):
+ for d in test_existing_variables:
+ my_doc = frappe.get_doc("Supplier Scorecard Variable", d.get("name"))
+ self.assertEquals(my_doc.param_name, d.get('param_name'))
+ self.assertEquals(my_doc.variable_label, d.get('variable_label'))
+ self.assertEquals(my_doc.path, d.get('path'))
+
+ def test_path_exists(self):
+ for d in test_good_variables:
+ if frappe.db.exists(d):
+ frappe.delete_doc(d.get("doctype"), d.get("name"))
+ frappe.get_doc(d).insert()
+
+ for d in test_bad_variables:
+ self.assertRaises(VariablePathNotFound,frappe.get_doc(d).insert)
+
+test_existing_variables = [
+ {
+ "param_name":"total_accepted_items",
+ "name":"Total Accepted Items",
+ "doctype":"Supplier Scorecard Variable",
+ "variable_label":"Total Accepted Items",
+ "path":"get_total_accepted_items"
+ },
+]
+
+test_good_variables = [
+ {
+ "param_name":"good_variable1",
+ "name":"Good Variable 1",
+ "doctype":"Supplier Scorecard Variable",
+ "variable_label":"Good Variable 1",
+ "path":"get_total_accepted_items"
+ },
+]
+
+test_bad_variables = [
+ {
+ "param_name":"fake_variable1",
+ "name":"Fake Variable 1",
+ "doctype":"Supplier Scorecard Variable",
+ "variable_label":"Fake Variable 1",
+ "path":"get_fake_variable1"
+ },
+]
\ No newline at end of file
diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py
index 990ca7a..ba29125 100644
--- a/erpnext/config/buying.py
+++ b/erpnext/config/buying.py
@@ -142,6 +142,32 @@
]
},
{
+ "label": _("Supplier Scorecard"),
+ "items": [
+ {
+ "type": "doctype",
+ "name": "Supplier Scorecard",
+ "description": _("All Supplier scorecards."),
+ },
+ {
+ "type": "doctype",
+ "name": "Supplier Scorecard Variable",
+ "description": _("Templates of supplier scorecard variables.")
+ },
+ {
+ "type": "doctype",
+ "name": "Supplier Scorecard Criteria",
+ "description": _("Templates of supplier scorecard criteria."),
+ },
+ {
+ "type": "doctype",
+ "name": "Supplier Scorecard Standing",
+ "description": _("Templates of supplier standings."),
+ },
+
+ ]
+ },
+ {
"label": _("Other Reports"),
"icon": "fa fa-list",
"items": [
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 8130af9..6d69a48 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -60,6 +60,7 @@
# searches for customer
def customer_query(doctype, txt, searchfield, start, page_len, filters):
+ conditions = []
cust_master_name = frappe.defaults.get_user_default("cust_master_name")
if cust_master_name == "Customer Name":
@@ -79,7 +80,7 @@
return frappe.db.sql("""select {fields} from `tabCustomer`
where docstatus < 2
and ({scond}) and disabled=0
- {mcond}
+ {fcond} {mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
if(locate(%(_txt)s, customer_name), locate(%(_txt)s, customer_name), 99999),
@@ -88,7 +89,8 @@
limit %(start)s, %(page_len)s""".format(**{
"fields": fields,
"scond": searchfields,
- "mcond": get_match_cond(doctype)
+ "mcond": get_match_cond(doctype),
+ "fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
}), {
'txt': "%%%s%%" % txt,
'_txt': txt.replace("%", ""),
diff --git a/erpnext/controllers/tests/test_recurring_document.py b/erpnext/controllers/tests/test_recurring_document.py
index d47c5c7..e218fa6 100644
--- a/erpnext/controllers/tests/test_recurring_document.py
+++ b/erpnext/controllers/tests/test_recurring_document.py
@@ -27,7 +27,8 @@
base_doc.set(date_field, today)
if base_doc.doctype == "Sales Order":
- base_doc.set("delivery_date", add_days(today, 15))
+ for d in base_doc.get("items"):
+ d.set("delivery_date", add_days(today, 15))
# monthly
doc1 = frappe.copy_doc(base_doc)
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index b078036..04d1bc5 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -30,7 +30,7 @@
else:
filters.append((doctype, "docstatus", "=", 1))
- if user != "Guest" and is_website_user():
+ if (user != "Guest" and is_website_user()) or doctype == 'Request for Quotation':
parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
# find party for this contact
customers, suppliers = get_customers_suppliers(parties_doctype, user)
diff --git a/erpnext/crm/doctype/item/test_item.js b/erpnext/crm/doctype/item/test_item.js
new file mode 100644
index 0000000..2f50ebe
--- /dev/null
+++ b/erpnext/crm/doctype/item/test_item.js
@@ -0,0 +1,82 @@
+QUnit.test("test: item", function (assert) {
+ assert.expect(6);
+ let done = assert.async();
+ let keyboard_cost = 800;
+ let screen_cost = 4000;
+ let CPU_cost = 15000;
+ let scrap_cost = 100;
+ let no_of_items_to_stock = 100;
+ let is_stock_item = 1;
+ frappe.run_serially([
+ // test item creation
+ () => frappe.set_route("List", "Item"),
+
+ // Create a keyboard item
+ () => frappe.tests.make(
+ "Item", [
+ {item_code: "Keyboard"},
+ {item_group: "Products"},
+ {is_stock_item: is_stock_item},
+ {standard_rate: keyboard_cost},
+ {opening_stock: no_of_items_to_stock}
+ ]
+ ),
+ () => {
+ assert.ok(cur_frm.doc.item_name.includes('Keyboard'),
+ 'Item Keyboard created correctly');
+ assert.ok(cur_frm.doc.item_code.includes('Keyboard'),
+ 'item_code for Keyboard set correctly');
+ assert.ok(cur_frm.doc.item_group.includes('Products'),
+ 'item_group for Keyboard set correctly');
+ assert.equal(cur_frm.doc.is_stock_item, is_stock_item,
+ 'is_stock_item for Keyboard set correctly');
+ assert.equal(cur_frm.doc.standard_rate, keyboard_cost,
+ 'standard_rate for Keyboard set correctly');
+ assert.equal(cur_frm.doc.opening_stock, no_of_items_to_stock,
+ 'opening_stock for Keyboard set correctly');
+ },
+
+ // Create a Screen item
+ () => frappe.tests.make(
+ "Item", [
+ {item_code: "Screen"},
+ {item_group: "Products"},
+ {is_stock_item: is_stock_item},
+ {standard_rate: screen_cost},
+ {opening_stock: no_of_items_to_stock}
+ ]
+ ),
+
+ // Create a CPU item
+ () => frappe.tests.make(
+ "Item", [
+ {item_code: "CPU"},
+ {item_group: "Products"},
+ {is_stock_item: is_stock_item},
+ {standard_rate: CPU_cost},
+ {opening_stock: no_of_items_to_stock}
+ ]
+ ),
+
+ // Create a laptop item
+ () => frappe.tests.make(
+ "Item", [
+ {item_code: "Laptop"},
+ {item_group: "Products"}
+ ]
+ ),
+
+ // Create a scrap item
+ () => frappe.tests.make(
+ "Item", [
+ {item_code: "Scrap item"},
+ {item_group: "Products"},
+ {is_stock_item: is_stock_item},
+ {standard_rate: scrap_cost},
+ {opening_stock: no_of_items_to_stock}
+ ]
+ ),
+
+ () => done()
+ ]);
+});
diff --git a/erpnext/crm/doctype/lead/test_lead.js b/erpnext/crm/doctype/lead/test_lead.js
new file mode 100644
index 0000000..66d3337
--- /dev/null
+++ b/erpnext/crm/doctype/lead/test_lead.js
@@ -0,0 +1,43 @@
+QUnit.module("sales");
+
+QUnit.test("test: lead", function (assert) {
+ assert.expect(4);
+ let done = assert.async();
+ let lead_name = frappe.utils.get_random(10);
+ frappe.run_serially([
+ // test lead creation
+ () => frappe.set_route("List", "Lead"),
+ () => frappe.new_doc("Lead"),
+ () => frappe.timeout(1),
+ () => cur_frm.set_value("lead_name", lead_name),
+ () => cur_frm.save(),
+ () => frappe.timeout(1),
+ () => {
+ assert.ok(cur_frm.doc.lead_name.includes(lead_name),
+ 'name correctly set');
+ frappe.lead_name = cur_frm.doc.name;
+ },
+ // create address and contact
+ () => frappe.click_link('Address & Contact'),
+ () => frappe.click_button('New Address'),
+ () => frappe.timeout(1),
+ () => frappe.set_control('address_line1', 'Gateway'),
+ () => frappe.set_control('city', 'Mumbai'),
+ () => cur_frm.save(),
+ () => frappe.timeout(3),
+ () => assert.equal(frappe.get_route()[1], 'Lead',
+ 'back to lead form'),
+ () => frappe.click_link('Address & Contact'),
+ () => assert.ok($('.address-box').text().includes('Mumbai'),
+ 'city is seen in address box'),
+
+ // make opportunity
+ () => frappe.click_button('Make'),
+ () => frappe.click_link('Opportunity'),
+ () => frappe.timeout(2),
+ () => assert.equal(cur_frm.doc.lead, frappe.lead_name,
+ 'lead name correctly mapped'),
+
+ () => done()
+ ]);
+});
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.js b/erpnext/crm/doctype/opportunity/test_opportunity.js
new file mode 100644
index 0000000..f2b04f8
--- /dev/null
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.js
@@ -0,0 +1,56 @@
+QUnit.test("test: opportunity", function (assert) {
+ assert.expect(8);
+ let done = assert.async();
+ frappe.run_serially([
+ () => frappe.set_route('List', 'Opportunity'),
+ () => frappe.timeout(1),
+ () => frappe.click_button('New'),
+ () => frappe.timeout(1),
+ () => cur_frm.set_value('enquiry_from', 'Customer'),
+ () => cur_frm.set_value('customer', 'Test Customer 1'),
+
+ // check items
+ () => cur_frm.set_value('with_items', 1),
+ () => frappe.tests.set_grid_values(cur_frm, 'items', [
+ [
+ {item_code:'Test Product 1'},
+ {qty: 4}
+ ]
+ ]),
+ () => cur_frm.save(),
+ () => frappe.timeout(1),
+ () => {
+ assert.notOk(cur_frm.is_new(), 'saved');
+ frappe.opportunity_name = cur_frm.doc.name;
+ },
+
+ // close and re-open
+ () => frappe.click_button('Close'),
+ () => frappe.timeout(1),
+ () => assert.equal(cur_frm.doc.status, 'Closed',
+ 'closed'),
+
+ () => frappe.click_button('Reopen'),
+ () => assert.equal(cur_frm.doc.status, 'Open',
+ 'reopened'),
+ () => frappe.timeout(1),
+
+ // make quotation
+ () => frappe.click_button('Make'),
+ () => frappe.click_link('Quotation', 1),
+ () => frappe.timeout(2),
+ () => {
+ assert.equal(frappe.get_route()[1], 'Quotation',
+ 'made quotation');
+ assert.equal(cur_frm.doc.customer, 'Test Customer 1',
+ 'customer set in quotation');
+ assert.equal(cur_frm.doc.items[0].item_code, 'Test Product 1',
+ 'item set in quotation');
+ assert.equal(cur_frm.doc.items[0].qty, 4,
+ 'qty set in quotation');
+ assert.equal(cur_frm.doc.items[0].prevdoc_docname, frappe.opportunity_name,
+ 'opportunity set in quotation');
+ },
+ () => done()
+ ]);
+});
diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py
index ddd36ef..666e201 100644
--- a/erpnext/demo/user/sales.py
+++ b/erpnext/demo/user/sales.py
@@ -118,7 +118,8 @@
from erpnext.selling.doctype.quotation.quotation import make_sales_order
so = frappe.get_doc(make_sales_order(q))
so.transaction_date = frappe.flags.current_date
- so.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
+ for d in so.get("items"):
+ d.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
so.insert()
frappe.db.commit()
so.submit()
diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard-criteria.png b/erpnext/docs/assets/img/buying/supplier-scorecard-criteria.png
new file mode 100644
index 0000000..0bc73c8
--- /dev/null
+++ b/erpnext/docs/assets/img/buying/supplier-scorecard-criteria.png
Binary files differ
diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard-standing.png b/erpnext/docs/assets/img/buying/supplier-scorecard-standing.png
new file mode 100644
index 0000000..8c507cb
--- /dev/null
+++ b/erpnext/docs/assets/img/buying/supplier-scorecard-standing.png
Binary files differ
diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard-weighing.png b/erpnext/docs/assets/img/buying/supplier-scorecard-weighing.png
new file mode 100644
index 0000000..d32e69e
--- /dev/null
+++ b/erpnext/docs/assets/img/buying/supplier-scorecard-weighing.png
Binary files differ
diff --git a/erpnext/docs/assets/img/buying/supplier-scorecard.png b/erpnext/docs/assets/img/buying/supplier-scorecard.png
new file mode 100644
index 0000000..1f8de17
--- /dev/null
+++ b/erpnext/docs/assets/img/buying/supplier-scorecard.png
Binary files differ
diff --git a/erpnext/docs/assets/img/selling/make-so.gif b/erpnext/docs/assets/img/selling/make-so.gif
index 5edd19b..0e568a8 100644
--- a/erpnext/docs/assets/img/selling/make-so.gif
+++ b/erpnext/docs/assets/img/selling/make-so.gif
Binary files differ
diff --git a/erpnext/docs/assets/img/setup/email/email-account-incoming-conditions.png b/erpnext/docs/assets/img/setup/email/email-account-incoming-conditions.png
new file mode 100644
index 0000000..61a86f5
--- /dev/null
+++ b/erpnext/docs/assets/img/setup/email/email-account-incoming-conditions.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/buying/index.txt b/erpnext/docs/user/manual/en/buying/index.txt
index 25c8797..4bd75f1 100644
--- a/erpnext/docs/user/manual/en/buying/index.txt
+++ b/erpnext/docs/user/manual/en/buying/index.txt
@@ -5,3 +5,4 @@
setup
articles
purchase-taxes
+supplier-scorecard
\ No newline at end of file
diff --git a/erpnext/docs/user/manual/en/buying/supplier-scorecard.md b/erpnext/docs/user/manual/en/buying/supplier-scorecard.md
new file mode 100644
index 0000000..cecdf9c
--- /dev/null
+++ b/erpnext/docs/user/manual/en/buying/supplier-scorecard.md
@@ -0,0 +1,76 @@
+A Supplier Scorecard is an evaluation tool used to assess the performance of
+suppliers. Supplier scorecards can be used to keep track of item quality,
+delivery and responsiveness of suppliers across long periods of time. This data
+is typically used to help in purchasing decisions.
+
+A Supplier Scorecard is manually created for each supplier.
+
+In ERPNext, you can create a supplier scorecard by going to:
+
+> Buying > Documents > Supplier Scorecard > New Supplier Scorecard
+
+### Create Supplier Scorecard
+A supplier scorecard is created for each supplier individually. Only one supplier scorecard can be created for each
+supplier.
+<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}
diff --git a/erpnext/docs/user/manual/en/selling/sales-order.md b/erpnext/docs/user/manual/en/selling/sales-order.md
index 99b9a85..b103038 100644
--- a/erpnext/docs/user/manual/en/selling/sales-order.md
+++ b/erpnext/docs/user/manual/en/selling/sales-order.md
@@ -31,7 +31,7 @@
There are a few amongst other things that a Sales Order will ask you to
update.
- * Expected date of delivery.
+ * Enter delivery date agaist each item. If there are multiple items and if you enter delivery date in the first row, the date will be copied to other rows as well where it is blank.
* Customer Purchase Order number: If your customer has sent you a Purchase Order, you can update its number for future reference (in billing).
### Packing List
@@ -89,9 +89,9 @@
Once you “Submit” your Sales Order, you can now trigger different aspects of
your organization:
- * To begin purchase click on “Make Purchase Request”
- * To make a shipment entry click on “Make Delivery Note”
- * To bill, make “Make Sales Invoice”
+ * To begin purchase click on Make -> Purchase Request
+ * To make a shipment entry click on Make -> Delivery Note. You can also make Delivery Note for selected items based on delivery date.
+ * To bill, make Make -> Sales Invoice
* To stop further process on this Sales Order, click on “Stop”
### Submission
diff --git a/erpnext/docs/user/manual/en/setting-up/email/email-account.md b/erpnext/docs/user/manual/en/setting-up/email/email-account.md
index ad448b0..d5aca1a 100644
--- a/erpnext/docs/user/manual/en/setting-up/email/email-account.md
+++ b/erpnext/docs/user/manual/en/setting-up/email/email-account.md
@@ -26,6 +26,12 @@
<img class="screenshot" alt="Incoming EMail" src="{{docs_base_url}}/assets/img/setup/email/email-account-incoming.png">
+### Setting Import Conditions for Email Import
+
+Email Accounts allows you to set conditions according to the data of the incoming emails. The email will be imported to ERPNext only if the all conditions are true. For example if you want to import an email if the subject is "Some important email", you put doc.subject == "Some important email" in the conditions textbox. You can also set more complex conditions by combining them, as shown on the following screenshot.
+
+<img class="screenshot" alt="Incoming EMail Conditions" src="{{docs_base_url}}/assets/img/setup/email/email-account-incoming-conditions.png">
+
### How ERPNext handles replies
In ERPNext when you send an email to a contact like a customer, the sender will be the user who sent the email. In the **Reply-To** property, the Email Address will be of the default incoming account (like `replies@yourcompany.com`). ERPNext will automatically extract these emails from the incoming account and tag it to the relevant communication
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 472d8f9..37fd869 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -72,14 +72,20 @@
"parents": [{"title": _("Invoices"), "name": "invoices"}]
}
},
- {"from_route": "/quotations", "to_route": "Supplier Quotation"},
- {"from_route": "/quotations/<path:name>", "to_route": "order",
+ {"from_route": "/supplier-quotations", "to_route": "Supplier Quotation"},
+ {"from_route": "/supplier-quotations/<path:name>", "to_route": "order",
"defaults": {
"doctype": "Supplier Quotation",
"parents": [{"title": _("Supplier Quotation"), "name": "quotations"}]
}
},
- {"from_route": "/quotation", "to_route": "Quotation"},
+ {"from_route": "/quotations", "to_route": "Quotation"},
+ {"from_route": "/quotations/<path:name>", "to_route": "order",
+ "defaults": {
+ "doctype": "Quotation",
+ "parents": [{"title": _("Quotations"), "name": "quotation"}]
+ }
+ },
{"from_route": "/shipments", "to_route": "Delivery Note"},
{"from_route": "/shipments/<path:name>", "to_route": "order",
"defaults": {
@@ -103,34 +109,38 @@
},
{"from_route": "/jobs", "to_route": "Job Opening"},
{"from_route": "/admissions", "to_route": "Student Admission"},
- {"from_route": "/boms", "to_route": "BOM"}
+ {"from_route": "/boms", "to_route": "BOM"},
+ {"from_route": "/timesheets", "to_route": "Timesheet"},
]
standard_portal_menu_items = [
{"title": _("Projects"), "route": "/project", "reference_doctype": "Project"},
{"title": _("Request for Quotations"), "route": "/rfq", "reference_doctype": "Request for Quotation", "role": "Supplier"},
- {"title": _("Supplier Quotation"), "route": "/quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier"},
- {"title": _("Quotations"), "route": "/quotation", "reference_doctype": "Quotation", "role":"Customer"},
+ {"title": _("Supplier Quotation"), "route": "/supplier-quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier"},
+ {"title": _("Quotations"), "route": "/quotations", "reference_doctype": "Quotation", "role":"Customer"},
{"title": _("Orders"), "route": "/orders", "reference_doctype": "Sales Order", "role":"Customer"},
{"title": _("Invoices"), "route": "/invoices", "reference_doctype": "Sales Invoice", "role":"Customer"},
{"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"},
{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"},
{"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"},
- {"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"}
+ {"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"},
+ {"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"}
]
default_roles = [
{'role': 'Customer', 'doctype':'Contact', 'email_field': 'email_id'},
{'role': 'Supplier', 'doctype':'Contact', 'email_field': 'email_id'},
- {'role': 'Student', 'doctype':'Student', 'email_field': 'student_email_id'}
+ {'role': 'Student', 'doctype':'Student', 'email_field': 'student_email_id'},
]
has_website_permission = {
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
+ "Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Supplier Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
- "Issue": "erpnext.support.doctype.issue.issue.has_website_permission"
+ "Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
+ "Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission"
}
dump_report_map = "erpnext.startup.report_data_map.data_map"
@@ -184,6 +194,7 @@
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries",
"erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status",
+ "erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.refresh_scorecards",
"erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history"
]
}
diff --git a/erpnext/hr/doctype/branch/test_branch.js b/erpnext/hr/doctype/branch/test_branch.js
new file mode 100644
index 0000000..25ea2fc
--- /dev/null
+++ b/erpnext/hr/doctype/branch/test_branch.js
@@ -0,0 +1,22 @@
+QUnit.module('hr');
+
+QUnit.test("Test: Branch [HR]", function (assert) {
+ assert.expect(1);
+ let done = assert.async();
+
+ frappe.run_serially([
+ // test branch creation
+ () => frappe.set_route("List", "Branch", "List"),
+ () => frappe.new_doc("Branch"),
+ () => frappe.timeout(1),
+ () => frappe.click_link('Edit in full page'),
+ () => cur_frm.set_value("branch", "Branch test"),
+
+ // save form
+ () => cur_frm.save(),
+ () => frappe.timeout(1),
+ () => assert.equal("Branch test", cur_frm.doc.branch,
+ 'name of branch correctly saved'),
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.js b/erpnext/hr/doctype/holiday_list/test_holiday_list.js
new file mode 100644
index 0000000..ed75285
--- /dev/null
+++ b/erpnext/hr/doctype/holiday_list/test_holiday_list.js
@@ -0,0 +1,42 @@
+QUnit.module('hr');
+
+QUnit.test("Test: Holiday list [HR]", function (assert) {
+ assert.expect(3);
+ let done = assert.async();
+ let date = frappe.datetime.add_months(frappe.datetime.nowdate(), -2); // date 2 months from now
+
+ frappe.run_serially([
+ // test holiday list creation
+ () => frappe.set_route("List", "Holiday List", "List"),
+ () => frappe.new_doc("Holiday List"),
+ () => frappe.timeout(1),
+ () => cur_frm.set_value("holiday_list_name", "Holiday list test"),
+ () => cur_frm.set_value("from_date", date),
+ () => cur_frm.set_value("weekly_off", "Sunday"), // holiday list for sundays
+ () => frappe.click_button('Get Weekly Off Dates'),
+
+ // save form
+ () => cur_frm.save(),
+ () => frappe.timeout(1),
+ () => assert.equal("Holiday list test", cur_frm.doc.holiday_list_name,
+ 'name of holiday list correctly saved'),
+
+ // check if holiday list contains correct days
+ () => {
+ var list = cur_frm.doc.holidays;
+ var list_length = list.length;
+ var i = 0;
+ for ( ; i < list_length; i++)
+ if (list[i].description != 'Sunday') break;
+ assert.equal(list_length, i, "all holidays are sundays in holiday list");
+ },
+
+ // check if to_date is set one year from from_date
+ () => {
+ var date_year_later = frappe.datetime.add_days(frappe.datetime.add_months(date, 12), -1); // date after one year
+ assert.equal(date_year_later, cur_frm.doc.to_date,
+ "to date set correctly");
+ },
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.js b/erpnext/hr/doctype/process_payroll/process_payroll.js
index 0ec7f70..a9ad429 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.js
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.js
@@ -1,6 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+var in_progress = false;
+
frappe.ui.form.on("Process Payroll", {
onload: function (frm) {
frm.doc.posting_date = frappe.datetime.nowdate();
@@ -47,11 +49,12 @@
},
start_date: function (frm) {
- frm.trigger("set_start_end_dates");
- },
-
- end_date: function (frm) {
- frm.trigger("set_start_end_dates");
+ if(!in_progress && frm.doc.start_date){
+ frm.trigger("set_end_date");
+ }else{
+ // reset flag
+ in_progress = false
+ }
},
salary_slip_based_on_timesheet: function (frm) {
@@ -68,10 +71,11 @@
method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_start_end_dates',
args: {
payroll_frequency: frm.doc.payroll_frequency,
- start_date: frm.doc.start_date || frm.doc.posting_date
+ start_date: frm.doc.posting_date
},
callback: function (r) {
if (r.message) {
+ in_progress = true;
frm.set_value('start_date', r.message.start_date);
frm.set_value('end_date', r.message.end_date);
}
@@ -79,6 +83,21 @@
})
}
},
+
+ set_end_date: function(frm){
+ frappe.call({
+ method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_end_date',
+ args: {
+ frequency: frm.doc.payroll_frequency,
+ start_date: frm.doc.start_date
+ },
+ callback: function (r) {
+ if (r.message) {
+ frm.set_value('end_date', r.message.end_date);
+ }
+ }
+ })
+ }
})
cur_frm.cscript.display_activity_log = function (msg) {
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.py b/erpnext/hr/doctype/process_payroll/process_payroll.py
index 7ca94b8..b58c787 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.py
@@ -3,7 +3,8 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money
+from dateutil.relativedelta import relativedelta
+from frappe.utils import cint, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT
from frappe import _
from erpnext.accounts.utils import get_fiscal_year
@@ -47,7 +48,6 @@
%s """% cond, {"sal_struct": sal_struct})
return emp_list
-
def get_filter_condition(self):
self.check_mandatory()
@@ -58,7 +58,6 @@
return cond
-
def get_joining_releiving_condition(self):
cond = """
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
@@ -66,7 +65,6 @@
""" % {"start_date": self.start_date, "end_date": self.end_date}
return cond
-
def check_mandatory(self):
for fieldname in ['company', 'start_date', 'end_date']:
if not self.get(fieldname):
@@ -110,7 +108,6 @@
ss_list.append(ss_dict)
return self.create_log(ss_list)
-
def create_log(self, ss_list):
if not ss_list or len(ss_list) < 1:
log = "<p>" + _("No employee for the above selected criteria OR salary slip already created") + "</p>"
@@ -134,7 +131,6 @@
""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
return ss_list
-
def submit_salary_slips(self):
"""
Submit all salary slips based on selected criteria
@@ -194,7 +190,6 @@
def format_as_links(self, salary_slip):
return ['<a href="#Form/Salary Slip/{0}">{0}</a>'.format(salary_slip)]
-
def get_total_salary_and_loan_amounts(self):
"""
Get total loan principal, loan interest and salary amount from submitted salary slip based on selected criteria
@@ -257,7 +252,6 @@
return payroll_payable_account
-
def make_accural_jv_entry(self):
self.check_permission('write')
earnings = self.get_salary_component_total(component_type = "earnings") or {}
@@ -358,7 +352,6 @@
self.update(get_start_end_dates(self.payroll_frequency,
self.start_date or self.posting_date, self.company))
-
@frappe.whitelist()
def get_start_end_dates(payroll_frequency, start_date=None, company=None):
'''Returns dict of start and end dates for given payroll frequency based on start_date'''
@@ -391,6 +384,29 @@
'start_date': start_date, 'end_date': end_date
})
+def get_frequency_kwargs(frequency_name):
+ frequency_dict = {
+ 'monthly': {'months': 1},
+ 'fortnightly': {'days': 14},
+ 'weekly': {'days': 7},
+ 'daily': {'days': 1}
+ }
+ return frequency_dict.get(frequency_name)
+
+@frappe.whitelist()
+def get_end_date(start_date, frequency):
+ start_date = getdate(start_date)
+ frequency = frequency.lower() if frequency else 'monthly'
+ kwargs = get_frequency_kwargs(frequency) if frequency != 'bimonthly' else get_frequency_kwargs('monthly')
+
+ # weekly, fortnightly and daily intervals have fixed days so no problems
+ end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1)
+ if frequency != 'bimonthly':
+ return dict(end_date=end_date.strftime(DATE_FORMAT))
+
+ else:
+ return dict(end_date='')
+
def get_month_details(year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
diff --git a/erpnext/hr/doctype/process_payroll/test_process_payroll.py b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
index 6347a2a..1f9ba26 100644
--- a/erpnext/hr/doctype/process_payroll/test_process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
@@ -3,10 +3,12 @@
from __future__ import unicode_literals
import unittest
-import frappe
+
import erpnext
-from frappe.utils import flt, add_months, cint, nowdate, getdate, add_days, random_string
-from frappe.utils.make_random import get_random
+import frappe
+from frappe.utils import nowdate
+from erpnext.hr.doctype.process_payroll.process_payroll import get_end_date
+
class TestProcessPayroll(unittest.TestCase):
def test_process_payroll(self):
@@ -30,6 +32,16 @@
process_payroll.submit_salary_slips()
if process_payroll.get_sal_slip_list(ss_status = 1):
r = process_payroll.make_payment_entry()
+
+ def test_get_end_date(self):
+ self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
+ self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
+ self.assertEqual(get_end_date('2017-02-01', 'fortnightly'), {'end_date': '2017-02-14'})
+ self.assertEqual(get_end_date('2017-02-01', 'bimonthly'), {'end_date': ''})
+ self.assertEqual(get_end_date('2017-01-01', 'bimonthly'), {'end_date': ''})
+ self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
+ self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
+ self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
def get_salary_component_account(sal_comp):
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js
index a96347a..de3276c 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.js
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.js
@@ -29,6 +29,27 @@
})
},
+ start_date: function(frm){
+ if(frm.doc.start_date){
+ frm.trigger("set_end_date");
+ }
+ },
+
+ set_end_date: function(frm){
+ frappe.call({
+ method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_end_date',
+ args: {
+ frequency: frm.doc.payroll_frequency,
+ start_date: frm.doc.start_date
+ },
+ callback: function (r) {
+ if (r.message) {
+ frm.set_value('end_date', r.message.end_date);
+ }
+ }
+ })
+ },
+
company: function(frm) {
var company = locals[':Company'][frm.doc.company];
if(!frm.doc.letter_head && company.default_letter_head) {
@@ -45,11 +66,17 @@
},
salary_slip_based_on_timesheet: function(frm) {
- frm.trigger("toggle_fields")
+ frm.trigger("toggle_fields");
+ frm.set_value('start_date', '');
},
payroll_frequency: function(frm) {
- frm.trigger("toggle_fields")
+ frm.trigger("toggle_fields");
+ frm.set_value('start_date', '');
+ },
+
+ employee: function(frm){
+ frm.set_value('start_date', '');
},
toggle_fields: function(frm) {
@@ -74,18 +101,19 @@
// Get leave details
//---------------------------------------------------------------------
cur_frm.cscript.start_date = function(doc, dt, dn){
- return frappe.call({
- method: 'get_emp_and_leave_details',
- doc: locals[dt][dn],
- callback: function(r, rt) {
- cur_frm.refresh();
- calculate_all(doc, dt, dn);
- }
- });
+ if(!doc.start_date){
+ return frappe.call({
+ method: 'get_emp_and_leave_details',
+ doc: locals[dt][dn],
+ callback: function(r, rt) {
+ cur_frm.refresh();
+ calculate_all(doc, dt, dn);
+ }
+ });
+ }
}
cur_frm.cscript.payroll_frequency = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.start_date;
-cur_frm.cscript.end_date = cur_frm.cscript.start_date;
cur_frm.cscript.employee = function(doc,dt,dn){
doc.salary_structure = ''
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 681b62f..7130a3e 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -51,8 +51,9 @@
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
- self.validate_materials()
+ self.update_stock_qty()
self.set_bom_material_details()
+ self.validate_materials()
self.validate_operations()
self.calculate_cost()
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.js b/erpnext/manufacturing/doctype/bom/test_bom.js
new file mode 100644
index 0000000..8187ef3
--- /dev/null
+++ b/erpnext/manufacturing/doctype/bom/test_bom.js
@@ -0,0 +1,62 @@
+QUnit.test("test: item", function (assert) {
+ assert.expect(1);
+ let done = assert.async();
+
+ frappe.run_serially([
+ // test item creation
+ () => frappe.set_route("List", "Item"),
+
+ // Create a BOM for a laptop
+ () => frappe.tests.make(
+ "BOM", [
+ {item: "Laptop"},
+ {quantity: 1},
+ {with_operations: 1},
+ {operations: [
+ [
+ {operation: "Assemble CPU"},
+ {time_in_mins: 60},
+ ],
+ [
+ {operation: "Assemble Keyboard"},
+ {time_in_mins: 30},
+ ],
+ [
+ {operation: "Assemble Screen"},
+ {time_in_mins: 30},
+ ]
+ ]},
+ {scrap_items: [
+ [
+ {item_code: "Scrap item"}
+ ]
+ ]},
+ {items: [
+ [
+ {item_code: "CPU"},
+ {qty: 1}
+ ],
+ [
+ {item_code: "Keyboard"},
+ {qty: 1}
+ ],
+ [
+ {item_code: "Screen"},
+ {qty: 1}
+ ]
+ ]},
+ ]
+ ),
+ () => cur_frm.savesubmit(),
+ () => frappe.timeout(1),
+ () => frappe.click_button('Yes'),
+ () => frappe.timeout(1),
+
+ () => {
+ assert.ok(cur_frm.doc.operating_cost + cur_frm.doc.raw_material_cost -
+ cur_frm.doc.scrap_material_cost == cur_frm.doc.total_cost, 'Total_Cost calculated correctly');
+ },
+
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/operation/test_operation.js b/erpnext/manufacturing/doctype/operation/test_operation.js
new file mode 100644
index 0000000..9fedaac
--- /dev/null
+++ b/erpnext/manufacturing/doctype/operation/test_operation.js
@@ -0,0 +1,65 @@
+QUnit.test("test: operation", function (assert) {
+ assert.expect(2);
+ let done = assert.async();
+ let set_op_name = (text) => {
+ $(`input.input-with-feedback.form-control.bold:visible`).val(`${text}`);
+ };
+ let click_create = () => {
+ $(`.btn-primary:contains("Create"):visible`).click();
+ };
+
+ frappe.run_serially([
+ // test operation creation
+ () => frappe.set_route("List", "Operation"),
+
+ // Create a Keyboard operation
+ () => {
+ frappe.tests.make(
+ "Operation", [
+ {workstation: "Keyboard assembly workstation"}
+ ]
+ );
+ },
+ () => frappe.timeout(4),
+ () => set_op_name("Assemble Keyboard"),
+ () => frappe.timeout(0.5),
+ () => click_create(),
+ () => frappe.timeout(1),
+ () => {
+ assert.ok(cur_frm.docname.includes('Assemble Keyboard'),
+ 'Assemble Keyboard created successfully');
+ assert.ok(cur_frm.doc.workstation.includes('Keyboard assembly workstation'),
+ 'Keyboard assembly workstation was linked successfully');
+ },
+
+ // Create a Screen operation
+ () => {
+ frappe.tests.make(
+ "Operation", [
+ {workstation: "Screen assembly workstation"}
+ ]
+ );
+ },
+ () => frappe.timeout(4),
+ () => set_op_name("Assemble Screen"),
+ () => frappe.timeout(0.5),
+ () => click_create(),
+ () => frappe.timeout(1),
+
+ // Create a CPU operation
+ () => {
+ frappe.tests.make(
+ "Operation", [
+ {workstation: "CPU assembly workstation"}
+ ]
+ );
+ },
+ () => frappe.timeout(4),
+ () => set_op_name("Assemble CPU"),
+ () => frappe.timeout(0.5),
+ () => click_create(),
+ () => frappe.timeout(1),
+
+ () => done()
+ ]);
+});
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js
index 657819a..c61eec6 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.js
+++ b/erpnext/manufacturing/doctype/production_order/production_order.js
@@ -77,7 +77,6 @@
if (!frm.doc.status)
frm.doc.status = 'Draft';
- frm.add_fetch("sales_order", "delivery_date", "expected_delivery_date");
frm.add_fetch("sales_order", "project", "project");
if(frm.doc.__islocal) {
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index 8506c18..9533613 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -50,8 +50,12 @@
def validate_sales_order(self):
if self.sales_order:
- so = frappe.db.sql("""select name, delivery_date, project from `tabSales Order`
- where name=%s and docstatus = 1""", self.sales_order, as_dict=1)
+ so = frappe.db.sql("""
+ select so.name, so_item.delivery_date, so.project
+ from `tabSales Order` so, `tabSales Order Item` so_item
+ where so.name=%s and so.name=so_item.parent
+ and so.docstatus = 1 and so_item.item_code=%s
+ """, (self.sales_order, self.production_item), as_dict=1)
if len(so):
if not self.expected_delivery_date:
diff --git a/erpnext/manufacturing/doctype/workstation/_test_workstation.js b/erpnext/manufacturing/doctype/workstation/_test_workstation.js
new file mode 100644
index 0000000..0f09bd1
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation/_test_workstation.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Workstation", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially('Workstation', [
+ // insert a new Workstation
+ () => frappe.tests.make([
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.js b/erpnext/manufacturing/doctype/workstation/test_workstation.js
new file mode 100644
index 0000000..1df53d0
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation/test_workstation.js
@@ -0,0 +1,89 @@
+QUnit.test("test: workstation", function (assert) {
+ assert.expect(9);
+ let done = assert.async();
+ let elec_rate = 50;
+ let rent = 100;
+ let consumable_rate = 20;
+ let labour_rate = 500;
+ frappe.run_serially([
+ // test workstation creation
+ () => frappe.set_route("List", "Workstation"),
+
+ // Create a keyboard workstation
+ () => frappe.tests.make(
+ "Workstation", [
+ {workstation_name: "Keyboard assembly workstation"},
+ {hour_rate_electricity: elec_rate},
+ {hour_rate_rent: rent},
+ {hour_rate_consumable: consumable_rate},
+ {hour_rate_labour: labour_rate},
+ {working_hours: [
+ [
+ {enabled: 1},
+ {start_time: '11:00:00'},
+ {end_time: '18:00:00'}
+ ]
+ ]}
+ ]
+ ),
+ () => {
+ assert.ok(cur_frm.doc.workstation_name.includes('Keyboard assembly workstation'),
+ 'Keyboard assembly workstation created successfully');
+ assert.equal(cur_frm.doc.hour_rate_electricity, elec_rate,
+ 'electricity rate set correctly');
+ assert.equal(cur_frm.doc.hour_rate_rent, rent,
+ 'rent set correctly');
+ assert.equal(cur_frm.doc.hour_rate_consumable, consumable_rate,
+ 'consumable rate set correctly');
+ assert.equal(cur_frm.doc.hour_rate_labour, labour_rate,
+ 'labour rate set correctly');
+ assert.equal(cur_frm.doc.working_hours[0].enabled, 1,
+ 'working hours enabled');
+ assert.ok(cur_frm.doc.working_hours[0].start_time.includes('11:00:0'),
+ 'start time set correctly');
+ assert.ok(cur_frm.doc.working_hours[0].end_time.includes('18:00:0'),
+ 'end time set correctly');
+ assert.ok(cur_frm.doc.hour_rate_electricity+cur_frm.doc.hour_rate_rent+
+ cur_frm.doc.hour_rate_consumable+cur_frm.doc.hour_rate_labour==
+ cur_frm.doc.hour_rate, 'Net hour rate set correctly');
+ },
+
+ // Create a Screen workstation
+ () => frappe.tests.make(
+ "Workstation", [
+ {workstation_name: "Screen assembly workstation"},
+ {hour_rate_electricity: elec_rate},
+ {hour_rate_rent: rent},
+ {hour_rate_consumable: consumable_rate},
+ {hour_rate_labour: labour_rate},
+ {working_hours: [
+ [
+ {enabled: 1},
+ {start_time: '11:00:00'},
+ {end_time: '18:00:00'}
+ ]
+ ]}
+ ]
+ ),
+
+ // Create a CPU workstation
+ () => frappe.tests.make(
+ "Workstation", [
+ {workstation_name: "CPU assembly workstation"},
+ {hour_rate_electricity: elec_rate},
+ {hour_rate_rent: rent},
+ {hour_rate_consumable: consumable_rate},
+ {hour_rate_labour: labour_rate},
+ {working_hours: [
+ [
+ {enabled: 1},
+ {start_time: '11:00:00'},
+ {end_time: '18:00:00'}
+ ]
+ ]}
+ ]
+ ),
+
+ () => done()
+ ]);
+});
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json
index 38188be..dca9891 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.json
+++ b/erpnext/manufacturing/doctype/workstation/workstation.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:workstation_name",
@@ -12,11 +13,12 @@
"editable_grid": 0,
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 0,
+ "collapsible": 1,
"columns": 0,
- "fieldname": "description_and_warehouse",
+ "fieldname": "description_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -25,7 +27,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "",
+ "label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -41,6 +43,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -71,6 +74,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -102,65 +106,7 @@
"width": "300px"
},
{
- "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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "holiday_list",
- "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": "Holiday List",
- "length": 0,
- "no_copy": 0,
- "options": "Holiday List",
- "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,
@@ -190,6 +136,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -221,6 +168,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -252,6 +200,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -280,6 +229,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -311,6 +261,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -342,6 +293,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -373,6 +325,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -402,6 +355,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -430,20 +384,52 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "",
+ "fieldname": "holiday_list",
+ "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": "Holiday List",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Holiday List",
+ "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,
"icon": "icon-wrench",
"idx": 1,
"image_view": 0,
"in_create": 0,
- "in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-02-17 17:26:57.238167",
+ "modified": "2017-07-18 22:28:50.163219",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation",
@@ -470,11 +456,11 @@
"write": 1
}
],
- "quick_entry": 0,
+ "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 1,
"sort_order": "ASC",
- "track_changes": 0,
+ "track_changes": 1,
"track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/workstation/workstation_list.js b/erpnext/manufacturing/doctype/workstation/workstation_list.js
new file mode 100644
index 0000000..6a89d21
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation/workstation_list.js
@@ -0,0 +1,5 @@
+/* eslint-disable */
+frappe.listview_settings['Workstation'] = {
+ // add_fields: ["status"],
+ // filters:[["status","=", "Open"]]
+};
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 48e3a35..00f395f 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -422,4 +422,7 @@
erpnext.patches.v8_3.set_restrict_to_domain_for_module_def
erpnext.patches.v8_1.update_expense_claim_status
erpnext.patches.v8_3.update_company_total_sales
-erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
\ No newline at end of file
+erpnext.patches.v8_4.make_scorecard_records
+erpnext.patches.v8_1.set_delivery_date_in_so_item
+erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
+erpnext.patches.v8_5.remove_quotations_route_in_sidebar
diff --git a/erpnext/patches/v8_1/set_delivery_date_in_so_item.py b/erpnext/patches/v8_1/set_delivery_date_in_so_item.py
new file mode 100644
index 0000000..6840424
--- /dev/null
+++ b/erpnext/patches/v8_1/set_delivery_date_in_so_item.py
@@ -0,0 +1,13 @@
+import frappe
+
+def execute():
+ frappe.reload_doctype("Sales Order")
+ frappe.reload_doctype("Sales Order Item")
+
+ frappe.db.sql("""update `tabSales Order` set final_delivery_date = delivery_date where docstatus=1""")
+
+ frappe.db.sql("""
+ update `tabSales Order` so, `tabSales Order Item` so_item
+ set so_item.delivery_date = so.delivery_date
+ where so.name = so_item.parent
+ """)
\ No newline at end of file
diff --git a/erpnext/patches/v8_4/__init__.py b/erpnext/patches/v8_4/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/patches/v8_4/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/patches/v8_4/make_scorecard_records.py b/erpnext/patches/v8_4/make_scorecard_records.py
new file mode 100644
index 0000000..73afa27
--- /dev/null
+++ b/erpnext/patches/v8_4/make_scorecard_records.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
+def execute():
+ frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_variable')
+ frappe.reload_doc('buying', 'doctype', 'supplier_scorecard_standing')
+ make_default_records()
\ No newline at end of file
diff --git a/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py b/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py
new file mode 100644
index 0000000..2d7df4a
--- /dev/null
+++ b/erpnext/patches/v8_5/remove_quotations_route_in_sidebar.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doctype("Portal Settings")
+
+ frappe.db.sql("""
+ delete from
+ `tabPortal Menu Item`
+ where
+ (route = '/quotations' and title = 'Supplier Quotation')
+ or (route = '/quotation' and title = 'Quotations')
+ """)
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 78cdb13..f16cf07 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -3,6 +3,22 @@
frappe.ui.form.on("Project", {
onload: function(frm) {
+ frm.set_indicator_formatter('title',
+ function(doc) {
+ let indicator = 'orange';
+ if (doc.status == 'Overdue') {
+ indicator = 'red';
+ }
+ else if (doc.status == 'Cancelled') {
+ indicator = 'dark grey';
+ }
+ else if (doc.status == 'Closed') {
+ indicator = 'green';
+ }
+ return indicator;
+ }
+ );
+
var so = frappe.meta.get_docfield("Project", "sales_order");
so.get_route_options_for_new_doc = function(field) {
if(frm.is_new()) return;
@@ -35,6 +51,7 @@
}
});
},
+
refresh: function(frm) {
if(frm.doc.__islocal) {
frm.web_link && frm.web_link.remove();
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 32a3ffd..28eb730 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -14,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -44,6 +45,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -76,6 +78,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -107,6 +110,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -138,6 +142,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -169,6 +174,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -197,6 +203,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -228,6 +235,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -258,6 +266,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -269,7 +278,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Expected End Date",
"length": 0,
@@ -288,6 +297,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -316,6 +326,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -346,6 +357,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -377,6 +389,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -405,6 +418,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -435,6 +449,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -464,6 +479,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -495,6 +511,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -525,6 +542,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -555,6 +573,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -584,6 +603,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -614,6 +634,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -644,6 +665,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -673,6 +695,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -702,6 +725,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -731,6 +755,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -759,6 +784,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -789,6 +815,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -819,6 +846,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -850,6 +878,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -880,6 +909,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -910,6 +940,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -939,6 +970,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -968,6 +1000,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -996,6 +1029,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1026,6 +1060,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1055,6 +1090,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1084,6 +1120,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1114,6 +1151,7 @@
"width": "50%"
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1145,6 +1183,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1173,6 +1212,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1215,8 +1255,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 4,
- "modified": "2017-04-19 13:16:32.462005",
- "modified_by": "faris@erpnext.com",
+ "modified": "2017-07-19 14:36:20.857673",
+ "modified_by": "Administrator",
"module": "Projects",
"name": "Project",
"owner": "Administrator",
diff --git a/erpnext/projects/doctype/task/test_task.js b/erpnext/projects/doctype/task/test_task.js
new file mode 100644
index 0000000..8a1a5bf
--- /dev/null
+++ b/erpnext/projects/doctype/task/test_task.js
@@ -0,0 +1,24 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Task", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(2);
+
+ frappe.run_serially([
+ // insert a new Task
+ () => frappe.tests.make('Task', [
+ // values to be set
+ {subject: 'new task'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.status, 'Open');
+ assert.equal(cur_frm.doc.priority, 'Low');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 2edfa9e..441ab1a 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -133,10 +133,15 @@
var child = locals[cdt][cdn];
var d = moment(child.from_time);
- d.add(child.hours, "hours");
- frm._setting_hours = true;
- frappe.model.set_value(cdt, cdn, "to_time", d.format(moment.defaultDatetimeFormat));
- frm._setting_hours = false;
+ if(child.hours) {
+ d.add(child.hours, "hours");
+ frm._setting_hours = true;
+ frappe.model.set_value(cdt, cdn, "to_time",
+ d.format(moment.defaultDatetimeFormat)).then(() => {
+ frm._setting_hours = false;
+ });
+ }
+
if((frm.doc.__islocal || frm.doc.__onload.maintain_bill_work_hours_same) && child.hours){
frappe.model.set_value(cdt, cdn, "billing_hours", child.hours);
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 6416176..95fd420 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -381,3 +381,26 @@
"end": end
}, as_dict=True, update={"allDay": 0})
+def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
+ user = frappe.session.user
+ # find customer name from contact.
+ customer = frappe.db.sql('''SELECT dl.link_name FROM `tabContact` AS c inner join \
+ `tabDynamic Link` AS dl ON c.first_name=dl.link_name WHERE c.email_id=%s''',user)
+ # find list of Sales Invoice for made for customer.
+ sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer)
+ if customer:
+ # Return timesheet related data to web portal.
+ return frappe. db.sql('''SELECT ts.name, tsd.activity_type, ts.status, ts.total_billable_hours, \
+ tsd.sales_invoice, tsd.project FROM `tabTimesheet` AS ts inner join `tabTimesheet Detail` \
+ AS tsd ON tsd.parent = ts.name where tsd.sales_invoice IN %s order by\
+ end_date asc limit {0} , {1}'''.format(limit_start, limit_page_length), [sales_invoice], as_dict = True)
+
+def get_list_context(context=None):
+ return {
+ "show_sidebar": True,
+ "show_search": True,
+ 'no_breadcrumbs': True,
+ "title": _("Timesheets"),
+ "get_list": get_timesheets_list,
+ "row_template": "templates/includes/timesheet/timesheet_row.html"
+ }
diff --git a/erpnext/schools/doctype/academic_term/test_academic_term.js b/erpnext/schools/doctype/academic_term/test_academic_term.js
new file mode 100644
index 0000000..c74071b
--- /dev/null
+++ b/erpnext/schools/doctype/academic_term/test_academic_term.js
@@ -0,0 +1,25 @@
+// Testing Setup Module in Schools
+QUnit.module('setup');
+
+// Testing Academic Term option
+QUnit.test('test academic term', function(assert){
+ assert.expect(4);
+ let done = assert.async();
+ frappe.run_serially([
+ () => {
+ return frappe.tests.make('Academic Term', [
+ {academic_year: '2016-17'},
+ {term_name: "Semester 1"},
+ {term_start_date: '2016-07-20'},
+ {term_end_date:'2017-06-20'},
+ ]);
+ },
+ () => {
+ assert.ok(cur_frm.doc.academic_year=='2016-17');
+ assert.ok(cur_frm.doc.term_name=='Semester 1');
+ assert.ok(cur_frm.doc.term_start_date=='2016-07-20');
+ assert.ok(cur_frm.doc.term_end_date=='2017-06-20');
+ },
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/schools/doctype/academic_year/test_academic_year.js b/erpnext/schools/doctype/academic_year/test_academic_year.js
new file mode 100644
index 0000000..5123b6e
--- /dev/null
+++ b/erpnext/schools/doctype/academic_year/test_academic_year.js
@@ -0,0 +1,24 @@
+// Testing Setup Module in Schools
+QUnit.module('setup');
+
+// Testing Academic Year Testing option
+QUnit.test('test academic year', function(assert){
+ assert.expect(3);
+ let done = assert.async();
+ frappe.run_serially([
+ () => {
+ return frappe.tests.make('Academic Year', [
+ {academic_year_name: '2015-16'},
+ {year_start_date: '2015-07-20'},
+ {year_end_date:'2016-06-20'},
+ ]);
+ },
+
+ () => {
+ assert.ok(cur_frm.doc.academic_year_name=='2015-16');
+ assert.ok(cur_frm.doc.year_start_date=='2015-07-20');
+ assert.ok(cur_frm.doc.year_end_date=='2016-06-20');
+ },
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 1b77418..52c6b6d 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -81,6 +81,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "eval:doc.customer_type!='Company'",
"fieldname": "salutation",
"fieldtype": "Link",
"hidden": 0,
@@ -143,6 +144,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "eval:doc.customer_type != 'Company'",
"fieldname": "gender",
"fieldtype": "Link",
"hidden": 0,
@@ -1200,7 +1202,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-06-28 14:55:39.910819",
+ "modified": "2017-07-24 00:55:07.445783",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
diff --git a/erpnext/selling/doctype/quotation/test_quotation.js b/erpnext/selling/doctype/quotation/test_quotation.js
new file mode 100644
index 0000000..44173cc
--- /dev/null
+++ b/erpnext/selling/doctype/quotation/test_quotation.js
@@ -0,0 +1,53 @@
+QUnit.test("test: quotation", function (assert) {
+ assert.expect(10);
+ let done = assert.async();
+ frappe.run_serially([
+ () => {
+ return frappe.tests.make("Quotation", [
+ {customer: "Test Customer 1"},
+ {items: [
+ [
+ {"item_code": "Test Product 1"},
+ {"qty": 5}
+ ]]
+ }
+ ]);
+ },
+ () => {
+ // get_item_details
+ assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
+
+ // calculate_taxes_and_totals
+ assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct");
+ },
+ () => cur_frm.set_value("customer_address", "Test1-Billing"),
+ () => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
+ () => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"),
+ () => cur_frm.set_value("currency", "USD"),
+ () => frappe.timeout(0.3),
+ () => cur_frm.set_value("selling_price_list", "Test-Selling-USD"),
+ () => frappe.timeout(0.5),
+ () => cur_frm.doc.items[0].rate = 200,
+ () => frappe.timeout(0.3),
+ () => cur_frm.set_value("tc_name", "Test Term 1"),
+ () => frappe.timeout(0.3),
+ () => cur_frm.save(),
+ () => {
+ // Check Address and Contact Info
+ assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed");
+ assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed");
+ assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed");
+
+ // Check Currency
+ assert.ok(cur_frm.doc_currency == "USD", "Currency Changed");
+ assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed");
+ assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
+ assert.equal(cur_frm.doc.total, 1000, "New Total Calculated");
+
+ // Check Terms and Condtions
+ assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
+
+ },
+ () => done()
+ ]);
+});
\ No newline at end of file
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 640c026..7fb4074 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -27,7 +27,8 @@
self.assertEquals(sales_order.get("items")[0].prevdoc_docname, quotation.name)
self.assertEquals(sales_order.customer, "_Test Customer")
- sales_order.delivery_date = "2014-01-01"
+ for d in sales_order.get("items"):
+ d.delivery_date = "2014-01-01"
sales_order.naming_series = "_T-Quotation-"
sales_order.transaction_date = "2013-05-12"
sales_order.insert()
@@ -51,9 +52,11 @@
quotation.submit()
sales_order = make_sales_order(quotation.name)
- sales_order.delivery_date = "2016-01-02"
sales_order.naming_series = "_T-Quotation-"
sales_order.transaction_date = "2016-01-01"
+ for d in sales_order.get("items"):
+ d.delivery_date = "2016-01-02"
+
sales_order.insert()
self.assertEquals(quotation.get("items")[0].rate, rate_with_margin)
@@ -86,4 +89,4 @@
'rate': 100
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 457b6aa..2e01a08 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -33,7 +33,13 @@
function(doc) { return (doc.stock_qty<=doc.delivered_qty) ? "green" : "orange" })
erpnext.queries.setup_warehouse_query(frm);
- }
+ },
+});
+
+frappe.ui.form.on("Sales Order Item", {
+ delivery_date: function(frm, cdt, cdn) {
+ erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "delivery_date");
+ }
});
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
@@ -77,7 +83,7 @@
// delivery note
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
this.frm.add_custom_button(__('Delivery'),
- function() { me.make_delivery_note() }, __("Make"));
+ function() { me.make_delivery_note_based_on_delivery_note(); }, __("Make"));
this.frm.add_custom_button(__('Production Order'),
function() { me.make_production_order() }, __("Make"));
@@ -235,7 +241,7 @@
},
order_type: function() {
- this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
+ this.frm.fields_dict.items.grid.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
},
tc_name: function() {
@@ -249,10 +255,72 @@
})
},
+ make_delivery_note_based_on_delivery_note: function() {
+ var me = this;
+
+ var delivery_dates = [];
+ $.each(this.frm.doc.items || [], function(i, d) {
+ if(!delivery_dates.includes(d.delivery_date)) {
+ delivery_dates.push(d.delivery_date);
+ }
+ });
+
+ var item_grid = this.frm.fields_dict["items"].grid;
+ if(!item_grid.get_selected().length && delivery_dates.length > 1) {
+ var dialog = new frappe.ui.Dialog({
+ title: __("Select Items based on Delivery Date"),
+ fields: [{fieldtype: "HTML", fieldname: "dates_html"}]
+ });
+
+ var html = $(`
+ <div style="border: 1px solid #d1d8dd">
+ <div class="list-item list-item--head">
+ <div class="list-item__content list-item__content--flex-2">
+ ${__('Delivery Date')}
+ </div>
+ </div>
+ ${delivery_dates.map(date => `
+ <div class="list-item">
+ <div class="list-item__content list-item__content--flex-2">
+ <label>
+ <input type="checkbox" data-date="${date}" checked="checked"/>
+ ${frappe.datetime.str_to_user(date)}
+ </label>
+ </div>
+ </div>
+ `).join("")}
+ </div>
+ `);
+
+ var wrapper = dialog.fields_dict.dates_html.$wrapper;
+ wrapper.html(html);
+
+ dialog.set_primary_action(__("Select"), function() {
+ var dates = wrapper.find('input[type=checkbox]:checked')
+ .map((i, el) => $(el).attr('data-date')).toArray();
+
+ if(!dates) return;
+
+ $.each(dates, function(i, d) {
+ $.each(item_grid.grid_rows || [], function(j, row) {
+ if(row.doc.delivery_date == d) {
+ row.doc.__checked = 1;
+ }
+ });
+ })
+ me.make_delivery_note();
+ dialog.hide();
+ });
+ dialog.show();
+ } else {
+ this.make_delivery_note();
+ }
+ },
+
make_delivery_note: function() {
frappe.model.open_mapped_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
- frm: this.frm
+ frm: me.frm
})
},
@@ -344,6 +412,11 @@
if(cint(frappe.boot.notification_settings.sales_order)) {
this.frm.email_doc(frappe.boot.notification_settings.sales_order_message);
}
+ },
+
+ items_add: function(doc, cdt, cdn) {
+ var row = frappe.get_doc(cdt, cdn);
+ this.frm.script_manager.copy_from_first_row("items", row, ["delivery_date"]);
}
});
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index c7c5ea2..8d19083 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -367,32 +367,29 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "eval:doc.order_type == 'Sales'",
- "fieldname": "delivery_date",
+ "fieldname": "final_delivery_date",
"fieldtype": "Date",
- "hidden": 0,
+ "hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
- "label": "Delivery Date",
+ "label": "Final Delivery Date",
"length": 0,
"no_copy": 1,
- "oldfieldname": "delivery_date",
- "oldfieldtype": "Date",
"permlevel": 0,
- "print_hide": 0,
+ "precision": "",
+ "print_hide": 1,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "unique": 0,
- "width": "160px"
+ "unique": 0
},
{
"allow_bulk_edit": 0,
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index da9660b..72b1154 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -30,7 +30,6 @@
self.validate_order_type()
self.validate_delivery_date()
- self.validate_mandatory()
self.validate_proj_cust()
self.validate_po()
self.validate_uom_is_integer("stock_uom", "stock_qty")
@@ -48,25 +47,20 @@
if not self.billing_status: self.billing_status = 'Not Billed'
if not self.delivery_status: self.delivery_status = 'Not Delivered'
- def validate_mandatory(self):
- # validate transaction date v/s delivery date
- if self.delivery_date:
- if getdate(self.transaction_date) > getdate(self.delivery_date):
- frappe.msgprint(_("Expected Delivery Date is be before Sales Order Date"),
- indicator='orange',
- title=_('Warning'))
-
def validate_po(self):
# validate p.o date v/s delivery date
- if self.po_date and self.delivery_date and getdate(self.po_date) > getdate(self.delivery_date):
- frappe.throw(_("Expected Delivery Date cannot be before Purchase Order Date"))
+ if self.po_date:
+ for d in self.get("items"):
+ if d.delivery_date and getdate(self.po_date) > getdate(d.delivery_date):
+ frappe.throw(_("Row #{0}: Expected Delivery Date cannot be before Purchase Order Date")
+ .format(d.idx))
if self.po_no and self.customer:
so = frappe.db.sql("select name from `tabSales Order` \
where ifnull(po_no, '') = %s and name != %s and docstatus < 2\
and customer = %s", (self.po_no, self.name, self.customer))
- if so and so[0][0] and not \
- cint(frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders")):
+ if so and so[0][0] and not cint(frappe.db.get_single_value("Selling Settings",
+ "allow_against_multiple_purchase_orders")):
frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no))
def validate_for_items(self):
@@ -78,7 +72,7 @@
d.transaction_date = self.transaction_date
tot_avail_qty = frappe.db.sql("select projected_qty from `tabBin` \
- where item_code = %s and warehouse = %s", (d.item_code,d.warehouse))
+ where item_code = %s and warehouse = %s", (d.item_code, d.warehouse))
d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
# check for same entry multiple times
@@ -97,16 +91,30 @@
def validate_sales_mntc_quotation(self):
for d in self.get('items'):
if d.prevdoc_docname:
- res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s", (d.prevdoc_docname, self.order_type))
+ res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s",
+ (d.prevdoc_docname, self.order_type))
if not res:
- frappe.msgprint(_("Quotation {0} not of type {1}").format(d.prevdoc_docname, self.order_type))
+ frappe.msgprint(_("Quotation {0} not of type {1}")
+ .format(d.prevdoc_docname, self.order_type))
def validate_order_type(self):
super(SalesOrder, self).validate_order_type()
def validate_delivery_date(self):
- if self.order_type == 'Sales' and not self.delivery_date:
- frappe.throw(_("Please enter 'Expected Delivery Date'"))
+ self.final_delivery_date = None
+ if self.order_type == 'Sales':
+ for d in self.get("items"):
+ if not d.delivery_date:
+ frappe.throw(_("Row #{0}: Please enter Delivery Date against item {1}")
+ .format(d.idx, d.item_code))
+
+ if getdate(self.transaction_date) > getdate(d.delivery_date):
+ frappe.msgprint(_("Expected Delivery Date should be after Sales Order Date"),
+ indicator='orange', title=_('Warning'))
+
+ if not self.final_delivery_date or \
+ (d.delivery_date and getdate(d.delivery_date) > getdate(self.final_delivery_date)):
+ self.final_delivery_date = d.delivery_date
self.validate_sales_mntc_quotation()
@@ -122,7 +130,7 @@
super(SalesOrder, self).validate_warehouse()
for d in self.get("items"):
- if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or
+ if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 or
(self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \
and not d.warehouse and not cint(d.delivered_by_supplier):
frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code),
@@ -337,11 +345,14 @@
return items
-
def on_recurring(self, reference_doc):
mcount = month_map[reference_doc.recurring_type]
- self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount,
- cint(reference_doc.repeat_on_day_of_month)))
+ for d in self.get("items"):
+ reference_delivery_date = frappe.db.get_value("Sales Order Item",
+ {"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date")
+
+ d.set("delivery_date",
+ get_next_date(reference_delivery_date, mcount, cint(reference_doc.repeat_on_day_of_month)))
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
@@ -424,7 +435,6 @@
},
"field_map":{
"name" : "sales_order",
- "delivery_date" : "expected_end_date",
"base_grand_total" : "estimated_costing",
}
},
@@ -615,12 +625,17 @@
from frappe.desk.calendar import get_event_conditions
conditions = get_event_conditions("Sales Order", filters)
- data = frappe.db.sql("""select name, customer_name, status, delivery_status, billing_status, delivery_date
- from `tabSales Order`
- where (ifnull(delivery_date, '0000-00-00')!= '0000-00-00') \
- and (delivery_date between %(start)s and %(end)s)
- and docstatus < 2
- {conditions}
+ data = frappe.db.sql("""
+ select
+ so.name, so.customer_name, so.status,
+ so.delivery_status, so.billing_status, so_item.delivery_date
+ from
+ `tabSales Order` so, `tabSales Order Item` so_item
+ where so.name = so_item.parent
+ and (ifnull(so_item.delivery_date, '0000-00-00')!= '0000-00-00') \
+ and (so_item.delivery_date between %(start)s and %(end)s)
+ and so.docstatus < 2
+ {conditions}
""".format(conditions=conditions), {
"start": start,
"end": end
@@ -660,7 +675,7 @@
target.run_method("calculate_taxes_and_totals")
def update_item(source, target, source_parent):
- target.schedule_date = source_parent.delivery_date
+ target.schedule_date = source.delivery_date
target.qty = flt(source.qty) - flt(source.ordered_qty)
target.stock_qty = (flt(source.qty) - flt(source.ordered_qty)) * flt(source.conversion_factor)
diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js
index d1ac6d9..0ee9cf3 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_list.js
+++ b/erpnext/selling/doctype/sales_order/sales_order_list.js
@@ -1,14 +1,14 @@
frappe.listview_settings['Sales Order'] = {
- add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date", "per_delivered", "per_billed",
- "status", "order_type"],
+ add_fields: ["base_grand_total", "customer_name", "currency", "final_delivery_date",
+ "per_delivered", "per_billed", "status", "order_type", "name"],
get_indicator: function(doc) {
if(doc.status==="Closed"){
return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.order_type !== "Maintenance"
- && flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
+ && flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.final_delivery_date) < 0) {
// to bill & overdue
- return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"];
+ return [__("Overdue"), "red", "per_delivered,<,100|final_delivery_date,<,Today|status,!=,Closed"];
} else if (doc.order_type !== "Maintenance"
&& flt(doc.per_delivered, 2) < 100 && doc.status!=="Closed") {
diff --git a/erpnext/selling/doctype/sales_order/test_records.json b/erpnext/selling/doctype/sales_order/test_records.json
index 12e953a..6cbd6c2 100644
--- a/erpnext/selling/doctype/sales_order/test_records.json
+++ b/erpnext/selling/doctype/sales_order/test_records.json
@@ -7,7 +7,6 @@
"customer": "_Test Customer",
"customer_group": "_Test Customer Group",
"customer_name": "_Test Customer",
- "delivery_date": "2013-02-23",
"doctype": "Sales Order",
"base_grand_total": 1000.0,
"grand_total": 1000.0,
@@ -23,6 +22,7 @@
"doctype": "Sales Order Item",
"item_code": "_Test Item Home Desktop 100",
"item_name": "CPU",
+ "delivery_date": "2013-02-23",
"parentfield": "items",
"qty": 10.0,
"rate": 100.0,
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 0417e5e..8c07118 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -512,7 +512,6 @@
so.company = args.company or "_Test Company"
so.customer = args.customer or "_Test Customer"
- so.delivery_date = add_days(so.transaction_date, 10)
so.currency = args.currency or "INR"
if args.selling_price_list:
so.selling_price_list = args.selling_price_list
@@ -533,6 +532,9 @@
"rate": args.rate or 100
})
+ for d in so.get("items"):
+ d.delivery_date = add_days(so.transaction_date, 10)
+
if not args.do_not_save:
so.insert()
if not args.do_not_submit:
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 2aae911..f14f50d 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -18,7 +18,7 @@
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
- "columns": 4,
+ "columns": 3,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
@@ -205,6 +205,36 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 2,
+ "fieldname": "delivery_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": "Delivery Date",
+ "length": 0,
+ "no_copy": 1,
+ "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_7",
"fieldtype": "Column Break",
@@ -324,7 +354,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 2,
+ "columns": 1,
"fieldname": "qty",
"fieldtype": "Float",
"hidden": 0,
@@ -1933,7 +1963,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2017-05-10 17:14:48.277982",
+ "modified": "2017-07-18 18:26:36.870342",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 1baa843..271755e 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -547,7 +547,7 @@
"in_standard_filter": 0,
"label": "Sales Monthly History",
"length": 0,
- "no_copy": 0,
+ "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -577,7 +577,7 @@
"in_standard_filter": 0,
"label": "Sales Target",
"length": 0,
- "no_copy": 0,
+ "no_copy": 1,
"options": "default_currency",
"permlevel": 0,
"precision": "",
@@ -637,7 +637,7 @@
"in_standard_filter": 0,
"label": "Total Monthly Sales",
"length": 0,
- "no_copy": 0,
+ "no_copy": 1,
"options": "default_currency",
"permlevel": 0,
"precision": "",
@@ -1961,7 +1961,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2017-07-17 15:50:27.218951",
+ "modified": "2017-07-19 03:16:27.171189",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index c8a0507..28f3a56 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -340,7 +340,7 @@
from frappe.utils.goal import get_monthly_results
import json
filter_str = "company = '{0}' and status != 'Draft'".format(frappe.db.escape(company))
- month_to_value_dict = get_monthly_results("Sales Invoice", "grand_total", "posting_date", filter_str, "sum")
+ month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total", "posting_date", filter_str, "sum")
frappe.db.sql(('''
update tabCompany set sales_monthly_history = %s where name=%s
diff --git a/erpnext/setup/setup_wizard/install_fixtures.py b/erpnext/setup/setup_wizard/install_fixtures.py
index 43baf2f..1301d33 100644
--- a/erpnext/setup/setup_wizard/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/install_fixtures.py
@@ -213,6 +213,10 @@
records += [{'doctype': 'Lead Source', 'source_name': _(d)} for d in default_lead_sources]
+ # Records for the Supplier Scorecard
+ from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
+ make_default_records()
+
from frappe.modules import scrub
for r in records:
doc = frappe.new_doc(r.get("doctype"))
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index a3ae0eb..80ef708 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -243,7 +243,8 @@
if meta.get_field("currency"):
validate_price_list(args)
- validate_conversion_rate(args, meta)
+ if args.price_list:
+ validate_conversion_rate(args, meta)
price_list_rate = get_price_list_rate_for(args.price_list, item_doc.name)
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 2bee422..bef8cb4 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -20,9 +20,11 @@
timeline_refresh: function(frm) {
// create button for "Add to Knowledge Base"
if(frappe.model.can_create('Help Article')) {
- $('<button class="btn btn-xs btn-default btn-add-to-kb pull-right" style="margin-top: -2px">'+
+ // Removing Knowledge Base button if exists to avoid multiple occurance
+ frm.timeline.wrapper.find('.comment-header .asset-details .btn-add-to-kb').remove();
+ $('<button class="btn btn-xs btn-default btn-add-to-kb hidden-xs pull-right" style="margin-top: -2px">'+
__('Add to Knowledge Base') + '</button>')
- .appendTo(frm.timeline.wrapper.find('.comment-header'))
+ .appendTo(frm.timeline.wrapper.find('.comment-header .asset-details'))
.on('click', function() {
var content = $(this).parents('.timeline-item:first').find('.timeline-item-content').html();
var doc = frappe.model.get_new_doc('Help Article');
diff --git a/erpnext/templates/includes/products_as_list.html b/erpnext/templates/includes/products_as_list.html
index 20ca59d..8418a90 100644
--- a/erpnext/templates/includes/products_as_list.html
+++ b/erpnext/templates/includes/products_as_list.html
@@ -6,12 +6,12 @@
{{ product_image_square(thumbnail or website_image) }}
</div>
<div class='col-xs-9 col-sm-10 text-left'>
- <div class="ellipsis product-text strong">{{ item_name }}</div>
+ <div class="product-text strong">{{ item_name }}</div>
{% set website_description = website_description or description %}
{% if website_description != item_name %}
- <div class="ellipsis text-muted">{{ website_description or description }}</div>
+ <div class="text-muted">{{ website_description or description }}</div>
{% elif item_code != item_name %}
- <div class="ellipsis text-muted">{{ _('Item Code') }}: {{ item_code }}</div>
+ <div class="text-muted">{{ _('Item Code') }}: {{ item_code }}</div>
{% endif %}
</div>
</div>
diff --git a/erpnext/templates/includes/timesheet/timesheet_row.html b/erpnext/templates/includes/timesheet/timesheet_row.html
new file mode 100644
index 0000000..e9cfcda
--- /dev/null
+++ b/erpnext/templates/includes/timesheet/timesheet_row.html
@@ -0,0 +1,13 @@
+<div class="web-list-item">
+ <a href="/timesheets?name={{ doc.name | urlencode }}" class="no-decoration">
+ <div class="row">
+ <div class="col-xs-3">
+ <span class="indicator {{ "red" if doc.status=="Cancelled" else "green" if doc.status=="Billed" else "blue" if doc.status=="Submitted" else "darkgrey" }}">{{ doc.name }}</span>
+ </div>
+ <div class="col-xs-3"> Billable Hours: {{ doc.total_billable_hours}} </div>
+ <div class="col-xs-2"> {{ _(doc.sales_invoice) }} </div>
+ <div class="col-xs-2"> {{ _(doc.project) }} </div>
+ <div class="col-xs-2"> {{ _(doc.activity_type) }} </div>
+ </div>
+ </a>
+</div>
diff --git a/erpnext/tests/ui/data/test_fixtures.js b/erpnext/tests/ui/make_fixtures.js
similarity index 87%
rename from erpnext/tests/ui/data/test_fixtures.js
rename to erpnext/tests/ui/make_fixtures.js
index 2ba5db5..0c5b4be 100644
--- a/erpnext/tests/ui/data/test_fixtures.js
+++ b/erpnext/tests/ui/make_fixtures.js
@@ -1,4 +1,11 @@
$.extend(frappe.test_data, {
+ // "Fiscal Year": {
+ // "2017-18": [
+ // {"year": "2017-18"},
+ // {"year_start_date": "2017-04-01"},
+ // {"year_end_date": "2018-03-31"},
+ // ]
+ // },
"Customer": {
"Test Customer 1": [
{customer_name: "Test Customer 1"}
@@ -198,19 +205,20 @@
{title: "Test Term 2"}
]
},
- "Sales Taxes and Charges Template": {
- "TEST In State GST": [
- {title: "TEST In State GST"},
- {taxes:[
- [
- {charge_type:"On Net Total"},
- {account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
- ],
- [
- {charge_type:"On Net Total"},
- {account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
- ]
- ]}
- ]
- }
+});
+
+
+// this is a script that creates all fixtures
+// called as a test
+QUnit.module('fixture');
+
+QUnit.test('Make fixtures', assert => {
+ // create all fixtures first
+ assert.expect(0);
+ let done = assert.async();
+ let tasks = [];
+ Object.keys(frappe.test_data).forEach(function(doctype) {
+ tasks.push(function() { return frappe.tests.setup_doctype(doctype); });
+ });
+ frappe.run_serially(tasks).then(() => done());
});
diff --git a/erpnext/tests/ui/selling/_test_lead.js b/erpnext/tests/ui/selling/_test_lead.js
deleted file mode 100644
index 2b895d9..0000000
--- a/erpnext/tests/ui/selling/_test_lead.js
+++ /dev/null
@@ -1,18 +0,0 @@
-QUnit.module("sales");
-
-QUnit.test("test: lead", function (assert) {
- assert.expect(1);
- let done = assert.async();
- let random = frappe.utils.get_random(10);
- frappe.run_serially([
- () => frappe.tests.setup_doctype("Lead"),
- () => frappe.set_route("List", "Lead"),
- () => frappe.new_doc("Lead"),
- () => cur_frm.set_value("lead_name", random),
- () => cur_frm.save(),
- () => {
- assert.ok(cur_frm.doc.lead_name.includes(random));
- return done();
- }
- ]);
-});
diff --git a/erpnext/tests/ui/selling/_test_opportunity.js b/erpnext/tests/ui/selling/_test_opportunity.js
deleted file mode 100644
index 716a36e..0000000
--- a/erpnext/tests/ui/selling/_test_opportunity.js
+++ /dev/null
@@ -1,19 +0,0 @@
-QUnit.test("test: opportunity", function (assert) {
- assert.expect(1);
- let done = assert.async();
- frappe.run_serially([
- () => {
- return frappe.tests.make("Opportunity", [{
- enquiry_from: "Lead"
- },
- {
- lead: "LEAD-00002"
- }
- ]);
- },
- () => {
- assert.ok(cur_frm.doc.lead === "LEAD-00002");
- return done();
- }
- ]);
-});
diff --git a/erpnext/tests/ui/selling/_test_quotation.js b/erpnext/tests/ui/selling/_test_quotation.js
deleted file mode 100644
index 62dd05d..0000000
--- a/erpnext/tests/ui/selling/_test_quotation.js
+++ /dev/null
@@ -1,81 +0,0 @@
-QUnit.test("test: quotation", function (assert) {
- assert.expect(18);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.tests.setup_doctype("Customer"),
- () => frappe.tests.setup_doctype("Item"),
- () => frappe.tests.setup_doctype("Address"),
- () => frappe.tests.setup_doctype("Contact"),
- () => frappe.tests.setup_doctype("Price List"),
- () => frappe.tests.setup_doctype("Terms and Conditions"),
- () => frappe.tests.setup_doctype("Sales Taxes and Charges Template"),
- () => {
- return frappe.tests.make("Quotation", [{
- customer: "Test Customer 1"
- },
- {
- items: [
- [{
- "item_code": "Test Product 1"
- },
- {
- "qty": 5
- }
- ]
- ]
- }
- ]);
- },
- () => {
- // get_item_details
- assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
-
- // calculate_taxes_and_totals
- assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct");
- },
- () => cur_frm.set_value("customer_address", "Test1-Billing"),
- () => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
- () => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"),
- () => cur_frm.set_value("currency", "USD"),
- () => frappe.timeout(0.3),
- () => cur_frm.set_value("selling_price_list", "Test-Selling-USD"),
- () => frappe.timeout(0.5),
- () => cur_frm.doc.items[0].rate = 200,
- () => frappe.timeout(0.3),
- () => cur_frm.set_value("tc_name", "Test Term 1"),
- () => cur_frm.set_value("taxes_and_charges", "TEST In State GST"),
- () => frappe.timeout(0.3),
- () => cur_frm.save(),
- () => {
- // Check Address and Contact Info
- assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed");
- assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed");
- assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed");
-
- // Check Currency
- assert.ok(cur_frm.doc_currency == "USD", "Currency Changed");
- assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed");
- assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
- assert.ok(cur_frm.doc.total == 1000, "New Total Calculated");
-
- // Check Terms and Condtions
- assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
-
- // Check Taxes
- assert.ok(cur_frm.doc.taxes[0].account_head.includes("CGST"));
- assert.ok(cur_frm.doc.taxes[1].account_head.includes("SGST"));
- assert.ok(cur_frm.doc.grand_total == 1180, "Tax Amount Added to Total");
- assert.ok(cur_frm.doc.taxes_and_charges == "TEST In State GST", "Tax Template Selected");
- },
- () => frappe.timeout(0.3),
- () => cur_frm.print_doc(),
- () => frappe.timeout(1),
- () => assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"),
- () => assert.ok(RegExp(/QTN-\d\d\d\d\d/g).test($("#header-html small").text())),
- () => assert.ok($(".important .text-right.value").text().includes("$ 1,180.00")),
- () => assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected"),
- () => frappe.timeout(0.3),
- () => cur_frm.print_doc(),
- () => done()
- ]);
-});
\ No newline at end of file
diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt
new file mode 100644
index 0000000..047ee9c
--- /dev/null
+++ b/erpnext/tests/ui/tests.txt
@@ -0,0 +1,11 @@
+erpnext/tests/ui/make_fixtures.js #long
+erpnext/accounts/doctype/account/test_account.js
+erpnext/crm/doctype/lead/test_lead.js
+erpnext/crm/doctype/opportunity/test_opportunity.js
+erpnext/selling/doctype/quotation/test_quotation.js
+erpnext/crm/doctype/item/test_item.js
+erpnext/manufacturing/doctype/workstation/test_workstation.js
+erpnext/manufacturing/doctype/operation/test_operation.js
+erpnext/hr/doctype/holiday_list/test_holiday_list.js
+erpnext/hr/doctype/branch/test_branch.js
+erpnext/manufacturing/doctype/bom/test_bom.js
\ No newline at end of file