Merge branch 'staging-fixes' into staging

This commit is contained in:
Ameya Shenoy 2018-09-26 07:32:44 +00:00
commit 47b82789ae
No known key found for this signature in database
GPG Key ID: AC016A555657D0A3
19 changed files with 573 additions and 99 deletions

View File

@ -4,12 +4,16 @@
<p align="center"> <p align="center">
<p>ERP made simple</p> <p>ERP made simple</p>
</p> </p>
</div>
[![Build Status](https://travis-ci.com/frappe/erpnext.png)](https://travis-ci.com/frappe/erpnext) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext) [![Build Status](https://travis-ci.com/frappe/erpnext.png)](https://travis-ci.com/frappe/erpnext)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)
[https://erpnext.com](https://erpnext.com) [https://erpnext.com](https://erpnext.com)
</div>
Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB. 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. ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
from frappe.utils import getdate from frappe.utils import getdate
__version__ = '10.1.53' __version__ = '10.1.54'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -12,6 +12,11 @@ class BalanceMismatchError(frappe.ValidationError): pass
class Account(NestedSet): class Account(NestedSet):
nsm_parent_field = 'parent_account' nsm_parent_field = 'parent_account'
def on_update(self):
if frappe.local.flags.ignore_on_update:
return
else:
super().on_update()
def onload(self): def onload(self):
frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", "Accounts Settings", frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", "Accounts Settings",

View File

@ -6,6 +6,7 @@ import frappe, os, json
from frappe.utils import cstr from frappe.utils import cstr
from unidecode import unidecode from unidecode import unidecode
from six import iteritems from six import iteritems
from frappe.utils.nestedset import rebuild_tree
def create_charts(company, chart_template=None, existing_company=None): def create_charts(company, chart_template=None, existing_company=None):
chart = get_chart(chart_template, existing_company) 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) _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) _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): def add_suffix_if_duplicate(account_name, account_number, accounts):
if account_number: if account_number:

View File

@ -572,7 +572,7 @@ def save_invoice(doc, name, name_list):
frappe.db.commit() frappe.db.commit()
name_list.append(name) name_list.append(name)
except Exception: except Exception:
frappe.log_error(frappe.get_traceback())
frappe.db.rollback() frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
return name_list return name_list

View File

@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext" source_link = "https://github.com/frappe/erpnext"
develop_version = '11.x.x-develop' develop_version = '11.x.x-develop'
staging_version = '11.0.2' staging_version = '11.0.3'
error_report_email = "support@erpnext.com" error_report_email = "support@erpnext.com"

View File

@ -16,7 +16,9 @@ class BOMUpdateTool(Document):
self.update_new_bom() self.update_new_bom()
bom_list = self.get_parent_boms(self.new_bom) bom_list = self.get_parent_boms(self.new_bom)
updated_bom = [] updated_bom = []
for bom in bom_list: for bom in bom_list:
try:
bom_obj = frappe.get_doc("BOM", bom) bom_obj = frappe.get_doc("BOM", bom)
bom_obj.get_doc_before_save() bom_obj.get_doc_before_save()
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
@ -27,6 +29,11 @@ class BOMUpdateTool(Document):
and bom_obj._doc_before_save and not bom_obj.flags.ignore_version): and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
bom_obj.save_version() bom_obj.save_version()
frappe.db.commit()
except Exception:
frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
def validate_bom(self): def validate_bom(self):
if cstr(self.current_bom) == cstr(self.new_bom): if cstr(self.current_bom) == cstr(self.new_bom):
frappe.throw(_("Current BOM and New BOM can not be same")) frappe.throw(_("Current BOM and New BOM can not be same"))

View File

@ -214,8 +214,9 @@ def get_loyalty_programs(doc):
"ifnull(to_date, '2500-01-01')": [">=", today()]}) "ifnull(to_date, '2500-01-01')": [">=", today()]})
for loyalty_program in loyalty_programs: for loyalty_program in loyalty_programs:
customer_groups = [d.value for d in get_children("Customer Group", loyalty_program.customer_group)] 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)] 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)\ 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): and (not loyalty_program.customer_territory or doc.territory in customer_territories):
lp_details.append(loyalty_program.name) lp_details.append(loyalty_program.name)

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"beta": 0, "beta": 0,
@ -9,18 +10,24 @@
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "sales_person", "fieldname": "sales_person",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 1, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Sales Person", "label": "Sales Person",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -32,24 +39,31 @@
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"print_width": "200px", "print_width": "200px",
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0, "unique": 0,
"width": "200px" "width": "200px"
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "contact_no", "fieldname": "contact_no",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Contact No.", "label": "Contact No.",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -60,24 +74,31 @@
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"print_width": "100px", "print_width": "100px",
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0, "unique": 0,
"width": "100px" "width": "100px"
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "allocated_percentage", "fieldname": "allocated_percentage",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Contribution (%)", "label": "Contribution (%)",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -88,24 +109,31 @@
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"print_width": "100px", "print_width": "100px",
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0, "unique": 0,
"width": "100px" "width": "100px"
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "allocated_amount", "fieldname": "allocated_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Contribution to Net Total", "label": "Contribution to Net Total",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -117,24 +145,63 @@
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"print_width": "120px", "print_width": "120px",
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0, "unique": 0,
"width": "120px" "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, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "incentives", "fieldname": "incentives",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Incentives", "label": "Incentives",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -145,24 +212,26 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0,
"unique": 0 "unique": 0
} }
], ],
"has_web_view": 0,
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"idx": 1, "idx": 1,
"image_view": 0, "image_view": 0,
"in_create": 0, "in_create": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-07-11 03:28:08.421297", "modified": "2018-09-17 13:03:14.755974",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Team", "name": "Sales Team",
@ -171,5 +240,8 @@
"quick_entry": 1, "quick_entry": 1,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"track_seen": 0 "show_name_in_global_search": 0,
"track_changes": 1,
"track_seen": 0,
"track_views": 0
} }

View File

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

View File

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

View File

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

View File

@ -12,6 +12,8 @@ frappe.provide("erpnext.selling");
erpnext.selling.SellingController = erpnext.TransactionController.extend({ erpnext.selling.SellingController = erpnext.TransactionController.extend({
setup: function() { setup: function() {
this._super(); this._super();
this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
this.frm.add_fetch("sales_person", "commission_rate", "commission_rate");
}, },
onload: function() { onload: function() {
@ -29,8 +31,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
setup_queries: function() { setup_queries: function() {
var me = this; var me = this;
this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
$.each([["customer", "customer"], $.each([["customer", "customer"],
["lead", "lead"]], ["lead", "lead"]],
function(i, opts) { function(i, opts) {
@ -171,19 +171,28 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
allocated_percentage: function(doc, cdt, cdn) { allocated_percentage: function(doc, cdt, cdn) {
var sales_person = frappe.get_doc(cdt, cdn); var sales_person = frappe.get_doc(cdt, cdn);
if(sales_person.allocated_percentage) { if(sales_person.allocated_percentage) {
sales_person.allocated_percentage = flt(sales_person.allocated_percentage, sales_person.allocated_percentage = flt(sales_person.allocated_percentage,
precision("allocated_percentage", sales_person)); precision("allocated_percentage", sales_person));
sales_person.allocated_amount = flt(this.frm.doc.base_net_total * sales_person.allocated_amount = flt(this.frm.doc.base_net_total *
sales_person.allocated_percentage / 100.0, sales_person.allocated_percentage / 100.0,
precision("allocated_amount", sales_person)); 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.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) { warehouse: function(doc, cdt, cdn) {
var me = this; var me = this;
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_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) { batch_no: function(doc, cdt, cdn) {
var me = this; var me = this;
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);

View File

@ -1,6 +1,15 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // 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.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly(doc); cur_frm.cscript.set_root_readonly(doc);

View File

@ -16,6 +16,7 @@
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -47,6 +48,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -75,10 +77,11 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 1
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -113,6 +116,39 @@
}, },
{ {
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -146,6 +182,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -178,6 +215,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -207,6 +245,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -238,6 +277,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -271,6 +311,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -303,6 +344,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -335,6 +377,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -367,6 +410,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -400,6 +444,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -433,6 +478,7 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -477,7 +523,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-05-17 09:44:18.203325", "modified": "2018-09-12 16:41:06.378899",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Sales Person", "name": "Sales Person",
@ -541,12 +587,13 @@
"write": 1 "write": 1
} }
], ],
"quick_entry": 0, "quick_entry": 1,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"search_fields": "parent_sales_person", "search_fields": "parent_sales_person",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 0, "track_changes": 1,
"track_seen": 0 "track_seen": 0,
"track_views": 0
} }

View File

@ -6,9 +6,10 @@ import frappe
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
from erpnext import get_default_currency
class SalesPerson(NestedSet): class SalesPerson(NestedSet):
nsm_parent_field = 'parent_sales_person'; nsm_parent_field = 'parent_sales_person'
def validate(self): def validate(self):
for d in self.get('targets') or []: for d in self.get('targets') or []:
@ -16,6 +17,24 @@ class SalesPerson(NestedSet):
frappe.throw(_("Either target qty or target amount is mandatory.")) frappe.throw(_("Either target qty or target amount is mandatory."))
self.validate_employee_id() 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): def on_update(self):
super(SalesPerson, self).on_update() super(SalesPerson, self).on_update()
self.validate_one_root() self.validate_one_root()
@ -36,3 +55,47 @@ class SalesPerson(NestedSet):
def on_doctype_update(): def on_doctype_update():
frappe.db.add_index("Sales Person", ["lft", "rgt"]) 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

View File

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

View File

@ -191,14 +191,14 @@ def process_serial_no(sle):
update_serial_nos(sle, item_det) update_serial_nos(sle, item_det)
def validate_serial_no(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 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), frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
SerialNoNotRequiredError) SerialNoNotRequiredError)
elif sle.is_cancelled == "No": elif sle.is_cancelled == "No":
if sle.serial_no: if serial_nos:
serial_nos = get_serial_nos(sle.serial_no)
if cint(sle.actual_qty) != flt(sle.actual_qty): 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)) frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
@ -285,6 +285,12 @@ def validate_serial_no(sle, item_det):
elif sle.actual_qty < 0 or not item_det.serial_no_series: 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), frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
SerialNoRequiredError) 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 validate_so_serial_no(sr, sales_order,): def validate_so_serial_no(sr, sales_order,):
if not sr.sales_order or sr.sales_order!= sales_order: if not sr.sales_order or sr.sales_order!= sales_order: