From f71a8e0afcffae02dee34fd20642ee439c6b6017 Mon Sep 17 00:00:00 2001 From: everyx Date: Tue, 24 Nov 2020 14:36:53 +0800 Subject: [PATCH 01/20] fix: `PRIMARY KEY must be NOT NULL` error when install with MySQL 5.7+ --- frappe/database/mariadb/framework_mariadb.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index 15b0bed699..a52efd01e3 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -233,7 +233,7 @@ CREATE TABLE `tabDocType` ( DROP TABLE IF EXISTS `tabSeries`; CREATE TABLE `tabSeries` ( - `name` varchar(100) DEFAULT NULL, + `name` varchar(100), `current` int(10) NOT NULL DEFAULT 0, PRIMARY KEY(`name`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; From 926d7e78fd75dcfc5713de7c684900d54d84a843 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 26 Nov 2020 15:33:55 +0530 Subject: [PATCH 02/20] fix: clear localstorage if quota exceeds --- frappe/public/js/frappe/model/model.js | 33 +++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 308d9bd5f8..1d302215dd 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -103,6 +103,31 @@ $.extend(frappe.model, { return docfield[0]; }, + get_from_localstorage: function(doctype) { + if (localStorage["_doctype:" + doctype]) { + return JSON.parse(localStorage["_doctype:" + doctype]); + } + }, + + set_in_localstorage: function(doctype, docs) { + try { + localStorage["_doctype:" + doctype] = JSON.stringify(docs); + } catch(e) { + // if quota is exceeded, clear local storage and set item + console.warn("localStorage quota exceeded, clearing doctype cache") + frappe.model.clear_local_storage(); + localStorage["_doctype:" + doctype] = JSON.stringify(docs); + } + }, + + clear_local_storage: function() { + for(var key in localStorage) { + if (key.startsWith("_doctype:")) { + localStorage.removeItem(key); + } + } + }, + with_doctype: function(doctype, callback, async) { if(locals.DocType[doctype]) { callback && callback(); @@ -110,13 +135,15 @@ $.extend(frappe.model, { let cached_timestamp = null; let cached_doc = null; - if(localStorage["_doctype:" + doctype]) { - let cached_docs = JSON.parse(localStorage["_doctype:" + doctype]); + let cached_docs = frappe.model.get_from_localstorage(doctype) + + if (cached_docs) { cached_doc = cached_docs.filter(doc => doc.name === doctype)[0]; if(cached_doc) { cached_timestamp = cached_doc.modified; } } + return frappe.call({ method:'frappe.desk.form.load.getdoctype', type: "GET", @@ -134,7 +161,7 @@ $.extend(frappe.model, { if(r.message=="use_cache") { frappe.model.sync(cached_doc); } else { - localStorage["_doctype:" + doctype] = JSON.stringify(r.docs); + frappe.model.set_in_localstorage(doctype, r.docs) } frappe.model.init_doctype(doctype); From 8192db7382a02fd4334c227ad15339c10f7bede5 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 26 Nov 2020 16:22:40 +0530 Subject: [PATCH 03/20] chore: remove stray console --- frappe/public/js/frappe/desk.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index c8ed29fb76..5fa7a9dbcb 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -148,7 +148,6 @@ frappe.Application = Class.extend({ user: frappe.session.user }, callback: function(r) { - console.log(r); if(r.message.show_alert){ frappe.show_alert({ indicator: 'red', From 9808c868d70ce334dadda790ca55b3819d557ca7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 30 Nov 2020 14:14:19 +0530 Subject: [PATCH 04/20] feat: allow html in email templates --- .../email_template/email_template.json | 22 +++++++++++++-- .../doctype/email_template/email_template.py | 27 ++++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/frappe/email/doctype/email_template/email_template.json b/frappe/email/doctype/email_template/email_template.json index 0d0922f16f..dc73acacc1 100644 --- a/frappe/email/doctype/email_template/email_template.json +++ b/frappe/email/doctype/email_template/email_template.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "Prompt", @@ -8,6 +9,8 @@ "engine": "InnoDB", "field_order": [ "subject", + "use_html", + "response_html", "response", "owner", "section_break_4", @@ -22,11 +25,12 @@ "reqd": 1 }, { + "depends_on": "eval:!doc.use_html", "fieldname": "response", "fieldtype": "Text Editor", "in_list_view": 1, "label": "Response", - "reqd": 1 + "mandatory_depends_on": "eval:!doc.use_html" }, { "default": "user", @@ -45,10 +49,24 @@ "fieldtype": "HTML", "label": "Email Reply Help", "options": "

Email Reply Example

\n\n
Order Overdue\n\nTransaction {{ name }} has exceeded Due Date. Please take necessary action.\n\nDetails\n\n- Customer: {{ customer }}\n- Amount: {{ grand_total }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

\n" + }, + { + "default": "0", + "fieldname": "use_html", + "fieldtype": "Check", + "label": "Use HTML" + }, + { + "depends_on": "eval:doc.use_html", + "fieldname": "response_html", + "fieldtype": "Code", + "label": "Response ", + "options": "HTML" } ], "icon": "fa fa-comment", - "modified": "2019-10-30 14:15:00.956347", + "links": [], + "modified": "2020-11-30 14:12:50.321633", "modified_by": "Administrator", "module": "Email", "name": "Email Template", diff --git a/frappe/email/doctype/email_template/email_template.py b/frappe/email/doctype/email_template/email_template.py index 2743032331..6708e9dd3f 100644 --- a/frappe/email/doctype/email_template/email_template.py +++ b/frappe/email/doctype/email_template/email_template.py @@ -9,7 +9,29 @@ from six import string_types class EmailTemplate(Document): def validate(self): - validate_template(self.response) + if self.use_html: + validate_template(self.response_html) + else: + validate_template(self.response) + + def get_formatted_subject(self, doc): + return frappe.render_template(self.subject, doc) + + def get_formatted_response(self, doc): + if self.use_html: + return frappe.render_template(self.response_html, doc) + + return frappe.render_template(self.response, doc) + + def get_formatted_email(self, doc): + if isinstance(doc, string_types): + doc = json.loads(doc) + + return { + "subject" : self.get_formatted_subject(doc), + "message" : self.get_formatted_response(doc) + } + @frappe.whitelist() def get_email_template(template_name, doc): @@ -18,5 +40,4 @@ def get_email_template(template_name, doc): doc = json.loads(doc) email_template = frappe.get_doc("Email Template", template_name) - return {"subject" : frappe.render_template(email_template.subject, doc), - "message" : frappe.render_template(email_template.response, doc)} \ No newline at end of file + return email_template.get_formatted_email(doc) \ No newline at end of file From 3296097df606df2998f01dde927541836504d6bc Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 30 Nov 2020 15:46:29 +0530 Subject: [PATCH 05/20] feat: show absolute value in print format --- frappe/model/base_document.py | 6 +++--- frappe/printing/doctype/print_format/print_format.json | 9 ++++++++- frappe/templates/print_formats/standard_macros.html | 5 ++--- frappe/www/printview.py | 1 + 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 0a219b4253..5d86b3bac8 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -802,12 +802,12 @@ class BaseDocument(object): if translated: val = _(val) - if absolute_value and isinstance(val, (int, float)): - val = abs(self.get(fieldname)) - if not doc: doc = getattr(self, "parent_doc", None) or self + if (absolute_value or doc.get('absolute_value')) and isinstance(val, (int, float)): + val = abs(self.get(fieldname)) + return format_value(val, df=df, doc=doc, currency=currency) def is_print_hide(self, fieldname, df=None, for_print=True): diff --git a/frappe/printing/doctype/print_format/print_format.json b/frappe/printing/doctype/print_format/print_format.json index 63448ccc39..3867ce4502 100644 --- a/frappe/printing/doctype/print_format/print_format.json +++ b/frappe/printing/doctype/print_format/print_format.json @@ -22,6 +22,7 @@ "align_labels_right", "show_section_headings", "line_breaks", + "absolute_value", "column_break_11", "font", "css_section", @@ -196,13 +197,19 @@ "fieldtype": "Check", "hidden": 1, "label": "Print Format Builder" + }, + { + "default": "0", + "fieldname": "absolute_value", + "fieldtype": "Check", + "label": "Show absolute values" } ], "icon": "fa fa-print", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-27 18:27:58.307070", + "modified": "2020-11-30 15:26:35.605213", "modified_by": "Administrator", "module": "Printing", "name": "Print Format", diff --git a/frappe/templates/print_formats/standard_macros.html b/frappe/templates/print_formats/standard_macros.html index 3681a87f53..168547798b 100644 --- a/frappe/templates/print_formats/standard_macros.html +++ b/frappe/templates/print_formats/standard_macros.html @@ -136,10 +136,9 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}" {%- if df.print_width %} style="width: {{ get_width(df) }};"{% endif %}> {% elif df.fieldtype=="HTML" %} {{ frappe.render_template(df.options, {"doc":doc}) }} - {% elif df.fieldtype=="Currency" %} - {{ doc.get_formatted(df.fieldname, doc, translated=df.translatable) }} {% else %} - {{ doc.get_formatted(df.fieldname, parent_doc or doc, translated=df.translatable) }} + {%- set parent = parent_doc or doc -%} + {{ doc.get_formatted(df.fieldname, parent, translated=df.translatable, absolute_value=parent.absolute_value) }} {% endif %} {%- endmacro %} diff --git a/frappe/www/printview.py b/frappe/www/printview.py index 545e5d581d..71316dc48c 100644 --- a/frappe/www/printview.py +++ b/frappe/www/printview.py @@ -100,6 +100,7 @@ def get_rendered_template(doc, name=None, print_format=None, meta=None, doc.print_section_headings = print_format.show_section_headings doc.print_line_breaks = print_format.line_breaks doc.align_labels_right = print_format.align_labels_right + doc.absolute_value = print_format.absolute_value def get_template_from_string(): return jenv.from_string(get_print_format(doc.doctype, From ee3fa3e4e0ebf414a9a7e777a509f6a7535be0a0 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 3 Dec 2020 22:39:38 +0530 Subject: [PATCH 06/20] fix: Remove unreferenced variable base_path --- frappe/commands/site.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index bc65aa178c..35f5b13582 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -100,12 +100,10 @@ def restore(context, sql_file_path, mariadb_root_username=None, mariadb_root_pas # Extract public and/or private files to the restored site, if user has given the path if with_public_files: - with_public_files = os.path.join(base_path, with_public_files) public = extract_files(site, with_public_files, 'public') os.remove(public) if with_private_files: - with_private_files = os.path.join(base_path, with_private_files) private = extract_files(site, with_private_files, 'private') os.remove(private) From 9ac14fb5abf6930234e4da41217dd74718c858da Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 3 Dec 2020 23:26:34 +0530 Subject: [PATCH 07/20] feat: Permission Query script type Add dynamic conditions in where clause of get_list query --- .../doctype/server_script/server_script.js | 41 ++++++++++++++----- .../doctype/server_script/server_script.json | 6 +-- .../doctype/server_script/server_script.py | 6 +++ .../server_script/server_script_utils.py | 11 ++++- .../server_script/test_server_script.py | 12 ++++++ frappe/model/db_query.py | 13 +++++- 6 files changed, 71 insertions(+), 18 deletions(-) diff --git a/frappe/core/doctype/server_script/server_script.js b/frappe/core/doctype/server_script/server_script.js index 78ef2d0509..a317d69166 100644 --- a/frappe/core/doctype/server_script/server_script.js +++ b/frappe/core/doctype/server_script/server_script.js @@ -48,29 +48,33 @@ frappe.ui.form.on('Server Script', { setup_help(frm) { frm.get_field('help_html').html(` -

Examples

DocType Event

-

+

Add logic for standard doctype events like Before Insert, After Submit, etc.

+
+	
 # set property
 if "test" in doc.description:
-    doc.status = 'Closed'
+	doc.status = 'Closed'
 
 
 # validate
 if "validate" in doc.description:
-    raise frappe.ValidationError
+	raise frappe.ValidationError
 
 # auto create another document
-if doc.allocted_to:
-    frappe.get_doc(dict(
-        doctype = 'ToDo'
-        owner = doc.allocated_to,
-        description = doc.subject
-    )).insert()
-
+if doc.allocated_to: + frappe.get_doc(dict( + doctype = 'ToDo' + owner = doc.allocated_to, + description = doc.subject + )).insert() +
+
+

API Call

+

Respond to /api/method/<method-name> calls, just like whitelisted methods


 # respond to API
 
@@ -79,6 +83,21 @@ if frappe.form_dict.message == "ping":
 else:
 	frappe.response['message'] = "ok"
 
+ +
+ +

Permission Query

+

Add conditions to the where clause of list queries.

+

+# generate dynamic conditions and set it in the conditions variable
+tenant_id = frappe.db.get_value(...)
+conditions = 'tenant_id = {}'.format(tenant_id)
+
+# resulting select query
+select name from \`tabPerson\`
+where tenant_id = 2
+order by creation desc
+
`); } diff --git a/frappe/core/doctype/server_script/server_script.json b/frappe/core/doctype/server_script/server_script.json index 420f96ec2f..94a48f196c 100644 --- a/frappe/core/doctype/server_script/server_script.json +++ b/frappe/core/doctype/server_script/server_script.json @@ -24,7 +24,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Script Type", - "options": "DocType Event\nScheduler Event\nAPI", + "options": "DocType Event\nScheduler Event\nPermission Query\nAPI", "reqd": 1 }, { @@ -35,7 +35,7 @@ "reqd": 1 }, { - "depends_on": "eval:doc.script_type==='DocType Event'", + "depends_on": "eval:['DocType Event', 'Permission Query'].includes(doc.script_type)", "fieldname": "reference_doctype", "fieldtype": "Link", "in_list_view": 1, @@ -88,7 +88,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-11 12:39:41.391052", + "modified": "2020-12-03 22:42:02.708148", "modified_by": "Administrator", "module": "Core", "name": "Server Script", diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index 839b784651..b1bf79dc52 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -41,6 +41,12 @@ class ServerScript(Document): # wrong report type! raise frappe.DoesNotExistError + def get_permission_query_conditions(self, user): + locals = {"user": user, "conditions": ""} + safe_exec(self.script, None, locals) + if locals["conditions"]: + return locals["conditions"] + @frappe.whitelist() def setup_scheduler_events(script_name, frequency): method = frappe.scrub('{0}-{1}'.format(script_name, frequency)) diff --git a/frappe/core/doctype/server_script/server_script_utils.py b/frappe/core/doctype/server_script/server_script_utils.py index e03504f30b..4dc4f12b34 100644 --- a/frappe/core/doctype/server_script/server_script_utils.py +++ b/frappe/core/doctype/server_script/server_script_utils.py @@ -50,6 +50,9 @@ def get_server_script_map(): # }, # '_api': { # '[path]': '[server script]' + # }, + # 'permission_query': { + # 'DocType': '[server script]' # } # } if frappe.flags.in_patch and not frappe.db.table_exists('Server Script'): @@ -57,16 +60,20 @@ def get_server_script_map(): script_map = frappe.cache().get_value('server_script_map') if script_map is None: - script_map = {} + script_map = { + 'permission_query': {} + } enabled_server_scripts = frappe.get_all('Server Script', fields=('name', 'reference_doctype', 'doctype_event','api_method', 'script_type'), filters={'disabled': 0}) for script in enabled_server_scripts: if script.script_type == 'DocType Event': script_map.setdefault(script.reference_doctype, {}).setdefault(script.doctype_event, []).append(script.name) + elif script.script_type == 'Permission Query': + script_map['permission_query'][script.reference_doctype] = script.name else: script_map.setdefault('_api', {})[script.api_method] = script.name frappe.cache().set_value('server_script_map', script_map) - return script_map \ No newline at end of file + return script_map diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index 3356e584af..565436d9f2 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -45,6 +45,14 @@ frappe.response['message'] = 'hello' allow_guest = 1, script = ''' frappe.flags = 'hello' +''' + ), + dict( + name='test_permission_query', + script_type = 'Permission Query', + reference_doctype = 'ToDo', + script = ''' +conditions = '1 = 1' ''' ) ] @@ -85,3 +93,7 @@ class TestServerScript(unittest.TestCase): def test_api_return(self): self.assertEqual(frappe.get_doc('Server Script', 'test_return_value').execute_method(), 'hello') + + def test_permission_query(self): + self.assertTrue('where (1 = 1)' in frappe.db.get_list('ToDo', return_query=1)) + self.assertTrue(isinstance(frappe.db.get_list('ToDo'), list)) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index ace9b04cec..b936251b50 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -18,6 +18,7 @@ from frappe.client import check_parent_permission from frappe.model.utils.user_settings import get_user_settings, update_user_settings from frappe.utils import flt, cint, get_time, make_filter_tuple, get_filter, add_to_date, cstr, get_timespan_date_range from frappe.model.meta import get_table_columns +from frappe.core.doctype.server_script.server_script_utils import get_server_script_map class DatabaseQuery(object): def __init__(self, doctype, user=None): @@ -683,15 +684,23 @@ class DatabaseQuery(object): self.match_filters.append(match_filters) def get_permission_query_conditions(self): + conditions = [] condition_methods = frappe.get_hooks("permission_query_conditions", {}).get(self.doctype, []) if condition_methods: - conditions = [] for method in condition_methods: c = frappe.call(frappe.get_attr(method), self.user) if c: conditions.append(c) - return " and ".join(conditions) if conditions else None + permision_script_name = get_server_script_map().get("permission_query").get(self.doctype) + if permision_script_name: + script = frappe.get_doc("Server Script", permision_script_name) + condition = script.get_permission_query_conditions(self.user) + if condition: + conditions.append(condition) + + return " and ".join(conditions) if conditions else "" + def run_custom_query(self, query): if '%(key)s' in query: From 1b609af8e5de0b0cc75fdd9d60e8fe9748371180 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 4 Dec 2020 18:15:17 +0530 Subject: [PATCH 08/20] fix: Handle paths relative to bench root and sites folder --- frappe/commands/site.py | 4 ++-- frappe/installer.py | 20 +++++++------------- frappe/utils/__init__.py | 24 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 35f5b13582..4a631be3ac 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -100,11 +100,11 @@ def restore(context, sql_file_path, mariadb_root_username=None, mariadb_root_pas # Extract public and/or private files to the restored site, if user has given the path if with_public_files: - public = extract_files(site, with_public_files, 'public') + public = extract_files(site, with_public_files) os.remove(public) if with_private_files: - private = extract_files(site, with_private_files, 'private') + private = extract_files(site, with_private_files) os.remove(private) # Removing temporarily created file diff --git a/frappe/installer.py b/frappe/installer.py index 1245a08cb7..a11c8dfbfa 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -440,20 +440,11 @@ def extract_sql_from_archive(sql_file_path): Returns: str: Path of the decompressed SQL file """ + from frappe.utils import get_bench_relative_path + sql_file_path = get_bench_relative_path(sql_file_path) # Extract the gzip file if user has passed *.sql.gz file instead of *.sql file - if not os.path.exists(sql_file_path): - base_path = '..' - sql_file_path = os.path.join(base_path, sql_file_path) - if not os.path.exists(sql_file_path): - print('Invalid path {0}'.format(sql_file_path[3:])) - sys.exit(1) - elif sql_file_path.startswith(os.sep): - base_path = os.sep - else: - base_path = '.' - if sql_file_path.endswith('sql.gz'): - decompressed_file_name = extract_sql_gzip(os.path.abspath(sql_file_path)) + decompressed_file_name = extract_sql_gzip(sql_file_path) else: decompressed_file_name = sql_file_path @@ -475,9 +466,12 @@ def extract_sql_gzip(sql_gz_path): return decompressed_file -def extract_files(site_name, file_path, folder_name): +def extract_files(site_name, file_path): import shutil import subprocess + from frappe.utils import get_bench_relative_path + + file_path = get_bench_relative_path(file_path) # Need to do frappe.init to maintain the site locals frappe.init(site=site_name) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index c209ee13c9..cc5b42acb7 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -729,3 +729,27 @@ def get_build_version(): # .build can sometimes not exist # this is not a major problem so send fallback return frappe.utils.random_string(8) + +def get_bench_relative_path(file_path): + """Fixes paths relative to the bench root directory if exists and returns the absolute path + + Args: + file_path (str, Path): Path of a file that exists on the file system + + Returns: + str: Absolute path of the file_path + """ + if not os.path.exists(file_path): + base_path = '..' + elif file_path.startswith(os.sep): + base_path = os.sep + else: + base_path = '.' + + file_path = os.path.join(base_path, file_path) + + if not os.path.exists(file_path): + print('Invalid path {0}'.format(file_path[3:])) + sys.exit(1) + + return os.path.abspath(file_path) From c5677b52d0af3dd31812625db8b22a2fab501983 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 7 Dec 2020 14:42:54 +0530 Subject: [PATCH 09/20] refactor: shorten_number function - pass min_length and max_no_of_decimals --- frappe/public/js/frappe/utils/utils.js | 41 +++++++++++++++---- .../js/frappe/widgets/number_card_widget.js | 2 +- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 4bf9c5bbd8..f8f25293b3 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -979,17 +979,42 @@ Object.assign(frappe.utils, { return route; }, - shorten_number: function (number, country) { - country = (country == 'India') ? country : ''; + shorten_number: function (number, country, min_length=4, max_no_of_decimals=2) { + /* returns the number as an abbreviated string + * PARAMS + * number - number to be shortened + * country - country that determines the numnber system to be used + * min_length - length below which the number will not be shortened + * max_no_of_decimals - max number of decimals of the shortened number + */ + + // return number if total digits is lesser than min_length + const len = String(number).match(/\d/g).length; + if (len < min_length) return number.toString(); + const number_system = this.get_number_system(country); let x = Math.abs(Math.round(number)); for (const map of number_system) { - const condition = map.condition ? map.condition(x) : x >= map.divisor; - if (condition) { - return (number/map.divisor).toFixed(2) + ' ' + map.symbol; + if (x >= map.divisor) { + let result = number/map.divisor; + const no_of_decimals = this.get_number_of_decimals(result); + /* + If no_of_decimals is greater than max_no_of_decimals, + round the number to max_no_of_decimals + */ + result = no_of_decimals > max_no_of_decimals + ? result.toFixed(max_no_of_decimals) + : result; + return result + ' ' + map.symbol; } } - return number.toFixed(); + + return number.toFixed(max_no_of_decimals); + }, + + get_number_of_decimals: function (number) { + if (Math.floor(number) === number) return 0; + return number.toString().split(".")[1].length || 0; }, get_number_system: function (country) { @@ -1019,9 +1044,11 @@ Object.assign(frappe.utils, { { divisor: 1.0e+3, symbol: 'K', - condition: (num) => num.toFixed().length > 5 }] }; + + if (!Object.keys(number_system_map).includes(country)) country = ''; + return number_system_map[country]; }, }); diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js index 8855de435b..c41f9bc6e7 100644 --- a/frappe/public/js/frappe/widgets/number_card_widget.js +++ b/frappe/public/js/frappe/widgets/number_card_widget.js @@ -204,7 +204,7 @@ export default class NumberCardWidget extends Widget { get_formatted_number(df) { const default_country = frappe.sys_defaults.country; - const shortened_number = frappe.utils.shorten_number(this.number, default_country); + const shortened_number = frappe.utils.shorten_number(this.number, default_country, 5); let number_parts = shortened_number.split(' '); const symbol = number_parts[1] || ''; From 3a11ef3365ed4838f1bd82929af5d4f5589110e0 Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 8 Dec 2020 12:46:16 +0530 Subject: [PATCH 10/20] fix: filter dashboards, dashboard charts, number cards by modules --- frappe/desk/doctype/dashboard/dashboard.py | 19 ++++++++++++ .../dashboard_chart/dashboard_chart.py | 30 ++++++++++++------- .../desk/doctype/number_card/number_card.py | 13 ++++++-- frappe/hooks.py | 1 + 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index b12bcfe27d..1c04b6a2fe 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals from frappe.model.document import Document from frappe.modules.export_file import export_to_files +from frappe.config import get_modules_from_all_apps_for_user import frappe from frappe import _ import json @@ -42,6 +43,24 @@ class Dashboard(Document): except ValueError as error: frappe.throw(_("Invalid json added in the custom options: {0}").format(error)) + +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_modules = [frappe.db.escape(module.get('module_name')) for module in get_modules_from_all_apps_for_user()] + module_condition = '`tabDashboard`.`module` in ({allowed_modules}) or `tabDashboard`.`module` is NULL'.format( + allowed_modules=','.join(allowed_modules)) + + return '{module_condition}'.format(module_condition=module_condition) + @frappe.whitelist() def get_permitted_charts(dashboard_name): permitted_charts = [] diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index 3f8d7c3c79..2fa36b5514 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -13,12 +13,12 @@ from frappe.utils.dateutils import\ get_period, get_period_beginning, get_from_date_from_timespan, get_dates_from_timegrain from frappe.model.naming import append_number_if_name_exists from frappe.boot import get_allowed_reports +from frappe.config import get_modules_from_all_apps_for_user from frappe.model.document import Document from frappe.modules.export_file import export_to_files def get_permission_query_conditions(user): - if not user: user = frappe.session.user @@ -31,9 +31,11 @@ def get_permission_query_conditions(user): doctype_condition = False report_condition = False + module_condition = False allowed_doctypes = [frappe.db.escape(doctype) for doctype in frappe.permissions.get_doctypes_with_read()] allowed_reports = [frappe.db.escape(key) if type(key) == str else key.encode('UTF8') for key in get_allowed_reports()] + allowed_modules = [frappe.db.escape(module.get('module_name')) for module in get_modules_from_all_apps_for_user()] if allowed_doctypes: doctype_condition = '`tabDashboard Chart`.`document_type` in ({allowed_doctypes})'.format( @@ -41,18 +43,24 @@ def get_permission_query_conditions(user): if allowed_reports: report_condition = '`tabDashboard Chart`.`report_name` in ({allowed_reports})'.format( allowed_reports=','.join(allowed_reports)) + if allowed_modules: + module_condition = '''`tabDashboard Chart`.`module` in ({allowed_modules}) + or `tabDashboard Chart`.`module` is NULL'''.format( + allowed_modules=','.join(allowed_modules)) return ''' - (`tabDashboard Chart`.`chart_type` in ('Count', 'Sum', 'Average') - and {doctype_condition}) - or - (`tabDashboard Chart`.`chart_type` = 'Report' - and {report_condition}) - '''.format( - doctype_condition=doctype_condition, - report_condition=report_condition - ) - + ((`tabDashboard Chart`.`chart_type` in ('Count', 'Sum', 'Average') + and {doctype_condition}) + or + (`tabDashboard Chart`.`chart_type` = 'Report' + and {report_condition})) + and + ({module_condition}) + '''.format( + doctype_condition=doctype_condition, + report_condition=report_condition, + module_condition=module_condition + ) def has_permission(doc, ptype, user): roles = frappe.get_roles(user) diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index d4a2b00c57..6bddd09fc7 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -8,6 +8,7 @@ from frappe.model.document import Document from frappe.utils import cint from frappe.model.naming import append_number_if_name_exists from frappe.modules.export_file import export_to_files +from frappe.config import get_modules_from_all_apps_for_user class NumberCard(Document): def autoname(self): @@ -33,16 +34,24 @@ def get_permission_query_conditions(user=None): return None doctype_condition = False + module_condition = False allowed_doctypes = [frappe.db.escape(doctype) for doctype in frappe.permissions.get_doctypes_with_read()] + allowed_modules = [frappe.db.escape(module.get('module_name')) for module in get_modules_from_all_apps_for_user()] if allowed_doctypes: doctype_condition = '`tabNumber Card`.`document_type` in ({allowed_doctypes})'.format( allowed_doctypes=','.join(allowed_doctypes)) + if allowed_modules: + module_condition = '''`tabNumber Card`.`module` in ({allowed_modules}) + or `tabNumber Card`.`module` is NULL'''.format( + allowed_modules=','.join(allowed_modules)) return ''' - {doctype_condition} - '''.format(doctype_condition=doctype_condition) + {doctype_condition} + and + {module_condition} + '''.format(doctype_condition=doctype_condition, module_condition=module_condition) def has_permission(doc, ptype, user): roles = frappe.get_roles(user) diff --git a/frappe/hooks.py b/frappe/hooks.py index d8c8cd841c..3d7ae0abb4 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -94,6 +94,7 @@ permission_query_conditions = { "User": "frappe.core.doctype.user.user.get_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": "frappe.desk.doctype.dashboard.dashboard.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", From e197a6fd7fbecfca9eadd8c2d92577eb20ba12b7 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 8 Dec 2020 11:35:41 +0530 Subject: [PATCH 11/20] feat: added data format support DD-Mon-YY --- frappe/public/js/frappe/data_import/import_preview.js | 1 + frappe/utils/data.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/frappe/public/js/frappe/data_import/import_preview.js b/frappe/public/js/frappe/data_import/import_preview.js index 6c17cb4351..477cfb0786 100644 --- a/frappe/public/js/frappe/data_import/import_preview.js +++ b/frappe/public/js/frappe/data_import/import_preview.js @@ -101,6 +101,7 @@ frappe.data_import.ImportPreview = class ImportPreview { .replace('%H', 'HH') .replace('%M', 'mm') .replace('%S', 'ss') + .replace('%b', 'Mon') : null; let column_title = ` diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 41f247da45..cef4243913 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -1300,12 +1300,14 @@ def generate_hash(*args, **kwargs): def guess_date_format(date_string): DATE_FORMATS = [ + r"%d/%b/%y", r"%d-%m-%Y", r"%m-%d-%Y", r"%Y-%m-%d", r"%d-%m-%y", r"%m-%d-%y", r"%y-%m-%d", + r"%y-%b-%d", r"%d/%m/%Y", r"%m/%d/%Y", r"%Y/%m/%d", From 309f483bb3c158fe0b3186e3d6c4762f66b2049c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 9 Dec 2020 16:54:40 +0530 Subject: [PATCH 12/20] refactor: update return statement --- frappe/desk/doctype/dashboard/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/doctype/dashboard/dashboard.py b/frappe/desk/doctype/dashboard/dashboard.py index 1c04b6a2fe..fa03bf8f80 100644 --- a/frappe/desk/doctype/dashboard/dashboard.py +++ b/frappe/desk/doctype/dashboard/dashboard.py @@ -59,7 +59,7 @@ def get_permission_query_conditions(user): module_condition = '`tabDashboard`.`module` in ({allowed_modules}) or `tabDashboard`.`module` is NULL'.format( allowed_modules=','.join(allowed_modules)) - return '{module_condition}'.format(module_condition=module_condition) + return module_condition @frappe.whitelist() def get_permitted_charts(dashboard_name): From c5d08305536c77162ba7821bd4ac9ec9a0cee13b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 10 Dec 2020 10:37:29 +0530 Subject: [PATCH 13/20] fix: cint seconds before operations --- frappe/utils/data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 34659e1cac..75e8dedbe9 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -369,6 +369,8 @@ def format_duration(seconds, hide_days=False): example: converts 12885 to '3h 34m 45s' where 12885 = seconds in float """ + + seconds = cint(seconds) total_duration = { 'days': math.floor(seconds / (3600 * 24)), From c805851ebe20ea60719a0d9929fbfb900265e28b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 10 Dec 2020 10:52:50 +0530 Subject: [PATCH 14/20] feat(Auto Repeat): Submit on Creation configuration --- .../automation/doctype/auto_repeat/auto_repeat.js | 14 ++++++++++++++ .../doctype/auto_repeat/auto_repeat.json | 11 ++++++++++- .../automation/doctype/auto_repeat/auto_repeat.py | 5 ++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.js b/frappe/automation/doctype/auto_repeat/auto_repeat.js index a11de1d881..121b4bd2f0 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.js +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.js @@ -44,6 +44,20 @@ frappe.ui.form.on('Auto Repeat', { // auto repeat schedule frappe.auto_repeat.render_schedule(frm); + + frm.trigger('toggle_submit_on_creation'); + }, + + reference_doctype: function(frm) { + frm.trigger('toggle_submit_on_creation'); + }, + + toggle_submit_on_creation: function(frm) { + // submit on creation checkbox + frappe.model.with_doctype(frm.doc.reference_doctype, () => { + let meta = frappe.get_meta(frm.doc.reference_doctype); + frm.toggle_display('submit_on_creation', meta.is_submittable); + }); }, template: function(frm) { diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.json b/frappe/automation/doctype/auto_repeat/auto_repeat.json index 8ee6ca1d45..80975dd4f5 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.json +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "format:AUT-AR-{#####}", @@ -12,6 +13,7 @@ "section_break_3", "reference_doctype", "reference_document", + "submit_on_creation", "column_break_5", "start_date", "end_date", @@ -186,9 +188,16 @@ "fieldname": "repeat_on_last_day", "fieldtype": "Check", "label": "Repeat on Last Day of the Month" + }, + { + "default": "0", + "fieldname": "submit_on_creation", + "fieldtype": "Check", + "label": "Submit on Creation" } ], - "modified": "2019-07-17 11:30:51.412317", + "links": [], + "modified": "2020-12-10 10:43:13.449172", "modified_by": "Administrator", "module": "Automation", "name": "Auto Repeat", diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index fcf24bf1a9..019692f136 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -150,6 +150,9 @@ class AutoRepeat(Document): self.update_doc(new_doc, reference_doc) new_doc.insert(ignore_permissions = True) + if self.submit_on_creation: + new_doc.submit(gnore_permissions = True) + return new_doc def update_doc(self, new_doc, reference_doc): @@ -160,7 +163,7 @@ class AutoRepeat(Document): if new_doc.meta.get_field('auto_repeat'): new_doc.set('auto_repeat', self.name) - for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time', 'select_print_heading', 'remarks', 'owner']: + for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time', 'select_print_heading', 'user_remark', 'remarks', 'owner']: if new_doc.meta.get_field(fieldname): new_doc.set(fieldname, reference_doc.get(fieldname)) From 30fbf37f25530cec068d39a98066684cfccd8848 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 10 Dec 2020 11:48:12 +0530 Subject: [PATCH 15/20] test: submit on creation --- .../doctype/auto_repeat/auto_repeat.py | 2 +- .../doctype/auto_repeat/test_auto_repeat.py | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 019692f136..7eb533ce44 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -151,7 +151,7 @@ class AutoRepeat(Document): new_doc.insert(ignore_permissions = True) if self.submit_on_creation: - new_doc.submit(gnore_permissions = True) + new_doc.submit() return new_doc diff --git a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py index 60fa9cb59e..e40b12e3b9 100644 --- a/frappe/automation/doctype/auto_repeat/test_auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py @@ -111,6 +111,25 @@ class TestAutoRepeat(unittest.TestCase): doc = make_auto_repeat(frequency='Daily', reference_document=todo.name, start_date=add_days(today(), -2)) self.assertEqual(getdate(doc.next_schedule_date), current_date) + def test_submit_on_creation(self): + doctype = 'Test Submittable DocType' + create_submittable_doctype(doctype) + + current_date = getdate() + submittable_doc = frappe.get_doc(dict(doctype=doctype, test='test submit on creation')).insert() + submittable_doc.submit() + doc = make_auto_repeat(frequency='Daily', reference_doctype=doctype, reference_document=submittable_doc.name, + start_date=add_days(current_date, -1), submit_on_creation=1) + + data = get_auto_repeat_entries(current_date) + create_repeated_entries(data) + docnames = frappe.db.get_all(doc.reference_doctype, + filters={'auto_repeat': doc.name}, + fields=['docstatus'], + limit=1 + ) + self.assertEquals(docnames[0].docstatus, 1) + def make_auto_repeat(**args): args = frappe._dict(args) @@ -118,6 +137,7 @@ def make_auto_repeat(**args): 'doctype': 'Auto Repeat', 'reference_doctype': args.reference_doctype or 'ToDo', 'reference_document': args.reference_document or frappe.db.get_value('ToDo', 'name'), + 'submit_on_creation': args.submit_on_creation or 0, 'frequency': args.frequency or 'Daily', 'start_date': args.start_date or add_days(today(), -1), 'end_date': args.end_date or "", @@ -128,3 +148,34 @@ def make_auto_repeat(**args): }).insert(ignore_permissions=True) return doc + + +def create_submittable_doctype(doctype): + if frappe.db.exists('DocType', doctype): + return + else: + doc = frappe.get_doc({ + 'doctype': 'DocType', + '__newname': doctype, + 'module': 'Custom', + 'custom': 1, + 'is_submittable': 1, + 'fields': [{ + 'fieldname': 'test', + 'label': 'Test', + 'fieldtype': 'Data' + }], + 'permissions': [{ + 'role': 'System Manager', + 'read': 1, + 'write': 1, + 'create': 1, + 'delete': 1, + 'submit': 1, + 'cancel': 1, + 'amend': 1 + }] + }).insert() + + doc.allow_auto_repeat = 1 + doc.save() \ No newline at end of file From 5a60048a0a9e57a5227796a3585b402c02435348 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 10 Dec 2020 12:31:01 +0530 Subject: [PATCH 16/20] test: for get_bench_relative_path --- frappe/tests/test_commands.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 8c76ce2f48..2da08718a4 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -14,7 +14,7 @@ import glob import frappe import frappe.recorder from frappe.installer import add_to_installed_apps -from frappe.utils import add_to_date, now +from frappe.utils import add_to_date, get_bench_relative_path, now from frappe.utils.backups import fetch_latest_backups @@ -364,3 +364,21 @@ class TestCommands(BaseTestCommands): else: installed_apps = set(frappe.get_installed_apps()) self.assertSetEqual(list_apps, installed_apps) + + def test_get_bench_relative_path(self): + bench_path = frappe.utils.get_bench_path() + test1_path = os.path.join(bench_path, 'test1.txt') + test2_path = os.path.join(bench_path, 'sites/test2.txt') + + with open(test1_path, 'w+') as test1: + test1.write('asdf') + with open(test2_path, 'w+') as test2: + test2.write('asdf') + + self.assertTrue('test1.txt' in get_bench_relative_path('test1.txt')) + self.assertTrue('sites/test2.txt' in get_bench_relative_path('test2.txt')) + with self.assertRaises(SystemExit): + get_bench_relative_path('test3.txt') + + os.remove(test1_path) + os.remove(test2_path) From 9e954a737257b3fb11821ae56255d3bf07082cb6 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 10 Dec 2020 13:52:21 +0530 Subject: [PATCH 17/20] style: quotes --- frappe/tests/test_commands.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 2da08718a4..0786a0e14f 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -367,18 +367,18 @@ class TestCommands(BaseTestCommands): def test_get_bench_relative_path(self): bench_path = frappe.utils.get_bench_path() - test1_path = os.path.join(bench_path, 'test1.txt') - test2_path = os.path.join(bench_path, 'sites/test2.txt') + test1_path = os.path.join(bench_path, "test1.txt") + test2_path = os.path.join(bench_path, "sites", "test2.txt") - with open(test1_path, 'w+') as test1: - test1.write('asdf') - with open(test2_path, 'w+') as test2: - test2.write('asdf') + with open(test1_path, "w+") as test1: + test1.write("asdf") + with open(test2_path, "w+") as test2: + test2.write("asdf") - self.assertTrue('test1.txt' in get_bench_relative_path('test1.txt')) - self.assertTrue('sites/test2.txt' in get_bench_relative_path('test2.txt')) + self.assertTrue("test1.txt" in get_bench_relative_path("test1.txt")) + self.assertTrue("sites/test2.txt" in get_bench_relative_path("test2.txt")) with self.assertRaises(SystemExit): - get_bench_relative_path('test3.txt') + get_bench_relative_path("test3.txt") os.remove(test1_path) os.remove(test2_path) From ec0d1dd63f727ca7d4713fbf2f739e1c49151a30 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 10 Dec 2020 17:18:13 +0530 Subject: [PATCH 18/20] fix: added server side validation for submit on creation --- frappe/automation/doctype/auto_repeat/auto_repeat.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 7eb533ce44..31d6539e61 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -21,6 +21,7 @@ class AutoRepeat(Document): def validate(self): self.update_status() self.validate_reference_doctype() + self.validate_submit_on_creation() self.validate_dates() self.validate_email_id() self.set_dates() @@ -60,6 +61,11 @@ class AutoRepeat(Document): if not frappe.get_meta(self.reference_doctype).allow_auto_repeat: frappe.throw(_("Enable Allow Auto Repeat for the doctype {0} in Customize Form").format(self.reference_doctype)) + def validate_submit_on_creation(self): + if self.submit_on_creation and not frappe.get_meta(self.reference_doctype).is_submittable: + frappe.throw(_('Cannot enable {0} for a non-submittable doctype').format( + frappe.bold('Submit on Creation'))) + def validate_dates(self): if frappe.flags.in_patch: return From 0651341bf8535af743d0fda128008ee180f3d533 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 10 Dec 2020 18:53:21 +0530 Subject: [PATCH 19/20] feat: show absolute value checkbox if doctype has appropriate fields --- .../doctype/print_format/print_format.js | 18 +++++++++++++++++- .../doctype/print_format/print_format.json | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/frappe/printing/doctype/print_format/print_format.js b/frappe/printing/doctype/print_format/print_format.js index e6599b2496..9ef5652dda 100644 --- a/frappe/printing/doctype/print_format/print_format.js +++ b/frappe/printing/doctype/print_format/print_format.js @@ -19,6 +19,7 @@ frappe.ui.form.on("Print Format", { } frm.trigger('render_buttons'); frm.toggle_display('standard', frappe.boot.developer_mode); + frm.trigger('hide_absolute_value_field'); }, render_buttons: function (frm) { frm.page.clear_inner_toolbar(); @@ -58,5 +59,20 @@ frappe.ui.form.on("Print Format", { frm.set_value('show_section_headings', value); frm.set_value('line_breaks', value); frm.trigger('render_buttons'); + }, + doc_type: function (frm) { + frm.trigger('hide_absolute_value_field'); + }, + hide_absolute_value_field: function (frm) { + // TODO: make it work with frm.doc.doc_type + // Problem: frm isn't updated in some random cases + const doctype = locals[frm.doc.doctype][frm.doc.name]; + if (doctype) { + frappe.model.with_doctype(doctype, () => { + const meta = frappe.get_meta(doctype); + const has_int_float_currency_field = meta.fields.filter(df => in_list(['Int', 'Float', 'Currency'], df.fieldtype)); + frm.toggle_display('absolute_value', has_int_float_currency_field.length); + }); + } } -}) +}); diff --git a/frappe/printing/doctype/print_format/print_format.json b/frappe/printing/doctype/print_format/print_format.json index 3867ce4502..6e64e802c9 100644 --- a/frappe/printing/doctype/print_format/print_format.json +++ b/frappe/printing/doctype/print_format/print_format.json @@ -200,6 +200,8 @@ }, { "default": "0", + "depends_on": "doc_type", + "description": "If checked, negative numberic values of Currency, Quantity or Count would be shown as positive", "fieldname": "absolute_value", "fieldtype": "Check", "label": "Show absolute values" @@ -209,7 +211,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-30 15:26:35.605213", + "modified": "2020-12-10 18:58:55.598269", "modified_by": "Administrator", "module": "Printing", "name": "Print Format", From 56302986036a367c86d38445a59116928f826624 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 11 Dec 2020 12:34:09 +0530 Subject: [PATCH 20/20] fix: Reload server script via patch (#12076) --- frappe/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/patches.txt b/frappe/patches.txt index 0daf29e001..b459019dd7 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -21,6 +21,7 @@ execute:frappe.reload_doc('email', 'doctype', 'document_follow') execute:frappe.reload_doc('core', 'doctype', 'communication_link') #2019-10-02 execute:frappe.reload_doc('core', 'doctype', 'has_role') execute:frappe.reload_doc('core', 'doctype', 'communication') #2019-10-02 +execute:frappe.reload_doc('core', 'doctype', 'server_script') frappe.patches.v11_0.replicate_old_user_permissions frappe.patches.v11_0.reload_and_rename_view_log #2019-01-03 frappe.patches.v7_1.rename_scheduler_log_to_error_log