From 1849c4f2cf3715e7b3afabb823a066ebee845ba9 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 30 Sep 2020 11:24:08 +0200
Subject: [PATCH 001/163] fix: improve ux of standard web templates
---
frappe/website/doctype/web_template/web_template.js | 12 +++++++++++-
frappe/website/doctype/web_template/web_template.py | 1 +
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/frappe/website/doctype/web_template/web_template.js b/frappe/website/doctype/web_template/web_template.js
index 99dc02bfbb..1488dbf1be 100644
--- a/frappe/website/doctype/web_template/web_template.js
+++ b/frappe/website/doctype/web_template/web_template.js
@@ -2,11 +2,21 @@
// For license information, please see license.txt
frappe.ui.form.on('Web Template', {
- refresh(frm) {
+ refresh: function(frm) {
if (!frappe.boot.developer_mode && frm.doc.standard) {
frm.disable_form();
}
frm.toggle_display('standard', frappe.boot.developer_mode);
+ frm.toggle_display('template', !frm.doc.standard);
+ },
+ standard: function(frm) {
+ if (!frm.doc.standard) {
+ // If standard changes from true to false, hide template until
+ // the next save. Changes will get overwritten from the backend
+ // on save and should not be possible in the UI.
+ frm.toggle_display('template', false);
+ frm.save();
+ }
}
});
diff --git a/frappe/website/doctype/web_template/web_template.py b/frappe/website/doctype/web_template/web_template.py
index 1b6e67a9d0..811e4f5ad9 100644
--- a/frappe/website/doctype/web_template/web_template.py
+++ b/frappe/website/doctype/web_template/web_template.py
@@ -35,6 +35,7 @@ class WebTemplate(Document):
if self.standard:
export_to_files(record_list=[["Web Template", self.name]], create_init=True)
self.create_template_file()
+ self.template = ""
# standard to custom
was_standard = (self.get_doc_before_save() or {}).get("standard")
From a507de886316462dc15dd6f85bc268052fe18a0f Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Fri, 2 Oct 2020 17:01:58 +0200
Subject: [PATCH 002/163] fix: allow empty type for Web Templates that are not
sections
---
frappe/website/doctype/web_template/web_template.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/website/doctype/web_template/web_template.json b/frappe/website/doctype/web_template/web_template.json
index fed7008cdb..492c265e55 100644
--- a/frappe/website/doctype/web_template/web_template.json
+++ b/frappe/website/doctype/web_template/web_template.json
@@ -41,7 +41,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Type",
- "options": "Section\nNavbar\nFooter"
+ "options": "\nSection\nNavbar\nFooter"
},
{
"depends_on": "standard",
@@ -58,7 +58,7 @@
"link_fieldname": "web_template"
}
],
- "modified": "2020-09-25 00:48:57.902292",
+ "modified": "2020-10-02 17:00:52.512209",
"modified_by": "Administrator",
"module": "Website",
"name": "Web Template",
From 4f82ebcef717665265efc2230fc936dfa206ddaf Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Mon, 5 Oct 2020 15:32:03 +0530
Subject: [PATCH 003/163] refactor: Fix excel export - Remove unnecessary code
---
frappe/desk/query_report.py | 110 +++++++++++++-----------------------
1 file changed, 38 insertions(+), 72 deletions(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index 5a9aae8435..95f93b612e 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -74,23 +74,25 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None)
res = report.execute_script_report(filters)
columns, result, message, chart, report_summary, skip_total_row = ljust_list(res, 6)
+ columns = [get_column_as_dict(col) for col in columns]
- if report.custom_columns:
- # Original query columns, needed to reorder data as per custom columns
- query_columns = columns
- # Reordered columns
- columns = json.loads(report.custom_columns)
-
- result = reorder_data_for_custom_columns(columns, query_columns, result)
-
- result = add_data_to_custom_columns(columns, result)
+ # convert to list of dicts
+ result = normalize_result(result, columns)
if custom_columns:
- result = add_data_to_custom_columns(custom_columns, result)
-
for custom_column in custom_columns:
columns.insert(custom_column["insert_after_index"] + 1, custom_column)
+ custom_columns = custom_columns or []
+
+ if isinstance(report.custom_columns, string_types):
+ custom_columns += json.loads(report.custom_columns) or []
+ else:
+ custom_columns += report.custom_columns or []
+
+ if custom_columns:
+ result = add_custom_column_data(custom_columns, result)
+
if result:
result = get_filtered_data(report.ref_doctype, columns, result, user)
@@ -109,6 +111,20 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None)
or 0,
}
+def normalize_result(result, columns):
+ # Converts to list of dicts from list of lists/tuples
+ data = []
+ column_names = [column["fieldname"] for column in columns]
+ if result and isinstance(result[0], (list, tuple)):
+ for row in result:
+ row_obj = {}
+ for idx, column_name in enumerate(column_names):
+ row_obj[column_name] = row[idx]
+ data.append(row_obj)
+ else:
+ data = result
+
+ return data
@frappe.whitelist()
def background_enqueue_run(report_name, filters=None, user=None):
@@ -221,69 +237,17 @@ def run(
return result
-def add_data_to_custom_columns(columns, result):
- custom_fields_data = get_data_for_custom_report(columns)
+def add_custom_column_data(custom_columns, result):
+ custom_column_data = get_data_for_custom_report(custom_columns)
- data = []
- for row in result:
- row_obj = {}
- if isinstance(row, tuple):
- row = list(row)
+ for column in custom_columns:
+ key = (column.get('doctype'), column.get('fieldname'))
+ if key in custom_column_data:
+ for row in result:
+ row_reference = row[column.get('link_field')]
+ row[column.get('fieldname')] = custom_column_data.get(key).get(row_reference)
- if isinstance(row, list):
- for idx, column in enumerate(columns):
- if column.get("link_field"):
- row_obj[column["fieldname"]] = None
- row.insert(idx, None)
- else:
- row_obj[column["fieldname"]] = row[idx]
- data.append(row_obj)
- else:
- data.append(row)
-
- for row in data:
- for column in columns:
- if column.get("link_field"):
- fieldname = column["fieldname"]
- key = (column["doctype"], fieldname)
- link_field = column["link_field"]
- row[fieldname] = custom_fields_data.get(key, {}).get(
- row.get(link_field)
- )
-
- return data
-
-
-def reorder_data_for_custom_columns(custom_columns, columns, result):
- if not result:
- return []
-
- columns = [get_column_as_dict(col) for col in columns]
- if isinstance(result[0], list) or isinstance(result[0], tuple):
- # If the result is a list of lists
- custom_column_names = [col["label"] for col in custom_columns]
- original_column_names = [col["label"] for col in columns]
- return get_columns_from_list(custom_column_names, original_column_names, result)
- else:
- # columns do not need to be reordered if result is a list of dicts
- return result
-
-
-def get_columns_from_list(columns, target_columns, result):
- reordered_result = []
-
- for res in result:
- r = []
- for col_name in columns:
- try:
- idx = target_columns.index(col_name)
- r.append(res[idx])
- except ValueError:
- pass
-
- reordered_result.append(r)
-
- return reordered_result
+ return result
def get_prepared_report_result(report, filters, dn="", user=None):
@@ -755,6 +719,8 @@ def get_column_as_dict(col):
col_dict["fieldtype"], col_dict["options"] = col[1].split("/")
else:
col_dict["fieldtype"] = col[1]
+ if len(col) == 3:
+ col_dict["width"] = col[2]
col_dict["label"] = col[0]
col_dict["fieldname"] = frappe.scrub(col[0])
From f8afe32ea4e0cd610add5fef0a654f5a81fe0f49 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Mon, 5 Oct 2020 22:20:59 +0530
Subject: [PATCH 004/163] fix: Scrub fieldname [wip]
---
frappe/desk/query_report.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index 95f93b612e..e8a03a1552 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -244,7 +244,7 @@ def add_custom_column_data(custom_columns, result):
key = (column.get('doctype'), column.get('fieldname'))
if key in custom_column_data:
for row in result:
- row_reference = row[column.get('link_field')]
+ row_reference = row[frappe.scrub(column.get('link_field'))]
row[column.get('fieldname')] = custom_column_data.get(key).get(row_reference)
return result
From 51f5bb4811032efa8ec468218d5a9fcd54530c66 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Mon, 5 Oct 2020 23:11:10 +0530
Subject: [PATCH 005/163] fix: Preserve column order
---
frappe/desk/query_report.py | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index e8a03a1552..922c50b036 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -75,23 +75,25 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None)
columns, result, message, chart, report_summary, skip_total_row = ljust_list(res, 6)
columns = [get_column_as_dict(col) for col in columns]
+ report_column_names = [col["fieldname"] for col in columns]
# convert to list of dicts
result = normalize_result(result, columns)
+ if report.custom_columns:
+ # saved columns (with custom columns / with different column order)
+ columns = json.loads(report.custom_columns)
+
+ # unsaved custom_columns
if custom_columns:
for custom_column in custom_columns:
columns.insert(custom_column["insert_after_index"] + 1, custom_column)
- custom_columns = custom_columns or []
+ # all columns which are not in original report
+ report_custom_columns = [column for column in columns if column["fieldname"] not in report_column_names]
- if isinstance(report.custom_columns, string_types):
- custom_columns += json.loads(report.custom_columns) or []
- else:
- custom_columns += report.custom_columns or []
-
- if custom_columns:
- result = add_custom_column_data(custom_columns, result)
+ if report_custom_columns:
+ result = add_custom_column_data(report_custom_columns, result)
if result:
result = get_filtered_data(report.ref_doctype, columns, result, user)
From 6e22a48e224f60921025cba1f3f4a0136a5ee93a Mon Sep 17 00:00:00 2001
From: prssanna
Date: Fri, 9 Oct 2020 12:10:32 +0530
Subject: [PATCH 006/163] feat: reset scroll position on list paging
---
frappe/public/js/frappe/list/base_list.js | 16 +++++++++++++---
frappe/public/js/frappe/list/list_view.js | 17 ++++++++++++++++-
frappe/public/js/frappe/utils/utils.js | 22 +++++++++++++---------
frappe/public/less/list.less | 9 +++++++++
4 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js
index bbe2fa2f95..af120b5a0b 100644
--- a/frappe/public/js/frappe/list/base_list.js
+++ b/frappe/public/js/frappe/list/base_list.js
@@ -308,7 +308,6 @@ frappe.views.BaseList = class BaseList {
this.$paging_area.on('click', '.btn-paging, .btn-more', e => {
const $this = $(e.currentTarget);
-
if ($this.is('.btn-paging')) {
// set active button
this.$paging_area.find('.btn-paging').removeClass('btn-info');
@@ -316,11 +315,22 @@ frappe.views.BaseList = class BaseList {
this.start = 0;
this.page_length = $this.data().value;
- this.refresh();
} else if ($this.is('.btn-more')) {
this.start = this.start + this.page_length;
- this.refresh();
}
+
+ // Trigger page change event so that scroll position can be set
+ const end = this.data.length;
+ const scroll_position = frappe.utils.get_scroll_position($(e.currentTarget));
+ let trigger_scroll = () => {
+ $(document.body).trigger('pageChange', [scroll_position, end]);
+ };
+
+ frappe.run_serially([
+ () => this.refresh(),
+ () => trigger_scroll(),
+ ]);
+
});
}
diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index 4d8121ebd6..06757229e6 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -243,7 +243,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
refresh(refresh_header=false) {
- super.refresh().then(() => {
+ return super.refresh().then(() => {
this.render_header(refresh_header);
});
}
@@ -856,6 +856,21 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.setup_realtime_updates();
this.setup_action_handler();
this.setup_keyboard_navigation();
+ this.setup_scroll_on_page_change();
+ }
+
+ setup_scroll_on_page_change() {
+ if (!this.view_name == 'List') return;
+
+ // Set scroll position to the last row and highlight the row
+ $(document.body).on('pageChange', (e, scroll_position, end) => {
+ const should_scroll = this.data.length > end;
+ if (should_scroll) {
+ const $last_row = this.$result.find('.list-row-container').eq(end-1);
+ frappe.utils.scroll_to(scroll_position, true, 50);
+ $last_row.addClass('yellow-highlight');
+ }
+ });
}
setup_keyboard_navigation() {
diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js
index b8eeefb046..5547c52ef1 100644
--- a/frappe/public/js/frappe/utils/utils.js
+++ b/frappe/public/js/frappe/utils/utils.js
@@ -122,18 +122,17 @@ Object.assign(frappe.utils, {
');
return content.html();
},
- scroll_to: function(element, animate, additional_offset, element_to_be_scrolled) {
+ scroll_to: function(element, animate=true, additional_offset, element_to_be_scrolled) {
element_to_be_scrolled = element_to_be_scrolled || $("html, body");
- var y = 0;
- if (element && typeof element==="number") {
- y = element;
- } else if(element) {
- var header_offset = $(".navbar").height() + $(".page-head").height();
- var y = $(element).offset().top - header_offset - cint(additional_offset);
+ let y = 0;
+ if (element) {
+ y = typeof element == "number"
+ ? element - cint(additional_offset)
+ : this.get_scroll_position(element, additional_offset);
}
- if(y < 0) {
+ if (y < 0) {
y = 0;
}
@@ -142,13 +141,18 @@ Object.assign(frappe.utils, {
return;
}
- if (animate !== false) {
+ if (animate) {
element_to_be_scrolled.animate({ scrollTop: y });
} else {
element_to_be_scrolled.scrollTop(y);
}
},
+ get_scroll_position: function(element, additional_offset) {
+ let header_offset = $(".navbar").height() + $(".page-head").height();
+ let scroll_top = $(element).offset().top - header_offset - cint(additional_offset);
+ return scroll_top;
+ },
filter_dict: function(dict, filters) {
var ret = [];
if(typeof filters=='string') {
diff --git a/frappe/public/less/list.less b/frappe/public/less/list.less
index 4c7d04406d..51409db82e 100644
--- a/frappe/public/less/list.less
+++ b/frappe/public/less/list.less
@@ -50,6 +50,15 @@
}
}
+@keyframes yellow-highlight {
+ 0% {background-color: @light-yellow;}
+ 100% {background-color: transparent;}
+}
+
+.yellow-highlight {
+ animation: yellow-highlight 2s ease-in-out;
+}
+
body.no-list-sidebar {
[data-page-route^="List/"] {
@media (min-width: @screen-md) {
From 50d2f6bb0816888ef4dd9c5103259125de84c9ae Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Sat, 10 Oct 2020 09:54:17 +0530
Subject: [PATCH 007/163] test: Use .get to avoid KeyError
---
frappe/core/doctype/report/test_report.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py
index 805b903300..78904afc5f 100644
--- a/frappe/core/doctype/report/test_report.py
+++ b/frappe/core/doctype/report/test_report.py
@@ -27,7 +27,7 @@ class TestReport(unittest.TestCase):
columns, data = report.get_data(filters={'user': 'Administrator', 'doctype': 'DocType'})
self.assertEqual(columns[0].get('label'), 'Name')
self.assertEqual(columns[1].get('label'), 'Module')
- self.assertTrue('User' in [d[0] for d in data])
+ self.assertTrue('User' in [d.get('name') for d in data])
def test_report_permissions(self):
frappe.set_user('test@example.com')
From 5dea5e227cf191dac63c7f914cf2ed339c1dc8e9 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Tue, 13 Oct 2020 16:08:59 +0530
Subject: [PATCH 008/163] test: Add tests to validate custom report data
---
frappe/core/doctype/report/test_report.py | 47 +++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py
index 78904afc5f..2d2290e448 100644
--- a/frappe/core/doctype/report/test_report.py
+++ b/frappe/core/doctype/report/test_report.py
@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe, json, os
import unittest
+from frappe.desk.query_report import run, save_report, get_report_doc
test_records = frappe.get_test_records('Report')
test_dependencies = ['User']
@@ -29,6 +30,52 @@ class TestReport(unittest.TestCase):
self.assertEqual(columns[1].get('label'), 'Module')
self.assertTrue('User' in [d.get('name') for d in data])
+ def test_custom_report(self):
+ custom_report_name = save_report(
+ 'Permitted Documents For User',
+ 'Permitted Documents For User Custom',
+ json.dumps([{
+ 'fieldname': 'email',
+ 'fieldtype': 'Data',
+ 'label': 'Email',
+ 'insert_after_index': 0,
+ 'link_field': 'name',
+ 'doctype': 'User',
+ 'options': 'Email',
+ 'width': 100,
+ 'id':'email',
+ 'name': 'Email'
+ }]))
+ custom_report = frappe.get_doc('Report', custom_report_name)
+ columns, result = custom_report.run_query_report(
+ filters={
+ 'user': 'Administrator',
+ 'doctype': 'User'
+ }, user=frappe.session.user)
+
+ self.assertListEqual(['email'], [column.get('fieldname') for column in columns])
+ self.assertDictEqual({'name': 'Administrator', 'user_type': 'System User', 'email': 'admin@example.com'}, result[0])
+
+ def test_report_with_custom_column(self):
+ response = run('Permitted Documents For User',
+ filters={'user': 'Administrator', 'doctype': 'User'},
+ custom_columns=[{
+ 'fieldname': 'email',
+ 'fieldtype': 'Data',
+ 'label': 'Email',
+ 'insert_after_index': 0,
+ 'link_field': 'name',
+ 'doctype': 'User',
+ 'options': 'Email',
+ 'width': 100,
+ 'id':'email',
+ 'name': 'Email'
+ }])
+ result = response.get('result')
+ columns = response.get('columns')
+ self.assertListEqual(['name', 'email', 'user_type'], [column.get('fieldname') for column in columns])
+ self.assertDictEqual({'name': 'Administrator', 'user_type': 'System User', 'email': 'admin@example.com'}, result[0])
+
def test_report_permissions(self):
frappe.set_user('test@example.com')
frappe.db.sql("""delete from `tabHas Role` where parent = %s
From ab368994b05c1071c0d7460c73af36406c9ae27f Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Tue, 13 Oct 2020 16:09:29 +0530
Subject: [PATCH 009/163] fix: Use get to avoid key error
---
frappe/desk/query_report.py | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index 922c50b036..4e7cbd9604 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -195,14 +195,7 @@ def get_script(report_name):
@frappe.whitelist()
@frappe.read_only()
-def run(
- report_name,
- filters=None,
- user=None,
- ignore_prepared_report=False,
- custom_columns=None,
-):
-
+def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None):
report = get_report_doc(report_name)
if not user:
user = frappe.session.user
@@ -246,7 +239,8 @@ def add_custom_column_data(custom_columns, result):
key = (column.get('doctype'), column.get('fieldname'))
if key in custom_column_data:
for row in result:
- row_reference = row[frappe.scrub(column.get('link_field'))]
+ row_reference = row.get(scrub(column.get('link_field')))
+ if not row_reference: continue
row[column.get('fieldname')] = custom_column_data.get(key).get(row_reference)
return result
From f7e45d1acddd76fac171ad7ee290663f47efc8ef Mon Sep 17 00:00:00 2001
From: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Date: Tue, 13 Oct 2020 15:28:06 +0200
Subject: [PATCH 010/163] fix: show headline instead of save
---
frappe/website/doctype/web_template/web_template.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/website/doctype/web_template/web_template.js b/frappe/website/doctype/web_template/web_template.js
index 1488dbf1be..def4e2dab1 100644
--- a/frappe/website/doctype/web_template/web_template.js
+++ b/frappe/website/doctype/web_template/web_template.js
@@ -16,7 +16,7 @@ frappe.ui.form.on('Web Template', {
// the next save. Changes will get overwritten from the backend
// on save and should not be possible in the UI.
frm.toggle_display('template', false);
- frm.save();
+ frm.dashboard.set_headline(__('Please save to edit the template.'));
}
}
});
From 80fad21311482ea67699613b0ba4c43d4879fe63 Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Wed, 14 Oct 2020 15:54:06 +0530
Subject: [PATCH 011/163] chore: add as_list parameter to msgprint
---
frappe/__init__.py | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/frappe/__init__.py b/frappe/__init__.py
index 554f1f9747..ec032fef19 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -312,7 +312,7 @@ def log(msg):
debug_log.append(as_unicode(msg))
-def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None, alert=False, primary_action=None, is_minimizable=None, wide=None):
+def msgprint(msg, title=None, raise_exception=0, as_table=False, as_list=False, indicator=None, alert=False, primary_action=None, is_minimizable=None, wide=None):
"""Print a message to the user (via HTTP response).
Messages are sent in the `__server_messages` property in the
response JSON and shown in a pop-up / modal.
@@ -321,6 +321,7 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None,
:param title: [optional] Message title.
:param raise_exception: [optional] Raise given exception and show message.
:param as_table: [optional] If `msg` is a list of lists, render as HTML table.
+ :param as_list: [optional] If `msg` is a list, render as un-ordered list.
:param primary_action: [optional] Bind a primary server/client side action.
:param is_minimizable: [optional] Allow users to minimize the modal
:param wide: [optional] Show wide modal
@@ -356,6 +357,16 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None,
out.message = ''''''.format(table_rows)
+
+ if as_list and type(msg) in (list, tuple):
+ if len(msg) > 1:
+ list_rows = ''
+ for row in msg:
+ list_rows += '{}'.format(row)
+
+ out.message = ''''''.format(list_rows)
+ elif len(msg) == 1:
+ out.message = msg[0]
if flags.print_messages and out.message:
print(f"Message: {repr(out.message).encode('utf-8')}")
From 350bc0258fc677ca0a64727d0f7423abc14c1a3e Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Wed, 14 Oct 2020 16:02:02 +0530
Subject: [PATCH 012/163] fix: pass as_list to frappe.throw
---
frappe/__init__.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/__init__.py b/frappe/__init__.py
index ec032fef19..8e6aaf71af 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -416,12 +416,12 @@ def clear_last_message():
if len(local.message_log) > 0:
local.message_log = local.message_log[:-1]
-def throw(msg, exc=ValidationError, title=None, is_minimizable=None, wide=None):
+def throw(msg, exc=ValidationError, title=None, is_minimizable=None, wide=None, **kwargs):
"""Throw execption and show message (`msgprint`).
:param msg: Message.
:param exc: Exception class. Default `frappe.ValidationError`"""
- msgprint(msg, raise_exception=exc, title=title, indicator='red', is_minimizable=is_minimizable, wide=wide)
+ msgprint(msg, raise_exception=exc, title=title, indicator='red', is_minimizable=is_minimizable, wide=wide, **kwargs)
def emit_js(js, user=False, **kwargs):
if user == False:
From 2ad5f1ea24afad2242c66ae9f4bd051f36ae8416 Mon Sep 17 00:00:00 2001
From: Saurabh
Date: Wed, 14 Oct 2020 19:13:52 +0530
Subject: [PATCH 013/163] fix: handle FileAlreadyAttachedException while
pulling email
---
frappe/email/receive.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 9ba080bfda..03aedec066 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -540,6 +540,8 @@ class Email:
except MaxFileSizeReachedError:
# WARNING: bypass max file size exception
pass
+ except frappe.FileAlreadyAttachedException:
+ pass
except frappe.DuplicateEntryError:
# same file attached twice??
pass
From d04e6cc95f1b10e35cde634c7f21db458b77d321 Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Wed, 14 Oct 2020 21:35:58 +0530
Subject: [PATCH 014/163] fix: handle msgprint html on client side
---
frappe/__init__.py | 22 +++-------------------
frappe/public/js/frappe/ui/messages.js | 13 +++++++++++++
2 files changed, 16 insertions(+), 19 deletions(-)
diff --git a/frappe/__init__.py b/frappe/__init__.py
index 8e6aaf71af..1144b228c0 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -347,26 +347,10 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, as_list=False,
return
if as_table and type(msg) in (list, tuple):
-
- table_rows = ''
- for row in msg:
- table_row_data = ''
- for data in row:
- table_row_data += '{} | '.format(data)
- table_rows += '{}
'.format(table_row_data)
-
- out.message = ''''''.format(table_rows)
+ out.as_table = 1
- if as_list and type(msg) in (list, tuple):
- if len(msg) > 1:
- list_rows = ''
- for row in msg:
- list_rows += '{}'.format(row)
-
- out.message = ''''''.format(list_rows)
- elif len(msg) == 1:
- out.message = msg[0]
+ if as_list and type(msg) in (list, tuple) and len(msg) > 1:
+ out.as_list = 1
if flags.print_messages and out.message:
print(f"Message: {repr(out.message).encode('utf-8')}")
diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js
index c37cc41650..bf1b13b424 100644
--- a/frappe/public/js/frappe/ui/messages.js
+++ b/frappe/public/js/frappe/ui/messages.js
@@ -128,6 +128,19 @@ frappe.msgprint = function(msg, title, is_minimizable) {
data.indicator = 'blue';
}
+ if (data.as_list) {
+ const list_rows = data.message.map(m => `${m}`).join('');
+ data.message = ``;
+ }
+
+ if (data.as_table) {
+ const rows = data.message.map(row => {
+ const cols = row.map(col => `${col} | `).join('');
+ return `${cols}
`
+ }).join('');
+ data.message = ``;
+ }
+
if(data.message instanceof Array) {
data.message.forEach(function(m) {
frappe.msgprint(m);
From 2e74cf2c77b275d25ae10fe48aaa5e13bb963258 Mon Sep 17 00:00:00 2001
From: Saqib Ansari
Date: Wed, 14 Oct 2020 21:38:18 +0530
Subject: [PATCH 015/163] fix: missing semicolon
---
frappe/public/js/frappe/ui/messages.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js
index bf1b13b424..9f40c59819 100644
--- a/frappe/public/js/frappe/ui/messages.js
+++ b/frappe/public/js/frappe/ui/messages.js
@@ -136,7 +136,7 @@ frappe.msgprint = function(msg, title, is_minimizable) {
if (data.as_table) {
const rows = data.message.map(row => {
const cols = row.map(col => `${col} | `).join('');
- return `${cols}
`
+ return `${cols}
`;
}).join('');
data.message = ``;
}
From c0494aac5abc57e9a6cf4fde23518f4169a98777 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Fri, 16 Oct 2020 10:46:56 +0530
Subject: [PATCH 016/163] fix: Handle empty row
---
frappe/desk/query_report.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index 4e7cbd9604..c577426be5 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -239,8 +239,10 @@ def add_custom_column_data(custom_columns, result):
key = (column.get('doctype'), column.get('fieldname'))
if key in custom_column_data:
for row in result:
- row_reference = row.get(scrub(column.get('link_field')))
- if not row_reference: continue
+ row_reference = row.get(column.get('link_field'))
+ # possible if the row is empty
+ if not row_reference:
+ continue
row[column.get('fieldname')] = custom_column_data.get(key).get(row_reference)
return result
From 5173b21bdbb061b24c07bba4a35f26911e61aaa1 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Fri, 16 Oct 2020 13:40:32 +0530
Subject: [PATCH 017/163] refactor: Make export_query code more readable
---
frappe/desk/query_report.py | 60 +++++++++++++++++--------------------
1 file changed, 28 insertions(+), 32 deletions(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index c577426be5..dd318c8de6 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -12,6 +12,7 @@ from frappe.modules import scrub, get_module_path
from frappe.utils import (
flt,
cint,
+ cstr,
get_html_format,
get_url_to_form,
gzip_decompress,
@@ -305,31 +306,27 @@ def get_prepared_report_result(report, filters, dn="", user=None):
@frappe.whitelist()
def export_query():
"""export from query reports"""
-
data = frappe._dict(frappe.local.form_dict)
-
- del data["cmd"]
- if "csrf_token" in data:
- del data["csrf_token"]
+ data.pop("cmd", None)
+ data.pop("csrf_token", None)
if isinstance(data.get("filters"), string_types):
filters = json.loads(data["filters"])
- if isinstance(data.get("report_name"), string_types):
+
+ if data.get("report_name"):
report_name = data["report_name"]
frappe.permissions.can_export(
frappe.get_cached_value("Report", report_name, "ref_doctype"),
raise_exception=True,
)
- if isinstance(data.get("file_format_type"), string_types):
- file_format_type = data["file_format_type"]
- custom_columns = frappe.parse_json(data["custom_columns"])
+ file_format_type = data.get("file_format_type")
+ custom_columns = frappe.parse_json(data.get("custom_columns", "[]"))
+ include_indentation = data.get("include_indentation")
+ visible_idx = data.get("visible_idx")
- include_indentation = data["include_indentation"]
- if isinstance(data.get("visible_idx"), string_types):
- visible_idx = json.loads(data.get("visible_idx"))
- else:
- visible_idx = None
+ if isinstance(visible_idx, string_types):
+ visible_idx = json.loads(visible_idx)
if file_format_type == "Excel":
data = run(report_name, filters, custom_columns=custom_columns)
@@ -384,28 +381,27 @@ def handle_duration_fieldtype_values(result, columns):
def build_xlsx_data(columns, data, visible_idx, include_indentation):
result = [[]]
- # add column headings
- for idx in range(len(data.columns)):
- if not columns[idx].get("hidden"):
- result[0].append(columns[idx]["label"])
+ for column in data.columns:
+ if column.get("hidden"):
+ continue
+ result[0].append(column["label"])
# build table from result
- for i, row in enumerate(data.result):
+ for row_idx, row in enumerate(data.result):
# only pick up rows that are visible in the report
- if i in visible_idx:
+ if row_idx in visible_idx:
row_data = []
-
- if isinstance(row, dict) and row:
- for idx in range(len(data.columns)):
- # check if column is not hidden
- if not columns[idx].get("hidden"):
- label = columns[idx]["label"]
- fieldname = columns[idx]["fieldname"]
- cell_value = row.get(fieldname, row.get(label, ""))
- if cint(include_indentation) and "indent" in row and idx == 0:
- cell_value = (" " * cint(row["indent"])) + cell_value
- row_data.append(cell_value)
- else:
+ if isinstance(row, dict):
+ for col_idx, column in enumerate(data.columns):
+ if column.get("hidden"):
+ continue
+ label = column.get("label")
+ fieldname = column.get("fieldname")
+ cell_value = row.get(fieldname, row.get(label, ""))
+ if cint(include_indentation) and "indent" in row and col_idx == 0:
+ cell_value = (" " * cint(row["indent"])) + cstr(cell_value)
+ row_data.append(cell_value)
+ elif row:
row_data = row
result.append(row_data)
From f1cb8f4e5ceedbad9d14481f502e98096fe376d1 Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Fri, 16 Oct 2020 13:42:00 +0530
Subject: [PATCH 018/163] feat: Pass custom widths for excel columns
---
frappe/desk/query_report.py | 11 ++++++++---
frappe/utils/xlsxutils.py | 11 ++++++++---
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index dd318c8de6..b9be0ad683 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -345,8 +345,8 @@ def export_query():
data["result"] = handle_duration_fieldtype_values(
data.get("result"), data.get("columns")
)
- xlsx_data = build_xlsx_data(columns, data, visible_idx, include_indentation)
- xlsx_file = make_xlsx(xlsx_data, "Query Report")
+ xlsx_data, column_widths = build_xlsx_data(columns, data, visible_idx, include_indentation)
+ xlsx_file = make_xlsx(xlsx_data, "Query Report", column_widths=column_widths)
frappe.response["filename"] = report_name + ".xlsx"
frappe.response["filecontent"] = xlsx_file.getvalue()
@@ -380,11 +380,16 @@ def handle_duration_fieldtype_values(result, columns):
def build_xlsx_data(columns, data, visible_idx, include_indentation):
result = [[]]
+ column_widths = []
for column in data.columns:
if column.get("hidden"):
continue
result[0].append(column["label"])
+ column_width = column.get('width', 0)
+ # to convert into scale accepted by openpyxl
+ column_width /= 10
+ column_widths.append(column_width)
# build table from result
for row_idx, row in enumerate(data.result):
@@ -406,7 +411,7 @@ def build_xlsx_data(columns, data, visible_idx, include_indentation):
result.append(row_data)
- return result
+ return result, column_widths
def add_total_row(result, columns, meta=None):
diff --git a/frappe/utils/xlsxutils.py b/frappe/utils/xlsxutils.py
index 2814c5ff40..3c7b027470 100644
--- a/frappe/utils/xlsxutils.py
+++ b/frappe/utils/xlsxutils.py
@@ -9,19 +9,24 @@ import xlrd
import re
from openpyxl.styles import Font
from openpyxl import load_workbook
+from openpyxl.utils import get_column_letter
from six import BytesIO, string_types
ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]')
# return xlsx file object
-def make_xlsx(data, sheet_name, wb=None):
-
+def make_xlsx(data, sheet_name, wb=None, column_widths=None):
+ column_widths = column_widths or []
if wb is None:
wb = openpyxl.Workbook(write_only=True)
ws = wb.create_sheet(sheet_name, 0)
+ for i, column_width in enumerate(column_widths):
+ if column_width:
+ ws.column_dimensions[get_column_letter(i + 1)].width = column_width
+
row1 = ws.row_dimensions[1]
- row1.font = Font(name='Calibri',bold=True)
+ row1.font = Font(name='Calibri', bold=True)
for row in data:
clean_row = []
From 44b1336a885483ed6e535b72080ffdc02e870f9b Mon Sep 17 00:00:00 2001
From: Suraj Shetty
Date: Fri, 16 Oct 2020 13:43:35 +0530
Subject: [PATCH 019/163] style: Remove unused import
---
frappe/core/doctype/report/test_report.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py
index 2d2290e448..fc47e3a8a8 100644
--- a/frappe/core/doctype/report/test_report.py
+++ b/frappe/core/doctype/report/test_report.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, json, os
import unittest
-from frappe.desk.query_report import run, save_report, get_report_doc
+from frappe.desk.query_report import run, save_report
test_records = frappe.get_test_records('Report')
test_dependencies = ['User']
From 51d50ff7afab78891a2071ed77d7570c54eb3c3e Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 1 Oct 2020 16:25:35 +0530
Subject: [PATCH 020/163] feat: rule in assignment rule to assign based on
field value
---
.../assignment_rule/assignment_rule.js | 30 +++++++--
.../assignment_rule/assignment_rule.json | 8 ++-
.../assignment_rule/assignment_rule.py | 35 ++++++-----
.../assignment_rule_user.json | 62 +++----------------
4 files changed, 61 insertions(+), 74 deletions(-)
diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.js b/frappe/automation/doctype/assignment_rule/assignment_rule.js
index e8d17527bf..dd5e1f84f4 100644
--- a/frappe/automation/doctype/assignment_rule/assignment_rule.js
+++ b/frappe/automation/doctype/assignment_rule/assignment_rule.js
@@ -9,11 +9,33 @@ frappe.ui.form.on('Assignment Rule', {
// refresh description
frm.events.rule(frm);
},
+
+ document_type: function(frm) {
+ frm.trigger('set_field_options');
+ },
+
rule: function(frm) {
- if (frm.doc.rule === 'Round Robin') {
- frm.get_field('rule').set_description(__('Assign one by one, in sequence'));
- } else {
- frm.get_field('rule').set_description(__('Assign to the one who has the least assignments'));
+ const description_map = {
+ 'Round Robin': __('Assign one by one, in sequence'),
+ 'Load Balancing': __('Assign to the one who has the least assignments'),
+ 'Based on Field': __('Assign to the user set in this field')
+ }
+ frm.get_field('rule').set_description(description_map[frm.doc.rule]);
+ },
+
+ set_field_options(frm) {
+ let doctype = frm.doc.document_type;
+ let user_link_fields = [{label: 'Owner', value: 'owner'}];
+ if (doctype) {
+ frappe.model.with_doctype(doctype, () => {
+ // get all date and datetime fields
+ frappe.get_meta(doctype).fields.map(df => {
+ if (df.fieldtype == 'Link' && df.options == 'User') {
+ user_link_fields.push({label: df.label, value: df.fieldname});
+ }
+ });
+ frm.set_df_property('field', 'options', user_link_fields);
+ });
}
},
document_type: (frm) => {
diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.json b/frappe/automation/doctype/assignment_rule/assignment_rule.json
index 858ad8aac4..415ebb1c45 100644
--- a/frappe/automation/doctype/assignment_rule/assignment_rule.json
+++ b/frappe/automation/doctype/assignment_rule/assignment_rule.json
@@ -24,6 +24,7 @@
"assignment_days",
"assign_to_users_section",
"rule",
+ "field",
"users",
"last_user"
],
@@ -93,15 +94,16 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Rule",
- "options": "Round Robin\nLoad Balancing",
+ "options": "Round Robin\nLoad Balancing\nBased on Field",
"reqd": 1
},
{
+ "depends_on": "eval: doc.rule !== 'Based on Field'",
"fieldname": "users",
"fieldtype": "Table MultiSelect",
"label": "Users",
- "options": "Assignment Rule User",
- "reqd": 1
+ "mandatory_depends_on": "eval: doc.rule !== 'Based on Field'",
+ "options": "Assignment Rule User"
},
{
"fieldname": "last_user",
diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py
index cd70799361..623df0a41e 100644
--- a/frappe/automation/doctype/assignment_rule/assignment_rule.py
+++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py
@@ -38,27 +38,30 @@ class AssignmentRule(Document):
def apply_assign(self, doc):
if self.safe_eval('assign_condition', doc):
- self.do_assignment(doc)
- return True
+ return self.do_assignment(doc)
def do_assignment(self, doc):
# clear existing assignment, to reassign
assign_to.clear(doc.get('doctype'), doc.get('name'))
- user = self.get_user()
+ user = self.get_user(doc)
- assign_to.add(dict(
- assign_to = [user],
- doctype = doc.get('doctype'),
- name = doc.get('name'),
- description = frappe.render_template(self.description, doc),
- assignment_rule = self.name,
- notify = True,
- date = doc.get(self.due_date_based_on) if self.due_date_based_on else None
- ))
+ if user:
+ assign_to.add(dict(
+ assign_to = [user],
+ doctype = doc.get('doctype'),
+ name = doc.get('name'),
+ description = frappe.render_template(self.description, doc),
+ assignment_rule = self.name,
+ notify = True,
+ date = doc.get(self.due_date_based_on) if self.due_date_based_on else None
+ ))
- # set for reference in round robin
- self.db_set('last_user', user)
+ # set for reference in round robin
+ self.db_set('last_user', user)
+ return True
+ else:
+ return False
def clear_assignment(self, doc):
'''Clear assignments'''
@@ -70,7 +73,7 @@ class AssignmentRule(Document):
if self.safe_eval('close_condition', doc):
return assign_to.close_all_assignments(doc.get('doctype'), doc.get('name'))
- def get_user(self):
+ def get_user(self, doc):
'''
Get the next user for assignment
'''
@@ -78,6 +81,8 @@ class AssignmentRule(Document):
return self.get_user_round_robin()
elif self.rule == 'Load Balancing':
return self.get_user_load_balancing()
+ elif self.rule == 'Based on Field':
+ return doc[self.field]
def get_user_round_robin(self):
'''
diff --git a/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.json b/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.json
index f529772c8e..5a159c8267 100644
--- a/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.json
+++ b/frappe/automation/doctype/assignment_rule_user/assignment_rule_user.json
@@ -1,76 +1,34 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
+ "actions": [],
+ "allow_read": 1,
"creation": "2019-02-27 11:41:46.602400",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "user"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "user",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "User",
- "length": 0,
- "no_copy": 0,
"options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
+ "index_web_pages_for_search": 1,
"istable": 1,
- "max_attachments": 0,
- "modified": "2019-02-27 17:16:41.399261",
+ "links": [],
+ "modified": "2020-09-29 20:12:14.456785",
"modified_by": "Administrator",
"module": "Automation",
"name": "Assignment Rule User",
- "name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
From ce1b6bde25cdde7b03302dfa7aaeb100027c0155 Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 1 Oct 2020 18:08:37 +0530
Subject: [PATCH 021/163] feat: buttons to fetch assignment days in table
---
.../assignment_rule/assignment_rule.js | 52 +++++++++++++++----
1 file changed, 43 insertions(+), 9 deletions(-)
diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.js b/frappe/automation/doctype/assignment_rule/assignment_rule.js
index dd5e1f84f4..bb262a7f58 100644
--- a/frappe/automation/doctype/assignment_rule/assignment_rule.js
+++ b/frappe/automation/doctype/assignment_rule/assignment_rule.js
@@ -5,42 +5,76 @@ frappe.ui.form.on('Assignment Rule', {
onload: (frm) => {
frm.trigger('set_due_date_field_options');
},
+ setup: function(frm) {
+ frm.trigger('setup_assignment_days_buttons');
+ },
refresh: function(frm) {
// refresh description
frm.events.rule(frm);
+ frm.trigger('set_field_options');
},
document_type: function(frm) {
frm.trigger('set_field_options');
+ frm.trigger('set_due_date_field_options');
+ },
+
+ setup_assignment_days_buttons: function(frm) {
+ const labels = ['Weekends', 'Weekdays', 'All Days'];
+ let get_days = (label) => {
+ const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
+ const weekends = ['Saturday', 'Sunday'];
+ return {
+ 'All Days': weekdays.concat(weekends),
+ 'Weekdays': weekdays,
+ 'Weekends': weekends,
+ }[label];
+ };
+
+ let get_button_html = (label) => ``;
+
+ const $wrapper = frm.get_field('assignment_days').$wrapper;
+ $(`
+ ${labels.map(get_button_html).join('')}
+
`).prependTo($wrapper);
+
+ frm.$wrapper.find('.fetch-days-buttons').on('click', '.btn', (e) => {
+ frm.clear_table('assignment_days');
+ const label = $(e.currentTarget).text();
+ get_days(label).forEach((day) =>
+ frm.add_child('assignment_days', { day: day })
+ );
+ frm.refresh_field('assignment_days');
+ });
},
rule: function(frm) {
const description_map = {
'Round Robin': __('Assign one by one, in sequence'),
'Load Balancing': __('Assign to the one who has the least assignments'),
- 'Based on Field': __('Assign to the user set in this field')
- }
+ 'Based on Field': __('Assign to the user set in this field'),
+ };
frm.get_field('rule').set_description(description_map[frm.doc.rule]);
},
set_field_options(frm) {
let doctype = frm.doc.document_type;
- let user_link_fields = [{label: 'Owner', value: 'owner'}];
+ let user_link_fields = [{ label: 'Owner', value: 'owner' }];
if (doctype) {
frappe.model.with_doctype(doctype, () => {
- // get all date and datetime fields
- frappe.get_meta(doctype).fields.map(df => {
+ frappe.get_meta(doctype).fields.map((df) => {
if (df.fieldtype == 'Link' && df.options == 'User') {
- user_link_fields.push({label: df.label, value: df.fieldname});
+ user_link_fields.push({ label: df.label, value: df.fieldname });
}
});
frm.set_df_property('field', 'options', user_link_fields);
});
}
},
- document_type: (frm) => {
- frm.trigger('set_due_date_field_options');
- },
set_due_date_field_options: (frm) => {
let doctype = frm.doc.document_type;
let datetime_fields = [];
From 5f20e85fe6be4374598bfba51b32314c06fb38fc Mon Sep 17 00:00:00 2001
From: prssanna
Date: Thu, 1 Oct 2020 18:33:42 +0530
Subject: [PATCH 022/163] fix: formatting issues
---
frappe/automation/doctype/assignment_rule/assignment_rule.js | 5 ++---
frappe/automation/doctype/assignment_rule/assignment_rule.py | 4 ++--
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.js b/frappe/automation/doctype/assignment_rule/assignment_rule.js
index bb262a7f58..dcdfa65985 100644
--- a/frappe/automation/doctype/assignment_rule/assignment_rule.js
+++ b/frappe/automation/doctype/assignment_rule/assignment_rule.js
@@ -33,9 +33,8 @@ frappe.ui.form.on('Assignment Rule', {
let get_button_html = (label) => ``;
+ style="margin-bottom: 10px; margin-right: 5px">${__(label)}
+ `;
const $wrapper = frm.get_field('assignment_days').$wrapper;
$(`