From 99c8fb19f793f6859b339eee173b2987c339d0f2 Mon Sep 17 00:00:00 2001
From: Aditya Hase
Date: Thu, 20 Sep 2018 17:43:04 +0530
Subject: [PATCH 01/12] fix(style): Put markdown for badges on separate lines
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3b3be965ae..fcdef97551 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,9 @@
-[](https://travis-ci.com/frappe/erpnext) [](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://www.codetriage.com/frappe/erpnext)
+[](https://travis-ci.com/frappe/erpnext)
+[](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](https://www.codetriage.com/frappe/erpnext)
[https://erpnext.com](https://erpnext.com)
From 6886c472513a6533b14a9b334bcd7d28c0433a05 Mon Sep 17 00:00:00 2001
From: Aditya Hase
Date: Thu, 20 Sep 2018 17:46:53 +0530
Subject: [PATCH 02/12] fix(style): Center align badges as well
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index fcdef97551..950f282f81 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@
ERP made simple
-
[](https://travis-ci.com/frappe/erpnext)
[](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -12,6 +11,8 @@
[https://erpnext.com](https://erpnext.com)
+
+
Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB.
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.
From 40880da28089c9f3fdcdff55649f15cb3e3d3dd3 Mon Sep 17 00:00:00 2001
From: Aditya Hase
Date: Thu, 20 Sep 2018 17:48:35 +0530
Subject: [PATCH 03/12] fix(readme): Add coverage badge
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 950f282f81..f4a08be548 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
[](https://travis-ci.com/frappe/erpnext)
[](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://www.codetriage.com/frappe/erpnext)
+[](https://coveralls.io/github/frappe/erpnext?branch=develop)
[https://erpnext.com](https://erpnext.com)
From b113349f31cfb91cdea2f3e864128052f5f69543 Mon Sep 17 00:00:00 2001
From: Zarrar
Date: Mon, 24 Sep 2018 14:30:07 +0530
Subject: [PATCH 04/12] minor fix for auto-opt-in for customer (#15483)
---
erpnext/selling/doctype/customer/customer.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index bd01ec9a91..d285704eb4 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -214,8 +214,9 @@ def get_loyalty_programs(doc):
"ifnull(to_date, '2500-01-01')": [">=", today()]})
for loyalty_program in loyalty_programs:
- customer_groups = [d.value for d in get_children("Customer Group", loyalty_program.customer_group)]
- customer_territories = [d.value for d in get_children("Territory", loyalty_program.customer_territory)]
+ customer_groups = [d.value for d in get_children("Customer Group", loyalty_program.customer_group)] + [loyalty_program.customer_group]
+ customer_territories = [d.value for d in get_children("Territory", loyalty_program.customer_territory)] + [loyalty_program.customer_territory]
+
if (not loyalty_program.customer_group or doc.customer_group in customer_groups)\
and (not loyalty_program.customer_territory or doc.territory in customer_territories):
lp_details.append(loyalty_program.name)
From a38b77cbfde29404e0562070eb87d04487c22712 Mon Sep 17 00:00:00 2001
From: Aditya Hase
Date: Tue, 25 Sep 2018 18:10:50 +0530
Subject: [PATCH 05/12] feature(chart-of-accounts): Rebuild HSM Tree after bulk
insertion (#15457)
* feature(chart-of-accounts): Rebuild HSM Tree after bulk insertion
* Update chart_of_accounts.py
---
erpnext/accounts/doctype/account/account.py | 5 +++++
.../doctype/account/chart_of_accounts/chart_of_accounts.py | 6 ++++++
2 files changed, 11 insertions(+)
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index eef85de0b9..635a8c2327 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -12,6 +12,11 @@ class BalanceMismatchError(frappe.ValidationError): pass
class Account(NestedSet):
nsm_parent_field = 'parent_account'
+ def on_update(self):
+ if frappe.local.flags.ignore_on_update:
+ return
+ else:
+ super().on_update()
def onload(self):
frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", "Accounts Settings",
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index f4cf6fabce..9b812a8b56 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -6,6 +6,7 @@ import frappe, os, json
from frappe.utils import cstr
from unidecode import unidecode
from six import iteritems
+from frappe.utils.nestedset import rebuild_tree
def create_charts(company, chart_template=None, existing_company=None):
chart = get_chart(chart_template, existing_company)
@@ -53,7 +54,12 @@ def create_charts(company, chart_template=None, existing_company=None):
_import_accounts(child, account.name, root_type)
+ # Rebuild NestedSet HSM tree for Account Doctype
+ # after all accounts are already inserted.
+ frappe.local.flags.ignore_on_update = True
_import_accounts(chart, None, None, root_account=True)
+ rebuild_tree("Account", "parent_account")
+ frappe.local.flags.ignore_on_update = False
def add_suffix_if_duplicate(account_name, account_number, accounts):
if account_number:
From 7a8c5b0c2c019063c5c98e4c1323ec6d0a4575a3 Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Tue, 25 Sep 2018 18:34:33 +0530
Subject: [PATCH 06/12] fix(setup wizard): Validate FY dates (#15473)
---
erpnext/public/js/setup_wizard.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index 6fa710d982..484d81decc 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -138,10 +138,15 @@ erpnext.setup.slides_settings = [
validate: function () {
// validate fiscal year start and end dates
- if (this.values.fy_start_date == 'Invalid date' || this.values.fy_end_date == 'Invalid date') {
+ const invalid = this.values.fy_start_date == 'Invalid date' ||
+ this.values.fy_end_date == 'Invalid date';
+ const start_greater_than_end = this.values.fy_start_date > this.values.fy_end_date;
+
+ if (invalid || start_greater_than_end) {
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
return false;
}
+
return true;
},
From fe1e4a41e66436e754b301d8904b6e648e4ea688 Mon Sep 17 00:00:00 2001
From: rohitwaghchaure
Date: Tue, 25 Sep 2018 18:36:32 +0530
Subject: [PATCH 07/12] Validate negative stock serial number (#15492)
---
erpnext/stock/doctype/serial_no/serial_no.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index a8cab80cbe..3d1979dd81 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -187,13 +187,14 @@ def process_serial_no(sle):
update_serial_nos(sle, item_det)
def validate_serial_no(sle, item_det):
+ serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else []
+
if item_det.has_serial_no==0:
- if sle.serial_no:
+ if serial_nos:
frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
SerialNoNotRequiredError)
elif sle.is_cancelled == "No":
- if sle.serial_no:
- serial_nos = get_serial_nos(sle.serial_no)
+ if serial_nos:
if cint(sle.actual_qty) != flt(sle.actual_qty):
frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
@@ -239,6 +240,12 @@ def validate_serial_no(sle, item_det):
elif sle.actual_qty < 0 or not item_det.serial_no_series:
frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
SerialNoRequiredError)
+ elif serial_nos:
+ for serial_no in serial_nos:
+ sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse"], as_dict=1)
+ if sr and sle.actual_qty < 0 and sr.warehouse != sle.warehouse:
+ frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
+ .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
def has_duplicate_serial_no(sn, sle):
if sn.warehouse:
From 865cb88bdd3c4585cce8cbb62e56f763beaa70d5 Mon Sep 17 00:00:00 2001
From: Aditya Hase
Date: Tue, 25 Sep 2018 18:51:17 +0530
Subject: [PATCH 08/12] fix(vscode-intellisense): Make VS Code Intellisense
work with ERPNext (#15453)
__init__.py in apps/erpnext directory confuses vs code.
Causing all erpnext dotted paths to need an extra `erpnext.` for
suggestion and completion to work properly.
This commit fixes this issue.
---
__init__.py | 0
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 __init__.py
diff --git a/__init__.py b/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
From fafc27766600f098f72b6175272bc62b724d47c3 Mon Sep 17 00:00:00 2001
From: rohitwaghchaure
Date: Tue, 25 Sep 2018 18:59:20 +0530
Subject: [PATCH 09/12] [Fix] BOM update tool, too many writes in one request.
Please send smaller requests (#15432)
---
erpnext/accounts/doctype/sales_invoice/pos.py | 2 +-
.../bom_update_tool/bom_update_tool.py | 25 ++++++++++++-------
2 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index a5157240ac..93cabb043c 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -506,7 +506,7 @@ def save_invoice(doc, name, name_list):
frappe.db.commit()
name_list.append(name)
except Exception:
- frappe.log_error(frappe.get_traceback())
frappe.db.rollback()
+ frappe.log_error(frappe.get_traceback())
return name_list
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
index c91bb8f332..3f6cb44c49 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -16,16 +16,23 @@ class BOMUpdateTool(Document):
self.update_new_bom()
bom_list = self.get_parent_boms(self.new_bom)
updated_bom = []
+
for bom in bom_list:
- bom_obj = frappe.get_doc("BOM", bom)
- bom_obj.get_doc_before_save()
- updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
- bom_obj.calculate_cost()
- bom_obj.update_parent_cost()
- bom_obj.db_update()
- if (getattr(bom_obj.meta, 'track_changes', False)
- and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
- bom_obj.save_version()
+ try:
+ bom_obj = frappe.get_doc("BOM", bom)
+ bom_obj.get_doc_before_save()
+ updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
+ bom_obj.calculate_cost()
+ bom_obj.update_parent_cost()
+ bom_obj.db_update()
+ if (getattr(bom_obj.meta, 'track_changes', False)
+ and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
+ bom_obj.save_version()
+
+ frappe.db.commit()
+ except Exception:
+ frappe.db.rollback()
+ frappe.log_error(frappe.get_traceback())
def validate_bom(self):
if cstr(self.current_bom) == cstr(self.new_bom):
From 86152eb5c8ee302f38e91c520a72cf2250401adc Mon Sep 17 00:00:00 2001
From: deepeshgarg007 <42651287+deepeshgarg007@users.noreply.github.com>
Date: Tue, 25 Sep 2018 19:08:16 +0530
Subject: [PATCH 10/12] Sales person Commission Report and more (#15431)
* Commission For Sales Person
* Changes Made
* Changes made in sales person dashboard and commission report
* Update sales_person_commission_summary.py
---
.../doctype/sales_team/sales_team.json | 80 +++++++-
.../__init__.py | 0
.../sales_person_commission_summary.js | 54 +++++
.../sales_person_commission_summary.json | 26 +++
.../sales_person_commission_summary.py | 142 ++++++++++++++
erpnext/selling/sales_common.js | 28 ++-
.../doctype/sales_person/sales_person.js | 9 +
.../doctype/sales_person/sales_person.json | 185 +++++++++++-------
.../doctype/sales_person/sales_person.py | 67 ++++++-
.../sales_person/sales_person_dashboard.py | 14 ++
10 files changed, 525 insertions(+), 80 deletions(-)
create mode 100644 erpnext/selling/report/sales_person_commission_summary/__init__.py
create mode 100644 erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js
create mode 100644 erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.json
create mode 100644 erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py
create mode 100644 erpnext/setup/doctype/sales_person/sales_person_dashboard.py
diff --git a/erpnext/selling/doctype/sales_team/sales_team.json b/erpnext/selling/doctype/sales_team/sales_team.json
index c77f9f4b2b..04027b2396 100644
--- a/erpnext/selling/doctype/sales_team/sales_team.json
+++ b/erpnext/selling/doctype/sales_team/sales_team.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -9,18 +10,24 @@
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 1,
+ "engine": "InnoDB",
"fields": [
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "sales_person",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
- "in_filter": 1,
+ "in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Sales Person",
"length": 0,
"no_copy": 0,
@@ -32,24 +39,31 @@
"print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0,
"width": "200px"
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "contact_no",
"fieldtype": "Data",
"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": "Contact No.",
"length": 0,
"no_copy": 0,
@@ -60,24 +74,31 @@
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0,
"width": "100px"
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "allocated_percentage",
"fieldtype": "Float",
"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": "Contribution (%)",
"length": 0,
"no_copy": 0,
@@ -88,24 +109,31 @@
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0,
"width": "100px"
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"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": "Contribution to Net Total",
"length": 0,
"no_copy": 0,
@@ -117,24 +145,63 @@
"print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
+ "translatable": 0,
"unique": 0,
"width": "120px"
},
{
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "commission_rate",
+ "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": "Commission Rate",
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "incentives",
"fieldtype": "Currency",
"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": "Incentives",
"length": 0,
"no_copy": 0,
@@ -145,24 +212,26 @@
"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,
+ "translatable": 0,
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
-
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-07-11 03:28:08.421297",
+ "modified": "2018-09-17 13:03:14.755974",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Team",
@@ -171,5 +240,8 @@
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
- "track_seen": 0
+ "show_name_in_global_search": 0,
+ "track_changes": 1,
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/selling/report/sales_person_commission_summary/__init__.py b/erpnext/selling/report/sales_person_commission_summary/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js
new file mode 100644
index 0000000000..ba6ee784b9
--- /dev/null
+++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Sales Person Commission Summary"] = {
+ "filters": [
+
+ {
+ fieldname: "sales_person",
+ label: __("Sales Person"),
+ fieldtype: "Link",
+ options: "Sales Person"
+ },
+ {
+ fieldname: "doc_type",
+ label: __("Document Type"),
+ fieldtype: "Select",
+ options: "Sales Order\nDelivery Note\nSales Invoice",
+ default: "Sales Order"
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_start_date"),
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.get_today()
+ },
+ {
+ fieldname:"company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company")
+ },
+ {
+ fieldname:"customer",
+ label: __("Customer"),
+ fieldtype: "Link",
+ options: "Customer",
+ },
+ {
+ fieldname:"territory",
+ label: __("Territory"),
+ fieldtype: "Link",
+ options: "Territory",
+ },
+
+ ]
+}
diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.json b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.json
new file mode 100644
index 0000000000..d5ad9f13e1
--- /dev/null
+++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 1,
+ "creation": "2018-09-11 17:49:27.256304",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2018-09-11 17:49:27.256304",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Person Commission Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Order",
+ "report_name": "Sales Person Commission Summary",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Sales Manager"
+ },
+ {
+ "role": "Maintenance User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py
new file mode 100644
index 0000000000..0c84909611
--- /dev/null
+++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py
@@ -0,0 +1,142 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import msgprint, _
+from frappe.utils import flt
+
+
+def execute(filters=None):
+ if not filters: filters = {}
+
+ columns = get_columns(filters)
+ entries = get_entries(filters)
+ data = []
+
+ for d in entries:
+ data.append([
+ d.name, d.customer, d.territory, d.posting_date,
+ d.base_net_amount, d.sales_person, d.allocated_percentage, d.commission_rate, d.allocated_amount,d.incentives
+ ])
+
+ if data:
+ total_row = [""]*len(data[0])
+ data.append(total_row)
+
+ return columns, data
+
+def get_columns(filters):
+ if not filters.get("doc_type"):
+ msgprint(_("Please select the document type first"), raise_exception=1)
+
+ columns =[
+ {
+ "label": _(filters["doc_type"]),
+ "options": filters["doc_type"],
+ "fieldname": filters['doc_type'],
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Customer"),
+ "options": "Customer",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Territory"),
+ "options": "Territory",
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "width": 100
+ },
+ {
+ "label": _("Posting Date"),
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "width": 100
+ },
+ {
+ "label": _("Amount"),
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "width": 120
+ },
+ {
+ "label": _("Sales Person"),
+ "options": "Sales Person",
+ "fieldname": "sales_person",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Contribution %"),
+ "fieldname": "contribution_percentage",
+ "fieldtype": "Data",
+ "width": 110
+ },
+ {
+ "label": _("Commission Rate %"),
+ "fieldname": "commission_rate",
+ "fieldtype": "Data",
+ "width": 100
+ },
+ {
+ "label": _("Contribution Amount"),
+ "fieldname": "contribution_amount",
+ "fieldtype": "Currency",
+ "width": 120
+ },
+ {
+ "label": _("Incentives"),
+ "fieldname": "incentives",
+ "fieldtype": "Currency",
+ "width": 120
+ }
+ ]
+
+ return columns
+
+def get_entries(filters):
+ date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date"
+
+ conditions, values = get_conditions(filters, date_field)
+ entries = frappe.db.sql("""
+ select
+ dt.name, dt.customer, dt.territory, dt.%s as posting_date,dt.base_net_total as base_net_amount,
+ st.commission_rate,st.sales_person, st.allocated_percentage, st.allocated_amount, st.incentives
+ from
+ `tab%s` dt, `tabSales Team` st
+ where
+ st.parent = dt.name and st.parenttype = %s
+ and dt.docstatus = 1 %s order by dt.name desc,st.sales_person
+ """ %(date_field, filters["doc_type"], '%s', conditions),
+ tuple([filters["doc_type"]] + values), as_dict=1)
+
+ return entries
+
+def get_conditions(filters, date_field):
+ conditions = [""]
+ values = []
+
+ for field in ["company", "customer", "territory"]:
+ if filters.get(field):
+ conditions.append("dt.{0}=%s".format(field))
+ values.append(filters[field])
+
+ if filters.get("sales_person"):
+ conditions.append("st.sales_person = '{0}'".format(filters.get("sales_person")))
+
+ if filters.get("from_date"):
+ conditions.append("dt.{0}>=%s".format(date_field))
+ values.append(filters["from_date"])
+
+ if filters.get("to_date"):
+ conditions.append("dt.{0}<=%s".format(date_field))
+ values.append(filters["to_date"])
+
+ return " and ".join(conditions), values
+
+
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 325f08af42..b7b186a8d6 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -12,6 +12,8 @@ frappe.provide("erpnext.selling");
erpnext.selling.SellingController = erpnext.TransactionController.extend({
setup: function() {
this._super();
+ this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
+ this.frm.add_fetch("sales_person", "commission_rate", "commission_rate");
},
onload: function() {
@@ -29,8 +31,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
setup_queries: function() {
var me = this;
- this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
-
$.each([["customer", "customer"],
["lead", "lead"]],
function(i, opts) {
@@ -171,17 +171,26 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
allocated_percentage: function(doc, cdt, cdn) {
var sales_person = frappe.get_doc(cdt, cdn);
-
if(sales_person.allocated_percentage) {
+
sales_person.allocated_percentage = flt(sales_person.allocated_percentage,
precision("allocated_percentage", sales_person));
+
sales_person.allocated_amount = flt(this.frm.doc.base_net_total *
sales_person.allocated_percentage / 100.0,
precision("allocated_amount", sales_person));
+ refresh_field(["allocated_amount"], sales_person);
- refresh_field(["allocated_percentage", "allocated_amount"], sales_person.name,
+ this.calculate_incentive(sales_person);
+ refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name,
sales_person.parentfield);
- }
+ }
+ },
+
+ sales_person: function(doc, cdt, cdn) {
+ var row = frappe.get_doc(cdt, cdn);
+ this.calculate_incentive(row);
+ refresh_field("incentives",row.name,row.parentfield);
},
warehouse: function(doc, cdt, cdn) {
@@ -250,6 +259,15 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
});
},
+ calculate_incentive: function(row) {
+ if(row.allocated_amount)
+ {
+ row.incentives = flt(
+ row.allocated_amount * row.commission_rate / 100.0,
+ precision("incentives", sales_person));
+ }
+ },
+
batch_no: function(doc, cdt, cdn) {
var me = this;
var item = frappe.get_doc(cdt, cdn);
diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js
index 2388739b7f..584f879431 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.js
+++ b/erpnext/setup/doctype/sales_person/sales_person.js
@@ -1,6 +1,15 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+frappe.ui.form.on('Sales Person', {
+ refresh: function(frm) {
+ if(frm.doc.__onload && frm.doc.__onload.dashboard_info) {
+ var info = frm.doc.__onload.dashboard_info;
+ frm.dashboard.add_indicator(__('Total Contribution Amount: {0}',
+ [format_currency(info.allocated_amount, info.currency)]), 'blue');
+ }
+ }
+});
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly(doc);
diff --git a/erpnext/setup/doctype/sales_person/sales_person.json b/erpnext/setup/doctype/sales_person/sales_person.json
index 715d8dad47..7eeb500f2a 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.json
+++ b/erpnext/setup/doctype/sales_person/sales_person.json
@@ -1,6 +1,6 @@
{
"allow_copy": 0,
- "allow_guest_to_view": 0,
+ "allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:sales_person_name",
@@ -15,7 +15,8 @@
"engine": "InnoDB",
"fields": [
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -42,11 +43,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -74,11 +76,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "translatable": 0,
+ "unique": 1
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -108,11 +111,44 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "commission_rate",
+ "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": "Commission Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -141,47 +177,49 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "1",
- "fieldname": "enabled",
- "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": "Enabled",
- "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,
- "translatable": 0,
+ "default": "1",
+ "fieldname": "enabled",
+ "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": "Enabled",
+ "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,
+ "translatable": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "cb0",
"fieldtype": "Column Break",
"hidden": 0,
@@ -202,11 +240,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -233,18 +272,19 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fetch_from": "employee.department",
- "fieldname": "department",
- "fieldtype": "Link",
+ "fetch_from": "employee.department",
+ "fieldname": "department",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -252,25 +292,26 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Department",
+ "label": "Department",
"length": 0,
"no_copy": 0,
- "options": "Department",
+ "options": "Department",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 1,
+ "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -298,11 +339,12 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -330,11 +372,12 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -362,11 +405,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -395,11 +439,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -428,11 +473,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
},
{
- "allow_bulk_edit": 0,
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -462,11 +508,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
- "translatable": 0,
+ "translatable": 0,
"unique": 0
}
],
- "has_web_view": 0,
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-user",
@@ -477,7 +523,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-05-17 09:44:18.203325",
+ "modified": "2018-09-12 16:41:06.378899",
"modified_by": "Administrator",
"module": "Setup",
"name": "Sales Person",
@@ -541,12 +587,13 @@
"write": 1
}
],
- "quick_entry": 0,
+ "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "parent_sales_person",
"show_name_in_global_search": 1,
"sort_order": "ASC",
- "track_changes": 0,
- "track_seen": 0
+ "track_changes": 1,
+ "track_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py
index 816ee842e3..ab65f7455e 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.py
+++ b/erpnext/setup/doctype/sales_person/sales_person.py
@@ -6,9 +6,10 @@ import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils.nestedset import NestedSet
+from erpnext import get_default_currency
class SalesPerson(NestedSet):
- nsm_parent_field = 'parent_sales_person';
+ nsm_parent_field = 'parent_sales_person'
def validate(self):
for d in self.get('targets') or []:
@@ -16,6 +17,24 @@ class SalesPerson(NestedSet):
frappe.throw(_("Either target qty or target amount is mandatory."))
self.validate_employee_id()
+ def onload(self):
+ self.load_dashboard_info()
+
+ def load_dashboard_info(self):
+ company_default_currency = get_default_currency()
+
+ allocated_amount = frappe.db.sql("""
+ select sum(allocated_amount)
+ from `tabSales Team`
+ where sales_person = %s and docstatus=1 and parenttype = 'Sales Order'
+ """,(self.sales_person_name))
+
+ info = {}
+ info["allocated_amount"] = flt(allocated_amount[0][0]) if allocated_amount else 0
+ info["currency"] = company_default_currency
+
+ self.set_onload('dashboard_info', info)
+
def on_update(self):
super(SalesPerson, self).on_update()
self.validate_one_root()
@@ -35,4 +54,48 @@ class SalesPerson(NestedSet):
frappe.throw(_("Another Sales Person {0} exists with the same Employee id").format(sales_person))
def on_doctype_update():
- frappe.db.add_index("Sales Person", ["lft", "rgt"])
\ No newline at end of file
+ frappe.db.add_index("Sales Person", ["lft", "rgt"])
+
+def get_timeline_data(doctype, name):
+
+ out = {}
+
+ out.update(dict(frappe.db.sql('''select
+ unix_timestamp(dt.transaction_date), count(st.parenttype)
+ from
+ `tabSales Order` dt, `tabSales Team` st
+ where
+ st.sales_person = %s and st.parent = dt.name and dt.transaction_date > date_sub(curdate(), interval 1 year)
+ group by dt.transaction_date ''', name)))
+
+ sales_invoice = dict(frappe.db.sql('''select
+ unix_timestamp(dt.posting_date), count(st.parenttype)
+ from
+ `tabSales Invoice` dt, `tabSales Team` st
+ where
+ st.sales_person = %s and st.parent = dt.name and dt.posting_date > date_sub(curdate(), interval 1 year)
+ group by dt.posting_date ''', name))
+
+ for key in sales_invoice:
+ if out.get(key):
+ out[key] += sales_invoice[key]
+ else:
+ out[key] = sales_invoice[key]
+
+ delivery_note = dict(frappe.db.sql('''select
+ unix_timestamp(dt.posting_date), count(st.parenttype)
+ from
+ `tabDelivery Note` dt, `tabSales Team` st
+ where
+ st.sales_person = %s and st.parent = dt.name and dt.posting_date > date_sub(curdate(), interval 1 year)
+ group by dt.posting_date ''', name))
+
+ for key in delivery_note:
+ if out.get(key):
+ out[key] += delivery_note[key]
+ else:
+ out[key] = delivery_note[key]
+
+ return out
+
+
diff --git a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
new file mode 100644
index 0000000000..42528d8832
--- /dev/null
+++ b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py
@@ -0,0 +1,14 @@
+from frappe import _
+
+def get_data():
+ return {
+ 'heatmap': True,
+ 'heatmap_message': _('This is based on transactions against this Sales Person. See timeline below for details'),
+ 'fieldname': 'sales_person',
+ 'transactions': [
+ {
+ 'label': _('Sales'),
+ 'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
+ },
+ ]
+ }
\ No newline at end of file
From e04431ea5c7f02b38f51861ed90418375a08020a Mon Sep 17 00:00:00 2001
From: Ameya Shenoy
Date: Wed, 26 Sep 2018 07:26:49 +0000
Subject: [PATCH 11/12] bumped to version 10.1.54
---
erpnext/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index a7ec84ade6..68f1fa749b 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '10.1.53'
+__version__ = '10.1.54'
def get_default_company(user=None):
'''Get default company for user'''
From b8a5fb73922adcf2b91d46e6293a8d3579210d65 Mon Sep 17 00:00:00 2001
From: Ameya Shenoy
Date: Wed, 26 Sep 2018 07:32:44 +0000
Subject: [PATCH 12/12] bumped to version 11.0.3
---
erpnext/hooks.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 22c92577e2..a11f63fc9f 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
develop_version = '11.x.x-develop'
-staging_version = '11.0.2'
+staging_version = '11.0.3'
error_report_email = "support@erpnext.com"