From 2e0abf4bb2c6bdcfd260239382efdd83a3512bef Mon Sep 17 00:00:00 2001
From: Gavin D'souza
Date: Tue, 7 Apr 2020 14:54:53 +0530
Subject: [PATCH 01/76] fix: setup invalid for grid forms
Co-authored-by: Prssanna Desai
---
frappe/public/js/frappe/form/controls/base_input.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js
index 0dbaaeb63c..685aac90a3 100644
--- a/frappe/public/js/frappe/form/controls/base_input.js
+++ b/frappe/public/js/frappe/form/controls/base_input.js
@@ -180,7 +180,12 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
this.$wrapper.toggleClass("has-error", (this.df.reqd && is_null(value)) ? true : false);
},
set_invalid: function () {
- this.$wrapper.toggleClass("has-error", (this.df.invalid ? true : false));
+ if(this.$input) {
+ this.$wrapper.toggleClass("has-error", (this.df.invalid ? true : false));
+ }
+ if(this.disp_area) {
+ $(this.disp_area).toggleClass("has-error", (this.df.invalid ? true : false));
+ }
},
set_bold: function() {
if(this.$input) {
From a23ba1fc56d4ee98bb0aa3a4025926a9eaaed299 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Tue, 7 Apr 2020 17:52:10 +0530
Subject: [PATCH 02/76] feat: data validation for child table
---
.../public/js/frappe/form/controls/base_control.js | 6 ++++--
frappe/public/js/frappe/form/controls/base_input.js | 12 +++++++-----
frappe/public/js/frappe/form/grid_row.js | 5 ++++-
frappe/public/less/form_grid.less | 8 ++++++++
4 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/frappe/public/js/frappe/form/controls/base_control.js b/frappe/public/js/frappe/form/controls/base_control.js
index c1ba41ab16..41e06537e1 100644
--- a/frappe/public/js/frappe/form/controls/base_control.js
+++ b/frappe/public/js/frappe/form/controls/base_control.js
@@ -152,12 +152,14 @@ frappe.ui.form.Control = Class.extend({
() => me.set_model_value(value),
() => {
me.set_mandatory && me.set_mandatory(value);
- me.set_invalid && me.set_invalid();
if(me.df.change || me.df.onchange) {
// onchange event specified in df
- return (me.df.change || me.df.onchange).apply(me, [e]);
+ let set = (me.df.change || me.df.onchange).apply(me, [e]);
+ me.set_invalid && me.set_invalid();
+ return set;
}
+ me.set_invalid && me.set_invalid();
}
]);
};
diff --git a/frappe/public/js/frappe/form/controls/base_input.js b/frappe/public/js/frappe/form/controls/base_input.js
index 685aac90a3..f3f04ec4d8 100644
--- a/frappe/public/js/frappe/form/controls/base_input.js
+++ b/frappe/public/js/frappe/form/controls/base_input.js
@@ -180,11 +180,13 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
this.$wrapper.toggleClass("has-error", (this.df.reqd && is_null(value)) ? true : false);
},
set_invalid: function () {
- if(this.$input) {
- this.$wrapper.toggleClass("has-error", (this.df.invalid ? true : false));
- }
- if(this.disp_area) {
- $(this.disp_area).toggleClass("has-error", (this.df.invalid ? true : false));
+ let invalid = !!this.df.invalid;
+ if (this.grid) {
+ this.$wrapper.parents('.grid-static-col').toggleClass('invalid', invalid);
+ this.$input.toggleClass('invalid', invalid);
+ this.grid_row.columns[this.df.fieldname].is_invalid = invalid;
+ } else {
+ this.$wrapper.toggleClass('has-error', invalid);
}
},
set_bold: function() {
diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js
index 0e36e671cc..1fd0fcce24 100644
--- a/frappe/public/js/frappe/form/grid_row.js
+++ b/frappe/public/js/frappe/form/grid_row.js
@@ -268,7 +268,10 @@ export default class GridRow {
if(df.reqd && !txt) {
column.addClass('error');
}
- if (df.reqd || df.bold) {
+ if (column.is_invalid) {
+ column.addClass('invalid');
+ }
+ else if ((df.reqd || df.bold)) {
column.addClass('bold');
}
}
diff --git a/frappe/public/less/form_grid.less b/frappe/public/less/form_grid.less
index 28f08635ba..102719308c 100644
--- a/frappe/public/less/form_grid.less
+++ b/frappe/public/less/form_grid.less
@@ -80,6 +80,10 @@
background-color: @extra-light-yellow;
}
+.editable-form .grid-static-col.invalid {
+ background-color: @label-danger-bg;
+}
+
.validated-form .grid-static-col.error {
background-color: @label-danger-bg;
}
@@ -150,6 +154,10 @@
}
}
+ input.form-control.invalid {
+ background-color: @label-danger-bg;
+ }
+
input[data-fieldtype="Int"], input[data-fieldtype="Float"], input[data-fieldtype="Currency"] {
text-align: right;
}
From 23b3f65b3bae0c04177c9a670e8f0af5d06be13a Mon Sep 17 00:00:00 2001
From: "Chinmay D. Pai"
Date: Thu, 16 Apr 2020 14:09:59 +0530
Subject: [PATCH 03/76] fix: limit file upload mimetype if user has no desk
access
limits file upload mimetype to jpg, png, and pdf in case the user does
not have desk access, to prevent abuse of the servers as a file storage
system
Signed-off-by: Chinmay D. Pai
---
frappe/handler.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/frappe/handler.py b/frappe/handler.py
index 6e0bf7a6be..d3fec3cf8f 100755
--- a/frappe/handler.py
+++ b/frappe/handler.py
@@ -148,12 +148,14 @@ def uploadfile():
@frappe.whitelist(allow_guest=True)
def upload_file():
+ user = None
if frappe.session.user == 'Guest':
if frappe.get_system_settings('allow_guests_to_upload_files'):
ignore_permissions = True
else:
return
else:
+ user = frappe.get_doc("User", frappe.session.user)
ignore_permissions = False
files = frappe.request.files
@@ -175,7 +177,7 @@ def upload_file():
frappe.local.uploaded_file = content
frappe.local.uploaded_filename = filename
- if frappe.session.user == 'Guest':
+ if frappe.session.user == 'Guest' or (user and not user.has_desk_access()):
import mimetypes
filetype = mimetypes.guess_type(filename)[0]
if filetype not in ['image/png', 'image/jpeg', 'application/pdf']:
From d7e9ef60b87ba8c947d2ce89f3c645691dd95bc4 Mon Sep 17 00:00:00 2001
From: "Chinmay D. Pai"
Date: Thu, 16 Apr 2020 14:31:25 +0530
Subject: [PATCH 04/76] fix: add support for more document mimetypes
Signed-off-by: Chinmay D. Pai
---
frappe/handler.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/frappe/handler.py b/frappe/handler.py
index d3fec3cf8f..9699ff6d44 100755
--- a/frappe/handler.py
+++ b/frappe/handler.py
@@ -14,6 +14,12 @@ from frappe.core.doctype.server_script.server_script_utils import run_server_scr
from werkzeug.wrappers import Response
from six import string_types
+ALLOWED_MIMETYPES = ('image/png', 'image/jpeg', 'application/pdf', 'application/msword',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet')
+
+
def handle():
"""handle request"""
validate_auth()
@@ -180,8 +186,8 @@ def upload_file():
if frappe.session.user == 'Guest' or (user and not user.has_desk_access()):
import mimetypes
filetype = mimetypes.guess_type(filename)[0]
- if filetype not in ['image/png', 'image/jpeg', 'application/pdf']:
- frappe.throw("You can only upload JPG, PNG or PDF files.")
+ if filetype not in ALLOWED_MIMETYPES:
+ frappe.throw("You can only upload JPG, PNG, PDF, or Microsoft documents.")
if method:
method = frappe.get_attr(method)
From a877fba5d938876e1484c9a63ab53f003adc50e5 Mon Sep 17 00:00:00 2001
From: Gavin D'souza
Date: Fri, 17 Apr 2020 17:44:41 +0530
Subject: [PATCH 05/76] test: set_invalid controller for child tables
---
cypress/integration/form.js | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/cypress/integration/form.js b/cypress/integration/form.js
index 9d1210ca2b..a622a66e13 100644
--- a/cypress/integration/form.js
+++ b/cypress/integration/form.js
@@ -41,4 +41,21 @@ context('Form', () => {
list_view.filter_area.filter_list.clear_filters();
});
});
+ it('validates behaviour of Data options validations in child table', () => {
+ // test email validations for set_invalid controller
+ let website_input = 'website.in';
+ let expectBackgroundColor = 'rgb(255, 220, 220)';
+
+ cy.visit('/desk#Form/Contact/New Contact 1');
+ cy.get('.frappe-control[data-fieldname="email_ids"]').as('table');
+ cy.get('@table').find('button.grid-add-row').click();
+ cy.get('.grid-body .rows [data-fieldname="email_id"]').click();
+ cy.get('@table').find('input.input-with-feedback.form-control').as('email_input');
+ cy.get('@email_input').type(website_input, { waitForAnimations: false });
+ cy.fill_field('company_name', 'Test Company');
+ cy.get('@email_input').should($div => {
+ const style = window.getComputedStyle($div[0]);
+ expect(style.backgroundColor).to.equal(expectBackgroundColor);
+ });
+ });
});
From 06ccba048fa25b22f4e89325852dd04f4945310c Mon Sep 17 00:00:00 2001
From: gavin
Date: Fri, 17 Apr 2020 17:53:59 +0530
Subject: [PATCH 06/76] style: block style fixes (via slider)
---
frappe/public/js/frappe/form/grid_row.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js
index 65c11b43bf..0b8e7e8d23 100644
--- a/frappe/public/js/frappe/form/grid_row.js
+++ b/frappe/public/js/frappe/form/grid_row.js
@@ -270,8 +270,7 @@ export default class GridRow {
}
if (column.is_invalid) {
column.addClass('invalid');
- }
- else if ((df.reqd || df.bold)) {
+ } else if (df.reqd || df.bold) {
column.addClass('bold');
}
}
From b66864b9b13631245c16cf3c803cc4cf57e2dff5 Mon Sep 17 00:00:00 2001
From: Saurabh
Date: Thu, 23 Apr 2020 18:31:53 +0530
Subject: [PATCH 07/76] fix: check message id communication while linking
communications
---
.../doctype/email_account/email_account.py | 47 ++++++++++---------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index c0a198f5e5..88a0980bee 100755
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -533,28 +533,33 @@ class EmailAccount(Document):
parent = None
in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>")
- if in_reply_to and "@{0}".format(frappe.local.site) in in_reply_to:
- # reply to a communication sent from the system
- email_queue = frappe.db.get_value('Email Queue', dict(message_id=in_reply_to), ['communication','reference_doctype', 'reference_name'])
- if email_queue:
- parent_communication, parent_doctype, parent_name = email_queue
- if parent_communication:
- communication.in_reply_to = parent_communication
+ if in_reply_to:
+ if "@{0}".format(frappe.local.site) in in_reply_to:
+ # reply to a communication sent from the system
+ email_queue = frappe.db.get_value('Email Queue', dict(message_id=in_reply_to), ['communication','reference_doctype', 'reference_name'])
+ if email_queue:
+ parent_communication, parent_doctype, parent_name = email_queue
+ if parent_communication:
+ communication.in_reply_to = parent_communication
+ else:
+ reference, domain = in_reply_to.split("@", 1)
+ parent_doctype, parent_name = 'Communication', reference
+
+ if frappe.db.exists(parent_doctype, parent_name):
+ parent = frappe._dict(doctype=parent_doctype, name=parent_name)
+
+ # set in_reply_to of current communication
+ if parent_doctype=='Communication':
+ # communication.in_reply_to = email_queue.communication
+
+ if parent.reference_name:
+ # the true parent is the communication parent
+ parent = frappe.get_doc(parent.reference_doctype,
+ parent.reference_name)
else:
- reference, domain = in_reply_to.split("@", 1)
- parent_doctype, parent_name = 'Communication', reference
-
- if frappe.db.exists(parent_doctype, parent_name):
- parent = frappe._dict(doctype=parent_doctype, name=parent_name)
-
- # set in_reply_to of current communication
- if parent_doctype=='Communication':
- # communication.in_reply_to = email_queue.communication
-
- if parent.reference_name:
- # the true parent is the communication parent
- parent = frappe.get_doc(parent.reference_doctype,
- parent.reference_name)
+ comm = frappe.db.get_value('Communication', dict(message_id=in_reply_to), ['reference_doctype', 'reference_name'], as_dict=1)
+ if comm:
+ parent = frappe._dict(doctype=comm.reference_doctype, name=comm.reference_name)
return parent
From a9be0758d4517d2b1b042ddfeefdc71ff75b979c Mon Sep 17 00:00:00 2001
From: Saurabh
Date: Thu, 23 Apr 2020 18:46:15 +0530
Subject: [PATCH 08/76] feat: compare meesage-id only with the communications
created between last 30 days
---
frappe/email/doctype/email_account/email_account.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index 88a0980bee..082b16c17a 100755
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -10,7 +10,7 @@ import socket
import time
from frappe import _
from frappe.model.document import Document
-from frappe.utils import validate_email_address, cint, get_datetime, DATE_FORMAT, strip, comma_or, sanitize_html
+from frappe.utils import validate_email_address, cint, get_datetime, DATE_FORMAT, strip, comma_or, sanitize_html, add_days
from frappe.utils.user import is_system_user
from frappe.utils.jinja import render_template
from frappe.email.smtp import SMTPServer
@@ -557,7 +557,11 @@ class EmailAccount(Document):
parent = frappe.get_doc(parent.reference_doctype,
parent.reference_name)
else:
- comm = frappe.db.get_value('Communication', dict(message_id=in_reply_to), ['reference_doctype', 'reference_name'], as_dict=1)
+ comm = frappe.db.get_value('Communication',
+ dict(
+ message_id=in_reply_to,
+ creation=['>=', add_days(get_datetime(), -30)]),
+ ['reference_doctype', 'reference_name'], as_dict=1)
if comm:
parent = frappe._dict(doctype=comm.reference_doctype, name=comm.reference_name)
From e7a163168b3f463d3ff69f9a906fc78f59b7b8c4 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sat, 18 Apr 2020 15:59:02 +0530
Subject: [PATCH 09/76] feat: Dashboard view for doctypes
---
frappe/desk/doctype/dashboard/dashboard.js | 8 +
.../dashboard_chart/dashboard_chart.js | 7 +
.../dashboard_chart/dashboard_chart.json | 13 +-
.../dashboard_chart/dashboard_chart.py | 22 +-
frappe/desk/doctype/number_card/__init__.py | 0
.../desk/doctype/number_card/number_card.js | 120 +++++
.../desk/doctype/number_card/number_card.json | 124 +++++
.../desk/doctype/number_card/number_card.py | 48 ++
.../doctype/number_card/test_number_card.py | 10 +
frappe/public/build.json | 2 +
frappe/public/js/frappe/form/layout.js | 11 +-
frappe/public/js/frappe/list/base_list.js | 2 +-
.../public/js/frappe/list/list_sidebar.html | 2 +
.../frappe/views/dashboard/dashboard_view.js | 453 ++++++++++++++++++
.../public/js/frappe/widgets/base_widget.js | 28 +-
.../public/js/frappe/widgets/chart_widget.js | 78 +--
frappe/public/js/frappe/widgets/new_widget.js | 14 +-
.../js/frappe/widgets/number_card_widget.js | 143 ++++++
frappe/public/js/frappe/widgets/utils.js | 12 +-
.../public/js/frappe/widgets/widget_dialog.js | 187 +++++++-
.../public/js/frappe/widgets/widget_group.js | 13 +
frappe/public/less/dashboard_view.less | 40 ++
frappe/public/less/desktop.less | 50 ++
23 files changed, 1301 insertions(+), 86 deletions(-)
create mode 100644 frappe/desk/doctype/number_card/__init__.py
create mode 100644 frappe/desk/doctype/number_card/number_card.js
create mode 100644 frappe/desk/doctype/number_card/number_card.json
create mode 100644 frappe/desk/doctype/number_card/number_card.py
create mode 100644 frappe/desk/doctype/number_card/test_number_card.py
create mode 100644 frappe/public/js/frappe/views/dashboard/dashboard_view.js
create mode 100644 frappe/public/js/frappe/widgets/number_card_widget.js
create mode 100644 frappe/public/less/dashboard_view.less
diff --git a/frappe/desk/doctype/dashboard/dashboard.js b/frappe/desk/doctype/dashboard/dashboard.js
index 19ce8eb1fd..dc2991a346 100644
--- a/frappe/desk/doctype/dashboard/dashboard.js
+++ b/frappe/desk/doctype/dashboard/dashboard.js
@@ -4,5 +4,13 @@
frappe.ui.form.on('Dashboard', {
refresh: function(frm) {
frm.add_custom_button(__("Show Dashboard"), () => frappe.set_route('dashboard', frm.doc.name));
+
+ frm.set_query("chart", "charts", function(doc, cdt, cdn) {
+ return {
+ filters: {
+ is_standard: 1
+ }
+ };
+ });
}
});
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
index 275028fc15..b869f60ce5 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
@@ -9,6 +9,12 @@ frappe.ui.form.on('Dashboard Chart', {
frm.add_fetch('source', 'timeseries', 'timeseries');
},
+ before_save: function(frm) {
+ if (frm.is_new() && (frappe.user.has_role('System Manager') || frappe.user.has_role('Dashboard Manager'))) {
+ frm.doc.is_standard = 1;
+ }
+ },
+
refresh: function(frm) {
frm.chart_filters = null;
frm.add_custom_button('Add Chart to Dashboard', () => {
@@ -348,6 +354,7 @@ frappe.ui.form.on('Dashboard Chart', {
on_change: () => {},
});
+ console.log('filters', filters);
frm.filter_group.add_filters_to_filter_group(filters);
}
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json
index cd32292783..7530d70fdc 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json
@@ -35,7 +35,8 @@
"color",
"custom_options",
"section_break_10",
- "last_synced_on"
+ "last_synced_on",
+ "is_standard"
],
"fields": [
{
@@ -220,10 +221,18 @@
"fieldname": "custom_options",
"fieldtype": "Code",
"label": "Custom Options"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_standard",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Is Standard",
+ "permlevel": 1
}
],
"links": [],
- "modified": "2020-04-20 23:49:11.389909",
+ "modified": "2020-04-17 18:11:33.486037",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard Chart",
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
index 36a75bd9d5..8242e3752d 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
@@ -92,20 +92,28 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
return chart_config
@frappe.whitelist()
-def create_report_chart(args):
+def create_dashboard_chart(args):
args = frappe.parse_json(args)
- _doc = frappe.new_doc('Dashboard Chart')
+ doc = frappe.new_doc('Dashboard Chart')
+ roles = frappe.get_roles(frappe.session.user)
+ if 'Sytem Manager' in roles or 'Dashboard Manager' in roles:
+ doc.is_standard = 1
- _doc.update(args)
+ doc.update(args)
- if (args.get("custom_options")):
- _doc.custom_options = json.dumps(args.get("custom_options"))
+ if args.get('custom_options'):
+ doc.custom_options = json.dumps(args.get('custom_options'))
if frappe.db.exists('Dashboard Chart', args.chart_name):
args.chart_name = append_number_if_name_exists('Dashboard Chart', args.chart_name)
- _doc.chart_name = args.chart_name
- _doc.insert(ignore_permissions=True)
+ doc.chart_name = args.chart_name
+ doc.insert(ignore_permissions=True)
+ return doc
+
+@frappe.whitelist()
+def create_report_chart(args):
+ create_dashboard_chart()
if args.dashboard:
add_chart_to_dashboard(json.dumps(args))
diff --git a/frappe/desk/doctype/number_card/__init__.py b/frappe/desk/doctype/number_card/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/desk/doctype/number_card/number_card.js b/frappe/desk/doctype/number_card/number_card.js
new file mode 100644
index 0000000000..2bf99b9107
--- /dev/null
+++ b/frappe/desk/doctype/number_card/number_card.js
@@ -0,0 +1,120 @@
+// Copyright (c) 2020, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Number Card', {
+ refresh: function(frm) {
+ frm.set_df_property("filters_section", "hidden", 1);
+ frm.trigger('set_options');
+ frm.trigger('render_filters_table');
+ },
+
+ before_save: function(frm) {
+ if (frm.is_new() && (frappe.user.has_role('System Manager') || frappe.user.has_role('Dashboard Manager'))) {
+ frm.doc.is_standard = 1;
+ }
+ },
+
+ document_type: function(frm) {
+ frm.set_query('document_type', function() {
+ return {
+ filters: {
+ 'issingle': false
+ }
+ }
+ });
+ frm.set_value('filters_json', '[]');
+ frm.set_value('aggregate_function_based_on', '');
+ if (frm.doc.document_type) {
+ frm.trigger('set_options');
+ }
+ },
+
+ set_options: function(frm) {
+ let aggregate_based_on_fields = [];
+ const doctype = frm.doc.document_type;
+
+ frappe.model.with_doctype(doctype, () => {
+ frappe.get_meta(doctype).fields.map(df => {
+ if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
+ aggregate_based_on_fields.push({label: df.label, value: df.fieldname});
+ }
+ });
+
+ frm.set_df_property('aggregate_function_based_on', 'options', aggregate_based_on_fields);
+ });
+ },
+
+ render_filters_table: function(frm) {
+ frm.set_df_property("filters_section", "hidden", 0);
+
+ let wrapper = $(frm.get_field('filters_json').wrapper).empty();
+ frm.filter_table = $(`
+
+
+ | ${__('Filter')} |
+ ${__('Condition')} |
+ ${__('Value')} |
+
+
+
+
`).appendTo(wrapper);
+
+ frm.filters = JSON.parse(frm.doc.filters_json || '[]');
+
+ frm.trigger('set_filters_in_table');
+
+ frm.filter_table.on('click', () => {
+ let dialog = new frappe.ui.Dialog({
+ title: __('Set Filters'),
+ fields: [{
+ fieldtype: 'HTML',
+ fieldname: 'filter_area',
+ }],
+ primary_action: function() {
+ let values = this.get_values();
+ if (values) {
+ this.hide();
+ frm.filters = frm.filter_group.get_filters();
+ frm.set_value('filters_json', JSON.stringify(frm.filters));
+ frm.trigger('set_filters_in_table');
+ }
+ },
+ primary_action_label: "Set"
+ });
+
+ frappe.dashboards.filters_dialog = dialog;
+
+ frm.filter_group = new frappe.ui.FilterGroup({
+ parent: dialog.get_field('filter_area').$wrapper,
+ doctype: frm.doc.document_type,
+ on_change: () => {},
+ });
+
+ frm.filter_group.add_filters_to_filter_group(frm.filters);
+
+ dialog.show();
+ dialog.set_values(frm.filters);
+ });
+
+ },
+
+ set_filters_in_table: function(frm) {
+ if (!frm.filters.length) {
+ const filter_row = $(`|
+ ${__("Click to Set Filters")} |
`);
+ frm.filter_table.find('tbody').html(filter_row);
+ } else {
+ let filter_rows = '';
+ frm.filters.forEach(filter => {
+ filter_rows +=
+ `
+ | ${filter[1]} |
+ ${filter[2] || ""} |
+ ${filter[3]} |
+
`;
+
+ });
+ frm.filter_table.find('tbody').html(filter_rows);
+ }
+ }
+});
diff --git a/frappe/desk/doctype/number_card/number_card.json b/frappe/desk/doctype/number_card/number_card.json
new file mode 100644
index 0000000000..222e4ed942
--- /dev/null
+++ b/frappe/desk/doctype/number_card/number_card.json
@@ -0,0 +1,124 @@
+{
+ "actions": [],
+ "autoname": "CARD.#####",
+ "creation": "2020-04-15 18:06:39.444683",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "label",
+ "function",
+ "column_break_2",
+ "document_type",
+ "aggregate_function_based_on",
+ "filters_section",
+ "filters_json",
+ "color",
+ "is_standard"
+ ],
+ "fields": [
+ {
+ "fieldname": "document_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Document Type",
+ "options": "DocType",
+ "reqd": 1
+ },
+ {
+ "depends_on": "eval: doc.document_type",
+ "fieldname": "function",
+ "fieldtype": "Select",
+ "label": "Function",
+ "options": "Count\nSum\nAverage\nMinimum\nMaximum",
+ "reqd": 1
+ },
+ {
+ "depends_on": "eval: doc.function !== 'Count'",
+ "fieldname": "aggregate_function_based_on",
+ "fieldtype": "Select",
+ "label": "Aggregate Function Based On",
+ "mandatory_depends_on": "eval: doc.function !== 'Count'"
+ },
+ {
+ "fieldname": "filters_json",
+ "fieldtype": "Code",
+ "label": "Filters JSON",
+ "options": "JSON"
+ },
+ {
+ "fieldname": "label",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Label",
+ "reqd": 1
+ },
+ {
+ "fieldname": "color",
+ "fieldtype": "Color",
+ "label": "Color"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_standard",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Is Standard",
+ "permlevel": 1
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "filters_section",
+ "fieldtype": "Section Break",
+ "label": "Filters Section"
+ }
+ ],
+ "links": [],
+ "modified": "2020-04-18 14:12:06.949445",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Number Card",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Dashboard Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "All",
+ "share": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "label",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
new file mode 100644
index 0000000000..509995392c
--- /dev/null
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.model.naming import append_number_if_name_exists
+
+class NumberCard(Document):
+ pass
+
+
+@frappe.whitelist()
+def get_result(doc):
+ doc = frappe.parse_json(doc)
+ fields = []
+ sql_function_map = {
+ 'Count': 'count',
+ 'Sum': 'sum',
+ 'Average': 'avg',
+ 'Minimum': 'min',
+ 'Maximum': 'max'
+ }
+
+ function = sql_function_map[doc.function]
+
+ if function == 'count':
+ fields = ['{function}(*) as result'.format(function=function)]
+ else:
+ fields = ['{function}({based_on}) as result'.format(function=function, based_on=doc.aggregate_function_based_on)]
+
+ filters = frappe.parse_json(doc.filters_json)
+ number = frappe.db.get_all(doc.document_type, fields = fields, filters = filters)[0]['result']
+ number = round(number, 2) if isinstance(number, float) else number
+
+ return number
+
+@frappe.whitelist()
+def create_number_card(args):
+ args = frappe.parse_json(args)
+ doc = frappe.new_doc('Number Card')
+ roles = frappe.get_roles(frappe.session.user)
+ if 'Sytem Manager' in roles or 'Dashboard Manager' in roles:
+ doc.is_standard = 1
+ doc.update(args)
+ doc.insert(ignore_permissions=True)
+ return doc
\ No newline at end of file
diff --git a/frappe/desk/doctype/number_card/test_number_card.py b/frappe/desk/doctype/number_card/test_number_card.py
new file mode 100644
index 0000000000..4aa1ecf282
--- /dev/null
+++ b/frappe/desk/doctype/number_card/test_number_card.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestNumberCard(unittest.TestCase):
+ pass
diff --git a/frappe/public/build.json b/frappe/public/build.json
index 7f55924a6b..df3d71f537 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -104,6 +104,7 @@
"public/less/form.less",
"public/less/mobile.less",
"public/less/kanban.less",
+ "public/less/dashboard_view.less",
"public/less/controls.less",
"public/less/chat.less",
"public/less/filters.less",
@@ -296,6 +297,7 @@
"public/js/frappe/views/gantt/gantt_view.js",
"public/js/frappe/views/calendar/calendar.js",
+ "public/js/frappe/views/dashboard/dashboard_view.js",
"public/js/frappe/views/image/image_view.js",
"public/js/frappe/views/kanban/kanban_view.js",
"public/js/frappe/views/inbox/inbox_view.js",
diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js
index 8a88ee0c0d..5aeb29b1ed 100644
--- a/frappe/public/js/frappe/form/layout.js
+++ b/frappe/public/js/frappe/form/layout.js
@@ -498,11 +498,18 @@ frappe.ui.form.Layout = Class.extend({
},
set_dependant_property: function(condition, fieldname, property) {
let set_property = this.evaluate_depends_on_value(condition);
+ let form_obj;
+
if (this.frm) {
+ form_obj = this.frm;
+ } else if (this.is_dialog) {
+ form_obj = this;
+ }
+ if (form_obj) {
if (set_property) {
- this.frm.set_df_property(fieldname, property, 1);
+ form_obj.set_df_property(fieldname, property, 1);
} else {
- this.frm.set_df_property(fieldname, property, 0);
+ form_obj.set_df_property(fieldname, property, 0);
}
}
},
diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js
index 0aae8b361f..76bda11f0a 100644
--- a/frappe/public/js/frappe/list/base_list.js
+++ b/frappe/public/js/frappe/list/base_list.js
@@ -686,5 +686,5 @@ class FilterArea {
}
// utility function to validate view modes
-frappe.views.view_modes = ['List', 'Gantt', 'Kanban', 'Calendar', 'Image', 'Inbox', 'Report'];
+frappe.views.view_modes = ['List', 'Gantt', 'Kanban', 'Calendar', 'Image', 'Inbox', 'Report', 'Dashboard'];
frappe.views.is_valid = view_mode => frappe.views.view_modes.includes(view_mode);
diff --git a/frappe/public/js/frappe/list/list_sidebar.html b/frappe/public/js/frappe/list/list_sidebar.html
index 880a91cf81..37f0dafb96 100644
--- a/frappe/public/js/frappe/list/list_sidebar.html
+++ b/frappe/public/js/frappe/list/list_sidebar.html
@@ -26,6 +26,8 @@
{%= __("List") %}
+
+ {%= __("Dashboard") %}
{%= __("Images") %}
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
new file mode 100644
index 0000000000..69bcc0ac74
--- /dev/null
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -0,0 +1,453 @@
+frappe.provide('frappe.views');
+
+frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
+ get view_name() {
+ return 'Dashboard';
+ }
+
+ setup_defaults() {
+ return super.setup_defaults()
+ .then(() => {
+ this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
+ });
+ }
+
+ render() {
+
+ }
+
+ setup_view() {
+ if (this.chart_group || this.number_card_group) {
+ return;
+ }
+
+ this.setup_dashboard_page();
+ this.setup_dashboard_customization();
+ this.make_dashboard();
+ }
+
+ setup_dashboard_customization() {
+ this.$customize = this.$chart_header.find('.customize-dashboard');
+ this.$save_or_discard = this.$chart_header.find('.customize-options');
+ }
+
+ setup_dashboard_page() {
+ const dashboard_name = __('{0} Dashboard', [this.doctype]);
+ const chart_wrapper_html = ``;
+
+ this.$frappe_list.html(chart_wrapper_html);
+ this.page.clear_secondary_action();
+ this.$dashboard_page = this.$page.find('.layout-main-section-wrapper').addClass('dashboard-page');
+ this.$page.find('.page-form').empty().html(
+ ``);
+
+ this.$dashboard_wrapper = this.$page.find('.dashboard-view');
+ this.$chart_header = this.$page.find('.dashboard-header');
+
+ frappe.utils.bind_actions_with_object(this.$dashboard_page, this);
+ }
+
+ make_dashboard() {
+ if (this.dashboard_settings) {
+ this.charts = this.dashboard_settings.charts;
+ this.number_cards = this.dashboard_settings.number_cards;
+ this.render_dashboard();
+ } else {
+ frappe.run_serially([
+ () => this.fetch_doctype_number_cards().then(cards => {
+ this.number_cards = cards;
+ }),
+ () => this.fetch_doctype_charts().then(charts => {
+ this.charts = charts;
+ }),
+ () => this.render_dashboard()
+ ]);
+ }
+ }
+
+
+ get_field_options() {
+ let date_fields = [
+ {label: __('Created On'), value: 'creation'},
+ {label: __('Last Modified On'), value: 'modified'}
+ ];
+ let value_fields = [];
+ let group_by_fields = [];
+ let aggregate_function_fields = [];
+
+ frappe.get_meta(this.doctype).fields.map(df => {
+ if (['Date', 'Datetime'].includes(df.fieldtype)) {
+ date_fields.push({label: df.label, value: df.fieldname});
+ }
+ if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
+ value_fields.push({label: df.label, value: df.fieldname});
+ aggregate_function_fields.push({label: df.label, value: df.fieldname});
+ }
+ if (['Link', 'Select'].includes(df.fieldtype)) {
+ group_by_fields.push({label: df.label, value: df.fieldname});
+ }
+ });
+ return {
+ date_fields: date_fields,
+ value_fields: value_fields,
+ group_by_fields: group_by_fields,
+ aggregate_function_fields: aggregate_function_fields
+ }
+ }
+
+ render_number_cards() {
+ this.number_card_group = new frappe.widget.WidgetGroup({
+ container: this.$dashboard_wrapper,
+ type: "number_card",
+ columns: 3,
+ options: {
+ allow_sorting: true,
+ allow_create: true,
+ allow_delete: true,
+ allow_hiding: true,
+ },
+ default_values: {doctype: this.doctype},
+ widgets: this.number_cards || [],
+ in_customize_mode: true,
+ });
+
+ this.in_customize_mode && this.number_card_group.customize();
+ }
+
+ render_dashboard_charts() {
+ this.chart_group = new frappe.widget.WidgetGroup({
+ container: this.$dashboard_wrapper,
+ type: "chart",
+ columns: 2,
+ options: {
+ allow_sorting: true,
+ allow_create: true,
+ allow_delete: true,
+ allow_hiding: true,
+ },
+ custom_dialog: () => this.show_add_chart_dialog(),
+ widgets: this.charts,
+ in_customize_mode: this.in_customize_mode || false,
+ });
+
+ this.in_customize_mode && this.chart_group.customize();
+ this.chart_group.container.find('.widget-group-head').hide();
+ }
+
+ render_dashboard() {
+ this.$dashboard_wrapper.empty();
+ frappe.dashboard_utils.get_dashboard_settings().then(settings => {
+ this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
+ this.charts.map(chart => {
+ chart.label = chart.name;
+ chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
+ });
+ this.render_dashboard_charts();
+ });
+ this.render_number_cards();
+
+ if (!this.charts.length && !this.number_cards.length) {
+ this.render_empty_state();
+ }
+ }
+
+ render_empty_state() {
+ const no_result_message_html =
+ `${__("You haven't added any Dashboard Charts or Number Cards yet.")}
+
${__("Click On Customize to add your first widget")}
`;
+
+ const customize_button =
+ ``;
+
+ const empty_state_image = '/assets/frappe/images/ui-states/empty.png';
+
+ const empty_state_html = `
+
+

+
+ ${no_result_message_html}
+ ${customize_button}
+
`;
+
+ this.$dashboard_wrapper.append(empty_state_html);
+
+ this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
+ }
+
+ fetch_doctype_charts() {
+ return frappe.db.get_list('Dashboard Chart', {
+ filters: {
+ chart_type: ['in', ['Count', 'Sum', 'Group By']],
+ document_type: this.doctype,
+ is_standard: true
+ },
+ fields: ['*']
+ }).then(charts => {
+ return charts;
+ });
+ }
+
+ fetch_doctype_number_cards() {
+ return frappe.db.get_list('Number Card', {
+ filters: {
+ document_type: this.doctype,
+ is_standard: true
+ },
+ fields: ['*']
+ }).then(cards => {
+ return cards;
+ });
+ }
+
+ customize() {
+ if (this.in_customize_mode) {
+ return;
+ }
+
+
+ if (this.$empty_state) {
+ this.$empty_state.remove();
+ }
+
+ this.toggle_customize(true);
+ this.chart_group.in_customize_mode = true;
+ this.chart_group.customize();
+ this.number_cards.in_customize_mode = true;
+ this.number_card_group.customize();
+ }
+
+ save_dashboard_customization() {
+ this.toggle_customize(false);
+
+ const number_card_config = this.number_card_group.get_widget_config();
+ const number_cards = [];
+ number_card_config.order.map(card_name => {
+ number_cards.push(number_card_config.widgets[card_name]);
+ });
+
+ const chart_config = this.chart_group.get_widget_config();
+ const charts = [];
+ chart_config.order.map(chart_name => {
+ charts.push(chart_config.widgets[chart_name]);
+ });
+
+ this.dashboard_settings = {
+ charts: charts,
+ number_cards: number_cards,
+ };
+
+ frappe.model.user_settings.save(this.doctype, 'dashboard_settings', this.dashboard_settings);
+ this.make_dashboard();
+ }
+
+ discard_dashboard_customization() {
+ this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
+ this.toggle_customize(false);
+ this.render_dashboard();
+ }
+
+ reset_dashboard_customization() {
+ this.dashboard_settings = null;
+ frappe.model.user_settings.save(
+ this.doctype, 'dashboard_settings', this.dashboard_settings
+ ).then(()=> this.make_dashboard());
+ this.toggle_customize(false);
+ }
+
+ toggle_customize(show) {
+ this.$customize.toggle(!show);
+ this.$save_or_discard.toggle(show);
+ this.in_customize_mode = show;
+ }
+
+ show_add_chart_dialog() {
+ let fields = this.get_field_options();
+ const dialog = new frappe.ui.Dialog({
+ title: __(`Add a ${this.doctype} Chart`),
+ fields: [
+ {
+ fieldname: 'new_or_existing',
+ fieldtype: 'Select',
+ label: 'Choose an existing chart or create a new chart',
+ options: ['New Chart', 'Existing Chart'],
+ reqd: 1,
+ },
+ {
+ label: 'Chart',
+ fieldname: 'chart',
+ fieldtype: 'Link',
+ get_query: () => {
+ return {
+ filters: {
+ document_type: this.doctype,
+ is_standard: 1,
+ }
+ };
+ },
+ options: 'Dashboard Chart',
+ depends_on: 'eval: doc.new_or_existing == "Existing Chart"'
+ },
+ {
+ fieldname: 'sb_2',
+ fieldtype: 'Section Break',
+ depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ label: 'Chart Label',
+ fieldname: 'label',
+ fieldtype: 'Data',
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ fieldname: 'cb_1',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Chart Type',
+ fieldname: 'chart_type',
+ fieldtype: 'Select',
+ options: ['Time Series', 'Group By'],
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"',
+ },
+ {
+ fieldname: 'sb_2',
+ fieldtype: 'Section Break',
+ label: 'Chart Config',
+ depends_on: 'eval: doc.chart_type == "Time Series" && doc.new_or_existing == "New Chart"',
+ },
+ {
+ label: 'Function',
+ fieldname: 'chart_function',
+ fieldtype: 'Select',
+ options: ['Count', 'Sum', 'Average'],
+ default: 'Count',
+ },
+ {
+ label: 'Timespan',
+ fieldtype: 'Select',
+ fieldname: 'timespan',
+ depends_on: 'eval: doc.chart_type == "Time Series"',
+ options: ['Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
+ default: 'Last Year',
+ },
+ {
+ fieldname: 'cb_2',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Value Based On',
+ fieldtype: 'Select',
+ fieldname: 'based_on',
+ options: fields.value_fields,
+ depends_on: 'eval: doc.chart_function=="Sum"'
+ },
+ {
+ label: 'Time Series Based On',
+ fieldtype: 'Select',
+ fieldname: 'based_on',
+ options: fields.date_fields,
+ mandatory_depends_on: 'eval: doc.chart_type == "Time Series"'
+ },
+ {
+ label: 'Time Interval',
+ fieldname: 'time_interval',
+ fieldtype: 'Select',
+ depends_on: 'eval: doc.chart_type == "Time Series"',
+ options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
+ default: 'Monthly'
+ },
+ {
+ fieldname: 'sb_2',
+ fieldtype: 'Section Break',
+ label: 'Chart Config',
+ depends_on: 'eval: doc.chart_type == "Group By" && doc.new_or_existing == "New Chart"',
+ },
+ {
+ label: 'Group By Type',
+ fieldname: 'group_by_type',
+ fieldtype: 'Select',
+ options: ['Count', 'Sum', 'Average'],
+ default: 'Count',
+ },
+ {
+ label: 'Aggregate Function Based On',
+ fieldtype: 'Select',
+ fieldname: 'aggregate_function_based_on',
+ options: fields.aggregate_function_fields,
+ depends_on: 'eval: ["Sum", "Avergage"].includes(doc.group_by_type)',
+ },
+ {
+ fieldname: 'cb_2',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Group By Based On',
+ fieldtype: 'Select',
+ fieldname: 'group_by_based_on',
+ options: fields.group_by_fields,
+ default: 'Last Year',
+ },
+ {
+ label: 'Number of Groups',
+ fieldtype: 'Int',
+ fieldname: 'number_of_groups',
+ default: 0,
+ },
+ {
+ fieldname: 'sb_3',
+ fieldtype: 'Section Break',
+ depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ label: 'Chart Type',
+ fieldname: 'type',
+ fieldtype: 'Select',
+ options: ['Line', 'Bar', 'Percentage', 'Pie'],
+ depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ fieldname: 'cb_1',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Chart Color',
+ fieldname: 'color',
+ fieldtype: 'Color',
+ depends_on: 'eval: doc.new_or_existing == "New Chart"',
+ },
+ ],
+ primary_action_label: __('Add'),
+ primary_action: (values) => {
+ let chart = values;
+ if (chart.new_or_existing == 'New Chart') {
+ chart.chart_name = chart.label;
+ chart.chart_type = chart.chart_type == 'Time Series'? chart.chart_function: chart.chart_type;
+ chart.document_type = this.doctype;
+ chart.filters_json = '[]';
+ frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart', {'args': chart}).then((doc)=> {
+ this.chart_group.new_widget.on_create({'chart_name': doc.chart_name, 'name': doc.chart_name, 'label': chart.label});
+ });
+ } else {
+ this.chart_group.new_widget.on_create({'chart_name': chart.chart, 'label': chart.chart, 'name': chart.chart});
+ }
+ dialog.hide();
+ }
+ });
+ dialog.show();
+ }
+
+}
diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js
index 730157ed4c..7b3770ec9e 100644
--- a/frappe/public/js/frappe/widgets/base_widget.js
+++ b/frappe/public/js/frappe/widgets/base_widget.js
@@ -72,12 +72,12 @@ export default class Widget {
make_widget() {
this.widget = $(`
+ }" data-widget-name="${this.name ? this.name : ''}">
-
+
@@ -91,8 +91,11 @@ export default class Widget {
this.refresh();
}
- set_title() {
- this.title_field[0].innerHTML = this.label;
+ set_title(max_chars) {
+ this.title_field[0].innerHTML = max_chars? frappe.ellipsis(this.label, max_chars): this.label;
+ if (max_chars) {
+ this.title_field[0].setAttribute('title', this.label);
+ }
}
add_custom_button(html, action, class_name = "") {
@@ -106,13 +109,20 @@ export default class Widget {
button.appendTo(this.action_area);
}
- delete() {
- this.widget.addClass("zoomOutDelete");
- // wait for animation
- setTimeout(() => {
+ delete(animate=true) {
+ let remove_widget = () => {
this.widget.remove();
this.options.on_delete && this.options.on_delete(this.name);
- }, 300);
+ }
+ if (animate) {
+ this.widget.addClass("zoomOutDelete");
+ // wait for animation
+ setTimeout(() => {
+ remove_widget();
+ }, 300);
+ } else {
+ remove_widget();
+ }
}
edit() {
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index 313b0ea293..f0388dda19 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -6,7 +6,7 @@ frappe.provide("frappe.dashboards.chart_sources");
export default class ChartWidget extends Widget {
constructor(opts) {
super(opts);
- this.height = 240;
+ this.height = this.height || 240;
}
get_config() {
@@ -14,12 +14,13 @@ export default class ChartWidget extends Widget {
name: this.name,
chart_name: this.chart_name,
label: this.label,
+ hidden: this.hidden
};
}
refresh() {
delete this.dashboard_chart;
- this.set_title();
+ this.set_title(15);
this.set_body();
this.make_chart();
}
@@ -29,7 +30,6 @@ export default class ChartWidget extends Widget {
if (this.width == "Full") {
this.widget.addClass("full-width");
}
- this.make_chart();
}
setup_container() {
@@ -40,7 +40,7 @@ export default class ChartWidget extends Widget {
"Loading..."
)}
`
);
- this.loading.hide().appendTo(this.body);
+ this.loading.appendTo(this.body);
this.empty = $(
`
${__(
@@ -69,6 +69,12 @@ export default class ChartWidget extends Widget {
make_chart() {
this.get_settings().then(() => {
+ if (!this.settings) {
+ this.deleted = true;
+ this.widget.remove();
+ return;
+ }
+
if (!this.chart_settings) {
this.chart_settings = {};
}
@@ -536,40 +542,42 @@ export default class ChartWidget extends Widget {
return frappe.model
.with_doc("Dashboard Chart", this.chart_name)
.then(chart_doc => {
- this.chart_doc = chart_doc;
- if (this.chart_doc.chart_type == "Custom") {
- // custom source
- if (
- frappe.dashboards.chart_sources[this.chart_doc.source]
- ) {
- this.settings =
- frappe.dashboards.chart_sources[
- this.chart_doc.source
- ];
+ if (chart_doc) {
+ this.chart_doc = chart_doc;
+ if (this.chart_doc.chart_type == "Custom") {
+ // custom source
+ if (
+ frappe.dashboards.chart_sources[this.chart_doc.source]
+ ) {
+ this.settings =
+ frappe.dashboards.chart_sources[
+ this.chart_doc.source
+ ];
+ return Promise.resolve();
+ } else {
+ const method =
+ "frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config";
+ return frappe
+ .xcall(method, { name: this.chart_doc.source })
+ .then(config => {
+ frappe.dom.eval(config);
+ this.settings =
+ frappe.dashboards.chart_sources[
+ this.chart_doc.source
+ ];
+ });
+ }
+ } else if (this.chart_doc.chart_type == "Report") {
+ this.settings = {
+ method: "frappe.desk.query_report.run"
+ };
return Promise.resolve();
} else {
- const method =
- "frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config";
- return frappe
- .xcall(method, { name: this.chart_doc.source })
- .then(config => {
- frappe.dom.eval(config);
- this.settings =
- frappe.dashboards.chart_sources[
- this.chart_doc.source
- ];
- });
+ this.settings = {
+ method: "frappe.desk.doctype.dashboard_chart.dashboard_chart.get"
+ };
+ return Promise.resolve();
}
- } else if (this.chart_doc.chart_type == "Report") {
- this.settings = {
- method: "frappe.desk.query_report.run"
- };
- return Promise.resolve();
- } else {
- this.settings = {
- method: "frappe.desk.doctype.dashboard_chart.dashboard_chart.get"
- };
- return Promise.resolve();
}
});
}
diff --git a/frappe/public/js/frappe/widgets/new_widget.js b/frappe/public/js/frappe/widgets/new_widget.js
index 787cb3a79c..66fa88b761 100644
--- a/frappe/public/js/frappe/widgets/new_widget.js
+++ b/frappe/public/js/frappe/widgets/new_widget.js
@@ -19,18 +19,25 @@ export default class NewWidget {
get_title() {
// DO NOT REMOVE: Comment to load translation
// __("New Chart") __("New Shortcut")
- return __(`New ${frappe.utils.to_title_case(this.type)}`);
+ return __(`New ${frappe.model.unscrub(this.type)}`);
}
make_widget() {
- this.widget = $(`
+ const new_widget_class = `new-${frappe.scrub(frappe.model.unscrub(this.type), '-')}-widget`;
+ this.widget = $(`
+ ${this.get_title()}
`);
this.body = this.widget;
}
setup_events() {
- this.widget.on("click", () => this.open_dialog());
+ this.widget.on("click", () => {
+ if (!this.custom_dialog) {
+ this.open_dialog();
+ } else {
+ this.custom_dialog();
+ }
+ });
}
open_dialog() {
@@ -40,6 +47,7 @@ export default class NewWidget {
label: this.label,
type: this.type,
values: false,
+ default_values: this.default_values,
primary_action: this.on_create,
});
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
new file mode 100644
index 0000000000..3de6a0c0eb
--- /dev/null
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -0,0 +1,143 @@
+import Widget from "./base_widget.js";
+import { go_to_list_with_filters } from "./utils";
+
+export default class NumberCardWidget extends Widget {
+ constructor(opts) {
+ super(opts);
+ }
+
+ get_config() {
+ return {
+ name: this.name,
+ label: this.label,
+ color: this.color,
+ hidden: this.hidden,
+ };
+ }
+
+ refresh() {
+ this.set_body();
+ }
+
+ set_body() {
+ this.widget.addClass("number-widget-box");
+ $(this.body).html(`
+ ${__('Loading...')}
+
`);
+ this.make_card();
+ }
+
+ make_card() {
+ frappe.model.with_doc("Number Card", this.name).then(card => {
+ if (!card) {
+ if (this.document_type) {
+ this.set_doc_args();
+ frappe.xcall(
+ 'frappe.desk.doctype.number_card.number_card.create_number_card',
+ {'args': this.card_doc}
+ ).then(doc => {
+ this.name = doc.name;
+ this.widget.attr('data-widget-name', this.name);
+ this.render_card();
+ });
+ } else {
+ // widget doesn't exist so delete
+ this.delete(false);
+ return;
+ }
+ } else {
+ this.card_doc = card;
+ this.render_card();
+ }
+ this.set_events();
+ });
+ }
+
+ set_events() {
+ $(this.body).click(() => {
+ if (this.in_customize_mode) return;
+ let filters = JSON.parse(this.card_doc.filters_json);
+ go_to_list_with_filters(this.card_doc.document_type, filters);
+ });
+ }
+
+ set_doc_args() {
+ this.card_doc = Object.assign({}, {
+ document_type: this.document_type,
+ label: this.label,
+ function: this.function,
+ aggregate_function_based_on: this.aggregate_function_based_on,
+ color: this.color,
+ filters_json: this.stats_filter
+ });
+ }
+
+ render_card() {
+ this.prepare_actions();
+ this.get_number().then(() => {
+ $(this.body).html(`
+
${this.number_html}
+
${this.card_doc.label}
+
`);
+ });
+ }
+
+ get_number() {
+ return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_result', {doc: this.card_doc}).then(res => {
+ const number_order = res.toFixed().length - 1;
+ if (number_order > 12) {
+ this.number_html = `≈ 10
${number_order}`;
+ } else {
+ this.number_html = res;
+ }
+ });
+ }
+
+ prepare_actions() {
+ let actions = [
+ {
+ label: __("Refresh"),
+ action: "action-refresh",
+ handler: () => {
+ this.render_card();
+ }
+ },
+ {
+ label: __("Edit"),
+ action: "action-edit",
+ handler: () => {
+ frappe.set_route(
+ "Form",
+ "Number Card",
+ this.name
+ );
+ }
+ },
+ ];
+ this.set_card_actions(actions);
+ }
+
+ set_card_actions(actions) {
+ this.card_actions = $(`
+ `);
+
+ this.card_actions.find("a[data-action]").each((i, o) => {
+ const action = o.dataset.action;
+ $(o).click(actions.find(a => a.action === action));
+ });
+ this.action_area.html(this.card_actions);
+ }
+}
diff --git a/frappe/public/js/frappe/widgets/utils.js b/frappe/public/js/frappe/widgets/utils.js
index f233266ce4..f408ecfdd2 100644
--- a/frappe/public/js/frappe/widgets/utils.js
+++ b/frappe/public/js/frappe/widgets/utils.js
@@ -113,4 +113,14 @@ const build_summary_item = (summary) => {
`);
};
-export { generate_route, generate_grid, build_summary_item };
\ No newline at end of file
+function go_to_list_with_filters(doctype, filters) {
+ const route = `List/${doctype}/List`;
+ frappe.set_route(route).then(()=> {
+ let list_view = frappe.views.list_view[route];
+ let filter_area = list_view.filter_area;
+ filter_area.clear();
+ filter_area.filter_list.add_filters_to_filter_group(filters);
+ })
+}
+
+export { generate_route, generate_grid, build_summary_item, go_to_list_with_filters };
\ No newline at end of file
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index 77e2d65ea3..7fff6c897a 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -19,7 +19,7 @@ class WidgetDialog {
primary_action: (data) => {
data = this.process_data(data);
- if (!this.editing) {
+ if (!this.editing && !data.name) {
data.name = `${this.type}-${this.label}-${frappe.utils.get_random(20)}`;
}
@@ -35,7 +35,7 @@ class WidgetDialog {
// __("New Chart") __("New Shortcut") __("Edit Chart") __("Edit Shortcut")
let action = this.editing ? "Edit" : "Add";
- return __(`${action} ${frappe.utils.to_title_case(this.type)}`);
+ return __(`${action} ${frappe.model.unscrub(this.type)}`);
}
get_fields() {
@@ -61,6 +61,38 @@ class WidgetDialog {
show_field(fieldname) {
this.dialog.set_df_property(fieldname, "hidden", false);
}
+
+ setup_filter(doctype) {
+ if (this.filter_group) {
+ this.filter_group.wrapper.empty();
+ delete this.filter_group;
+ }
+
+ let $loading = this.dialog.get_field("filter_area_loading").$wrapper;
+ $(`
Loading Filters...`).appendTo($loading);
+
+ this.filters = [];
+
+ if (this.values && this.values.stats_filter) {
+ const filters_json = JSON.parse(this.values.stats_filter);
+ this.filters = Object.keys(filters_json).map((filter) => {
+ let val = filters_json[filter];
+ return [this.values.link_to, filter, val[0], val[1], false];
+ });
+ }
+
+ this.filter_group = new frappe.ui.FilterGroup({
+ parent: this.dialog.get_field("filter_area").$wrapper,
+ doctype: doctype,
+ on_change: () => {},
+ });
+
+ frappe.model.with_doctype(doctype, () => {
+ this.filter_group.add_filters_to_filter_group(this.filters);
+ this.hide_field("filter_area_loading");
+ this.show_field("filter_area");
+ });
+ }
}
class ChartDialog extends WidgetDialog {
@@ -222,37 +254,139 @@ class ShortcutDialog extends WidgetDialog {
return data;
}
+}
- setup_filter(doctype) {
- if (this.filter_group) {
- this.filter_group.wrapper.empty();
- delete this.filter_group;
+class NumberCardDialog extends WidgetDialog {
+ constructor(opts) {
+ super(opts);
+ }
+
+ get_fields() {
+ let fields;
+ fields = [
+ {
+ fieldtype: 'Select',
+ label: 'Choose Existing Card or create New Card',
+ fieldname: 'new_or_existing',
+ options: ['New Card', 'Existing Card']
+ },
+ {
+ fieldtype: 'Link',
+ fieldname: 'card',
+ label: 'Number Cards',
+ options: 'Number Card',
+ get_query: () => {
+ return {
+ filters: {
+ document_type: this.document_type,
+ is_standard: 1
+ }
+ };
+ },
+ depends_on: 'eval: doc.new_or_existing == "Existing Card"'
+ },
+ {
+ fieldtype: 'Section Break',
+ fieldname: 'sb_1',
+ depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ label: 'Label',
+ fieldname: 'label',
+ fieldtype: 'Data',
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ label: 'Doctype',
+ fieldname: 'document_type',
+ fieldtype: 'Link',
+ options: 'DocType',
+ onchange: () => {
+ this.document_type = this.dialog.get_value("document_type");
+ this.set_aggregate_function_fields(this.dialog.get_values());
+ this.setup_filter(doctype);
+ },
+ hidden: 1
+ },
+ {
+ label: 'Color',
+ fieldname: 'color',
+ fieldtype: 'Color'
+ },
+ {
+ fieldtype: "Column Break",
+ fieldname: "cb_1",
+ },
+ {
+ label: 'Function',
+ fieldname: 'function',
+ fieldtype: 'Select',
+ options: ['Count', 'Sum', 'Average', 'Minimum', 'Maximum'],
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ label: 'Function Based On',
+ fieldname: 'aggregate_function_based_on',
+ fieldtype: 'Select',
+ depends_on: "eval: doc.function !== 'Count'",
+ mandatory_depends_on: "eval: doc.function !== 'Count'"
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "sb_1",
+ label: 'Add Filters',
+ depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ fieldtype: "HTML",
+ fieldname: "filter_area_loading",
+ },
+ {
+ fieldtype: "HTML",
+ fieldname: "filter_area",
+ hidden: 1,
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "sb_1",
+ },
+ ];
+
+ return fields;
+ }
+
+ setup_dialog_events() {
+ if (!this.document_type) {
+ if (this.default_values['doctype']) {
+ this.document_type = this.default_values['doctype'];
+ this.setup_filter(this.default_values['doctype']);
+ this.set_aggregate_function_fields(this.dialog.get_values());
+ } else {
+ this.show_field('document_type');
+ }
}
+ }
- let $loading = this.dialog.get_field("filter_area_loading").$wrapper;
- $(`
Loading Filters...`).appendTo($loading);
-
- this.filters = [];
-
- if (this.values && this.values.stats_filter) {
- const filters_json = JSON.parse(this.values.stats_filter);
- this.filters = Object.keys(filters_json).map((filter) => {
- let val = filters_json[filter];
- return [this.values.link_to, filter, val[0], val[1], false];
+ set_aggregate_function_fields(values) {
+ let aggregate_function_fields = [];
+ if (values.function !== 'Count' && this.document_type) {
+ frappe.get_meta(this.document_type).fields.map(df => {
+ if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
+ aggregate_function_fields.push({label: df.label, value: df.fieldname});
+ }
});
}
+ this.dialog.set_df_property('aggregate_function_based_on', 'options', aggregate_function_fields);
+ }
- this.filter_group = new frappe.ui.FilterGroup({
- parent: this.dialog.get_field("filter_area").$wrapper,
- doctype: doctype,
- on_change: () => {},
- });
+ process_data(data) {
+ if (data.new_or_existing == 'Existing Card') {
+ data.name = data.card;
+ }
+ data.stats_filter = JSON.stringify(this.filter_group.get_filters());
+ data.document_type = this.document_type;
- frappe.model.with_doctype(doctype, () => {
- this.filter_group.add_filters_to_filter_group(this.filters);
- this.hide_field("filter_area_loading");
- this.show_field("filter_area");
- });
+ return data;
}
}
@@ -260,6 +394,7 @@ export default function get_dialog_constructor(type) {
const widget_map = {
chart: ChartDialog,
shortcut: ShortcutDialog,
+ number_card: NumberCardDialog,
};
return widget_map[type] || WidgetDialog;
diff --git a/frappe/public/js/frappe/widgets/widget_group.js b/frappe/public/js/frappe/widgets/widget_group.js
index 4124e4b76e..bbf56189d7 100644
--- a/frappe/public/js/frappe/widgets/widget_group.js
+++ b/frappe/public/js/frappe/widgets/widget_group.js
@@ -4,6 +4,7 @@ import ShortcutWidget from "../widgets/shortcut_widget";
import LinksWidget from "../widgets/links_widget";
import OnboardingWidget from "../widgets/onboarding_widget";
import NewWidget from "../widgets/new_widget";
+import NumberCardWidget from "../widgets/number_card_widget";
frappe.provide("frappe.widget");
@@ -13,6 +14,7 @@ const widget_factory = {
shortcut: ShortcutWidget,
links: LinksWidget,
onboarding: OnboardingWidget,
+ number_card: NumberCardWidget,
};
export default class WidgetGroup {
@@ -77,6 +79,14 @@ export default class WidgetGroup {
return widget_object;
}
+ remove_widget(widget_obj) {
+ widget_obj.widget.remove();
+ this.widgets_list.filter((widget) => {
+ if (widget.name == widget_obj.name) return false;
+ });
+ delete this.widgets_dict[widget_obj.name];
+ }
+
customize() {
this.widget_area.show();
this.widgets_list.forEach((wid) => {
@@ -96,6 +106,9 @@ export default class WidgetGroup {
this.new_widget = new NewWidget({
container: this.body,
type: this.type,
+ custom_dialog: this.custom_dialog,
+ custom_dialog_function: this.custom_dialog_function,
+ default_values: this.default_values,
on_create: (config) => {
// Remove new widget
this.new_widget.delete();
diff --git a/frappe/public/less/dashboard_view.less b/frappe/public/less/dashboard_view.less
new file mode 100644
index 0000000000..6325a713d2
--- /dev/null
+++ b/frappe/public/less/dashboard_view.less
@@ -0,0 +1,40 @@
+.dashboard-page {
+ .dashboard-view {
+ min-height: calc(100vh - 284px);
+ padding: 20px 20px 0 20px;
+
+ .new-widget {
+ text-align: center;
+ }
+
+ .new-chart-widget {
+ min-height: 200px;
+ }
+
+ .new-number-card-widget {
+ min-height: 110px;
+ }
+ }
+
+ .page-form {
+ height: 50px;
+
+ .dashboard-header {
+ padding: 10px;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ }
+
+ .customize-dashboard {
+ font-size: 13px;
+ cursor: pointer;
+ }
+
+ .customize-options {
+ display: none;
+ cursor: pointer;
+ }
+ }
+}
+
diff --git a/frappe/public/less/desktop.less b/frappe/public/less/desktop.less
index 2230c59ac9..081dbb64fb 100644
--- a/frappe/public/less/desktop.less
+++ b/frappe/public/less/desktop.less
@@ -392,6 +392,56 @@
}
}
}
+
+ &.number-widget-box {
+ cursor: pointer;
+ height: 110px;
+
+ &:hover {
+ border-color: @text-muted;
+ }
+
+ .widget-head {
+ position: relative;
+
+ .widget-title {
+ display: none;
+ }
+
+ .widget-control {
+ position: absolute;
+ right: -10px;
+ top: -10px;
+ }
+ }
+
+ .widget-body {
+ text-align: center;
+
+ .number-card-loading {
+ display: flex;
+ height: 80px;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .widget-content {
+ padding: 5px;
+
+ .number {
+ font-size: 30px;
+ font-weight: bold;
+ }
+
+ .number-text {
+ color: @text-muted;
+ padding: 5px;
+ font-size: 14px;
+ }
+ }
+ }
+ }
+
}
.pill {
From 759f76f9cdeb968b9fc33199eefad2a7bad1e6f2 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 01:08:45 +0530
Subject: [PATCH 10/76] fix: show cards created by user
---
frappe/desk/doctype/number_card/number_card.py | 6 +++++-
frappe/public/js/frappe/widgets/widget_dialog.js | 10 +++++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index 509995392c..14d7aeea3d 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -45,4 +45,8 @@ def create_number_card(args):
doc.is_standard = 1
doc.update(args)
doc.insert(ignore_permissions=True)
- return doc
\ No newline at end of file
+ return doc
+
+def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
+ or_filters = {'creation': frappe.session.user, 'is_standard': 1}
+ return frappe.db.get_list('Number Card', fields=['name', 'label'], or_filters=or_filters, as_list = 1)
\ No newline at end of file
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index 7fff6c897a..d729012a3b 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -277,9 +277,9 @@ class NumberCardDialog extends WidgetDialog {
options: 'Number Card',
get_query: () => {
return {
+ 'query': 'frappe.desk.doctype.number_card.number_card.get_cards_for_user',
filters: {
- document_type: this.document_type,
- is_standard: 1
+ document_type: this.document_type,
}
};
},
@@ -360,16 +360,16 @@ class NumberCardDialog extends WidgetDialog {
if (this.default_values['doctype']) {
this.document_type = this.default_values['doctype'];
this.setup_filter(this.default_values['doctype']);
- this.set_aggregate_function_fields(this.dialog.get_values());
+ this.set_aggregate_function_fields();
} else {
this.show_field('document_type');
}
}
}
- set_aggregate_function_fields(values) {
+ set_aggregate_function_fields() {
let aggregate_function_fields = [];
- if (values.function !== 'Count' && this.document_type) {
+ if (this.document_type) {
frappe.get_meta(this.document_type).fields.map(df => {
if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
aggregate_function_fields.push({label: df.label, value: df.fieldname});
From f3db07fe016186f706efe92b66459dc5ebc332d5 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 02:35:24 +0530
Subject: [PATCH 11/76] fix(patch): patch to set existing dashboard charts as
standard
---
.../desk/doctype/number_card/number_card.py | 8 ++++--
frappe/patches.txt | 1 +
...t_existing_dashboard_charts_as_standard.py | 27 +++++++++++++++++++
.../public/js/frappe/widgets/widget_dialog.js | 2 +-
4 files changed, 35 insertions(+), 3 deletions(-)
create mode 100644 frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index 14d7aeea3d..545e88b7c6 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -48,5 +48,9 @@ def create_number_card(args):
return doc
def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
- or_filters = {'creation': frappe.session.user, 'is_standard': 1}
- return frappe.db.get_list('Number Card', fields=['name', 'label'], or_filters=or_filters, as_list = 1)
\ No newline at end of file
+ or_filters = {'owner': frappe.session.user, 'is_standard': 1}
+ return frappe.db.get_list('Number Card',
+ fields=['name', 'label'],
+ filters=filters,
+ or_filters=or_filters,
+ as_list = 1)
\ No newline at end of file
diff --git a/frappe/patches.txt b/frappe/patches.txt
index cbda8cf677..c5daf872b1 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -273,3 +273,4 @@ execute:frappe.delete_doc_if_exists('DocType', 'GCalendar Settings')
frappe.patches.v12_0.remove_parent_and_parenttype_from_print_formats
execute:from frappe.desk.page.setup_wizard.install_fixtures import update_genders;update_genders()
frappe.patches.v13_0.website_theme_custom_scss
+frappe.patches.v13_0.set_existing_dashboard_charts_as_standard
diff --git a/frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py b/frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py
new file mode 100644
index 0000000000..0e7abea8bd
--- /dev/null
+++ b/frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py
@@ -0,0 +1,27 @@
+import frappe
+
+def execute():
+ if not frappe.db.table_exists('Dashboard Chart'):
+ return
+
+ users_with_permission = frappe.get_all(
+ "Has Role",
+ fields=["parent"],
+ filters={"role": ['in', ['System Manager', 'Dashboard Manager']], "parenttype": "User"},
+ distinct=True,
+ as_list=True
+ )
+
+ users = tuple(
+ [item if type(item) == str else item.encode('utf8') for sublist in users_with_permission for item in sublist]
+ )
+
+ frappe.db.sql("""
+ UPDATE
+ `tabDashboard Chart`
+ SET
+ `tabDashboard Chart`.`is_standard`=1
+ WHERE
+ `tabDashboard Chart`.owner in {users}
+ """.format(users=users)
+ )
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index d729012a3b..7d02693dc0 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -329,7 +329,7 @@ class NumberCardDialog extends WidgetDialog {
fieldname: 'aggregate_function_based_on',
fieldtype: 'Select',
depends_on: "eval: doc.function !== 'Count'",
- mandatory_depends_on: "eval: doc.function !== 'Count'"
+ mandatory_depends_on: 'eval: doc.function !== "Count" && doc.new_or_existing == "New Card"'
},
{
fieldtype: "Section Break",
From adfcf5c8176fbdbf63868053f6d0ab2329010c17 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 16:26:08 +0530
Subject: [PATCH 12/76] fix: get dashboard settings from db
---
frappe/public/js/frappe/utils/dashboard_utils.js | 10 +++++++---
.../public/js/frappe/views/dashboard/dashboard_view.js | 2 ++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/frappe/public/js/frappe/utils/dashboard_utils.js b/frappe/public/js/frappe/utils/dashboard_utils.js
index 66f3d5e21f..16f54a1f84 100644
--- a/frappe/public/js/frappe/utils/dashboard_utils.js
+++ b/frappe/public/js/frappe/utils/dashboard_utils.js
@@ -57,13 +57,17 @@ frappe.dashboard_utils = {
},
get_dashboard_settings() {
- return frappe.model.with_doc('Dashboard Settings', frappe.session.user).then(settings => {
- if (!settings) {
+ return frappe.db.get_list('Dashboard Settings',
+ {
+ filters: {name: frappe.session.user},
+ fields: ['*']
+ }).then(settings => {
+ if (!settings.length) {
return this.create_dashboard_settings().then(settings => {
return settings;
});
} else {
- return settings;
+ return settings[0];
}
});
},
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 69bcc0ac74..de4e82840a 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -148,6 +148,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
render_dashboard() {
this.$dashboard_wrapper.empty();
+
frappe.dashboard_utils.get_dashboard_settings().then(settings => {
this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
this.charts.map(chart => {
@@ -156,6 +157,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
});
this.render_dashboard_charts();
});
+
this.render_number_cards();
if (!this.charts.length && !this.number_cards.length) {
From 4c99c1dfb689ee9b2f0bcf6ac6f6a1242e24ee6e Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 17:04:41 +0530
Subject: [PATCH 13/76] fix: don't allow duplicates in the view
---
.../frappe/views/dashboard/dashboard_view.js | 149 +++++++++---------
1 file changed, 78 insertions(+), 71 deletions(-)
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index de4e82840a..713191026e 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -77,36 +77,50 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
}
}
+ render_dashboard() {
+ this.$dashboard_wrapper.empty();
- get_field_options() {
- let date_fields = [
- {label: __('Created On'), value: 'creation'},
- {label: __('Last Modified On'), value: 'modified'}
- ];
- let value_fields = [];
- let group_by_fields = [];
- let aggregate_function_fields = [];
-
- frappe.get_meta(this.doctype).fields.map(df => {
- if (['Date', 'Datetime'].includes(df.fieldtype)) {
- date_fields.push({label: df.label, value: df.fieldname});
- }
- if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
- value_fields.push({label: df.label, value: df.fieldname});
- aggregate_function_fields.push({label: df.label, value: df.fieldname});
- }
- if (['Link', 'Select'].includes(df.fieldtype)) {
- group_by_fields.push({label: df.label, value: df.fieldname});
- }
+ frappe.dashboard_utils.get_dashboard_settings().then(settings => {
+ this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
+ this.charts.map(chart => {
+ chart.label = chart.chart_name;
+ chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
+ });
+ this.render_dashboard_charts();
});
- return {
- date_fields: date_fields,
- value_fields: value_fields,
- group_by_fields: group_by_fields,
- aggregate_function_fields: aggregate_function_fields
+
+ this.render_number_cards();
+
+ if (!this.charts.length && !this.number_cards.length) {
+ this.render_empty_state();
}
}
+ fetch_doctype_charts() {
+ return frappe.db.get_list('Dashboard Chart', {
+ filters: {
+ chart_type: ['in', ['Count', 'Sum', 'Group By']],
+ document_type: this.doctype,
+ is_standard: true
+ },
+ fields: ['*']
+ }).then(charts => {
+ return charts;
+ });
+ }
+
+ fetch_doctype_number_cards() {
+ return frappe.db.get_list('Number Card', {
+ filters: {
+ document_type: this.doctype,
+ is_standard: true
+ },
+ fields: ['*']
+ }).then(cards => {
+ return cards;
+ });
+ }
+
render_number_cards() {
this.number_card_group = new frappe.widget.WidgetGroup({
container: this.$dashboard_wrapper,
@@ -146,25 +160,6 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.chart_group.container.find('.widget-group-head').hide();
}
- render_dashboard() {
- this.$dashboard_wrapper.empty();
-
- frappe.dashboard_utils.get_dashboard_settings().then(settings => {
- this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
- this.charts.map(chart => {
- chart.label = chart.name;
- chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
- });
- this.render_dashboard_charts();
- });
-
- this.render_number_cards();
-
- if (!this.charts.length && !this.number_cards.length) {
- this.render_empty_state();
- }
- }
-
render_empty_state() {
const no_result_message_html =
`${__("You haven't added any Dashboard Charts or Number Cards yet.")}
@@ -190,31 +185,6 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
}
- fetch_doctype_charts() {
- return frappe.db.get_list('Dashboard Chart', {
- filters: {
- chart_type: ['in', ['Count', 'Sum', 'Group By']],
- document_type: this.doctype,
- is_standard: true
- },
- fields: ['*']
- }).then(charts => {
- return charts;
- });
- }
-
- fetch_doctype_number_cards() {
- return frappe.db.get_list('Number Card', {
- filters: {
- document_type: this.doctype,
- is_standard: true
- },
- fields: ['*']
- }).then(cards => {
- return cards;
- });
- }
-
customize() {
if (this.in_customize_mode) {
return;
@@ -236,17 +206,21 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.toggle_customize(false);
const number_card_config = this.number_card_group.get_widget_config();
- const number_cards = [];
+ let number_cards = [];
number_card_config.order.map(card_name => {
number_cards.push(number_card_config.widgets[card_name]);
});
const chart_config = this.chart_group.get_widget_config();
- const charts = [];
+ let charts = [];
chart_config.order.map(chart_name => {
charts.push(chart_config.widgets[chart_name]);
});
+ // Don't allow duplicates of the same card or chart
+ charts = this.remove_duplicates(charts);
+ number_cards = this.remove_duplicates(number_cards);
+
this.dashboard_settings = {
charts: charts,
number_cards: number_cards,
@@ -452,4 +426,37 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
dialog.show();
}
+ get_field_options() {
+ let date_fields = [
+ {label: __('Created On'), value: 'creation'},
+ {label: __('Last Modified On'), value: 'modified'}
+ ];
+ let value_fields = [];
+ let group_by_fields = [];
+ let aggregate_function_fields = [];
+
+ frappe.get_meta(this.doctype).fields.map(df => {
+ if (['Date', 'Datetime'].includes(df.fieldtype)) {
+ date_fields.push({label: df.label, value: df.fieldname});
+ }
+ if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
+ value_fields.push({label: df.label, value: df.fieldname});
+ aggregate_function_fields.push({label: df.label, value: df.fieldname});
+ }
+ if (['Link', 'Select'].includes(df.fieldtype)) {
+ group_by_fields.push({label: df.label, value: df.fieldname});
+ }
+ });
+ return {
+ date_fields: date_fields,
+ value_fields: value_fields,
+ group_by_fields: group_by_fields,
+ aggregate_function_fields: aggregate_function_fields
+ }
+ }
+
+ remove_duplicates(items) {
+ return items.filter((item, index) => items.indexOf(item) === index);
+ }
+
}
From beb9cd0c030646e0003430a3a8b2d91b1bd76d58 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 17:32:15 +0530
Subject: [PATCH 14/76] fix: indent using tabs
---
.../frappe/views/dashboard/dashboard_view.js | 784 +++++++++---------
1 file changed, 392 insertions(+), 392 deletions(-)
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 713191026e..dd8bfbd3fd 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -3,460 +3,460 @@ frappe.provide('frappe.views');
frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
get view_name() {
return 'Dashboard';
- }
+ }
- setup_defaults() {
+ setup_defaults() {
return super.setup_defaults()
.then(() => {
- this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
+ this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
});
- }
+ }
- render() {
+ render() {
- }
+ }
- setup_view() {
- if (this.chart_group || this.number_card_group) {
- return;
- }
+ setup_view() {
+ if (this.chart_group || this.number_card_group) {
+ return;
+ }
- this.setup_dashboard_page();
- this.setup_dashboard_customization();
- this.make_dashboard();
- }
+ this.setup_dashboard_page();
+ this.setup_dashboard_customization();
+ this.make_dashboard();
+ }
- setup_dashboard_customization() {
- this.$customize = this.$chart_header.find('.customize-dashboard');
- this.$save_or_discard = this.$chart_header.find('.customize-options');
- }
+ setup_dashboard_customization() {
+ this.$customize = this.$chart_header.find('.customize-dashboard');
+ this.$save_or_discard = this.$chart_header.find('.customize-options');
+ }
- setup_dashboard_page() {
- const dashboard_name = __('{0} Dashboard', [this.doctype]);
- const chart_wrapper_html = ``;
+ setup_dashboard_page() {
+ const dashboard_name = __('{0} Dashboard', [this.doctype]);
+ const chart_wrapper_html = ``;
- this.$frappe_list.html(chart_wrapper_html);
- this.page.clear_secondary_action();
- this.$dashboard_page = this.$page.find('.layout-main-section-wrapper').addClass('dashboard-page');
- this.$page.find('.page-form').empty().html(
- ``);
+ this.$frappe_list.html(chart_wrapper_html);
+ this.page.clear_secondary_action();
+ this.$dashboard_page = this.$page.find('.layout-main-section-wrapper').addClass('dashboard-page');
+ this.$page.find('.page-form').empty().html(
+ ``);
- this.$dashboard_wrapper = this.$page.find('.dashboard-view');
- this.$chart_header = this.$page.find('.dashboard-header');
+ this.$dashboard_wrapper = this.$page.find('.dashboard-view');
+ this.$chart_header = this.$page.find('.dashboard-header');
- frappe.utils.bind_actions_with_object(this.$dashboard_page, this);
- }
+ frappe.utils.bind_actions_with_object(this.$dashboard_page, this);
+ }
- make_dashboard() {
- if (this.dashboard_settings) {
- this.charts = this.dashboard_settings.charts;
- this.number_cards = this.dashboard_settings.number_cards;
- this.render_dashboard();
- } else {
- frappe.run_serially([
- () => this.fetch_doctype_number_cards().then(cards => {
- this.number_cards = cards;
- }),
- () => this.fetch_doctype_charts().then(charts => {
- this.charts = charts;
- }),
- () => this.render_dashboard()
- ]);
- }
- }
+ make_dashboard() {
+ if (this.dashboard_settings) {
+ this.charts = this.dashboard_settings.charts;
+ this.number_cards = this.dashboard_settings.number_cards;
+ this.render_dashboard();
+ } else {
+ frappe.run_serially([
+ () => this.fetch_doctype_number_cards().then(cards => {
+ this.number_cards = cards;
+ }),
+ () => this.fetch_doctype_charts().then(charts => {
+ this.charts = charts;
+ }),
+ () => this.render_dashboard()
+ ]);
+ }
+ }
- render_dashboard() {
- this.$dashboard_wrapper.empty();
+ render_dashboard() {
+ this.$dashboard_wrapper.empty();
- frappe.dashboard_utils.get_dashboard_settings().then(settings => {
- this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
- this.charts.map(chart => {
- chart.label = chart.chart_name;
- chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
- });
- this.render_dashboard_charts();
- });
+ frappe.dashboard_utils.get_dashboard_settings().then(settings => {
+ this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
+ this.charts.map(chart => {
+ chart.label = chart.chart_name;
+ chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
+ });
+ this.render_dashboard_charts();
+ });
- this.render_number_cards();
+ this.render_number_cards();
- if (!this.charts.length && !this.number_cards.length) {
- this.render_empty_state();
- }
- }
+ if (!this.charts.length && !this.number_cards.length) {
+ this.render_empty_state();
+ }
+ }
- fetch_doctype_charts() {
- return frappe.db.get_list('Dashboard Chart', {
+ fetch_doctype_charts() {
+ return frappe.db.get_list('Dashboard Chart', {
filters: {
chart_type: ['in', ['Count', 'Sum', 'Group By']],
- document_type: this.doctype,
- is_standard: true
- },
- fields: ['*']
+ document_type: this.doctype,
+ is_standard: true
+ },
+ fields: ['*']
}).then(charts => {
- return charts;
- });
- }
+ return charts;
+ });
+ }
- fetch_doctype_number_cards() {
- return frappe.db.get_list('Number Card', {
+ fetch_doctype_number_cards() {
+ return frappe.db.get_list('Number Card', {
filters: {
- document_type: this.doctype,
- is_standard: true
- },
- fields: ['*']
+ document_type: this.doctype,
+ is_standard: true
+ },
+ fields: ['*']
}).then(cards => {
- return cards;
- });
- }
+ return cards;
+ });
+ }
- render_number_cards() {
- this.number_card_group = new frappe.widget.WidgetGroup({
- container: this.$dashboard_wrapper,
- type: "number_card",
- columns: 3,
- options: {
- allow_sorting: true,
- allow_create: true,
- allow_delete: true,
- allow_hiding: true,
- },
- default_values: {doctype: this.doctype},
- widgets: this.number_cards || [],
- in_customize_mode: true,
- });
+ render_number_cards() {
+ this.number_card_group = new frappe.widget.WidgetGroup({
+ container: this.$dashboard_wrapper,
+ type: "number_card",
+ columns: 3,
+ options: {
+ allow_sorting: true,
+ allow_create: true,
+ allow_delete: true,
+ allow_hiding: true,
+ },
+ default_values: {doctype: this.doctype},
+ widgets: this.number_cards || [],
+ in_customize_mode: true,
+ });
- this.in_customize_mode && this.number_card_group.customize();
- }
+ this.in_customize_mode && this.number_card_group.customize();
+ }
- render_dashboard_charts() {
- this.chart_group = new frappe.widget.WidgetGroup({
- container: this.$dashboard_wrapper,
- type: "chart",
- columns: 2,
- options: {
- allow_sorting: true,
- allow_create: true,
- allow_delete: true,
- allow_hiding: true,
- },
- custom_dialog: () => this.show_add_chart_dialog(),
- widgets: this.charts,
- in_customize_mode: this.in_customize_mode || false,
- });
+ render_dashboard_charts() {
+ this.chart_group = new frappe.widget.WidgetGroup({
+ container: this.$dashboard_wrapper,
+ type: "chart",
+ columns: 2,
+ options: {
+ allow_sorting: true,
+ allow_create: true,
+ allow_delete: true,
+ allow_hiding: true,
+ },
+ custom_dialog: () => this.show_add_chart_dialog(),
+ widgets: this.charts,
+ in_customize_mode: this.in_customize_mode || false,
+ });
- this.in_customize_mode && this.chart_group.customize();
- this.chart_group.container.find('.widget-group-head').hide();
- }
+ this.in_customize_mode && this.chart_group.customize();
+ this.chart_group.container.find('.widget-group-head').hide();
+ }
- render_empty_state() {
- const no_result_message_html =
- `${__("You haven't added any Dashboard Charts or Number Cards yet.")}
-
${__("Click On Customize to add your first widget")}
`;
+ render_empty_state() {
+ const no_result_message_html =
+ `${__("You haven't added any Dashboard Charts or Number Cards yet.")}
+
${__("Click On Customize to add your first widget")}
`;
- const customize_button =
- ``;
- const empty_state_image = '/assets/frappe/images/ui-states/empty.png';
+ const empty_state_image = '/assets/frappe/images/ui-states/empty.png';
- const empty_state_html = `
+ const empty_state_html = `

-
- ${no_result_message_html}
+
+ ${no_result_message_html}
${customize_button}
-
`;
+
`;
- this.$dashboard_wrapper.append(empty_state_html);
+ this.$dashboard_wrapper.append(empty_state_html);
- this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
- }
+ this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
+ }
- customize() {
- if (this.in_customize_mode) {
- return;
- }
+ customize() {
+ if (this.in_customize_mode) {
+ return;
+ }
- if (this.$empty_state) {
- this.$empty_state.remove();
- }
+ if (this.$empty_state) {
+ this.$empty_state.remove();
+ }
- this.toggle_customize(true);
- this.chart_group.in_customize_mode = true;
- this.chart_group.customize();
- this.number_cards.in_customize_mode = true;
- this.number_card_group.customize();
- }
+ this.toggle_customize(true);
+ this.chart_group.in_customize_mode = true;
+ this.chart_group.customize();
+ this.number_cards.in_customize_mode = true;
+ this.number_card_group.customize();
+ }
- save_dashboard_customization() {
- this.toggle_customize(false);
+ save_dashboard_customization() {
+ this.toggle_customize(false);
- const number_card_config = this.number_card_group.get_widget_config();
- let number_cards = [];
- number_card_config.order.map(card_name => {
- number_cards.push(number_card_config.widgets[card_name]);
- });
+ const number_card_config = this.number_card_group.get_widget_config();
+ let number_cards = [];
+ number_card_config.order.map(card_name => {
+ number_cards.push(number_card_config.widgets[card_name]);
+ });
- const chart_config = this.chart_group.get_widget_config();
- let charts = [];
- chart_config.order.map(chart_name => {
- charts.push(chart_config.widgets[chart_name]);
- });
+ const chart_config = this.chart_group.get_widget_config();
+ let charts = [];
+ chart_config.order.map(chart_name => {
+ charts.push(chart_config.widgets[chart_name]);
+ });
- // Don't allow duplicates of the same card or chart
- charts = this.remove_duplicates(charts);
- number_cards = this.remove_duplicates(number_cards);
+ // Don't allow duplicates of the same card or chart
+ charts = this.remove_duplicates(charts);
+ number_cards = this.remove_duplicates(number_cards);
- this.dashboard_settings = {
- charts: charts,
- number_cards: number_cards,
- };
+ this.dashboard_settings = {
+ charts: charts,
+ number_cards: number_cards,
+ };
- frappe.model.user_settings.save(this.doctype, 'dashboard_settings', this.dashboard_settings);
- this.make_dashboard();
- }
+ frappe.model.user_settings.save(this.doctype, 'dashboard_settings', this.dashboard_settings);
+ this.make_dashboard();
+ }
- discard_dashboard_customization() {
- this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
- this.toggle_customize(false);
- this.render_dashboard();
- }
+ discard_dashboard_customization() {
+ this.dashboard_settings = frappe.get_user_settings(this.doctype)['dashboard_settings'] || null;
+ this.toggle_customize(false);
+ this.render_dashboard();
+ }
- reset_dashboard_customization() {
- this.dashboard_settings = null;
- frappe.model.user_settings.save(
- this.doctype, 'dashboard_settings', this.dashboard_settings
- ).then(()=> this.make_dashboard());
- this.toggle_customize(false);
- }
+ reset_dashboard_customization() {
+ this.dashboard_settings = null;
+ frappe.model.user_settings.save(
+ this.doctype, 'dashboard_settings', this.dashboard_settings
+ ).then(()=> this.make_dashboard());
+ this.toggle_customize(false);
+ }
- toggle_customize(show) {
- this.$customize.toggle(!show);
- this.$save_or_discard.toggle(show);
- this.in_customize_mode = show;
- }
+ toggle_customize(show) {
+ this.$customize.toggle(!show);
+ this.$save_or_discard.toggle(show);
+ this.in_customize_mode = show;
+ }
- show_add_chart_dialog() {
- let fields = this.get_field_options();
- const dialog = new frappe.ui.Dialog({
- title: __(`Add a ${this.doctype} Chart`),
- fields: [
- {
+ show_add_chart_dialog() {
+ let fields = this.get_field_options();
+ const dialog = new frappe.ui.Dialog({
+ title: __(`Add a ${this.doctype} Chart`),
+ fields: [
+ {
fieldname: 'new_or_existing',
fieldtype: 'Select',
- label: 'Choose an existing chart or create a new chart',
- options: ['New Chart', 'Existing Chart'],
- reqd: 1,
- },
- {
- label: 'Chart',
- fieldname: 'chart',
- fieldtype: 'Link',
- get_query: () => {
- return {
- filters: {
- document_type: this.doctype,
- is_standard: 1,
- }
- };
- },
- options: 'Dashboard Chart',
- depends_on: 'eval: doc.new_or_existing == "Existing Chart"'
- },
- {
- fieldname: 'sb_2',
- fieldtype: 'Section Break',
- depends_on: 'eval: doc.new_or_existing == "New Chart"'
- },
- {
- label: 'Chart Label',
- fieldname: 'label',
- fieldtype: 'Data',
- mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"'
- },
- {
- fieldname: 'cb_1',
- fieldtype: 'Column Break'
- },
- {
- label: 'Chart Type',
- fieldname: 'chart_type',
- fieldtype: 'Select',
- options: ['Time Series', 'Group By'],
- mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"',
- },
- {
- fieldname: 'sb_2',
- fieldtype: 'Section Break',
- label: 'Chart Config',
- depends_on: 'eval: doc.chart_type == "Time Series" && doc.new_or_existing == "New Chart"',
- },
- {
- label: 'Function',
- fieldname: 'chart_function',
- fieldtype: 'Select',
- options: ['Count', 'Sum', 'Average'],
- default: 'Count',
- },
- {
- label: 'Timespan',
- fieldtype: 'Select',
- fieldname: 'timespan',
- depends_on: 'eval: doc.chart_type == "Time Series"',
- options: ['Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
- default: 'Last Year',
- },
- {
- fieldname: 'cb_2',
- fieldtype: 'Column Break'
- },
- {
- label: 'Value Based On',
- fieldtype: 'Select',
- fieldname: 'based_on',
- options: fields.value_fields,
- depends_on: 'eval: doc.chart_function=="Sum"'
- },
- {
- label: 'Time Series Based On',
- fieldtype: 'Select',
- fieldname: 'based_on',
- options: fields.date_fields,
- mandatory_depends_on: 'eval: doc.chart_type == "Time Series"'
- },
- {
- label: 'Time Interval',
- fieldname: 'time_interval',
- fieldtype: 'Select',
- depends_on: 'eval: doc.chart_type == "Time Series"',
- options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
- default: 'Monthly'
- },
- {
- fieldname: 'sb_2',
- fieldtype: 'Section Break',
- label: 'Chart Config',
- depends_on: 'eval: doc.chart_type == "Group By" && doc.new_or_existing == "New Chart"',
- },
- {
- label: 'Group By Type',
- fieldname: 'group_by_type',
- fieldtype: 'Select',
- options: ['Count', 'Sum', 'Average'],
- default: 'Count',
- },
- {
- label: 'Aggregate Function Based On',
- fieldtype: 'Select',
- fieldname: 'aggregate_function_based_on',
- options: fields.aggregate_function_fields,
- depends_on: 'eval: ["Sum", "Avergage"].includes(doc.group_by_type)',
- },
- {
- fieldname: 'cb_2',
- fieldtype: 'Column Break'
- },
- {
- label: 'Group By Based On',
- fieldtype: 'Select',
- fieldname: 'group_by_based_on',
- options: fields.group_by_fields,
- default: 'Last Year',
- },
- {
- label: 'Number of Groups',
- fieldtype: 'Int',
- fieldname: 'number_of_groups',
- default: 0,
- },
- {
- fieldname: 'sb_3',
- fieldtype: 'Section Break',
- depends_on: 'eval: doc.new_or_existing == "New Chart"'
- },
- {
- label: 'Chart Type',
- fieldname: 'type',
- fieldtype: 'Select',
- options: ['Line', 'Bar', 'Percentage', 'Pie'],
- depends_on: 'eval: doc.new_or_existing == "New Chart"'
- },
- {
- fieldname: 'cb_1',
- fieldtype: 'Column Break'
- },
- {
- label: 'Chart Color',
- fieldname: 'color',
- fieldtype: 'Color',
- depends_on: 'eval: doc.new_or_existing == "New Chart"',
- },
- ],
- primary_action_label: __('Add'),
- primary_action: (values) => {
- let chart = values;
- if (chart.new_or_existing == 'New Chart') {
- chart.chart_name = chart.label;
- chart.chart_type = chart.chart_type == 'Time Series'? chart.chart_function: chart.chart_type;
- chart.document_type = this.doctype;
- chart.filters_json = '[]';
- frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart', {'args': chart}).then((doc)=> {
- this.chart_group.new_widget.on_create({'chart_name': doc.chart_name, 'name': doc.chart_name, 'label': chart.label});
- });
- } else {
- this.chart_group.new_widget.on_create({'chart_name': chart.chart, 'label': chart.chart, 'name': chart.chart});
- }
- dialog.hide();
- }
- });
- dialog.show();
- }
+ label: 'Choose an existing chart or create a new chart',
+ options: ['New Chart', 'Existing Chart'],
+ reqd: 1,
+ },
+ {
+ label: 'Chart',
+ fieldname: 'chart',
+ fieldtype: 'Link',
+ get_query: () => {
+ return {
+ filters: {
+ document_type: this.doctype,
+ is_standard: 1,
+ }
+ };
+ },
+ options: 'Dashboard Chart',
+ depends_on: 'eval: doc.new_or_existing == "Existing Chart"'
+ },
+ {
+ fieldname: 'sb_2',
+ fieldtype: 'Section Break',
+ depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ label: 'Chart Label',
+ fieldname: 'label',
+ fieldtype: 'Data',
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ fieldname: 'cb_1',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Chart Type',
+ fieldname: 'chart_type',
+ fieldtype: 'Select',
+ options: ['Time Series', 'Group By'],
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Chart"',
+ },
+ {
+ fieldname: 'sb_2',
+ fieldtype: 'Section Break',
+ label: 'Chart Config',
+ depends_on: 'eval: doc.chart_type == "Time Series" && doc.new_or_existing == "New Chart"',
+ },
+ {
+ label: 'Function',
+ fieldname: 'chart_function',
+ fieldtype: 'Select',
+ options: ['Count', 'Sum', 'Average'],
+ default: 'Count',
+ },
+ {
+ label: 'Timespan',
+ fieldtype: 'Select',
+ fieldname: 'timespan',
+ depends_on: 'eval: doc.chart_type == "Time Series"',
+ options: ['Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
+ default: 'Last Year',
+ },
+ {
+ fieldname: 'cb_2',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Value Based On',
+ fieldtype: 'Select',
+ fieldname: 'based_on',
+ options: fields.value_fields,
+ depends_on: 'eval: doc.chart_function=="Sum"'
+ },
+ {
+ label: 'Time Series Based On',
+ fieldtype: 'Select',
+ fieldname: 'based_on',
+ options: fields.date_fields,
+ mandatory_depends_on: 'eval: doc.chart_type == "Time Series"'
+ },
+ {
+ label: 'Time Interval',
+ fieldname: 'time_interval',
+ fieldtype: 'Select',
+ depends_on: 'eval: doc.chart_type == "Time Series"',
+ options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
+ default: 'Monthly'
+ },
+ {
+ fieldname: 'sb_2',
+ fieldtype: 'Section Break',
+ label: 'Chart Config',
+ depends_on: 'eval: doc.chart_type == "Group By" && doc.new_or_existing == "New Chart"',
+ },
+ {
+ label: 'Group By Type',
+ fieldname: 'group_by_type',
+ fieldtype: 'Select',
+ options: ['Count', 'Sum', 'Average'],
+ default: 'Count',
+ },
+ {
+ label: 'Aggregate Function Based On',
+ fieldtype: 'Select',
+ fieldname: 'aggregate_function_based_on',
+ options: fields.aggregate_function_fields,
+ depends_on: 'eval: ["Sum", "Avergage"].includes(doc.group_by_type)',
+ },
+ {
+ fieldname: 'cb_2',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Group By Based On',
+ fieldtype: 'Select',
+ fieldname: 'group_by_based_on',
+ options: fields.group_by_fields,
+ default: 'Last Year',
+ },
+ {
+ label: 'Number of Groups',
+ fieldtype: 'Int',
+ fieldname: 'number_of_groups',
+ default: 0,
+ },
+ {
+ fieldname: 'sb_3',
+ fieldtype: 'Section Break',
+ depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ label: 'Chart Type',
+ fieldname: 'type',
+ fieldtype: 'Select',
+ options: ['Line', 'Bar', 'Percentage', 'Pie'],
+ depends_on: 'eval: doc.new_or_existing == "New Chart"'
+ },
+ {
+ fieldname: 'cb_1',
+ fieldtype: 'Column Break'
+ },
+ {
+ label: 'Chart Color',
+ fieldname: 'color',
+ fieldtype: 'Color',
+ depends_on: 'eval: doc.new_or_existing == "New Chart"',
+ },
+ ],
+ primary_action_label: __('Add'),
+ primary_action: (values) => {
+ let chart = values;
+ if (chart.new_or_existing == 'New Chart') {
+ chart.chart_name = chart.label;
+ chart.chart_type = chart.chart_type == 'Time Series'? chart.chart_function: chart.chart_type;
+ chart.document_type = this.doctype;
+ chart.filters_json = '[]';
+ frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart', {'args': chart}).then((doc)=> {
+ this.chart_group.new_widget.on_create({'chart_name': doc.chart_name, 'name': doc.chart_name, 'label': chart.label});
+ });
+ } else {
+ this.chart_group.new_widget.on_create({'chart_name': chart.chart, 'label': chart.chart, 'name': chart.chart});
+ }
+ dialog.hide();
+ }
+ });
+ dialog.show();
+ }
- get_field_options() {
- let date_fields = [
+ get_field_options() {
+ let date_fields = [
{label: __('Created On'), value: 'creation'},
{label: __('Last Modified On'), value: 'modified'}
];
let value_fields = [];
let group_by_fields = [];
- let aggregate_function_fields = [];
+ let aggregate_function_fields = [];
- frappe.get_meta(this.doctype).fields.map(df => {
- if (['Date', 'Datetime'].includes(df.fieldtype)) {
- date_fields.push({label: df.label, value: df.fieldname});
- }
- if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
- value_fields.push({label: df.label, value: df.fieldname});
- aggregate_function_fields.push({label: df.label, value: df.fieldname});
- }
- if (['Link', 'Select'].includes(df.fieldtype)) {
- group_by_fields.push({label: df.label, value: df.fieldname});
- }
- });
- return {
- date_fields: date_fields,
- value_fields: value_fields,
- group_by_fields: group_by_fields,
- aggregate_function_fields: aggregate_function_fields
- }
- }
+ frappe.get_meta(this.doctype).fields.map(df => {
+ if (['Date', 'Datetime'].includes(df.fieldtype)) {
+ date_fields.push({label: df.label, value: df.fieldname});
+ }
+ if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) {
+ value_fields.push({label: df.label, value: df.fieldname});
+ aggregate_function_fields.push({label: df.label, value: df.fieldname});
+ }
+ if (['Link', 'Select'].includes(df.fieldtype)) {
+ group_by_fields.push({label: df.label, value: df.fieldname});
+ }
+ });
+ return {
+ date_fields: date_fields,
+ value_fields: value_fields,
+ group_by_fields: group_by_fields,
+ aggregate_function_fields: aggregate_function_fields
+ }
+ }
- remove_duplicates(items) {
- return items.filter((item, index) => items.indexOf(item) === index);
- }
+ remove_duplicates(items) {
+ return items.filter((item, index) => items.indexOf(item) === index);
+ }
}
From 71349a958b5ced329c85339e7c1f2f9e041c79d2 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 17:38:49 +0530
Subject: [PATCH 15/76] fix: indent
---
.../public/js/frappe/widgets/widget_dialog.js | 170 +++++++++---------
1 file changed, 85 insertions(+), 85 deletions(-)
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index 7d02693dc0..adfdacabbd 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -264,93 +264,93 @@ class NumberCardDialog extends WidgetDialog {
get_fields() {
let fields;
fields = [
- {
- fieldtype: 'Select',
- label: 'Choose Existing Card or create New Card',
- fieldname: 'new_or_existing',
- options: ['New Card', 'Existing Card']
+ {
+ fieldtype: 'Select',
+ label: 'Choose Existing Card or create New Card',
+ fieldname: 'new_or_existing',
+ options: ['New Card', 'Existing Card']
+ },
+ {
+ fieldtype: 'Link',
+ fieldname: 'card',
+ label: 'Number Cards',
+ options: 'Number Card',
+ get_query: () => {
+ return {
+ 'query': 'frappe.desk.doctype.number_card.number_card.get_cards_for_user',
+ filters: {
+ document_type: this.document_type,
+ }
+ };
},
- {
- fieldtype: 'Link',
- fieldname: 'card',
- label: 'Number Cards',
- options: 'Number Card',
- get_query: () => {
- return {
- 'query': 'frappe.desk.doctype.number_card.number_card.get_cards_for_user',
- filters: {
- document_type: this.document_type,
- }
- };
- },
- depends_on: 'eval: doc.new_or_existing == "Existing Card"'
+ depends_on: 'eval: doc.new_or_existing == "Existing Card"'
+ },
+ {
+ fieldtype: 'Section Break',
+ fieldname: 'sb_1',
+ depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ label: 'Label',
+ fieldname: 'label',
+ fieldtype: 'Data',
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ label: 'Doctype',
+ fieldname: 'document_type',
+ fieldtype: 'Link',
+ options: 'DocType',
+ onchange: () => {
+ this.document_type = this.dialog.get_value("document_type");
+ this.set_aggregate_function_fields(this.dialog.get_values());
+ this.setup_filter(doctype);
},
- {
- fieldtype: 'Section Break',
- fieldname: 'sb_1',
- depends_on: 'eval: doc.new_or_existing == "New Card"'
- },
- {
- label: 'Label',
- fieldname: 'label',
- fieldtype: 'Data',
- mandatory_depends_on: 'eval: doc.new_or_existing == "New Card"'
- },
- {
- label: 'Doctype',
- fieldname: 'document_type',
- fieldtype: 'Link',
- options: 'DocType',
- onchange: () => {
- this.document_type = this.dialog.get_value("document_type");
- this.set_aggregate_function_fields(this.dialog.get_values());
- this.setup_filter(doctype);
- },
- hidden: 1
- },
- {
- label: 'Color',
- fieldname: 'color',
- fieldtype: 'Color'
- },
- {
- fieldtype: "Column Break",
- fieldname: "cb_1",
- },
- {
- label: 'Function',
- fieldname: 'function',
- fieldtype: 'Select',
- options: ['Count', 'Sum', 'Average', 'Minimum', 'Maximum'],
- mandatory_depends_on: 'eval: doc.new_or_existing == "New Card"'
- },
- {
- label: 'Function Based On',
- fieldname: 'aggregate_function_based_on',
- fieldtype: 'Select',
- depends_on: "eval: doc.function !== 'Count'",
- mandatory_depends_on: 'eval: doc.function !== "Count" && doc.new_or_existing == "New Card"'
- },
- {
- fieldtype: "Section Break",
- fieldname: "sb_1",
- label: 'Add Filters',
- depends_on: 'eval: doc.new_or_existing == "New Card"'
- },
- {
- fieldtype: "HTML",
- fieldname: "filter_area_loading",
- },
- {
- fieldtype: "HTML",
- fieldname: "filter_area",
- hidden: 1,
- },
- {
- fieldtype: "Section Break",
- fieldname: "sb_1",
- },
- ];
+ hidden: 1
+ },
+ {
+ label: 'Color',
+ fieldname: 'color',
+ fieldtype: 'Color'
+ },
+ {
+ fieldtype: "Column Break",
+ fieldname: "cb_1",
+ },
+ {
+ label: 'Function',
+ fieldname: 'function',
+ fieldtype: 'Select',
+ options: ['Count', 'Sum', 'Average', 'Minimum', 'Maximum'],
+ mandatory_depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ label: 'Function Based On',
+ fieldname: 'aggregate_function_based_on',
+ fieldtype: 'Select',
+ depends_on: "eval: doc.function !== 'Count'",
+ mandatory_depends_on: 'eval: doc.function !== "Count" && doc.new_or_existing == "New Card"'
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "sb_1",
+ label: 'Add Filters',
+ depends_on: 'eval: doc.new_or_existing == "New Card"'
+ },
+ {
+ fieldtype: "HTML",
+ fieldname: "filter_area_loading",
+ },
+ {
+ fieldtype: "HTML",
+ fieldname: "filter_area",
+ hidden: 1,
+ },
+ {
+ fieldtype: "Section Break",
+ fieldname: "sb_1",
+ },
+ ];
return fields;
}
From aeedefc3121bad4db2b83bb2a6da1d1116b9623b Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 18:30:04 +0530
Subject: [PATCH 16/76] refactor: simplify code
---
.../frappe/views/dashboard/dashboard_view.js | 74 ++++++++-----------
1 file changed, 32 insertions(+), 42 deletions(-)
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index dd8bfbd3fd..2f3bd98f5b 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -66,12 +66,22 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.render_dashboard();
} else {
frappe.run_serially([
- () => this.fetch_doctype_number_cards().then(cards => {
- this.number_cards = cards;
- }),
- () => this.fetch_doctype_charts().then(charts => {
- this.charts = charts;
- }),
+ () => this.fetch_dashboard_items(
+ 'Dashboard Chart',
+ {
+ chart_type: ['in', ['Count', 'Sum', 'Group By']],
+ document_type: this.doctype,
+ is_standard: true
+ },
+ 'charts'
+ ),
+ () => this.fetch_dashboard_items('Number Card',
+ {
+ document_type: this.doctype,
+ is_standard: true
+ },
+ 'number_cards'
+ ),
() => this.render_dashboard()
]);
}
@@ -96,28 +106,12 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
}
}
- fetch_doctype_charts() {
- return frappe.db.get_list('Dashboard Chart', {
- filters: {
- chart_type: ['in', ['Count', 'Sum', 'Group By']],
- document_type: this.doctype,
- is_standard: true
- },
+ fetch_dashboard_items(doctype, filters, obj_name) {
+ return frappe.db.get_list(doctype, {
+ filters: filters,
fields: ['*']
- }).then(charts => {
- return charts;
- });
- }
-
- fetch_doctype_number_cards() {
- return frappe.db.get_list('Number Card', {
- filters: {
- document_type: this.doctype,
- is_standard: true
- },
- fields: ['*']
- }).then(cards => {
- return cards;
+ }).then(items => {
+ this[obj_name] = items;
});
}
@@ -202,24 +196,20 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.number_card_group.customize();
}
+ get_widgets_to_save(widget_group) {
+ const config = widget_group.get_widget_config();
+ let widgets = [];
+ config.order.map(widget_name => {
+ widgets.push(config.widgets[widget_name]);
+ });
+ return this.remove_duplicates(widgets);
+ }
+
save_dashboard_customization() {
this.toggle_customize(false);
- const number_card_config = this.number_card_group.get_widget_config();
- let number_cards = [];
- number_card_config.order.map(card_name => {
- number_cards.push(number_card_config.widgets[card_name]);
- });
-
- const chart_config = this.chart_group.get_widget_config();
- let charts = [];
- chart_config.order.map(chart_name => {
- charts.push(chart_config.widgets[chart_name]);
- });
-
- // Don't allow duplicates of the same card or chart
- charts = this.remove_duplicates(charts);
- number_cards = this.remove_duplicates(number_cards);
+ const charts = this.get_widgets_to_save(this.chart_group);
+ const number_cards = this.get_widgets_to_save(this.number_card_group);
this.dashboard_settings = {
charts: charts,
From 5ebe5d0a2fe0684fc3682add6f12d7037302891f Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 19 Apr 2020 19:18:08 +0530
Subject: [PATCH 17/76] feat: add number cards to Dashboard
---
frappe/core/page/dashboard/dashboard.js | 54 ++++++++++++++++---
frappe/desk/doctype/dashboard/dashboard.js | 10 +++-
frappe/desk/doctype/dashboard/dashboard.json | 11 +++-
frappe/desk/doctype/dashboard/dashboard.py | 9 ++++
.../dashboard_chart/dashboard_chart.js | 1 -
.../desk/doctype/number_card/number_card.py | 32 +++++++++++
.../desk/doctype/number_card_link/__init__.py | 0
.../number_card_link/number_card_link.json | 31 +++++++++++
.../number_card_link/number_card_link.py | 10 ++++
frappe/hooks.py | 2 +
.../frappe/views/dashboard/dashboard_view.js | 2 +-
11 files changed, 151 insertions(+), 11 deletions(-)
create mode 100644 frappe/desk/doctype/number_card_link/__init__.py
create mode 100644 frappe/desk/doctype/number_card_link/number_card_link.json
create mode 100644 frappe/desk/doctype/number_card_link/number_card_link.py
diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js
index ad65b05894..0c5286597b 100644
--- a/frappe/core/page/dashboard/dashboard.js
+++ b/frappe/core/page/dashboard/dashboard.js
@@ -76,7 +76,16 @@ class Dashboard {
}
refresh() {
- this.get_permitted_dashboard_charts().then(charts => {
+ frappe.run_serially([
+ () => this.render_cards(),
+ () => this.render_charts()
+ ]);
+ }
+
+ render_charts() {
+ return this.get_permitted_items(
+ 'frappe.desk.doctype.dashboard.dashboard.get_permitted_charts'
+ ).then(charts => {
if (!charts.length) {
frappe.msgprint(__('No Permitted Charts on this Dashboard'), __('No Permitted Charts'))
}
@@ -92,6 +101,7 @@ class Dashboard {
...chart
}
});
+
this.chart_group = new frappe.widget.WidgetGroup({
title: null,
container: this.container,
@@ -110,14 +120,46 @@ class Dashboard {
});
}
- get_permitted_dashboard_charts() {
+ render_cards() {
+ return this.get_permitted_items(
+ 'frappe.desk.doctype.dashboard.dashboard.get_permitted_cards'
+ ).then(cards => {
+ if (!cards.length) {
+ frappe.msgprint(__('No Permitted Cards on this Dashboard'), __('No Permitted Cards'))
+ }
+
+ this.number_cards =
+ cards.map(card => {
+ return {
+ name: card.card,
+ }
+ });
+
+ this.number_card_group = new frappe.widget.WidgetGroup({
+ container: this.container,
+ type: "number_card",
+ columns: 3,
+ options: {
+ allow_sorting: false,
+ allow_create: false,
+ allow_delete: false,
+ allow_hiding: false,
+ allow_edit: false,
+ },
+ widgets: this.number_cards,
+ });
+ });
+ }
+
+ get_permitted_items(method) {
return frappe.xcall(
- 'frappe.desk.doctype.dashboard.dashboard.get_permitted_charts',
+ method,
{
dashboard_name: this.dashboard_name
- }).then(charts => {
- return charts;
- });
+ }).then(items => {
+ return items;
+ }
+ );
}
set_dropdown() {
diff --git a/frappe/desk/doctype/dashboard/dashboard.js b/frappe/desk/doctype/dashboard/dashboard.js
index dc2991a346..7109c7ed38 100644
--- a/frappe/desk/doctype/dashboard/dashboard.js
+++ b/frappe/desk/doctype/dashboard/dashboard.js
@@ -5,7 +5,15 @@ frappe.ui.form.on('Dashboard', {
refresh: function(frm) {
frm.add_custom_button(__("Show Dashboard"), () => frappe.set_route('dashboard', frm.doc.name));
- frm.set_query("chart", "charts", function(doc, cdt, cdn) {
+ frm.set_query("chart", "charts", function() {
+ return {
+ filters: {
+ is_standard: 1
+ }
+ };
+ });
+
+ frm.set_query("card", "cards", function() {
return {
filters: {
is_standard: 1
diff --git a/frappe/desk/doctype/dashboard/dashboard.json b/frappe/desk/doctype/dashboard/dashboard.json
index c177ee70ac..c17bc3235c 100644
--- a/frappe/desk/doctype/dashboard/dashboard.json
+++ b/frappe/desk/doctype/dashboard/dashboard.json
@@ -8,7 +8,8 @@
"field_order": [
"dashboard_name",
"is_default",
- "charts"
+ "charts",
+ "cards"
],
"fields": [
{
@@ -31,10 +32,16 @@
"label": "Charts",
"options": "Dashboard Chart Link",
"reqd": 1
+ },
+ {
+ "fieldname": "cards",
+ "fieldtype": "Table",
+ "label": "Cards",
+ "options": "Number Card Link"
}
],
"links": [],
- "modified": "2020-03-25 21:09:37.080132",
+ "modified": "2020-04-19 17:44:36.237163",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard",
diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py
index 5c344956bf..b85e135071 100644
--- a/frappe/desk/doctype/dashboard/dashboard.py
+++ b/frappe/desk/doctype/dashboard/dashboard.py
@@ -21,3 +21,12 @@ def get_permitted_charts(dashboard_name):
if frappe.has_permission('Dashboard Chart', doc=chart.chart):
permitted_charts.append(chart)
return permitted_charts
+
+@frappe.whitelist()
+def get_permitted_cards(dashboard_name):
+ permitted_cards = []
+ dashboard = frappe.get_doc('Dashboard', dashboard_name)
+ for card in dashboard.cards:
+ if frappe.has_permission('Number Card', doc=card.card):
+ permitted_cards.append(card)
+ return permitted_cards
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
index b869f60ce5..060f938f28 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
@@ -354,7 +354,6 @@ frappe.ui.form.on('Dashboard Chart', {
on_change: () => {},
});
- console.log('filters', filters);
frm.filter_group.add_filters_to_filter_group(filters);
}
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index 545e88b7c6..a1d9fd07d2 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -11,6 +11,38 @@ class NumberCard(Document):
pass
+def get_permission_query_conditions(user):
+
+ if not user:
+ user = frappe.session.user
+
+ if user == 'Administrator':
+ return
+
+ roles = frappe.get_roles(user)
+ if "System Manager" in roles:
+ return None
+
+ allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read())
+ print('allowed doctypes', allowed_doctypes)
+
+ return '''
+ `tabNumber Card`.`document_type` in {allowed_doctypes}
+ '''.format(
+ allowed_doctypes=allowed_doctypes,
+ )
+
+def has_permission(doc, ptype, user):
+ roles = frappe.get_roles(user)
+ if "System Manager" in roles:
+ return True
+
+ allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read())
+ if doc.document_type in allowed_doctypes:
+ return True
+
+ return False
+
@frappe.whitelist()
def get_result(doc):
doc = frappe.parse_json(doc)
diff --git a/frappe/desk/doctype/number_card_link/__init__.py b/frappe/desk/doctype/number_card_link/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/desk/doctype/number_card_link/number_card_link.json b/frappe/desk/doctype/number_card_link/number_card_link.json
new file mode 100644
index 0000000000..ac035b32d8
--- /dev/null
+++ b/frappe/desk/doctype/number_card_link/number_card_link.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2020-04-19 17:43:50.858343",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "card"
+ ],
+ "fields": [
+ {
+ "fieldname": "card",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Card",
+ "options": "Number Card"
+ }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-04-19 17:45:11.878472",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Number Card Link",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/number_card_link/number_card_link.py b/frappe/desk/doctype/number_card_link/number_card_link.py
new file mode 100644
index 0000000000..67ad7e70cd
--- /dev/null
+++ b/frappe/desk/doctype/number_card_link/number_card_link.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class NumberCardLink(Document):
+ pass
diff --git a/frappe/hooks.py b/frappe/hooks.py
index 2561399a78..a63fba14f9 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -89,6 +89,7 @@ permission_query_conditions = {
"Dashboard Settings": "frappe.desk.doctype.dashboard_settings.dashboard_settings.get_permission_query_conditions",
"Notification Log": "frappe.desk.doctype.notification_log.notification_log.get_permission_query_conditions",
"Dashboard Chart": "frappe.desk.doctype.dashboard_chart.dashboard_chart.get_permission_query_conditions",
+ "Number Card": "frappe.desk.doctype.number_card.number_card.get_permission_query_conditions",
"Notification Settings": "frappe.desk.doctype.notification_settings.notification_settings.get_permission_query_conditions",
"Note": "frappe.desk.doctype.note.note.get_permission_query_conditions",
"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions",
@@ -105,6 +106,7 @@ has_permission = {
"User": "frappe.core.doctype.user.user.has_permission",
"Note": "frappe.desk.doctype.note.note.has_permission",
"Dashboard Chart": "frappe.desk.doctype.dashboard_chart.dashboard_chart.has_permission",
+ "Number Card": "frappe.desk.doctype.number_card.number_card.has_permission",
"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.has_permission",
"Contact": "frappe.contacts.address_and_contact.has_permission",
"Address": "frappe.contacts.address_and_contact.has_permission",
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 2f3bd98f5b..0914838977 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -128,7 +128,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
},
default_values: {doctype: this.doctype},
widgets: this.number_cards || [],
- in_customize_mode: true,
+ in_customize_mode: this.in_customize_mode || false,
});
this.in_customize_mode && this.number_card_group.customize();
From 886d24c221f3effe53f8df660c791b6ea7c405d6 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Mon, 20 Apr 2020 16:42:33 +0530
Subject: [PATCH 18/76] fix: set max chars according to widget width
---
frappe/public/js/frappe/widgets/chart_widget.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index f0388dda19..69fb214ab0 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -20,11 +20,15 @@ export default class ChartWidget extends Widget {
refresh() {
delete this.dashboard_chart;
- this.set_title(15);
this.set_body();
this.make_chart();
}
+ set_chart_title() {
+ const max_chars = this.widget.width() < 500? 30: 60;
+ this.set_title(max_chars);
+ }
+
set_body() {
this.widget.addClass("dashboard-widget-box");
if (this.width == "Full") {
@@ -51,6 +55,8 @@ export default class ChartWidget extends Widget {
this.chart_wrapper = $(``);
this.chart_wrapper.appendTo(this.body);
+
+ this.set_chart_title();
}
set_summary() {
From 785593681b860ac8d5b5a2b882a2705f12c69bed Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 23 Apr 2020 12:43:57 +0530
Subject: [PATCH 19/76] fix: reduce max char for chart title
---
frappe/public/js/frappe/widgets/chart_widget.js | 2 +-
frappe/public/js/frappe/widgets/widget_dialog.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index 69fb214ab0..b31949cd9d 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -25,7 +25,7 @@ export default class ChartWidget extends Widget {
}
set_chart_title() {
- const max_chars = this.widget.width() < 500? 30: 60;
+ const max_chars = this.widget.width() < 500? 20: 60;
this.set_title(max_chars);
}
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index adfdacabbd..a9ea8637cf 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -304,7 +304,7 @@ class NumberCardDialog extends WidgetDialog {
onchange: () => {
this.document_type = this.dialog.get_value("document_type");
this.set_aggregate_function_fields(this.dialog.get_values());
- this.setup_filter(doctype);
+ this.setup_filter(this.document_type);
},
hidden: 1
},
From 9c7763ee21bc946df73bd4c91ecb9752accb1032 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 23 Apr 2020 14:10:28 +0530
Subject: [PATCH 20/76] fix: use is_public and don't set as hidden
---
frappe/desk/doctype/dashboard/dashboard.js | 4 ++--
.../dashboard_chart/dashboard_chart.js | 5 -----
.../dashboard_chart/dashboard_chart.json | 15 +++++++------
.../dashboard_chart/dashboard_chart.py | 9 ++++++--
.../desk/doctype/number_card/number_card.js | 6 -----
.../desk/doctype/number_card/number_card.json | 22 +++++++++----------
.../desk/doctype/number_card/number_card.py | 5 ++---
frappe/patches.txt | 2 +-
...et_existing_dashboard_charts_as_public.py} | 2 +-
.../frappe/views/dashboard/dashboard_view.js | 6 ++---
10 files changed, 34 insertions(+), 42 deletions(-)
rename frappe/patches/v13_0/{set_existing_dashboard_charts_as_standard.py => set_existing_dashboard_charts_as_public.py} (93%)
diff --git a/frappe/desk/doctype/dashboard/dashboard.js b/frappe/desk/doctype/dashboard/dashboard.js
index 7109c7ed38..609e943995 100644
--- a/frappe/desk/doctype/dashboard/dashboard.js
+++ b/frappe/desk/doctype/dashboard/dashboard.js
@@ -8,7 +8,7 @@ frappe.ui.form.on('Dashboard', {
frm.set_query("chart", "charts", function() {
return {
filters: {
- is_standard: 1
+ is_public: 1
}
};
});
@@ -16,7 +16,7 @@ frappe.ui.form.on('Dashboard', {
frm.set_query("card", "cards", function() {
return {
filters: {
- is_standard: 1
+ is_public: 1
}
};
});
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
index 060f938f28..f8d5886b26 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
@@ -9,11 +9,6 @@ frappe.ui.form.on('Dashboard Chart', {
frm.add_fetch('source', 'timeseries', 'timeseries');
},
- before_save: function(frm) {
- if (frm.is_new() && (frappe.user.has_role('System Manager') || frappe.user.has_role('Dashboard Manager'))) {
- frm.doc.is_standard = 1;
- }
- },
refresh: function(frm) {
frm.chart_filters = null;
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json
index 7530d70fdc..75941a9019 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json
@@ -22,6 +22,7 @@
"aggregate_function_based_on",
"number_of_groups",
"column_break_6",
+ "is_public",
"timespan",
"from_date",
"to_date",
@@ -35,8 +36,7 @@
"color",
"custom_options",
"section_break_10",
- "last_synced_on",
- "is_standard"
+ "last_synced_on"
],
"fields": [
{
@@ -100,7 +100,7 @@
},
{
"default": "0",
- "depends_on": "eval:doc.chart_type !== 'Group By'",
+ "depends_on": "eval: ['Count', 'Sum', 'Average'].includes(doc.chart_type)",
"fieldname": "timeseries",
"fieldtype": "Check",
"label": "Time Series"
@@ -224,15 +224,15 @@
},
{
"default": "0",
- "fieldname": "is_standard",
+ "description": "This chart will be public to all Users if this is set",
+ "fieldname": "is_public",
"fieldtype": "Check",
- "hidden": 1,
- "label": "Is Standard",
+ "label": "Is Public",
"permlevel": 1
}
],
"links": [],
- "modified": "2020-04-17 18:11:33.486037",
+ "modified": "2020-04-23 13:01:07.178866",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard Chart",
@@ -263,6 +263,7 @@
"write": 1
},
{
+ "create": 1,
"email": 1,
"export": 1,
"print": 1,
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
index 8242e3752d..9636dcb347 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
@@ -96,8 +96,6 @@ def create_dashboard_chart(args):
args = frappe.parse_json(args)
doc = frappe.new_doc('Dashboard Chart')
roles = frappe.get_roles(frappe.session.user)
- if 'Sytem Manager' in roles or 'Dashboard Manager' in roles:
- doc.is_standard = 1
doc.update(args)
@@ -364,6 +362,13 @@ def get_year_ending(date):
# last day of this month
return add_to_date(date, days=-1)
+def get_charts_for_user(doctype, txt, searchfield, start, page_len, filters):
+ or_filters = {'owner': frappe.session.user, 'is_public': 1}
+ return frappe.db.get_list('Dashboard Chart',
+ fields=['name'],
+ filters=filters,
+ or_filters=or_filters,
+ as_list = 1)
class DashboardChart(Document):
diff --git a/frappe/desk/doctype/number_card/number_card.js b/frappe/desk/doctype/number_card/number_card.js
index 2bf99b9107..a4823e2692 100644
--- a/frappe/desk/doctype/number_card/number_card.js
+++ b/frappe/desk/doctype/number_card/number_card.js
@@ -8,12 +8,6 @@ frappe.ui.form.on('Number Card', {
frm.trigger('render_filters_table');
},
- before_save: function(frm) {
- if (frm.is_new() && (frappe.user.has_role('System Manager') || frappe.user.has_role('Dashboard Manager'))) {
- frm.doc.is_standard = 1;
- }
- },
-
document_type: function(frm) {
frm.set_query('document_type', function() {
return {
diff --git a/frappe/desk/doctype/number_card/number_card.json b/frappe/desk/doctype/number_card/number_card.json
index 222e4ed942..6af3f34aaf 100644
--- a/frappe/desk/doctype/number_card/number_card.json
+++ b/frappe/desk/doctype/number_card/number_card.json
@@ -9,12 +9,12 @@
"label",
"function",
"column_break_2",
+ "is_public",
"document_type",
"aggregate_function_based_on",
"filters_section",
"filters_json",
- "color",
- "is_standard"
+ "color"
],
"fields": [
{
@@ -58,14 +58,6 @@
"fieldtype": "Color",
"label": "Color"
},
- {
- "default": "0",
- "fieldname": "is_standard",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Is Standard",
- "permlevel": 1
- },
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
@@ -74,10 +66,18 @@
"fieldname": "filters_section",
"fieldtype": "Section Break",
"label": "Filters Section"
+ },
+ {
+ "default": "0",
+ "description": "This card will be public to all Users if this is set",
+ "fieldname": "is_public",
+ "fieldtype": "Check",
+ "label": "Is Public",
+ "permlevel": 1
}
],
"links": [],
- "modified": "2020-04-18 14:12:06.949445",
+ "modified": "2020-04-23 13:03:57.103284",
"modified_by": "Administrator",
"module": "Desk",
"name": "Number Card",
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index a1d9fd07d2..f8f2548a49 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -73,14 +73,13 @@ def create_number_card(args):
args = frappe.parse_json(args)
doc = frappe.new_doc('Number Card')
roles = frappe.get_roles(frappe.session.user)
- if 'Sytem Manager' in roles or 'Dashboard Manager' in roles:
- doc.is_standard = 1
+
doc.update(args)
doc.insert(ignore_permissions=True)
return doc
def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
- or_filters = {'owner': frappe.session.user, 'is_standard': 1}
+ or_filters = {'owner': frappe.session.user, 'is_public': 1}
return frappe.db.get_list('Number Card',
fields=['name', 'label'],
filters=filters,
diff --git a/frappe/patches.txt b/frappe/patches.txt
index c5daf872b1..97a79df889 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -273,4 +273,4 @@ execute:frappe.delete_doc_if_exists('DocType', 'GCalendar Settings')
frappe.patches.v12_0.remove_parent_and_parenttype_from_print_formats
execute:from frappe.desk.page.setup_wizard.install_fixtures import update_genders;update_genders()
frappe.patches.v13_0.website_theme_custom_scss
-frappe.patches.v13_0.set_existing_dashboard_charts_as_standard
+frappe.patches.v13_0.set_existing_dashboard_charts_as_public
diff --git a/frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py b/frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py
similarity index 93%
rename from frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py
rename to frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py
index 0e7abea8bd..29732688c7 100644
--- a/frappe/patches/v13_0/set_existing_dashboard_charts_as_standard.py
+++ b/frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py
@@ -20,7 +20,7 @@ def execute():
UPDATE
`tabDashboard Chart`
SET
- `tabDashboard Chart`.`is_standard`=1
+ `tabDashboard Chart`.`is_public`=1
WHERE
`tabDashboard Chart`.owner in {users}
""".format(users=users)
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 0914838977..f545fd7bd3 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -71,14 +71,12 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
{
chart_type: ['in', ['Count', 'Sum', 'Group By']],
document_type: this.doctype,
- is_standard: true
},
'charts'
),
() => this.fetch_dashboard_items('Number Card',
{
document_type: this.doctype,
- is_standard: true
},
'number_cards'
),
@@ -258,9 +256,9 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
fieldtype: 'Link',
get_query: () => {
return {
+ 'query': 'frappe.desk.doctype.dashboard_chart.dashboard_chart.get_charts_for_user',
filters: {
- document_type: this.doctype,
- is_standard: 1,
+ document_type: this.doctype,
}
};
},
From 299b24f09c48fbe8141340a096f97208161a6bee Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sat, 25 Apr 2020 16:38:16 +0530
Subject: [PATCH 21/76] feat: add stats to number cards
---
.../desk/doctype/number_card/number_card.json | 31 ++++-
.../desk/doctype/number_card/number_card.py | 44 ++++++-
.../js/frappe/widgets/number_card_widget.js | 116 ++++++++++++++----
frappe/public/js/frappe/widgets/utils.js | 17 ++-
frappe/public/less/desktop.less | 34 +++--
5 files changed, 203 insertions(+), 39 deletions(-)
diff --git a/frappe/desk/doctype/number_card/number_card.json b/frappe/desk/doctype/number_card/number_card.json
index 6af3f34aaf..d97e711d28 100644
--- a/frappe/desk/doctype/number_card/number_card.json
+++ b/frappe/desk/doctype/number_card/number_card.json
@@ -8,10 +8,13 @@
"field_order": [
"label",
"function",
- "column_break_2",
- "is_public",
- "document_type",
"aggregate_function_based_on",
+ "column_break_2",
+ "document_type",
+ "is_public",
+ "stats_section",
+ "show_percentage_stats",
+ "stats_time_interval",
"filters_section",
"filters_json",
"color"
@@ -74,10 +77,30 @@
"fieldtype": "Check",
"label": "Is Public",
"permlevel": 1
+ },
+ {
+ "default": "1",
+ "fieldname": "show_percentage_stats",
+ "fieldtype": "Check",
+ "label": "Show Percentage Stats"
+ },
+ {
+ "default": "Daily",
+ "depends_on": "eval: doc.show_percentage_stats",
+ "description": "Show percentage difference according to this time interval",
+ "fieldname": "stats_time_interval",
+ "fieldtype": "Select",
+ "label": "Stats Time Interval",
+ "options": "Daily\nWeekly\nMonthly\nYearly"
+ },
+ {
+ "fieldname": "stats_section",
+ "fieldtype": "Section Break",
+ "label": "Stats"
}
],
"links": [],
- "modified": "2020-04-23 13:03:57.103284",
+ "modified": "2020-04-25 15:59:57.004756",
"modified_by": "Administrator",
"module": "Desk",
"name": "Number Card",
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index f8f2548a49..a6104c3b01 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -24,7 +24,6 @@ def get_permission_query_conditions(user):
return None
allowed_doctypes = tuple(frappe.permissions.get_doctypes_with_read())
- print('allowed doctypes', allowed_doctypes)
return '''
`tabNumber Card`.`document_type` in {allowed_doctypes}
@@ -44,7 +43,7 @@ def has_permission(doc, ptype, user):
return False
@frappe.whitelist()
-def get_result(doc):
+def get_result(doc, to_date=None):
doc = frappe.parse_json(doc)
fields = []
sql_function_map = {
@@ -63,16 +62,51 @@ def get_result(doc):
fields = ['{function}({based_on}) as result'.format(function=function, based_on=doc.aggregate_function_based_on)]
filters = frappe.parse_json(doc.filters_json)
- number = frappe.db.get_all(doc.document_type, fields = fields, filters = filters)[0]['result']
- number = round(number, 2) if isinstance(number, float) else number
+ if to_date:
+ filters.append([doc.document_type, 'creation', '<', to_date, False])
+
+ number = frappe.db.get_all(doc.document_type, fields = fields, filters = filters)[0]['result']
+
+ frappe.db.set_value('Number Card', doc.name, 'previous_result', number)
+ return number
+
+@frappe.whitelist()
+def get_percentage_difference(doc, result):
+ doc = frappe.parse_json(doc)
+ result = frappe.parse_json(result)
+
+ doc = frappe.get_doc('Number Card', doc.name)
+
+ if not doc.get('show_percentage_stats'):
+ return
+
+ previous_result = calculate_previous_result(doc)
+ difference = (result - previous_result)/100.0
+
+ return difference
+
+
+def calculate_previous_result(doc):
+ from frappe.utils import add_to_date
+
+ current_date = frappe.utils.now()
+ if doc.stats_time_interval == 'Daily':
+ previous_date = frappe.utils.add_to_date(current_date, days=-1)
+ elif doc.stats_time_interval == 'Weekly':
+ previous_date = frappe.utils.add_to_date(current_date, weeks=-1)
+ elif doc.stats_time_interval == 'Monthly':
+ previous_date = frappe.utils.add_to_date(current_date, months=-1)
+ else:
+ previous_date = frappe.utils.add_to_date(current_date, years=-1)
+
+ number = get_result(doc, previous_date)
return number
@frappe.whitelist()
def create_number_card(args):
args = frappe.parse_json(args)
doc = frappe.new_doc('Number Card')
- roles = frappe.get_roles(frappe.session.user)
doc.update(args)
doc.insert(ignore_permissions=True)
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
index 3de6a0c0eb..86b7fa687a 100644
--- a/frappe/public/js/frappe/widgets/number_card_widget.js
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -1,5 +1,5 @@
import Widget from "./base_widget.js";
-import { go_to_list_with_filters } from "./utils";
+import { go_to_list_with_filters, shorten_number } from "./utils";
export default class NumberCardWidget extends Widget {
constructor(opts) {
@@ -21,25 +21,19 @@ export default class NumberCardWidget extends Widget {
set_body() {
this.widget.addClass("number-widget-box");
- $(this.body).html(`
- ${__('Loading...')}
-
`);
this.make_card();
}
+ set_title() {
+ $(this.title_field).html(`${this.card_doc.label}
`);
+ }
+
make_card() {
frappe.model.with_doc("Number Card", this.name).then(card => {
if (!card) {
if (this.document_type) {
- this.set_doc_args();
- frappe.xcall(
- 'frappe.desk.doctype.number_card.number_card.create_number_card',
- {'args': this.card_doc}
- ).then(doc => {
- this.name = doc.name;
- this.widget.attr('data-widget-name', this.name);
- this.render_card();
- });
+ this.create_number_card();
+ this.render_card();
} else {
// widget doesn't exist so delete
this.delete(false);
@@ -49,10 +43,22 @@ export default class NumberCardWidget extends Widget {
this.card_doc = card;
this.render_card();
}
+
this.set_events();
});
}
+ create_number_card() {
+ this.set_doc_args();
+ frappe.xcall(
+ 'frappe.desk.doctype.number_card.number_card.create_number_card',
+ {'args': this.card_doc}
+ ).then(doc => {
+ this.name = doc.name;
+ this.widget.attr('data-widget-name', this.name);
+ });
+ }
+
set_events() {
$(this.body).click(() => {
if (this.in_customize_mode) return;
@@ -74,25 +80,91 @@ export default class NumberCardWidget extends Widget {
render_card() {
this.prepare_actions();
- this.get_number().then(() => {
- $(this.body).html(`
-
${this.number_html}
-
${this.card_doc.label}
-
`);
- });
+ this.set_title();
+ this.set_loading_state();
+
+ frappe.run_serially([
+ () => this.render_number(),
+ () => this.render_stats(),
+ ]);
+ }
+
+ set_loading_state() {
+ $(this.body).html(`
+ ${__('Loading...')}
+
`);
}
get_number() {
return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_result', {doc: this.card_doc}).then(res => {
- const number_order = res.toFixed().length - 1;
- if (number_order > 12) {
- this.number_html = `≈ 10${number_order}`;
+ this.number = res;
+ if (this.card_doc.function !== 'Count') {
+ this.get_formatted_number(res);
} else {
this.number_html = res;
}
});
}
+ get_formatted_number(number) {
+ frappe.model.with_doctype(this.card_doc.document_type, () => {
+ const based_on_df =
+ frappe.meta.get_docfield(this.card_doc.document_type, this.card_doc.aggregate_function_based_on);
+ const shortened_number = shorten_number(number);
+ let number_parts = shortened_number.split(' ');
+
+ const symbol = number_parts[1] || '';
+ const formatted_number = $(frappe.format(number_parts[0], based_on_df)).text();
+
+ this.number_html = formatted_number + ' ' + symbol;
+ });
+
+ }
+
+ render_number() {
+ return this.get_number().then(() => {
+ $(this.body).html(``);
+ });
+ }
+
+ render_stats() {
+ let caret_html ='';
+ let color_class = '';
+
+ return this.get_percentage_stats().then(() => {
+ if (this.percentage_stat == undefined) return;
+
+ if (this.percentage_stat == 0) {
+ color_class = 'grey-stat';
+ } else if (this.percentage_stat > 0) {
+ caret_html = '';
+ color_class = 'green-stat';
+ } else {
+ caret_html = '';
+ color_class = 'red-stat';
+ }
+
+ $(this.body).find('.widget-content').append(`
+ ${caret_html}
+ ${Math.abs(this.percentage_stat)} %
+
`)
+ });
+ }
+
+ get_percentage_stats() {
+ return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_percentage_difference',
+ {
+ doc: this.card_doc,
+ result: this.number
+ }).then(res => {
+ if (res !== undefined) {
+ this.percentage_stat = +res.toFixed(2);
+ }
+ });
+ }
+
prepare_actions() {
let actions = [
{
diff --git a/frappe/public/js/frappe/widgets/utils.js b/frappe/public/js/frappe/widgets/utils.js
index f408ecfdd2..a3f6726264 100644
--- a/frappe/public/js/frappe/widgets/utils.js
+++ b/frappe/public/js/frappe/widgets/utils.js
@@ -123,4 +123,19 @@ function go_to_list_with_filters(doctype, filters) {
})
}
-export { generate_route, generate_grid, build_summary_item, go_to_list_with_filters };
\ No newline at end of file
+function shorten_number(number) {
+ let x = Math.abs(Math.round(number))
+
+ switch(true) {
+ case x >= 1.0e+12:
+ return Math.round(number/1.0e+12) + " T";
+ case x >= 1.0e+9:
+ return Math.round(number/1.0e+9) + " B";
+ case x >= 1.0e+6:
+ return Math.round(number/1.0e+6) + " M";
+ default:
+ return number.toFixed();
+ }
+}
+
+export { generate_route, generate_grid, build_summary_item, go_to_list_with_filters, shorten_number };
\ No newline at end of file
diff --git a/frappe/public/less/desktop.less b/frappe/public/less/desktop.less
index 081dbb64fb..414d6133fa 100644
--- a/frappe/public/less/desktop.less
+++ b/frappe/public/less/desktop.less
@@ -395,38 +395,42 @@
&.number-widget-box {
cursor: pointer;
- height: 110px;
+ height: 100px;
+ padding: 10px;
&:hover {
border-color: @text-muted;
}
.widget-head {
- position: relative;
.widget-title {
- display: none;
+ font-weight: normal;
+ color: @text-muted;
+ font-size: 14px;
+ margin-top: -5px;
}
.widget-control {
- position: absolute;
right: -10px;
top: -10px;
}
}
.widget-body {
- text-align: center;
+ text-align: left;
.number-card-loading {
display: flex;
- height: 80px;
+ height: 50px;
align-items: center;
justify-content: center;
}
.widget-content {
- padding: 5px;
+ padding-top: 15px;
+ display: flex;
+ justify-content: space-between;
.number {
font-size: 30px;
@@ -438,6 +442,22 @@
padding: 5px;
font-size: 14px;
}
+
+ .card-stats {
+ padding-top: 20px;
+ }
+
+ .green-stat {
+ color: green;
+ }
+
+ .red-stat {
+ color: @red;
+ }
+
+ .grey-stat {
+ color: @text-muted;
+ }
}
}
}
From ba83728412b05a162c015a9d55b1e2d0c9f1842b Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sat, 25 Apr 2020 17:06:01 +0530
Subject: [PATCH 22/76] fix: fix rendering of formatted number
---
.../js/frappe/widgets/number_card_widget.js | 23 +++++++++----------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
index 86b7fa687a..ed124ef656 100644
--- a/frappe/public/js/frappe/widgets/number_card_widget.js
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -99,26 +99,25 @@ export default class NumberCardWidget extends Widget {
return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_result', {doc: this.card_doc}).then(res => {
this.number = res;
if (this.card_doc.function !== 'Count') {
- this.get_formatted_number(res);
+ return frappe.model.with_doctype(this.card_doc.document_type, () => {
+ this.get_formatted_number();
+ });
} else {
this.number_html = res;
}
});
}
- get_formatted_number(number) {
- frappe.model.with_doctype(this.card_doc.document_type, () => {
- const based_on_df =
- frappe.meta.get_docfield(this.card_doc.document_type, this.card_doc.aggregate_function_based_on);
- const shortened_number = shorten_number(number);
- let number_parts = shortened_number.split(' ');
+ get_formatted_number() {
+ const based_on_df =
+ frappe.meta.get_docfield(this.card_doc.document_type, this.card_doc.aggregate_function_based_on);
+ const shortened_number = shorten_number(this.number);
+ let number_parts = shortened_number.split(' ');
- const symbol = number_parts[1] || '';
- const formatted_number = $(frappe.format(number_parts[0], based_on_df)).text();
-
- this.number_html = formatted_number + ' ' + symbol;
- });
+ const symbol = number_parts[1] || '';
+ const formatted_number = $(frappe.format(number_parts[0], based_on_df)).text();
+ this.number_html = formatted_number + ' ' + symbol;
}
render_number() {
From fde95c7f3ae05af4a7b65127df79c08689728cd4 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sat, 25 Apr 2020 17:16:15 +0530
Subject: [PATCH 23/76] fix: decrease font size of number
---
frappe/public/less/desktop.less | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/frappe/public/less/desktop.less b/frappe/public/less/desktop.less
index 414d6133fa..fd24ec02bb 100644
--- a/frappe/public/less/desktop.less
+++ b/frappe/public/less/desktop.less
@@ -428,12 +428,12 @@
}
.widget-content {
- padding-top: 15px;
+ padding-top: 20px;
display: flex;
justify-content: space-between;
.number {
- font-size: 30px;
+ font-size: 26px;
font-weight: bold;
}
@@ -444,7 +444,7 @@
}
.card-stats {
- padding-top: 20px;
+ padding-top: 15px;
}
.green-stat {
From 2916eeb5620956c8a364a3cec41ec8435b188b3b Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sat, 25 Apr 2020 17:45:59 +0530
Subject: [PATCH 24/76] fix: add label and document type to search fields
---
frappe/desk/doctype/number_card/number_card.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/frappe/desk/doctype/number_card/number_card.json b/frappe/desk/doctype/number_card/number_card.json
index d97e711d28..8d04ce07f5 100644
--- a/frappe/desk/doctype/number_card/number_card.json
+++ b/frappe/desk/doctype/number_card/number_card.json
@@ -100,7 +100,7 @@
}
],
"links": [],
- "modified": "2020-04-25 15:59:57.004756",
+ "modified": "2020-04-25 17:31:34.204607",
"modified_by": "Administrator",
"module": "Desk",
"name": "Number Card",
@@ -140,6 +140,7 @@
"share": 1
}
],
+ "search_fields": "label, document_type",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "label",
From 3c63423331681c8afce9f2b92ca8c254b964c566 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 26 Apr 2020 12:21:42 +0530
Subject: [PATCH 25/76] fix: remove msgprint for cards in dashboard
---
frappe/core/page/dashboard/dashboard.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js
index 0c5286597b..c6d697aa38 100644
--- a/frappe/core/page/dashboard/dashboard.js
+++ b/frappe/core/page/dashboard/dashboard.js
@@ -125,7 +125,7 @@ class Dashboard {
'frappe.desk.doctype.dashboard.dashboard.get_permitted_cards'
).then(cards => {
if (!cards.length) {
- frappe.msgprint(__('No Permitted Cards on this Dashboard'), __('No Permitted Cards'))
+ return;
}
this.number_cards =
From d43ec4320bebc150a6a3f9f6440f64084d51a937 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 26 Apr 2020 12:23:11 +0530
Subject: [PATCH 26/76] fix: fix indent
---
frappe/core/page/dashboard/dashboard.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js
index c6d697aa38..7733ee5bcf 100644
--- a/frappe/core/page/dashboard/dashboard.js
+++ b/frappe/core/page/dashboard/dashboard.js
@@ -129,10 +129,10 @@ class Dashboard {
}
this.number_cards =
- cards.map(card => {
- return {
- name: card.card,
- }
+ cards.map(card => {
+ return {
+ name: card.card,
+ };
});
this.number_card_group = new frappe.widget.WidgetGroup({
From de8f7faa477cf8ff9d2e6d090456af3877304360 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 26 Apr 2020 12:25:17 +0530
Subject: [PATCH 27/76] fix: remove unused imports and variables
---
frappe/desk/doctype/dashboard_chart/dashboard_chart.py | 1 -
frappe/desk/doctype/number_card/number_card.py | 9 ++++-----
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
index 9636dcb347..f97f1e6edf 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py
@@ -95,7 +95,6 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
def create_dashboard_chart(args):
args = frappe.parse_json(args)
doc = frappe.new_doc('Dashboard Chart')
- roles = frappe.get_roles(frappe.session.user)
doc.update(args)
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index a6104c3b01..ecdcc56929 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -5,7 +5,6 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
-from frappe.model.naming import append_number_if_name_exists
class NumberCard(Document):
pass
@@ -92,13 +91,13 @@ def calculate_previous_result(doc):
current_date = frappe.utils.now()
if doc.stats_time_interval == 'Daily':
- previous_date = frappe.utils.add_to_date(current_date, days=-1)
+ previous_date = add_to_date(current_date, days=-1)
elif doc.stats_time_interval == 'Weekly':
- previous_date = frappe.utils.add_to_date(current_date, weeks=-1)
+ previous_date = add_to_date(current_date, weeks=-1)
elif doc.stats_time_interval == 'Monthly':
- previous_date = frappe.utils.add_to_date(current_date, months=-1)
+ previous_date = add_to_date(current_date, months=-1)
else:
- previous_date = frappe.utils.add_to_date(current_date, years=-1)
+ previous_date = add_to_date(current_date, years=-1)
number = get_result(doc, previous_date)
return number
From ec7fc7f3450280f747c8f786afc904e16a33d792 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 26 Apr 2020 12:37:40 +0530
Subject: [PATCH 28/76] fix: code formatting
---
.../desk/doctype/number_card/number_card.js | 2 +-
.../public/js/frappe/utils/dashboard_utils.js | 11 ++--
.../frappe/views/dashboard/dashboard_view.js | 11 ++--
.../public/js/frappe/widgets/base_widget.js | 3 +-
.../js/frappe/widgets/number_card_widget.js | 51 ++++++++++---------
frappe/public/js/frappe/widgets/utils.js | 4 +-
6 files changed, 45 insertions(+), 37 deletions(-)
diff --git a/frappe/desk/doctype/number_card/number_card.js b/frappe/desk/doctype/number_card/number_card.js
index a4823e2692..812582ae05 100644
--- a/frappe/desk/doctype/number_card/number_card.js
+++ b/frappe/desk/doctype/number_card/number_card.js
@@ -14,7 +14,7 @@ frappe.ui.form.on('Number Card', {
filters: {
'issingle': false
}
- }
+ };
});
frm.set_value('filters_json', '[]');
frm.set_value('aggregate_function_based_on', '');
diff --git a/frappe/public/js/frappe/utils/dashboard_utils.js b/frappe/public/js/frappe/utils/dashboard_utils.js
index 16f54a1f84..a1628be34a 100644
--- a/frappe/public/js/frappe/utils/dashboard_utils.js
+++ b/frappe/public/js/frappe/utils/dashboard_utils.js
@@ -57,9 +57,10 @@ frappe.dashboard_utils = {
},
get_dashboard_settings() {
- return frappe.db.get_list('Dashboard Settings',
- {
- filters: {name: frappe.session.user},
+ return frappe.db.get_list('Dashboard Settings', {
+ filters: {
+ name: frappe.session.user
+ },
fields: ['*']
}).then(settings => {
if (!settings.length) {
@@ -75,7 +76,9 @@ frappe.dashboard_utils = {
create_dashboard_settings() {
return frappe.xcall(
'frappe.desk.doctype.dashboard_settings.dashboard_settings.create_dashboard_settings',
- {user: frappe.session.user}
+ {
+ user: frappe.session.user
+ }
).then(settings => {
return settings;
});
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index f545fd7bd3..a3fe931f49 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -96,7 +96,6 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
});
this.render_dashboard_charts();
});
-
this.render_number_cards();
if (!this.charts.length && !this.number_cards.length) {
@@ -173,7 +172,6 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
`;
this.$dashboard_wrapper.append(empty_state_html);
-
this.$empty_state = this.$dashboard_wrapper.find('.empty-dashboard');
}
@@ -182,7 +180,6 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
return;
}
-
if (this.$empty_state) {
this.$empty_state.remove();
}
@@ -228,7 +225,8 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.dashboard_settings = null;
frappe.model.user_settings.save(
this.doctype, 'dashboard_settings', this.dashboard_settings
- ).then(()=> this.make_dashboard());
+ ).then(() => this.make_dashboard());
+
this.toggle_customize(false);
}
@@ -435,16 +433,17 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
group_by_fields.push({label: df.label, value: df.fieldname});
}
});
+
return {
date_fields: date_fields,
value_fields: value_fields,
group_by_fields: group_by_fields,
aggregate_function_fields: aggregate_function_fields
- }
+ };
}
remove_duplicates(items) {
return items.filter((item, index) => items.indexOf(item) === index);
}
-}
+};
diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js
index 7b3770ec9e..e2df42472a 100644
--- a/frappe/public/js/frappe/widgets/base_widget.js
+++ b/frappe/public/js/frappe/widgets/base_widget.js
@@ -113,7 +113,8 @@ export default class Widget {
let remove_widget = () => {
this.widget.remove();
this.options.on_delete && this.options.on_delete(this.name);
- }
+ };
+
if (animate) {
this.widget.addClass("zoomOutDelete");
// wait for animation
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
index ed124ef656..9eb985ca82 100644
--- a/frappe/public/js/frappe/widgets/number_card_widget.js
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -19,7 +19,7 @@ export default class NumberCardWidget extends Widget {
this.set_body();
}
- set_body() {
+ set_body() {
this.widget.addClass("number-widget-box");
this.make_card();
}
@@ -29,7 +29,7 @@ export default class NumberCardWidget extends Widget {
}
make_card() {
- frappe.model.with_doc("Number Card", this.name).then(card => {
+ frappe.model.with_doc('Number Card', this.name).then(card => {
if (!card) {
if (this.document_type) {
this.create_number_card();
@@ -52,7 +52,9 @@ export default class NumberCardWidget extends Widget {
this.set_doc_args();
frappe.xcall(
'frappe.desk.doctype.number_card.number_card.create_number_card',
- {'args': this.card_doc}
+ {
+ 'args': this.card_doc
+ }
).then(doc => {
this.name = doc.name;
this.widget.attr('data-widget-name', this.name);
@@ -96,7 +98,9 @@ export default class NumberCardWidget extends Widget {
}
get_number() {
- return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_result', {doc: this.card_doc}).then(res => {
+ return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_result', {
+ doc: this.card_doc
+ }).then(res => {
this.number = res;
if (this.card_doc.function !== 'Count') {
return frappe.model.with_doctype(this.card_doc.document_type, () => {
@@ -148,43 +152,43 @@ export default class NumberCardWidget extends Widget {
$(this.body).find('.widget-content').append(`
${caret_html}
${Math.abs(this.percentage_stat)} %
-
`)
+ `);
});
}
get_percentage_stats() {
- return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_percentage_difference',
- {
- doc: this.card_doc,
- result: this.number
- }).then(res => {
- if (res !== undefined) {
- this.percentage_stat = +res.toFixed(2);
- }
+ return frappe.xcall('frappe.desk.doctype.number_card.number_card.get_percentage_difference', {
+ doc: this.card_doc,
+ result: this.number
+ }).then(res => {
+ if (res !== undefined) {
+ this.percentage_stat = +res.toFixed(2);
+ }
});
}
prepare_actions() {
let actions = [
{
- label: __("Refresh"),
- action: "action-refresh",
+ label: __('Refresh'),
+ action: 'action-refresh',
handler: () => {
this.render_card();
}
},
{
- label: __("Edit"),
- action: "action-edit",
+ label: __('Edit'),
+ action: 'action-edit',
handler: () => {
frappe.set_route(
- "Form",
- "Number Card",
+ 'Form',
+ 'Number Card',
this.name
);
}
},
];
+
this.set_card_actions(actions);
}
@@ -196,11 +200,11 @@ export default class NumberCardWidget extends Widget {
`);
@@ -209,6 +213,7 @@ export default class NumberCardWidget extends Widget {
const action = o.dataset.action;
$(o).click(actions.find(a => a.action === action));
});
+
this.action_area.html(this.card_actions);
}
}
diff --git a/frappe/public/js/frappe/widgets/utils.js b/frappe/public/js/frappe/widgets/utils.js
index a3f6726264..039ede305a 100644
--- a/frappe/public/js/frappe/widgets/utils.js
+++ b/frappe/public/js/frappe/widgets/utils.js
@@ -124,9 +124,9 @@ function go_to_list_with_filters(doctype, filters) {
}
function shorten_number(number) {
- let x = Math.abs(Math.round(number))
+ let x = Math.abs(Math.round(number));
- switch(true) {
+ switch (true) {
case x >= 1.0e+12:
return Math.round(number/1.0e+12) + " T";
case x >= 1.0e+9:
From d2a9f1b46e17b7f4c631e0494feec862aac789a0 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Sun, 26 Apr 2020 16:08:48 +0530
Subject: [PATCH 29/76] fix: indent
---
frappe/core/page/dashboard/dashboard.js | 6 ++--
.../js/frappe/widgets/number_card_widget.js | 28 +++++++++----------
frappe/public/js/frappe/widgets/utils.js | 2 +-
3 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js
index 7733ee5bcf..222a31a863 100644
--- a/frappe/core/page/dashboard/dashboard.js
+++ b/frappe/core/page/dashboard/dashboard.js
@@ -156,10 +156,10 @@ class Dashboard {
method,
{
dashboard_name: this.dashboard_name
- }).then(items => {
- return items;
}
- );
+ ).then(items => {
+ return items;
+ });
}
set_dropdown() {
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
index 9eb985ca82..4766d97e92 100644
--- a/frappe/public/js/frappe/widgets/number_card_widget.js
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -193,21 +193,19 @@ export default class NumberCardWidget extends Widget {
}
set_card_actions(actions) {
- this.card_actions = $(`
- `);
+ this.card_actions =
+ $(``);
this.card_actions.find("a[data-action]").each((i, o) => {
const action = o.dataset.action;
diff --git a/frappe/public/js/frappe/widgets/utils.js b/frappe/public/js/frappe/widgets/utils.js
index 039ede305a..59067bd9a0 100644
--- a/frappe/public/js/frappe/widgets/utils.js
+++ b/frappe/public/js/frappe/widgets/utils.js
@@ -120,7 +120,7 @@ function go_to_list_with_filters(doctype, filters) {
let filter_area = list_view.filter_area;
filter_area.clear();
filter_area.filter_list.add_filters_to_filter_group(filters);
- })
+ });
}
function shorten_number(number) {
From 6c7127000cb0de77194026ddff3243d25d2b4549 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Mon, 27 Apr 2020 19:17:48 +0530
Subject: [PATCH 30/76] fix(patch): reload doctype in patch
---
frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py b/frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py
index 29732688c7..36e83e14e3 100644
--- a/frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py
+++ b/frappe/patches/v13_0/set_existing_dashboard_charts_as_public.py
@@ -1,6 +1,8 @@
import frappe
def execute():
+ frappe.reload_doctype('Dashboard Chart')
+
if not frappe.db.table_exists('Dashboard Chart'):
return
From 099a9bf851790af67d1870fbf9e1caec5556bb8b Mon Sep 17 00:00:00 2001
From: prssanna
Date: Mon, 27 Apr 2020 22:51:43 +0530
Subject: [PATCH 31/76] fix: add search fields to number cards link query
---
.../desk/doctype/number_card/number_card.py | 35 ++++++++++++++++---
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index ecdcc56929..6157ba271c 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -112,9 +112,34 @@ def create_number_card(args):
return doc
def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
- or_filters = {'owner': frappe.session.user, 'is_public': 1}
- return frappe.db.get_list('Number Card',
- fields=['name', 'label'],
+ meta = frappe.get_meta(doctype)
+ searchfields = meta.get_search_fields()
+ search_conditions = []
+
+ if txt:
+ for field in searchfields:
+ search_conditions.append('`tab{doctype}`.`{field}` like %(txt)s'.format(field=field, doctype=doctype, txt=txt))
+
+ search_conditions = ' or '.join(search_conditions)
+
+ search_conditions = 'and (' + search_conditions +')' if search_conditions else ''
+ conditions, values = frappe.db.build_conditions(filters)
+ values['txt'] = '%' + txt + '%'
+
+ return frappe.db.sql(
+ '''select
+ `tabNumber Card`.name, `tabNumber Card`.label, `tabNumber Card`.document_type
+ from
+ `tabNumber Card`
+ where
+ {conditions} and
+ (`tabNumber Card`.owner = '{user}' or
+ `tabNumber Card`.is_public = 1)
+ {search_conditions}
+ '''.format(
filters=filters,
- or_filters=or_filters,
- as_list = 1)
\ No newline at end of file
+ user = frappe.session.user,
+ search_conditions = search_conditions,
+ conditions=conditions
+ ), values)
+
From d1ee6d163c2f0d58f0a9b8d6e821d9c794a4cbf2 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Mon, 27 Apr 2020 22:52:41 +0530
Subject: [PATCH 32/76] fix: fix height of new card widget
---
frappe/public/less/dashboard_view.less | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/less/dashboard_view.less b/frappe/public/less/dashboard_view.less
index 6325a713d2..9a989400b2 100644
--- a/frappe/public/less/dashboard_view.less
+++ b/frappe/public/less/dashboard_view.less
@@ -12,7 +12,7 @@
}
.new-number-card-widget {
- min-height: 110px;
+ min-height: 100px;
}
}
From 5f52e51e7b5b048ec7e972d55847fc83fe995e9b Mon Sep 17 00:00:00 2001
From: prssanna
Date: Mon, 27 Apr 2020 23:05:11 +0530
Subject: [PATCH 33/76] fix: add top margin to empty state message
---
frappe/public/less/dashboard_view.less | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/frappe/public/less/dashboard_view.less b/frappe/public/less/dashboard_view.less
index 9a989400b2..2b3d642cfb 100644
--- a/frappe/public/less/dashboard_view.less
+++ b/frappe/public/less/dashboard_view.less
@@ -16,6 +16,10 @@
}
}
+ .empty-dashboard {
+ margin-top: 45px;
+ }
+
.page-form {
height: 50px;
From 957aede112c1e5855d930bfca33ab8f9180a8690 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 28 Apr 2020 13:09:24 +0530
Subject: [PATCH 34/76] fix: validate options for Table and Table Multiselect
---
frappe/core/doctype/doctype/doctype.py | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py
index d922cfe166..2d671476f6 100644
--- a/frappe/core/doctype/doctype/doctype.py
+++ b/frappe/core/doctype/doctype/doctype.py
@@ -903,6 +903,16 @@ def validate_fields(meta):
frappe.msgprint(text_str + df_options_str, title="Invalid Data Field", raise_exception=True)
+ def check_child_table_option(docfield):
+ if not docfield.fieldtype in ['Table MultiSelect', 'Table']: return
+
+ doctype = docfield.options
+ meta = frappe.get_meta(doctype)
+
+ if not meta.istable:
+ frappe.throw(_('Option {0} for field {1} is not a child table')
+ .format(frappe.bold(doctype), frappe.bold(docfield.fieldname)), title=_("Invalid Option"))
+
fields = meta.get("fields")
fieldname_list = [d.fieldname for d in fields]
@@ -931,6 +941,7 @@ def validate_fields(meta):
check_illegal_default(d)
check_unique_and_text(meta.get("name"), d)
check_illegal_depends_on_conditions(d)
+ check_child_table_option(d)
check_table_multiselect_option(d)
scrub_options_in_select(d)
scrub_fetch_from(d)
From e47b138cb04880d236f50699920ffb504a45e978 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Tue, 28 Apr 2020 13:14:27 +0530
Subject: [PATCH 35/76] feat: add resize to customize options and enable for
charts
---
.../frappe/views/dashboard/dashboard_view.js | 1 +
.../public/js/frappe/widgets/base_widget.js | 37 ++++++++++++++++---
.../public/js/frappe/widgets/chart_widget.js | 5 ++-
3 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index a3fe931f49..946b68160b 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -141,6 +141,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
allow_create: true,
allow_delete: true,
allow_hiding: true,
+ allow_resize: true,
},
custom_dialog: () => this.show_add_chart_dialog(),
widgets: this.charts,
diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js
index e2df42472a..bc9b7db985 100644
--- a/frappe/public/js/frappe/widgets/base_widget.js
+++ b/frappe/public/js/frappe/widgets/base_widget.js
@@ -27,14 +27,16 @@ export default class Widget {
options.allow_delete &&
this.add_custom_button(
'',
- () => this.delete()
+ () => this.delete(),
+ "",
+ `${__('Delete')}`
);
options.allow_sorting &&
this.add_custom_button(
'',
null,
- "drag-handle"
+ "drag-handle",
);
if (options.allow_hiding) {
@@ -45,10 +47,12 @@ export default class Widget {
this.footer.css("opacity", 0.5);
}
const classname = this.hidden ? 'fa fa-eye' : 'fa fa-eye-slash';
+ const title = this.hidden ? `${__('Show')}` : `${__('Hide')}`;
this.add_custom_button(
``,
() => this.hide_or_show(),
- "show-or-hide-button"
+ "show-or-hide-button",
+ title
);
this.show_or_hide_button = this.action_area.find(
@@ -62,6 +66,13 @@ export default class Widget {
() => this.edit()
);
+ options.allow_resize &&
+ this.add_custom_button(
+ '',
+ () => this.toggle_width(),
+ "",
+ `${__('Resize')}`
+ );
}
make() {
@@ -98,9 +109,9 @@ export default class Widget {
}
}
- add_custom_button(html, action, class_name = "") {
+ add_custom_button(html, action, class_name = "", title="") {
let button = $(
- `${html}`
+ `${html}`
);
button.click(event => {
event.stopPropagation();
@@ -145,6 +156,18 @@ export default class Widget {
this.edit_dialog.make();
}
+ toggle_width() {
+ if (!this.width) {
+ this.widget.addClass("full-width");
+ this.width = 'Full';
+ this.refresh();
+ } else {
+ this.widget.removeClass("full-width");
+ this.width = null;
+ this.refresh();
+ }
+ }
+
hide_or_show() {
if (!this.hidden) {
this.body.css("opacity", 0.5);
@@ -160,7 +183,9 @@ export default class Widget {
this.show_or_hide_button.empty();
const classname = this.hidden ? 'fa fa-eye' : 'fa fa-eye-slash';
- $(``).appendTo(
+ const title = this.hidden ? `${__('Show')}` : `${__('Hide')}`;
+
+ $(``).appendTo(
this.show_or_hide_button
);
}
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index b31949cd9d..425dcdf2b9 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -14,7 +14,8 @@ export default class ChartWidget extends Widget {
name: this.name,
chart_name: this.chart_name,
label: this.label,
- hidden: this.hidden
+ hidden: this.hidden,
+ width: this.width,
};
}
@@ -33,6 +34,8 @@ export default class ChartWidget extends Widget {
this.widget.addClass("dashboard-widget-box");
if (this.width == "Full") {
this.widget.addClass("full-width");
+ } else {
+ this.widget.removeClass("full-width");
}
}
From 84c8d20ce9b39583394da2c2a0fe1bdaff448c7e Mon Sep 17 00:00:00 2001
From: prssanna
Date: Tue, 28 Apr 2020 13:41:32 +0530
Subject: [PATCH 36/76] fix: set resize button tooltip according to state
---
.../public/js/frappe/widgets/base_widget.js | 25 +++++++++++++------
.../public/js/frappe/widgets/chart_widget.js | 2 --
2 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js
index bc9b7db985..ae5d27c36e 100644
--- a/frappe/public/js/frappe/widgets/base_widget.js
+++ b/frappe/public/js/frappe/widgets/base_widget.js
@@ -66,13 +66,19 @@ export default class Widget {
() => this.edit()
);
- options.allow_resize &&
+ if (options.allow_resize) {
+ const title = this.width == 'Full'? `${__('Collapse')}` : `${__('Expand')}`;
this.add_custom_button(
'',
() => this.toggle_width(),
- "",
- `${__('Resize')}`
+ "resize-button",
+ title
);
+
+ this.resize_button = this.action_area.find(
+ ".resize-button"
+ );
+ }
}
make() {
@@ -157,15 +163,18 @@ export default class Widget {
}
toggle_width() {
- if (!this.width) {
- this.widget.addClass("full-width");
- this.width = 'Full';
- this.refresh();
- } else {
+ if (this.width == 'Full') {
this.widget.removeClass("full-width");
this.width = null;
this.refresh();
+ } else {
+ this.widget.addClass("full-width");
+ this.width = 'Full';
+ this.refresh();
}
+
+ const title = this.width == 'Full' ? `${__('Collapse')}` : `${__('Expand')}`;
+ this.resize_button.attr('title', title);
}
hide_or_show() {
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index 425dcdf2b9..78026aa869 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -34,8 +34,6 @@ export default class ChartWidget extends Widget {
this.widget.addClass("dashboard-widget-box");
if (this.width == "Full") {
this.widget.addClass("full-width");
- } else {
- this.widget.removeClass("full-width");
}
}
From 79ffeac421495759420f98bc19b6a8fd20544365 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Tue, 28 Apr 2020 13:48:13 +0530
Subject: [PATCH 37/76] fix: remove unused option in widget config
---
frappe/public/js/frappe/widgets/widget_group.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/frappe/public/js/frappe/widgets/widget_group.js b/frappe/public/js/frappe/widgets/widget_group.js
index bbf56189d7..7e685201f5 100644
--- a/frappe/public/js/frappe/widgets/widget_group.js
+++ b/frappe/public/js/frappe/widgets/widget_group.js
@@ -107,7 +107,6 @@ export default class WidgetGroup {
container: this.body,
type: this.type,
custom_dialog: this.custom_dialog,
- custom_dialog_function: this.custom_dialog_function,
default_values: this.default_values,
on_create: (config) => {
// Remove new widget
From ad66cfcb8ba105068d60b2fd633744830ee0b1fb Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 28 Apr 2020 13:44:52 +0530
Subject: [PATCH 38/76] fix: trigger after_load and after_save explicitly
---
frappe/public/js/frappe/web_form/web_form.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js
index 62a339f650..caec796f6d 100644
--- a/frappe/public/js/frappe/web_form/web_form.js
+++ b/frappe/public/js/frappe/web_form/web_form.js
@@ -29,7 +29,7 @@ export default class WebForm extends frappe.ui.FieldGroup {
// webform client script
frappe.init_client_script && frappe.init_client_script();
- frappe.web_form.events.trigger('after_load');
+ this.after_load && this.after_load();
}
on(fieldname, handler) {
@@ -134,7 +134,7 @@ export default class WebForm extends frappe.ui.FieldGroup {
if (!response.exc) {
// Success
this.handle_success(response.message);
- frappe.web_form.events.trigger('after_save');
+ this.after_save && this.after_save();
}
},
always: function() {
From 14d02cbbfb7f849c94de07f9c4238a86d7e26e19 Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 28 Apr 2020 13:55:47 +0530
Subject: [PATCH 39/76] fix: explicit check of return value of validate
---
frappe/public/js/frappe/web_form/web_form.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js
index caec796f6d..13bba7064c 100644
--- a/frappe/public/js/frappe/web_form/web_form.js
+++ b/frappe/public/js/frappe/web_form/web_form.js
@@ -102,7 +102,9 @@ export default class WebForm extends frappe.ui.FieldGroup {
}
save() {
- this.validate && this.validate();
+ if (this.validate && !this.validate()) {
+ frappe.throw(__("Couldn't save, please check the data you have entered"), __("Validation"))
+ }
// validation hack: get_values will check for missing data
let doc_values = super.get_values(this.allow_incomplete);
From 80346e1bd6de3be4d028745ce6e4266a02a0807f Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 28 Apr 2020 14:00:04 +0530
Subject: [PATCH 40/76] fix: title
---
frappe/public/js/frappe/web_form/web_form.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js
index 13bba7064c..b2059d0c38 100644
--- a/frappe/public/js/frappe/web_form/web_form.js
+++ b/frappe/public/js/frappe/web_form/web_form.js
@@ -103,7 +103,7 @@ export default class WebForm extends frappe.ui.FieldGroup {
save() {
if (this.validate && !this.validate()) {
- frappe.throw(__("Couldn't save, please check the data you have entered"), __("Validation"))
+ frappe.throw(__("Couldn't save, please check the data you have entered"), __("Validation Error"))
}
// validation hack: get_values will check for missing data
From 2190b44a6f46e028cee8773afcc23eca9f4f9ccf Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 28 Apr 2020 19:06:58 +0530
Subject: [PATCH 41/76] fix (style): added semi-colon
---
frappe/public/js/frappe/web_form/web_form.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/web_form/web_form.js b/frappe/public/js/frappe/web_form/web_form.js
index b2059d0c38..81b219aece 100644
--- a/frappe/public/js/frappe/web_form/web_form.js
+++ b/frappe/public/js/frappe/web_form/web_form.js
@@ -103,7 +103,7 @@ export default class WebForm extends frappe.ui.FieldGroup {
save() {
if (this.validate && !this.validate()) {
- frappe.throw(__("Couldn't save, please check the data you have entered"), __("Validation Error"))
+ frappe.throw(__("Couldn't save, please check the data you have entered"), __("Validation Error"));
}
// validation hack: get_values will check for missing data
From a195421097b0ded8b531ce74ad9cf831f95780b1 Mon Sep 17 00:00:00 2001
From: Sagar Vora
Date: Tue, 28 Apr 2020 19:22:20 +0530
Subject: [PATCH 42/76] fix: do not check for frappe.conf.monitor while
stopping monitor
---
frappe/monitor.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/monitor.py b/frappe/monitor.py
index 7181bd92ad..cd365fd13f 100644
--- a/frappe/monitor.py
+++ b/frappe/monitor.py
@@ -23,7 +23,7 @@ def start(transaction_type="request", method=None, kwargs=None):
def stop(response=None):
- if frappe.conf.monitor and hasattr(frappe.local, "monitor"):
+ if hasattr(frappe.local, "monitor"):
frappe.local.monitor.dump(response)
From d49941f2d1bf760d8fbb98b1aa64f151cf00809a Mon Sep 17 00:00:00 2001
From: Shivam Mishra
Date: Tue, 28 Apr 2020 20:25:23 +0530
Subject: [PATCH 43/76] style: membership check
---
frappe/core/doctype/doctype/doctype.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py
index 2d671476f6..09a5e999b0 100644
--- a/frappe/core/doctype/doctype/doctype.py
+++ b/frappe/core/doctype/doctype/doctype.py
@@ -904,7 +904,7 @@ def validate_fields(meta):
frappe.msgprint(text_str + df_options_str, title="Invalid Data Field", raise_exception=True)
def check_child_table_option(docfield):
- if not docfield.fieldtype in ['Table MultiSelect', 'Table']: return
+ if docfield.fieldtype not in ['Table MultiSelect', 'Table']: return
doctype = docfield.options
meta = frappe.get_meta(doctype)
From 031aad0d8524db5725a0fe4d43cb73d29a949c4e Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 29 Apr 2020 12:28:08 +0530
Subject: [PATCH 44/76] perf: Cache get_tables values - To avoid redundant db
calls
---
frappe/database/database.py | 11 ++++++++++-
frappe/database/schema.py | 1 +
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/frappe/database/database.py b/frappe/database/database.py
index b083ff1014..cc76745c2e 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -769,7 +769,16 @@ class Database(object):
return ("tab" + doctype) in self.get_tables()
def get_tables(self):
- return [d[0] for d in self.sql("select table_name from information_schema.tables where table_schema not in ('pg_catalog', 'information_schema')")]
+ tables = frappe.cache().get_value('db_tables')
+ if not tables:
+ table_rows = self.sql("""
+ SELECT table_name
+ FROM information_schema.tables
+ WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
+ """)
+ tables = set([d[0] for d in table_rows])
+ frappe.cache().set_value('db_tables', tables)
+ return tables
def a_row_exists(self, doctype):
"""Returns True if atleast one row exists."""
diff --git a/frappe/database/schema.py b/frappe/database/schema.py
index 28e055f382..3d484a1f1b 100644
--- a/frappe/database/schema.py
+++ b/frappe/database/schema.py
@@ -31,6 +31,7 @@ class DBTable:
def sync(self):
if self.is_new():
+ frappe.cache().delete_key('db_tables')
self.create()
else:
frappe.cache().hdel('table_columns', self.table_name)
From 8002bd2da88dcfa8038e36668bfd262e8999f09b Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 13:37:20 +0530
Subject: [PATCH 45/76] fix: formatting
---
frappe/desk/doctype/number_card/number_card.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index 6157ba271c..4e5047b5d4 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -10,8 +10,7 @@ class NumberCard(Document):
pass
-def get_permission_query_conditions(user):
-
+def get_permission_query_conditions(user=None):
if not user:
user = frappe.session.user
@@ -65,7 +64,7 @@ def get_result(doc, to_date=None):
if to_date:
filters.append([doc.document_type, 'creation', '<', to_date, False])
- number = frappe.db.get_all(doc.document_type, fields = fields, filters = filters)[0]['result']
+ number = frappe.db.get_all(doc.document_type, fields=fields, filters=filters)[0]['result']
frappe.db.set_value('Number Card', doc.name, 'previous_result', number)
return number
@@ -138,8 +137,8 @@ def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters):
{search_conditions}
'''.format(
filters=filters,
- user = frappe.session.user,
- search_conditions = search_conditions,
+ user=frappe.session.user,
+ search_conditions=search_conditions,
conditions=conditions
), values)
From ec9463c105a2a2059e3fcd3e08a9124bea86d180 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 13:48:37 +0530
Subject: [PATCH 46/76] fix: check if list is empty
---
frappe/desk/doctype/number_card/number_card.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index 4e5047b5d4..e26d5cc9a5 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -64,7 +64,8 @@ def get_result(doc, to_date=None):
if to_date:
filters.append([doc.document_type, 'creation', '<', to_date, False])
- number = frappe.db.get_all(doc.document_type, fields=fields, filters=filters)[0]['result']
+ res = frappe.db.get_all(doc.document_type, fields=fields, filters=filters)
+ number = res[0]['result'] if res else 0
frappe.db.set_value('Number Card', doc.name, 'previous_result', number)
return number
From 6833cbe8224bb08e220e2b3b584b33392d8a47a5 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 13:49:32 +0530
Subject: [PATCH 47/76] fix: add number card to translation comment
---
frappe/public/js/frappe/widgets/new_widget.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/widgets/new_widget.js b/frappe/public/js/frappe/widgets/new_widget.js
index 66fa88b761..e0cfd51ab4 100644
--- a/frappe/public/js/frappe/widgets/new_widget.js
+++ b/frappe/public/js/frappe/widgets/new_widget.js
@@ -18,7 +18,7 @@ export default class NewWidget {
get_title() {
// DO NOT REMOVE: Comment to load translation
- // __("New Chart") __("New Shortcut")
+ // __("New Chart") __("New Shortcut") __("New Number Card")
return __(`New ${frappe.model.unscrub(this.type)}`);
}
From 651109bb004fa9edd9a400358b1db7e6ffed3019 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 29 Apr 2020 14:07:51 +0530
Subject: [PATCH 48/76] fix: Translatable string
---
frappe/database/schema.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/frappe/database/schema.py b/frappe/database/schema.py
index 3d484a1f1b..9c7008a8ed 100644
--- a/frappe/database/schema.py
+++ b/frappe/database/schema.py
@@ -144,10 +144,9 @@ class DBTable:
if max_length and max_length[0][0] and max_length[0][0] > new_length:
if col.fieldname in self.columns:
self.columns[col.fieldname].length = current_length
-
- frappe.msgprint(_("""Reverting length to {0} for '{1}' in '{2}';
- Setting the length as {3} will cause truncation of data.""")
- .format(current_length, col.fieldname, self.doctype, new_length))
+ info_message = _("Reverting length to {0} for '{1}' in '{2}'. Setting the length as {3} will cause truncation of data.") \
+ .format(current_length, col.fieldname, self.doctype, new_length)
+ frappe.msgprint(info_message)
def is_new(self):
return self.table_name not in frappe.db.get_tables()
From 7a6a9b2b207733dc77821b2e5d382fe28e89bc2a Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Wed, 29 Apr 2020 14:15:06 +0530
Subject: [PATCH 49/76] fix: Use standard button components in Hero
---
frappe/website/web_template/hero/hero.html | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/frappe/website/web_template/hero/hero.html b/frappe/website/web_template/hero/hero.html
index a3fb4aa466..e7b69ab782 100644
--- a/frappe/website/web_template/hero/hero.html
+++ b/frappe/website/web_template/hero/hero.html
@@ -9,22 +9,12 @@
{%- endif -%}
{%- if primary_action or secondary_action -%}
-
+
{%- if primary_action -%}
-
+ {{ c('button', label=primary_action_label, url=primary_action, variant="primary", size="large") }}
{%- endif -%}
{%- if secondary_action -%}
-
+ {{ c('button', label=secondary_action_label, url=secondary_action, variant="secondary", size="large", class="ml-4") }}
{%- endif -%}
{%- endif -%}
From 811d0d64d90986bf637b0c4608327e212766ddd7 Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Wed, 29 Apr 2020 14:15:46 +0530
Subject: [PATCH 50/76] fix: Option to bleed image outside Hero
---
.../hero_with_right_image.html | 16 ++++++++++++----
.../hero_with_right_image.json | 8 +++++++-
.../website/website_theme/standard/standard.json | 2 +-
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html b/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
index 57a1a02af7..11d49eb287 100644
--- a/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
+++ b/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
@@ -1,5 +1,5 @@
-
+
{%- if image -%}
-
-

-
+ {{ c('image_with_blur',
+ class=["hidden md:block", "w-full md:w-6/12" if contain_image else "md:max-w-md lg:max-w-lg xl:max-w-xl xxl:max-w-2xl"],
+ src=image,
+ alt="")
+ }}
{%- endif -%}
+
+{%- if not contain_image -%}
+
+{%- endif -%}
diff --git a/frappe/website/web_template/hero_with_right_image/hero_with_right_image.json b/frappe/website/web_template/hero_with_right_image/hero_with_right_image.json
index bb841a3156..3cb4701e7c 100644
--- a/frappe/website/web_template/hero_with_right_image/hero_with_right_image.json
+++ b/frappe/website/web_template/hero_with_right_image/hero_with_right_image.json
@@ -21,6 +21,12 @@
"label": "Image",
"reqd": 0
},
+ {
+ "fieldname": "contain_image",
+ "fieldtype": "Check",
+ "label": "Restrict Image inside Container",
+ "reqd": 0
+ },
{
"fieldname": "primary_action_label",
"fieldtype": "Data",
@@ -47,7 +53,7 @@
}
],
"idx": 0,
- "modified": "2020-04-26 15:08:26.937576",
+ "modified": "2020-04-29 14:12:31.613545",
"modified_by": "Administrator",
"name": "Hero with Right Image",
"owner": "Administrator",
diff --git a/frappe/website/website_theme/standard/standard.json b/frappe/website/website_theme/standard/standard.json
index ca9e81bf27..1729e4616a 100644
--- a/frappe/website/website_theme/standard/standard.json
+++ b/frappe/website/website_theme/standard/standard.json
@@ -10,7 +10,7 @@
"font_properties": "300,600",
"footer": [],
"idx": 26,
- "modified": "2020-04-24 23:52:27.211811",
+ "modified": "2020-04-29 12:26:48.399125",
"modified_by": "Administrator",
"module": "Website",
"name": "Standard",
From a5724699c61b9a918172c03da3faff732cfe0cbd Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 14:03:26 +0530
Subject: [PATCH 51/76] fix: formatting
---
.../js/frappe/views/dashboard/dashboard_view.js | 4 ++--
frappe/public/js/frappe/widgets/base_widget.js | 2 +-
frappe/public/js/frappe/widgets/chart_widget.js | 11 +++++------
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
index 946b68160b..40cbd1986f 100644
--- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js
+++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js
@@ -89,7 +89,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
this.$dashboard_wrapper.empty();
frappe.dashboard_utils.get_dashboard_settings().then(settings => {
- this.dashboard_chart_settings = settings.chart_config? JSON.parse(settings.chart_config): {};
+ this.dashboard_chart_settings = settings.chart_config ? JSON.parse(settings.chart_config) : {};
this.charts.map(chart => {
chart.label = chart.chart_name;
chart.chart_settings = this.dashboard_chart_settings[chart.chart_name] || {};
@@ -398,7 +398,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView {
let chart = values;
if (chart.new_or_existing == 'New Chart') {
chart.chart_name = chart.label;
- chart.chart_type = chart.chart_type == 'Time Series'? chart.chart_function: chart.chart_type;
+ chart.chart_type = chart.chart_type == 'Time Series' ? chart.chart_function : chart.chart_type;
chart.document_type = this.doctype;
chart.filters_json = '[]';
frappe.xcall('frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart', {'args': chart}).then((doc)=> {
diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js
index ae5d27c36e..aea26d762d 100644
--- a/frappe/public/js/frappe/widgets/base_widget.js
+++ b/frappe/public/js/frappe/widgets/base_widget.js
@@ -109,7 +109,7 @@ export default class Widget {
}
set_title(max_chars) {
- this.title_field[0].innerHTML = max_chars? frappe.ellipsis(this.label, max_chars): this.label;
+ this.title_field[0].innerHTML = max_chars ? frappe.ellipsis(this.label, max_chars) : this.label;
if (max_chars) {
this.title_field[0].setAttribute('title', this.label);
}
diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js
index 78026aa869..68811a81ae 100644
--- a/frappe/public/js/frappe/widgets/chart_widget.js
+++ b/frappe/public/js/frappe/widgets/chart_widget.js
@@ -26,7 +26,7 @@ export default class ChartWidget extends Widget {
}
set_chart_title() {
- const max_chars = this.widget.width() < 500? 20: 60;
+ const max_chars = this.widget.width() < 500 ? 20 : 60;
this.set_title(max_chars);
}
@@ -455,11 +455,10 @@ export default class ChartWidget extends Widget {
chart_name: this.chart_doc.name,
filters: filters,
refresh: refresh ? 1 : 0,
- time_interval:
- args && args.time_interval? args.time_interval: null,
- timespan: args && args.timespan? args.timespan: null,
- from_date: args && args.from_date? args.from_date: null,
- to_date: args && args.to_date? args.to_date: null
+ time_interval: args && args.time_interval ? args.time_interval : null,
+ timespan: args && args.timespan ? args.timespan : null,
+ from_date: args && args.from_date ? args.from_date : null,
+ to_date: args && args.to_date ? args.to_date : null
};
}
return frappe.xcall(method, args);
From bf3a80f05de7fc9dbcf41d2199d71c832cbc5148 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 29 Apr 2020 14:39:14 +0530
Subject: [PATCH 52/76] fix: Directly used set comprehension
As indicated by deepsource
---
frappe/database/database.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/database/database.py b/frappe/database/database.py
index cc76745c2e..103538944c 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -776,7 +776,7 @@ class Database(object):
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
""")
- tables = set([d[0] for d in table_rows])
+ tables = set(d[0] for d in table_rows)
frappe.cache().set_value('db_tables', tables)
return tables
From a73be732fd55d1ba8a2952ab9043530a8ef2ad75 Mon Sep 17 00:00:00 2001
From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com>
Date: Wed, 29 Apr 2020 16:43:01 +0530
Subject: [PATCH 53/76] refactor: Fix redundant cache value set
---
frappe/database/database.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/database/database.py b/frappe/database/database.py
index 103538944c..3092e6c0ae 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -777,7 +777,7 @@ class Database(object):
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
""")
tables = set(d[0] for d in table_rows)
- frappe.cache().set_value('db_tables', tables)
+ frappe.cache().set_value('db_tables', tables)
return tables
def a_row_exists(self, doctype):
From e4731d0fd7d86a69d657a9f7e4efb503bd88169e Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 16:06:52 +0530
Subject: [PATCH 54/76] fix: fix indent
---
.../public/js/frappe/widgets/number_card_widget.js | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js
index 4766d97e92..741b7cc6ea 100644
--- a/frappe/public/js/frappe/widgets/number_card_widget.js
+++ b/frappe/public/js/frappe/widgets/number_card_widget.js
@@ -193,19 +193,23 @@ export default class NumberCardWidget extends Widget {
}
set_card_actions(actions) {
+ /* eslint-disable indent */
this.card_actions =
$(``);
+ /* eslint-disable indent */
this.card_actions.find("a[data-action]").each((i, o) => {
const action = o.dataset.action;
From 10b4f1335cfd35395dba2165aa6db24168a44cbf Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Wed, 29 Apr 2020 18:27:51 +0530
Subject: [PATCH 55/76] fix: Only show "No changes in document" if form is not
dirty
---
frappe/public/js/frappe/form/save.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js
index 8f88ff8db0..8386cb6c7e 100644
--- a/frappe/public/js/frappe/form/save.js
+++ b/frappe/public/js/frappe/form/save.js
@@ -36,7 +36,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
freeze_message: freeze_message
});
} else {
- frappe.show_alert({message: __("No changes in document"), indicator: "blue"});
+ !frm.is_dirty() && frappe.show_alert({message: __("No changes in document"), indicator: "blue"});
$(btn).prop("disabled", false);
}
};
From 74f0f3019b631236ab602a5944e9fab04962d338 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 19:24:26 +0530
Subject: [PATCH 56/76] fix: make arrow on select box clickable
---
frappe/public/less/controls.less | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/less/controls.less b/frappe/public/less/controls.less
index d88e6adaec..564c77c07f 100644
--- a/frappe/public/less/controls.less
+++ b/frappe/public/less/controls.less
@@ -162,6 +162,6 @@
top: 8px;
height: 15px;
right: 12px;
- top: 8px;
+ pointer-events: none;
}
}
From ee3d0ee2a4901db8d63c830fe0f3f2270fc4bf7f Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 19:58:20 +0530
Subject: [PATCH 57/76] fix: fix error for child table in_list_view check
---
frappe/core/doctype/doctype/doctype.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py
index 0509ea9af7..31beabc56a 100644
--- a/frappe/core/doctype/doctype/doctype.py
+++ b/frappe/core/doctype/doctype/doctype.py
@@ -712,9 +712,10 @@ def validate_fields(meta):
if d.fieldtype == "Currency" and cint(d.width) < 100:
frappe.throw(_("Max width for type Currency is 100px in row {0}").format(d.idx))
- def check_in_list_view(d):
+ def check_in_list_view(is_table, d):
if d.in_list_view and (d.fieldtype in not_allowed_in_list_view):
- frappe.throw(_("'In List View' not allowed for type {0} in row {1}").format(d.fieldtype, d.idx))
+ property_label = 'In Grid View' if is_table else 'In List View'
+ frappe.throw(_("'{0}' not allowed for type {1} in row {2}").format(property_label, d.fieldtype, d.idx))
def check_in_global_search(d):
if d.in_global_search and d.fieldtype in no_value_fields:
@@ -929,7 +930,7 @@ def validate_fields(meta):
check_link_table_options(meta.get("name"), d)
check_dynamic_link_options(d)
check_hidden_and_mandatory(meta.get("name"), d)
- check_in_list_view(d)
+ check_in_list_view(meta.get('istable'), d)
check_in_global_search(d)
check_illegal_default(d)
check_unique_and_text(meta.get("name"), d)
From fb8ca03640b9be45a37bf755ef45d40fbfd69e6d Mon Sep 17 00:00:00 2001
From: prssanna
Date: Wed, 29 Apr 2020 21:42:24 +0530
Subject: [PATCH 58/76] fix: remove unnecessary set_value
---
frappe/desk/doctype/number_card/number_card.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py
index e26d5cc9a5..41be5fac47 100644
--- a/frappe/desk/doctype/number_card/number_card.py
+++ b/frappe/desk/doctype/number_card/number_card.py
@@ -67,7 +67,6 @@ def get_result(doc, to_date=None):
res = frappe.db.get_all(doc.document_type, fields=fields, filters=filters)
number = res[0]['result'] if res else 0
- frappe.db.set_value('Number Card', doc.name, 'previous_result', number)
return number
@frappe.whitelist()
From 2f821f9c2d27834142e1f89bbedbee9874c355c2 Mon Sep 17 00:00:00 2001
From: "Chinmay D. Pai"
Date: Wed, 29 Apr 2020 21:58:00 +0530
Subject: [PATCH 59/76] chore: fix deepsource issues
* use set comprehension instead of generator
* remove redundant else statement
Signed-off-by: Chinmay D. Pai
---
frappe/database/database.py | 2 +-
frappe/database/schema.py | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/frappe/database/database.py b/frappe/database/database.py
index 3092e6c0ae..364f0c73cd 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -776,7 +776,7 @@ class Database(object):
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
""")
- tables = set(d[0] for d in table_rows)
+ tables = {d[0] for d in table_rows}
frappe.cache().set_value('db_tables', tables)
return tables
diff --git a/frappe/database/schema.py b/frappe/database/schema.py
index 9c7008a8ed..83c22090eb 100644
--- a/frappe/database/schema.py
+++ b/frappe/database/schema.py
@@ -138,8 +138,7 @@ class DBTable:
if frappe.db.is_missing_column(e):
# Unknown column 'column_name' in 'field list'
continue
- else:
- raise
+ raise
if max_length and max_length[0][0] and max_length[0][0] > new_length:
if col.fieldname in self.columns:
From 551722c7254b77ccb391552168ecac871f587b34 Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Wed, 29 Apr 2020 23:01:16 +0530
Subject: [PATCH 60/76] fix: Ability to hide blocks
---
frappe/templates/components/web_block.html | 2 ++
.../doctype/web_page_block/web_page_block.json | 13 ++++++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/frappe/templates/components/web_block.html b/frappe/templates/components/web_block.html
index d898417970..f0ed8f2df4 100644
--- a/frappe/templates/components/web_block.html
+++ b/frappe/templates/components/web_block.html
@@ -7,6 +7,7 @@
web_block.css_class
]) -%}
+{%- if not web_block.hide_block -%}
<{{htmltag}} class="{{ classes }}" data-section-idx="{{ web_block.idx | e }}"
data-section-template="{{ web_block.web_template | e }}">
{%- if web_block.add_container -%}
@@ -17,3 +18,4 @@
{%- endif -%}
{{htmltag}}>
+{%- endif -%}
diff --git a/frappe/website/doctype/web_page_block/web_page_block.json b/frappe/website/doctype/web_page_block/web_page_block.json
index b415567bd0..77f2e25469 100644
--- a/frappe/website/doctype/web_page_block/web_page_block.json
+++ b/frappe/website/doctype/web_page_block/web_page_block.json
@@ -12,7 +12,8 @@
"column_break_5",
"add_container",
"add_padding",
- "add_shade"
+ "add_shade",
+ "hide_block"
],
"fields": [
{
@@ -54,18 +55,24 @@
"default": "0",
"fieldname": "add_shade",
"fieldtype": "Check",
- "label": "Shaded Section"
+ "label": "Add Gray Background"
},
{
"default": "1",
"fieldname": "add_container",
"fieldtype": "Check",
"label": "Add Container"
+ },
+ {
+ "default": "0",
+ "fieldname": "hide_block",
+ "fieldtype": "Check",
+ "label": "Hide Block"
}
],
"istable": 1,
"links": [],
- "modified": "2020-04-19 16:16:44.524042",
+ "modified": "2020-04-29 15:08:25.976179",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Page Block",
From 4620074db49d94db938f1b1bf0549a7abe9d3b2d Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Wed, 29 Apr 2020 23:02:22 +0530
Subject: [PATCH 61/76] fix: Add Section Break for Cards Section template
---
.../web_template_field.json | 4 +-
.../section_with_cards.json | 98 ++++++++++++++-----
2 files changed, 75 insertions(+), 27 deletions(-)
diff --git a/frappe/website/doctype/web_template_field/web_template_field.json b/frappe/website/doctype/web_template_field/web_template_field.json
index 09335adf3a..900655e207 100644
--- a/frappe/website/doctype/web_template_field/web_template_field.json
+++ b/frappe/website/doctype/web_template_field/web_template_field.json
@@ -31,7 +31,7 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Fieldtype",
- "options": "Attach Image\nCheck\nData\nInt\nSelect\nSmall Text\nText\nMarkdown Editor",
+ "options": "Attach Image\nCheck\nData\nInt\nSelect\nSmall Text\nText\nMarkdown Editor\nSection Break\nColumn Break",
"reqd": 1
},
{
@@ -48,7 +48,7 @@
],
"istable": 1,
"links": [],
- "modified": "2020-04-24 17:05:25.322767",
+ "modified": "2020-04-29 14:53:23.192395",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Template Field",
diff --git a/frappe/website/web_template/section_with_cards/section_with_cards.json b/frappe/website/web_template/section_with_cards/section_with_cards.json
index 89426651ad..9ec430ae60 100644
--- a/frappe/website/web_template/section_with_cards/section_with_cards.json
+++ b/frappe/website/web_template/section_with_cards/section_with_cards.json
@@ -22,153 +22,201 @@
"options": "Small\nMedium\nLarge",
"reqd": 0
},
+ {
+ "fieldname": "card_1",
+ "fieldtype": "Section Break",
+ "label": "Card 1",
+ "reqd": 0
+ },
{
"fieldname": "card_1_title",
"fieldtype": "Data",
- "label": "Card 1 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_1_content",
"fieldtype": "Small Text",
- "label": "Card 1 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_1_url",
"fieldtype": "Data",
- "label": "Card 1 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_2",
+ "fieldtype": "Section Break",
+ "label": "Card 2",
"reqd": 0
},
{
"fieldname": "card_2_title",
"fieldtype": "Data",
- "label": "Card 2 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_2_content",
"fieldtype": "Small Text",
- "label": "Card 2 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_2_url",
"fieldtype": "Data",
- "label": "Card 2 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_3",
+ "fieldtype": "Section Break",
+ "label": "Card 3",
"reqd": 0
},
{
"fieldname": "card_3_title",
"fieldtype": "Data",
- "label": "Card 3 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_3_content",
"fieldtype": "Small Text",
- "label": "Card 3 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_3_url",
"fieldtype": "Data",
- "label": "Card 3 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_4",
+ "fieldtype": "Section Break",
+ "label": "Card 4",
"reqd": 0
},
{
"fieldname": "card_4_title",
"fieldtype": "Data",
- "label": "Card 4 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_4_content",
"fieldtype": "Small Text",
- "label": "Card 4 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_4_url",
"fieldtype": "Data",
- "label": "Card 4 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_5",
+ "fieldtype": "Section Break",
+ "label": "Card 5",
"reqd": 0
},
{
"fieldname": "card_5_title",
"fieldtype": "Data",
- "label": "Card 5 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_5_content",
"fieldtype": "Small Text",
- "label": "Card 5 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_5_url",
"fieldtype": "Data",
- "label": "Card 5 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_6",
+ "fieldtype": "Section Break",
+ "label": "Card 6",
"reqd": 0
},
{
"fieldname": "card_6_title",
"fieldtype": "Data",
- "label": "Card 6 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_6_content",
"fieldtype": "Small Text",
- "label": "Card 6 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_6_url",
"fieldtype": "Data",
- "label": "Card 6 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_7",
+ "fieldtype": "Section Break",
+ "label": "Card 7",
"reqd": 0
},
{
"fieldname": "card_7_title",
"fieldtype": "Data",
- "label": "Card 7 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_7_content",
"fieldtype": "Small Text",
- "label": "Card 7 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_7_url",
"fieldtype": "Data",
- "label": "Card 7 URL",
+ "label": "URL",
+ "reqd": 0
+ },
+ {
+ "fieldname": "card_8",
+ "fieldtype": "Section Break",
+ "label": "Card 8",
"reqd": 0
},
{
"fieldname": "card_8_title",
"fieldtype": "Data",
- "label": "Card 8 Title",
+ "label": "Title",
"reqd": 0
},
{
"fieldname": "card_8_content",
"fieldtype": "Small Text",
- "label": "Card 8 Content",
+ "label": "Content",
"reqd": 0
},
{
"fieldname": "card_8_url",
"fieldtype": "Data",
- "label": "Card 8 URL",
+ "label": "URL",
"reqd": 0
}
],
"idx": 0,
- "modified": "2020-04-21 21:24:04.192839",
+ "modified": "2020-04-29 22:40:03.362229",
"modified_by": "Administrator",
"name": "Section with Cards",
"owner": "Administrator",
From 2bbe26ada024824faeadefb5d85cff774eef4376 Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Wed, 29 Apr 2020 23:02:43 +0530
Subject: [PATCH 62/76] fix: Max height for hero image
---
.../hero_with_right_image/hero_with_right_image.html | 2 +-
tailwind.config.js | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html b/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
index 11d49eb287..8efc46a1b7 100644
--- a/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
+++ b/frappe/website/web_template/hero_with_right_image/hero_with_right_image.html
@@ -23,7 +23,7 @@
{%- if image -%}
{{ c('image_with_blur',
- class=["hidden md:block", "w-full md:w-6/12" if contain_image else "md:max-w-md lg:max-w-lg xl:max-w-xl xxl:max-w-2xl"],
+ class=["hidden md:block max-h-144", "w-full md:w-6/12" if contain_image else "md:max-w-md lg:max-w-lg xl:max-w-xl xxl:max-w-2xl"],
src=image,
alt="")
}}
diff --git a/tailwind.config.js b/tailwind.config.js
index e75ff16e85..03678ee166 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -46,6 +46,9 @@ module.exports = {
borderRadius: {
xl: '0.75rem'
},
+ maxHeight: {
+ '144': '36rem'
+ },
boxShadow: theme => ({
'outline-primary': `0 0 0 3px ${rgba(theme('colors.blue.300'), 0.45)}`
}),
From 2788cf3d196cbf67bcfdc4ae182f2d2181c108bd Mon Sep 17 00:00:00 2001
From: Faris Ansari
Date: Wed, 29 Apr 2020 23:03:46 +0530
Subject: [PATCH 63/76] fix: Clickable navbar button in firefox
---
.../navbar_with_links_on_right.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/website/web_template/navbar_with_links_on_right/navbar_with_links_on_right.html b/frappe/website/web_template/navbar_with_links_on_right/navbar_with_links_on_right.html
index ffa8be24da..01652f8c80 100644
--- a/frappe/website/web_template/navbar_with_links_on_right/navbar_with_links_on_right.html
+++ b/frappe/website/web_template/navbar_with_links_on_right/navbar_with_links_on_right.html
@@ -6,8 +6,8 @@
-
-
+
+