Merge pull request #21731 from prssanna/custom-chart-fix
fix: add heatmap_year parameter to get
diff --git a/.travis.yml b/.travis.yml
index 213445b..77d427e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
-dist: trusty
-
language: python
+dist: trusty
git:
depth: 1
@@ -14,21 +13,10 @@
jobs:
include:
- - name: "Python 2.7 Server Side Test"
- python: 2.7
- script: bench --site test_site run-tests --app erpnext --coverage
-
- name: "Python 3.6 Server Side Test"
python: 3.6
script: bench --site test_site run-tests --app erpnext --coverage
- - name: "Python 2.7 Patch Test"
- python: 2.7
- before_script:
- - wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
- - bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz
- script: bench --site test_site migrate
-
- name: "Python 3.6 Patch Test"
python: 3.6
before_script:
@@ -40,8 +28,7 @@
- cd ~
- nvm install 10
- - git clone https://github.com/frappe/bench --depth 1
- - pip install -e ./bench
+ - pip install frappe-bench
- git clone https://github.com/frappe/frappe --branch $TRAVIS_BRANCH --depth 1
- bench init --skip-assets --frappe-path ~/frappe --python $(which python) frappe-bench
diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py
index a106f70..cdd1661 100644
--- a/erpnext/accounts/dashboard_fixtures.py
+++ b/erpnext/accounts/dashboard_fixtures.py
@@ -1,15 +1,22 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
+from erpnext import get_default_company
import frappe
import json
def get_data():
- return frappe._dict({
- "dashboards": get_dashboards(),
- "charts": get_charts(),
+ data = frappe._dict({
+ "dashboards": [],
+ "charts": []
})
+ company = get_company_for_dashboards()
+ if company:
+ company_doc = frappe.get_doc("Company", company)
+ data.dashboards = get_dashboards()
+ data.charts = get_charts(company_doc)
+ return data
def get_dashboards():
return [{
@@ -24,88 +31,87 @@
]
}]
-def get_charts():
- company = frappe.get_doc("Company", get_company_for_dashboards())
+def get_charts(company):
income_account = company.default_income_account or get_account("Income Account", company.name)
expense_account = company.default_expense_account or get_account("Expense Account", company.name)
bank_account = company.default_bank_account or get_account("Bank", company.name)
return [
- {
- "doctype": "Dashboard Chart",
- "time_interval": "Quarterly",
- "name": "Income",
- "chart_name": "Income",
- "timespan": "Last Year",
- "color": None,
- "filters_json": json.dumps({"company": company.name, "account": income_account}),
- "source": "Account Balance Timeline",
- "chart_type": "Custom",
- "timeseries": 1,
- "owner": "Administrator",
- "type": "Line"
- },
- {
- "doctype": "Dashboard Chart",
- "time_interval": "Quarterly",
- "name": "Expenses",
- "chart_name": "Expenses",
- "timespan": "Last Year",
- "color": None,
- "filters_json": json.dumps({"company": company.name, "account": expense_account}),
- "source": "Account Balance Timeline",
- "chart_type": "Custom",
- "timeseries": 1,
- "owner": "Administrator",
- "type": "Line"
- },
- {
- "doctype": "Dashboard Chart",
- "time_interval": "Quarterly",
- "name": "Bank Balance",
- "chart_name": "Bank Balance",
- "timespan": "Last Year",
- "color": "#ffb868",
- "filters_json": json.dumps({"company": company.name, "account": bank_account}),
- "source": "Account Balance Timeline",
- "chart_type": "Custom",
- "timeseries": 1,
- "owner": "Administrator",
- "type": "Line"
- },
- {
- "doctype": "Dashboard Chart",
- "time_interval": "Monthly",
- "name": "Incoming Bills (Purchase Invoice)",
- "chart_name": "Incoming Bills (Purchase Invoice)",
- "timespan": "Last Year",
- "color": "#a83333",
- "value_based_on": "base_grand_total",
- "filters_json": json.dumps({}),
- "chart_type": "Sum",
- "timeseries": 1,
- "based_on": "posting_date",
- "owner": "Administrator",
- "document_type": "Purchase Invoice",
- "type": "Bar"
- },
- {
- "doctype": "Dashboard Chart",
- "time_interval": "Monthly",
- "name": "Outgoing Bills (Sales Invoice)",
- "chart_name": "Outgoing Bills (Sales Invoice)",
- "timespan": "Last Year",
- "color": "#7b933d",
- "value_based_on": "base_grand_total",
- "filters_json": json.dumps({}),
- "chart_type": "Sum",
- "timeseries": 1,
- "based_on": "posting_date",
- "owner": "Administrator",
- "document_type": "Sales Invoice",
- "type": "Bar"
- }
- ]
+ {
+ "doctype": "Dashboard Chart",
+ "time_interval": "Quarterly",
+ "name": "Income",
+ "chart_name": "Income",
+ "timespan": "Last Year",
+ "color": None,
+ "filters_json": json.dumps({"company": company.name, "account": income_account}),
+ "source": "Account Balance Timeline",
+ "chart_type": "Custom",
+ "timeseries": 1,
+ "owner": "Administrator",
+ "type": "Line"
+ },
+ {
+ "doctype": "Dashboard Chart",
+ "time_interval": "Quarterly",
+ "name": "Expenses",
+ "chart_name": "Expenses",
+ "timespan": "Last Year",
+ "color": None,
+ "filters_json": json.dumps({"company": company.name, "account": expense_account}),
+ "source": "Account Balance Timeline",
+ "chart_type": "Custom",
+ "timeseries": 1,
+ "owner": "Administrator",
+ "type": "Line"
+ },
+ {
+ "doctype": "Dashboard Chart",
+ "time_interval": "Quarterly",
+ "name": "Bank Balance",
+ "chart_name": "Bank Balance",
+ "timespan": "Last Year",
+ "color": "#ffb868",
+ "filters_json": json.dumps({"company": company.name, "account": bank_account}),
+ "source": "Account Balance Timeline",
+ "chart_type": "Custom",
+ "timeseries": 1,
+ "owner": "Administrator",
+ "type": "Line"
+ },
+ {
+ "doctype": "Dashboard Chart",
+ "time_interval": "Monthly",
+ "name": "Incoming Bills (Purchase Invoice)",
+ "chart_name": "Incoming Bills (Purchase Invoice)",
+ "timespan": "Last Year",
+ "color": "#a83333",
+ "value_based_on": "base_grand_total",
+ "filters_json": json.dumps({}),
+ "chart_type": "Sum",
+ "timeseries": 1,
+ "based_on": "posting_date",
+ "owner": "Administrator",
+ "document_type": "Purchase Invoice",
+ "type": "Bar"
+ },
+ {
+ "doctype": "Dashboard Chart",
+ "time_interval": "Monthly",
+ "name": "Outgoing Bills (Sales Invoice)",
+ "chart_name": "Outgoing Bills (Sales Invoice)",
+ "timespan": "Last Year",
+ "color": "#7b933d",
+ "value_based_on": "base_grand_total",
+ "filters_json": json.dumps({}),
+ "chart_type": "Sum",
+ "timeseries": 1,
+ "based_on": "posting_date",
+ "owner": "Administrator",
+ "document_type": "Sales Invoice",
+ "type": "Bar"
+ }
+ ]
def get_account(account_type, company):
accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
@@ -113,11 +119,9 @@
return accounts[0].name
def get_company_for_dashboards():
- company = frappe.defaults.get_defaults().company
- if company:
- return company
- else:
+ company = get_default_company()
+ if not company:
company_list = frappe.get_list("Company")
if company_list:
- return company_list[0].name
- return None
\ No newline at end of file
+ company = company_list[0].name
+ return company
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 1188bea..2aecd6b 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -53,7 +53,7 @@
"label": __("Voucher No"),
"fieldtype": "Data",
on_change: function() {
- frappe.query_report.set_filter_value('group_by', "");
+ frappe.query_report.set_filter_value('group_by', "Group by Voucher (Consolidated)");
}
},
{
diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json
index 6a35130..f56fd05 100644
--- a/erpnext/assets/doctype/location/location.json
+++ b/erpnext/assets/doctype/location/location.json
@@ -141,7 +141,7 @@
],
"is_tree": 1,
"links": [],
- "modified": "2020-03-18 18:00:08.885805",
+ "modified": "2020-05-08 16:11:11.375701",
"modified_by": "Administrator",
"module": "Assets",
"name": "Location",
@@ -221,7 +221,6 @@
}
],
"quick_entry": 1,
- "restrict_to_domain": "Agriculture",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 9161ed8..ebf8723 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -13,12 +13,12 @@
"stop_birthday_reminders",
"expense_approver_mandatory_in_expense_claim",
"payroll_settings",
- "payroll_based_on",
- "max_working_hours_against_timesheet",
+ "payroll_based_on",
+ "max_working_hours_against_timesheet",
"include_holidays_in_total_working_days",
"disable_rounded_total",
"column_break_11",
- "daily_wages_fraction_for_half_day",
+ "daily_wages_fraction_for_half_day",
"email_salary_slip_to_employee",
"encrypt_salary_slips_in_emails",
"password_policy",
@@ -191,7 +191,7 @@
"default": "Leave",
"fieldname": "payroll_based_on",
"fieldtype": "Select",
- "label": "Calculate Working Days in Payroll based on",
+ "label": "Calculate Payroll Working Days Based On",
"options": "Leave\nAttendance"
},
{
@@ -206,7 +206,7 @@
"idx": 1,
"issingle": 1,
"links": [],
- "modified": "2020-04-13 21:20:59.382394",
+ "modified": "2020-05-11 13:02:51.274347",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
index f97e596..961c05c 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
@@ -38,7 +38,7 @@
for pledge in self.securities:
if not pledge.qty and not pledge.amount:
- frappe.throw(_("Qty or Amount is mandatroy for loan security"))
+ frappe.throw(_("Qty or Amount is mandatory for loan security!"))
if not (self.loan_application and pledge.loan_security_price):
pledge.loan_security_price = get_loan_security_price(pledge.loan_security)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 3f90d36..f721724 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -680,3 +680,4 @@
erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
+execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
index 14954fb..4622376 100644
--- a/erpnext/patches/v11_0/set_default_email_template_in_hr.py
+++ b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
+from frappe import _
import frappe
def execute():
hr_settings = frappe.get_single("HR Settings")
- hr_settings.leave_approval_notification_template = "Leave Approval Notification"
- hr_settings.leave_status_notification_template = "Leave Status Notification"
- hr_settings.save()
\ No newline at end of file
+ hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
+ hr_settings.leave_status_notification_template = _("Leave Status Notification")
+ hr_settings.save()
diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
index 179be2c..ec94cd0 100644
--- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
+++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py
@@ -7,7 +7,7 @@
from frappe.model.utils.rename_field import rename_field
def execute():
- if not frappe.db.table_exists("Payroll Period"):
+ if not (frappe.db.table_exists("Payroll Period") and frappe.db.table_exists("Taxable Salary Slab")):
return
for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"):
@@ -60,6 +60,9 @@
""", (income_tax_slab.name, company.name, period.start_date))
# move other incomes to separate document
+ if not frappe.db.table_exists("Employee Tax Exemption Proof Submission"):
+ return
+
migrated = []
proofs = frappe.get_all("Employee Tax Exemption Proof Submission",
filters = {'docstatus': 1},
@@ -79,6 +82,9 @@
except:
pass
+ if not frappe.db.table_exists("Employee Tax Exemption Declaration"):
+ return
+
declerations = frappe.get_all("Employee Tax Exemption Declaration",
filters = {'docstatus': 1},
fields =['payroll_period', 'employee', 'company', 'income_from_other_sources']
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
index a854fa9..d93ffb7 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js
@@ -4,6 +4,14 @@
frappe.query_reports["Customer Acquisition and Loyalty"] = {
"filters": [
{
+ "fieldname": "view_type",
+ "label": __("View Type"),
+ "fieldtype": "Select",
+ "options": ["Monthly", "Territory Wise"],
+ "default": "Monthly",
+ "reqd": 1
+ },
+ {
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
@@ -24,6 +32,13 @@
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"reqd": 1
- },
- ]
-}
+ }
+ ],
+ 'formatter': function(value, row, column, data, default_formatter) {
+ value = default_formatter(value, row, column, data);
+ if (data && data.bold) {
+ value = value.bold();
+ }
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
index aa57665..88bd9c1 100644
--- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
+++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py
@@ -2,65 +2,186 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
+import calendar
import frappe
from frappe import _
-from frappe.utils import getdate, cint, cstr
-import calendar
+from frappe.utils import cint, cstr
def execute(filters=None):
- # key yyyy-mm
- new_customers_in = {}
- repeat_customers_in = {}
- customers = []
- company_condition = ""
+ common_columns = [
+ {
+ 'label': _('New Customers'),
+ 'fieldname': 'new_customers',
+ 'fieldtype': 'Int',
+ 'default': 0,
+ 'width': 125
+ },
+ {
+ 'label': _('Repeat Customers'),
+ 'fieldname': 'repeat_customers',
+ 'fieldtype': 'Int',
+ 'default': 0,
+ 'width': 125
+ },
+ {
+ 'label': _('Total'),
+ 'fieldname': 'total',
+ 'fieldtype': 'Int',
+ 'default': 0,
+ 'width': 100
+ },
+ {
+ 'label': _('New Customer Revenue'),
+ 'fieldname': 'new_customer_revenue',
+ 'fieldtype': 'Currency',
+ 'default': 0.0,
+ 'width': 175
+ },
+ {
+ 'label': _('Repeat Customer Revenue'),
+ 'fieldname': 'repeat_customer_revenue',
+ 'fieldtype': 'Currency',
+ 'default': 0.0,
+ 'width': 175
+ },
+ {
+ 'label': _('Total Revenue'),
+ 'fieldname': 'total_revenue',
+ 'fieldtype': 'Currency',
+ 'default': 0.0,
+ 'width': 175
+ }
+ ]
+ if filters.get('view_type') == 'Monthly':
+ return get_data_by_time(filters, common_columns)
+ else:
+ return get_data_by_territory(filters, common_columns)
- if filters.get("company"):
- company_condition = ' and company=%(company)s'
+def get_data_by_time(filters, common_columns):
+ # key yyyy-mm
+ columns = [
+ {
+ 'label': _('Year'),
+ 'fieldname': 'year',
+ 'fieldtype': 'Data',
+ 'width': 100
+ },
+ {
+ 'label': _('Month'),
+ 'fieldname': 'month',
+ 'fieldtype': 'Data',
+ 'width': 100
+ },
+ ]
+ columns += common_columns
- for si in frappe.db.sql("""select posting_date, customer, base_grand_total from `tabSales Invoice`
- where docstatus=1 and posting_date <= %(to_date)s
- {company_condition} order by posting_date""".format(company_condition=company_condition),
- filters, as_dict=1):
+ customers_in = get_customer_stats(filters)
- key = si.posting_date.strftime("%Y-%m")
- if not si.customer in customers:
- new_customers_in.setdefault(key, [0, 0.0])
- new_customers_in[key][0] += 1
- new_customers_in[key][1] += si.base_grand_total
- customers.append(si.customer)
- else:
- repeat_customers_in.setdefault(key, [0, 0.0])
- repeat_customers_in[key][0] += 1
- repeat_customers_in[key][1] += si.base_grand_total
+ # time series
+ from_year, from_month, temp = filters.get('from_date').split('-')
+ to_year, to_month, temp = filters.get('to_date').split('-')
- # time series
- from_year, from_month, temp = filters.get("from_date").split("-")
- to_year, to_month, temp = filters.get("to_date").split("-")
+ from_year, from_month, to_year, to_month = \
+ cint(from_year), cint(from_month), cint(to_year), cint(to_month)
- from_year, from_month, to_year, to_month = \
- cint(from_year), cint(from_month), cint(to_year), cint(to_month)
+ out = []
+ for year in range(from_year, to_year+1):
+ for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13):
+ key = '{year}-{month:02d}'.format(year=year, month=month)
+ data = customers_in.get(key)
+ new = data['new'] if data else [0, 0.0]
+ repeat = data['repeat'] if data else [0, 0.0]
+ out.append({
+ 'year': cstr(year),
+ 'month': calendar.month_name[month],
+ 'new_customers': new[0],
+ 'repeat_customers': repeat[0],
+ 'total': new[0] + repeat[0],
+ 'new_customer_revenue': new[1],
+ 'repeat_customer_revenue': repeat[1],
+ 'total_revenue': new[1] + repeat[1]
+ })
+ return columns, out
- out = []
- for year in range(from_year, to_year+1):
- for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13):
- key = "{year}-{month:02d}".format(year=year, month=month)
+def get_data_by_territory(filters, common_columns):
+ columns = [{
+ 'label': 'Territory',
+ 'fieldname': 'territory',
+ 'fieldtype': 'Link',
+ 'options': 'Territory',
+ 'width': 150
+ }]
+ columns += common_columns
- new = new_customers_in.get(key, [0,0.0])
- repeat = repeat_customers_in.get(key, [0,0.0])
+ customers_in = get_customer_stats(filters, tree_view=True)
- out.append([cstr(year), calendar.month_name[month],
- new[0], repeat[0], new[0] + repeat[0],
- new[1], repeat[1], new[1] + repeat[1]])
+ territory_dict = {}
+ for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1):
+ territory_dict.update({
+ t.name: {
+ 'parent': t.parent_territory,
+ 'is_group': t.is_group
+ }
+ })
- return [
- _("Year") + "::100",
- _("Month") + "::100",
- _("New Customers") + ":Int:100",
- _("Repeat Customers") + ":Int:100",
- _("Total") + ":Int:100",
- _("New Customer Revenue") + ":Currency:150",
- _("Repeat Customer Revenue") + ":Currency:150",
- _("Total Revenue") + ":Currency:150"
- ], out
+ depth_map = frappe._dict()
+ for name, info in territory_dict.items():
+ default = depth_map.get(info['parent']) + 1 if info['parent'] else 0
+ depth_map.setdefault(name, default)
+ data = []
+ for name, indent in depth_map.items():
+ condition = customers_in.get(name)
+ new = customers_in[name]['new'] if condition else [0, 0.0]
+ repeat = customers_in[name]['repeat'] if condition else [0, 0.0]
+ temp = {
+ 'territory': name,
+ 'parent_territory': territory_dict[name]['parent'],
+ 'indent': indent,
+ 'new_customers': new[0],
+ 'repeat_customers': repeat[0],
+ 'total': new[0] + repeat[0],
+ 'new_customer_revenue': new[1],
+ 'repeat_customer_revenue': repeat[1],
+ 'total_revenue': new[1] + repeat[1],
+ 'bold': 0 if indent else 1
+ }
+ data.append(temp)
+ loop_data = sorted(data, key=lambda k: k['indent'], reverse=True)
+
+ for ld in loop_data:
+ if ld['parent_territory']:
+ parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0]
+ for key in parent_data.keys():
+ if key not in ['indent', 'territory', 'parent_territory', 'bold']:
+ parent_data[key] += ld[key]
+
+ return columns, data, None, None, None, 1
+
+def get_customer_stats(filters, tree_view=False):
+ """ Calculates number of new and repeated customers. """
+ company_condition = ''
+ if filters.get('company'):
+ company_condition = ' and company=%(company)s'
+
+ customers = []
+ customers_in = {}
+
+ for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice`
+ where docstatus=1 and posting_date <= %(to_date)s and posting_date >= %(from_date)s
+ {company_condition} order by posting_date'''.format(company_condition=company_condition),
+ filters, as_dict=1):
+
+ key = si.territory if tree_view else si.posting_date.strftime('%Y-%m')
+ customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]})
+
+ if not si.customer in customers:
+ customers_in[key]['new'][0] += 1
+ customers_in[key]['new'][1] += si.base_grand_total
+ customers.append(si.customer)
+ else:
+ customers_in[key]['repeat'][0] += 1
+ customers_in[key]['repeat'][1] += si.base_grand_total
+
+ return customers_in
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json
index 7193261..bf9edd6 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.json
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.json
@@ -1,31 +1,31 @@
{
- "add_total_row": 0,
- "creation": "2018-09-21 12:46:29.451048",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2019-05-24 05:37:02.866139",
- "modified_by": "Administrator",
- "module": "Selling",
- "name": "Sales Analytics",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Sales Order",
- "report_name": "Sales Analytics",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2018-09-21 12:46:29.451048",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-04-30 19:49:02.303320",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Analytics",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Order",
+ "report_name": "Sales Analytics",
+ "report_type": "Script Report",
"roles": [
{
"role": "Stock User"
- },
+ },
{
"role": "Maintenance User"
- },
+ },
{
"role": "Accounts User"
- },
+ },
{
"role": "Sales Manager"
}
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index f1726ab..97d9322 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -194,6 +194,9 @@
def get_rows(self):
self.data = []
self.get_periodic_data()
+ total_row = {
+ "entity": "Total",
+ }
for entity, period_data in iteritems(self.entity_periodic_data):
row = {
@@ -207,6 +210,9 @@
row[scrub(period)] = amount
total += amount
+ if not total_row.get(scrub(period)): total_row[scrub(period)] = 0
+ total_row[scrub(period)] += amount
+
row["total"] = total
if self.filters.tree_type == "Item":
@@ -214,6 +220,8 @@
self.data.append(row)
+ self.data.append(total_row)
+
def get_rows_by_group(self):
self.get_periodic_data()
out = []
@@ -232,8 +240,10 @@
self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0)
self.entity_periodic_data[d.parent][period] += amount
total += amount
+
row["total"] = total
out = [row] + out
+
self.data = out
def get_periodic_data(self):
diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py
index 4d81a1e..7e8501d 100644
--- a/erpnext/selling/report/sales_analytics/test_analytics.py
+++ b/erpnext/selling/report/sales_analytics/test_analytics.py
@@ -34,6 +34,21 @@
expected_data = [
{
+ 'entity': 'Total',
+ 'apr_2017': 0.0,
+ 'may_2017': 0.0,
+ 'jun_2017': 2000.0,
+ 'jul_2017': 1000.0,
+ 'aug_2017': 0.0,
+ 'sep_2017': 1500.0,
+ 'oct_2017': 1000.0,
+ 'nov_2017': 0.0,
+ 'dec_2017': 0.0,
+ 'jan_2018': 0.0,
+ 'feb_2018': 2000.0,
+ 'mar_2018': 0.0
+ },
+ {
"entity": "_Test Customer 1",
"entity_name": "_Test Customer 1",
"apr_2017": 0.0,
@@ -135,6 +150,21 @@
expected_data = [
{
+ 'entity': 'Total',
+ 'apr_2017': 0.0,
+ 'may_2017': 0.0,
+ 'jun_2017': 20.0,
+ 'jul_2017': 10.0,
+ 'aug_2017': 0.0,
+ 'sep_2017': 15.0,
+ 'oct_2017': 10.0,
+ 'nov_2017': 0.0,
+ 'dec_2017': 0.0,
+ 'jan_2018': 0.0,
+ 'feb_2018': 20.0,
+ 'mar_2018': 0.0
+ },
+ {
"entity": "_Test Customer 1",
"entity_name": "_Test Customer 1",
"apr_2017": 0.0,
diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
index f2db478..e883500 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
@@ -20,31 +20,36 @@
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
- "options": "Territory"
+ "options": "Territory",
+ "width": 150
},
{
"label": _("Opportunity Amount"),
"fieldname": "opportunity_amount",
"fieldtype": "Currency",
- "options": currency
+ "options": currency,
+ "width": 150
},
{
"label": _("Quotation Amount"),
"fieldname": "quotation_amount",
"fieldtype": "Currency",
- "options": currency
+ "options": currency,
+ "width": 150
},
{
"label": _("Order Amount"),
"fieldname": "order_amount",
"fieldtype": "Currency",
- "options": currency
+ "options": currency,
+ "width": 150
},
{
"label": _("Billing Amount"),
"fieldname": "billing_amount",
"fieldtype": "Currency",
- "options": currency
+ "options": currency,
+ "width": 150
}
]
@@ -62,8 +67,7 @@
territory_opportunities = list(filter(lambda x: x.territory == territory.name, opportunities))
t_opportunity_names = []
if territory_opportunities:
- t_opportunity_names = [t.name for t in territory_opportunities]
-
+ t_opportunity_names = [t.name for t in territory_opportunities]
territory_quotations = []
if t_opportunity_names and quotations:
territory_quotations = list(filter(lambda x: x.opportunity in t_opportunity_names, quotations))
@@ -76,7 +80,7 @@
list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
t_order_names = []
if territory_orders:
- t_order_names = [t.name for t in territory_orders]
+ t_order_names = [t.name for t in territory_orders]
territory_invoices = list(filter(lambda x: x.sales_order in t_order_names, sales_invoices)) if t_order_names and sales_invoices else []
@@ -96,12 +100,12 @@
if filters.get('transaction_date'):
conditions = " WHERE transaction_date between {0} and {1}".format(
- frappe.db.escape(filters['transaction_date'][0]),
+ frappe.db.escape(filters['transaction_date'][0]),
frappe.db.escape(filters['transaction_date'][1]))
-
+
if filters.company:
if conditions:
- conditions += " AND"
+ conditions += " AND"
else:
conditions += " WHERE"
conditions += " company = %(company)s"
@@ -115,7 +119,7 @@
def get_quotations(opportunities):
if not opportunities:
return []
-
+
opportunity_names = [o.name for o in opportunities]
return frappe.db.sql("""
@@ -155,5 +159,5 @@
total = 0
for doc in doclist:
total += doc.get(amount_field, 0)
-
+
return total
diff --git a/erpnext/setup/desk_page/getting_started/getting_started.json b/erpnext/setup/desk_page/home/home.json
similarity index 96%
rename from erpnext/setup/desk_page/getting_started/getting_started.json
rename to erpnext/setup/desk_page/home/home.json
index 63d8984..63cd5c5 100644
--- a/erpnext/setup/desk_page/getting_started/getting_started.json
+++ b/erpnext/setup/desk_page/home/home.json
@@ -47,26 +47,20 @@
}
],
"category": "Modules",
- "charts": [
- {
- "chart_name": "Bank Balance",
- "label": "Bank Balance"
- }
- ],
+ "charts": [],
"creation": "2020-01-23 13:46:38.833076",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
- "icon": "",
"idx": 0,
"is_standard": 1,
- "label": "Getting Started",
- "modified": "2020-04-01 11:30:19.763099",
+ "label": "Home",
+ "modified": "2020-05-11 10:20:37.358701",
"modified_by": "Administrator",
"module": "Setup",
- "name": "Getting Started",
+ "name": "Home",
"owner": "Administrator",
"pin_to_bottom": 0,
"pin_to_top": 1,
diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py
index b37cc17..29f6c37 100644
--- a/erpnext/setup/doctype/company/test_company.py
+++ b/erpnext/setup/doctype/company/test_company.py
@@ -47,9 +47,7 @@
frappe.delete_doc("Company", "COA from Existing Company")
def test_coa_based_on_country_template(self):
- countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France",
- "Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore",
- "Brazil", "Argentina", "Hungary", "Taiwan"]
+ countries = ["Canada", "Germany", "France"]
for country in countries:
templates = get_charts_for_country(country)
diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py
index 095bd1c..808b538 100644
--- a/erpnext/setup/doctype/territory/territory.py
+++ b/erpnext/setup/doctype/territory/territory.py
@@ -3,8 +3,6 @@
from __future__ import unicode_literals
import frappe
-
-
from frappe.utils import flt
from frappe import _
@@ -14,6 +12,7 @@
nsm_parent_field = 'parent_territory'
def validate(self):
+
for d in self.get('targets') or []:
if not flt(d.target_qty) and not flt(d.target_amount):
frappe.throw(_("Either target qty or target amount is mandatory"))
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 10f2555..62c9eb1 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -470,7 +470,7 @@
"qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty),
"serial_no": item.serial_no,
"voucher_type": self.doctype,
- "voucher_no": item.name,
+ "voucher_no": self.name,
"company": self.company,
"allow_zero_valuation": item.allow_zero_valuation_rate,
})
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b4cb8ca..e1b3730 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -548,7 +548,16 @@
if not allow_zero_rate and not valuation_rate and raise_error_if_no_rate \
and cint(erpnext.is_perpetual_inventory_enabled(company)):
frappe.local.message_log = []
- frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.")
- .format(item_code, voucher_type, voucher_no))
+ form_link = frappe.utils.get_link_to_form("Item", item_code)
+
+ message = _("Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}.").format(form_link, voucher_type, voucher_no)
+ message += "<br><br>" + _(" Here are the options to proceed:")
+ solutions = "<li>" + _("If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table.").format(voucher_type) + "</li>"
+ solutions += "<li>" + _("If not, you can Cancel / Submit this entry ") + _("{0}").format(frappe.bold("after")) + _(" performing either one below:") + "</li>"
+ sub_solutions = "<ul><li>" + _("Create an incoming stock transaction for the Item.") + "</li>"
+ sub_solutions += "<li>" + _("Mention Valuation Rate in the Item master.") + "</li></ul>"
+ msg = message + solutions + sub_solutions + "</li>"
+
+ frappe.throw(msg=msg, title=_("Valuation Rate Missing"))
return valuation_rate