From 6cdf514fcbf6d8abd6663da63fc62ffafc10109d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 26 Apr 2021 14:30:17 +0530 Subject: [PATCH 01/31] fix: auto repeat schedule not rendered in the dashboard --- frappe/automation/doctype/auto_repeat/auto_repeat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.js b/frappe/automation/doctype/auto_repeat/auto_repeat.js index 7028ac486d..896a10dfe0 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.js +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.js @@ -103,7 +103,7 @@ frappe.ui.form.on('Auto Repeat', { frappe.auto_repeat.render_schedule = function(frm) { if (!frm.is_dirty() && frm.doc.status !== 'Disabled') { frm.call("get_auto_repeat_schedule").then(r => { - frm.dashboard.wrapper.empty(); + frm.dashboard.reset(); frm.dashboard.add_section( frappe.render_template("auto_repeat_schedule", { schedule_details: r.message || [] From b7b92845f0a4d34f91b49b486535ea78e8681b21 Mon Sep 17 00:00:00 2001 From: David Angulo Date: Mon, 3 May 2021 13:38:17 -0500 Subject: [PATCH 02/31] fix: Use docfields from options if no docfields are returned from meta --- frappe/public/js/frappe/form/grid_row.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index f6da88df57..453b8b5f24 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -7,7 +7,8 @@ export default class GridRow { $.extend(this, opts); if (this.doc && this.parent_df.options) { frappe.meta.make_docfield_copy_for(this.parent_df.options, this.doc.name, this.docfields); - this.docfields = frappe.meta.get_docfields(this.parent_df.options, this.doc.name); + const docfields = frappe.meta.get_docfields(this.parent_df.options, this.doc.name); + this.docfields = docfields.length ? docfields : opts.docfields; } this.columns = {}; this.columns_list = []; From 483cd85eba617624da4fd60c8b3d2a1181ce4ea3 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Thu, 6 May 2021 16:44:18 +0530 Subject: [PATCH 03/31] fix: Revert naming for custom naming series --- frappe/model/naming.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 1a3f90da37..1cfcd56350 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -202,7 +202,12 @@ def revert_series_if_last(key, name, doc=None): if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: - return + key = key.rsplit(".") + hash = list(filter(re.compile(".*#").match, key))[0] + if not hash: + return + name = name.replace(hashes, "") + prefix, hashes = key[:key.index(hash)+1] else: prefix = key From 9aa2f366dd6f51adde6fbd9be4fbc2b5c7a85ab9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 20 Jan 2021 13:52:05 +0530 Subject: [PATCH 04/31] fix: Don't execute dynamic properties to check if conflicts exist --- frappe/core/doctype/doctype/doctype.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3588cc553a..6f33221155 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1176,9 +1176,15 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): def check_if_fieldname_conflicts_with_methods(doctype, fieldname): doc = frappe.get_doc({"doctype": doctype}) - method_list = [method for method in dir(doc) if isinstance(method, str) and callable(getattr(doc, method))] + available_objects = [x for x in dir(doc) if isinstance(x, str)] + property_list = [ + x for x in available_objects if isinstance(getattr(type(doc), x, None), property) + ] + method_list = [ + x for x in available_objects if x not in property_list and callable(getattr(doc, x)) + ] - if fieldname in method_list: + if fieldname in method_list + property_list: frappe.throw(_("Fieldname {0} conflicting with meta object").format(fieldname)) def clear_linked_doctype_cache(): From 255a959a3eb706ab25845aa94890708c37fe7573 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 4 Jan 2021 10:41:18 +0530 Subject: [PATCH 05/31] chore: Rename function to validate conflicting methods and properties --- frappe/core/doctype/doctype/doctype.py | 2 +- frappe/custom/doctype/custom_field/custom_field.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 6f33221155..d277a217d1 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1174,7 +1174,7 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): else: raise -def check_if_fieldname_conflicts_with_methods(doctype, fieldname): +def check_if_fieldname_conflicts_with_properties_and_methods(doctype, fieldname): doc = frappe.get_doc({"doctype": doctype}) available_objects = [x for x in dir(doc) if isinstance(x, str)] property_list = [ diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 3126326636..ac1213c727 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -64,8 +64,8 @@ class CustomField(Document): self.translatable = 0 if not self.flags.ignore_validate: - from frappe.core.doctype.doctype.doctype import check_if_fieldname_conflicts_with_methods - check_if_fieldname_conflicts_with_methods(self.dt, self.fieldname) + from frappe.core.doctype.doctype.doctype import check_if_fieldname_conflicts_with_properties_and_methods + check_if_fieldname_conflicts_with_properties_and_methods(self.dt, self.fieldname) def on_update(self): frappe.clear_cache(doctype=self.dt) From c652c7b7f52ad8d9a5b00b84af3f1ab33c3d1769 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 6 May 2021 11:58:17 +0530 Subject: [PATCH 06/31] feat: Validate field name conflicts in DocType.validate # Conflicts: # frappe/core/doctype/doctype/doctype.py --- frappe/core/doctype/doctype/doctype.py | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index d277a217d1..13ebcd82ac 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -70,6 +70,7 @@ class DocType(Document): validate_series(self) self.validate_document_type() validate_fields(self) + self.validate_field_name_conflicts() if not self.istable: validate_permissions(self) @@ -89,6 +90,39 @@ class DocType(Document): if self.default_print_format and not self.custom: frappe.throw(_('Standard DocType cannot have default print format, use Customize Form')) + if frappe.conf.get('developer_mode'): + self.owner = 'Administrator' + self.modified_by = 'Administrator' + + def validate_field_name_conflicts(self): + """Check if field names dont conflict with controller properties and methods""" + from frappe.model.base_document import get_controller + + controller = get_controller(self.name) + available_objects = {x for x in dir(controller) if isinstance(x, str)} + property_set = { + x for x in available_objects if isinstance(getattr(controller, x, None), property) + } + method_set = { + x for x in available_objects if x not in property_set and callable(getattr(controller, x, None)) + } + + for docfield in self.get("fields") or []: + conflict_type = "" + field = docfield.fieldname + field_label = docfield.label or docfield.fieldname + + if docfield.fieldname in method_set: + conflict_type = "controller method" + if docfield.fieldname in property_set: + conflict_type = "class property" + + if conflict_type: + frappe.throw( + _("Fieldname '{0}' conflicting with a {1} of the name {2} in {3}") + .format(field_label, conflict_type, field, self.name) + ) + def after_insert(self): # clear user cache so that on the next reload this doctype is included in boot clear_user_cache(frappe.session.user) From 78e1297392706eaa209e53d3acb42faae66dfa65 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 22 Jan 2021 18:08:18 +0530 Subject: [PATCH 07/31] chore: Drop dead file --- frappe/email/doctype/newsletter/newsletter..json | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 frappe/email/doctype/newsletter/newsletter..json diff --git a/frappe/email/doctype/newsletter/newsletter..json b/frappe/email/doctype/newsletter/newsletter..json deleted file mode 100644 index e69de29bb2..0000000000 From ce4253c6318546a7249f0edd518ad1f499f8fd88 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 25 Jan 2021 13:55:10 +0530 Subject: [PATCH 08/31] fix: Ignore 'special' DocTypes to avoid breaking changes eg: Fieldname 'Delete' conflicting with a controller method of the name delete in Custom DocPerm Fieldname 'Delete' conflicting with a controller method of the name delete in DocPerm Fieldname 'Precision' conflicting with a controller method of the name precision in Custom Field Fieldname 'Precision' conflicting with a controller method of the name precision in Customize Form Field Fieldname 'Precision' conflicting with a controller method of the name precision in DocField --- frappe/core/doctype/doctype/doctype.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 13ebcd82ac..c1a8527512 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -96,6 +96,17 @@ class DocType(Document): def validate_field_name_conflicts(self): """Check if field names dont conflict with controller properties and methods""" + core_doctypes = [ + "Custom DocPerm", + "DocPerm", + "Custom Field", + "Customize Form Field", + "DocField", + ] + + if self.name in core_doctypes: + return + from frappe.model.base_document import get_controller controller = get_controller(self.name) From a3b79081d64e754d73fad46f26aa24ac42a32a2e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 18 Jan 2020 04:11:19 +0530 Subject: [PATCH 09/31] fix: Use Document in case get_controller raises import errors --- frappe/core/doctype/doctype/doctype.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index c1a8527512..d361bb8a98 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -109,7 +109,11 @@ class DocType(Document): from frappe.model.base_document import get_controller - controller = get_controller(self.name) + try: + controller = get_controller(self.name) + except ImportError: + controller = Document + available_objects = {x for x in dir(controller) if isinstance(x, str)} property_set = { x for x in available_objects if isinstance(getattr(controller, x, None), property) From 05712abc60721f4633d6b0166648743154b56cca Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 18 Feb 2021 19:55:12 +0530 Subject: [PATCH 10/31] fix: Check for db value if cache doesn't exist in get_controller, if cached value doesn't exist for a DocType in the frappe.db.value_cache, then query the db as fallback before the final fallback of assuming module as Core and non custom --- frappe/model/base_document.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 05435482bd..9288d621c9 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -34,8 +34,11 @@ def get_controller(doctype): from frappe.model.document import Document from frappe.utils.nestedset import NestedSet - module_name, custom = frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) \ + module_name, custom = ( + frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) + or frappe.db.get_value("DocType", doctype, ("module", "custom")) or ["Core", False] + ) if custom: if frappe.db.field_exists("DocType", "is_tree"): From 843c544117e7f661cc97cc78c33349833b12df21 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 16 Mar 2021 12:23:10 +0530 Subject: [PATCH 11/31] refactor: Rename function and add docstring Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/core/doctype/doctype/doctype.py | 4 +++- frappe/custom/doctype/custom_field/custom_field.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index d361bb8a98..2cdccda8e1 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1223,7 +1223,9 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): else: raise -def check_if_fieldname_conflicts_with_properties_and_methods(doctype, fieldname): +def check_fieldname_conflicts(doctype, fieldname): + """Checks if fieldname conflicts with methods or properties""" + doc = frappe.get_doc({"doctype": doctype}) available_objects = [x for x in dir(doc) if isinstance(x, str)] property_list = [ diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index ac1213c727..b3f61c707d 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -64,8 +64,8 @@ class CustomField(Document): self.translatable = 0 if not self.flags.ignore_validate: - from frappe.core.doctype.doctype.doctype import check_if_fieldname_conflicts_with_properties_and_methods - check_if_fieldname_conflicts_with_properties_and_methods(self.dt, self.fieldname) + from frappe.core.doctype.doctype.doctype import check_fieldname_conflicts + check_fieldname_conflicts(self.dt, self.fieldname) def on_update(self): frappe.clear_cache(doctype=self.dt) From 877f9d08df70a741aa493421a002651ef0098cbc Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 16 Mar 2021 16:28:38 +0530 Subject: [PATCH 12/31] fix: Use fallback values if doctype values unset --- frappe/model/base_document.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 9288d621c9..2724e320c4 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -34,11 +34,15 @@ def get_controller(doctype): from frappe.model.document import Document from frappe.utils.nestedset import NestedSet - module_name, custom = ( - frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) - or frappe.db.get_value("DocType", doctype, ("module", "custom")) - or ["Core", False] - ) + if frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_patch: + module_name, custom = ["Core", False] + else: + # this could be simplified in PY3.8 using walrus operators + result = frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) + if result: + module_name, custom = result + else: + module_name, custom = ["Core", bool(not frappe.db.exists(doctype))] if custom: if frappe.db.field_exists("DocType", "is_tree"): From 87ed7796ded4053d8c1f04924716b29bc4848a6d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 23 Apr 2021 12:43:13 +0530 Subject: [PATCH 13/31] fix: Use older logic to set module_name and custom vars --- frappe/model/base_document.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 2724e320c4..154a091b8a 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -34,15 +34,9 @@ def get_controller(doctype): from frappe.model.document import Document from frappe.utils.nestedset import NestedSet - if frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_patch: - module_name, custom = ["Core", False] - else: - # this could be simplified in PY3.8 using walrus operators - result = frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) - if result: - module_name, custom = result - else: - module_name, custom = ["Core", bool(not frappe.db.exists(doctype))] + module_name, custom = frappe.db.get_value( + "DocType", doctype, ("module", "custom"), cache=True + ) or ["Core", False] if custom: if frappe.db.field_exists("DocType", "is_tree"): From bc08459ca77194bc5853a21b311aebfd125d62e9 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Thu, 6 May 2021 17:22:59 +0530 Subject: [PATCH 14/31] test: Test case for revert series --- frappe/tests/test_naming.py | 88 +++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index b47fb809ca..fbfb1e677d 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -16,50 +16,50 @@ class TestNaming(unittest.TestCase): todo_doctype.autoname = 'hash' todo_doctype.save() - def test_append_number_if_name_exists(self): - ''' - Append number to name based on existing values - if Bottle exists - Bottle -> Bottle-1 - if Bottle-1 exists - Bottle -> Bottle-2 - ''' + # def test_append_number_if_name_exists(self): + # ''' + # Append number to name based on existing values + # if Bottle exists + # Bottle -> Bottle-1 + # if Bottle-1 exists + # Bottle -> Bottle-2 + # ''' - note = frappe.new_doc('Note') - note.title = 'Test' - note.insert() + # note = frappe.new_doc('Note') + # note.title = 'Test' + # note.insert() - title2 = append_number_if_name_exists('Note', 'Test') - self.assertEqual(title2, 'Test-1') + # title2 = append_number_if_name_exists('Note', 'Test') + # self.assertEqual(title2, 'Test-1') - title2 = append_number_if_name_exists('Note', 'Test', 'title', '_') - self.assertEqual(title2, 'Test_1') + # title2 = append_number_if_name_exists('Note', 'Test', 'title', '_') + # self.assertEqual(title2, 'Test_1') - def test_format_autoname(self): - ''' - Test if braced params are replaced in format autoname - ''' - doctype = 'ToDo' + # def test_format_autoname(self): + # ''' + # Test if braced params are replaced in format autoname + # ''' + # doctype = 'ToDo' - todo_doctype = frappe.get_doc('DocType', doctype) - todo_doctype.autoname = 'format:TODO-{MM}-{status}-{##}' - todo_doctype.save() + # todo_doctype = frappe.get_doc('DocType', doctype) + # todo_doctype.autoname = 'format:TODO-{MM}-{status}-{##}' + # todo_doctype.save() - description = 'Format' + # description = 'Format' - todo = frappe.new_doc(doctype) - todo.description = description - todo.insert() + # todo = frappe.new_doc(doctype) + # todo.description = description + # todo.insert() - series = getseries('', 2) + # series = getseries('', 2) - series = str(int(series)-1) + # series = str(int(series)-1) - if len(series) < 2: - series = '0' + series + # if len(series) < 2: + # series = '0' + series - self.assertEqual(todo.name, 'TODO-{month}-{status}-{series}'.format( - month=now_datetime().strftime('%m'), status=todo.status, series=series)) + # self.assertEqual(todo.name, 'TODO-{month}-{status}-{series}'.format( + # month=now_datetime().strftime('%m'), status=todo.status, series=series)) def test_revert_series(self): from datetime import datetime @@ -95,3 +95,25 @@ class TestNaming(unittest.TestCase): self.assertEqual(count.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) + + series = 'TEST1-' + key = 'TEST1-.#####.-2021-22' + name = 'TEST1-00003-2021-22' + frappe.db.sql("DELETE FROM `tabSeries` WHERE `name`=%s", series) + frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 3)""", (series,)) + revert_series_if_last(key, name) + count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + + self.assertEqual(count.get('current'), 2) + frappe.db.sql("""delete from `tabSeries` where name = %s""", series) + + series = '' + key = '.#####.-2021-22' + name = '00003-2021-22' + frappe.db.sql("DELETE FROM `tabSeries` WHERE `name`=%s", series) + frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 3)""", (series,)) + revert_series_if_last(key, name) + count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + + self.assertEqual(count.get('current'), 2) + frappe.db.sql("""delete from `tabSeries` where name = %s""", series) From 47d1f3d50abff0d253c157e760a72ad1eff3b696 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Thu, 6 May 2021 17:24:12 +0530 Subject: [PATCH 15/31] test: uncomment --- frappe/tests/test_naming.py | 66 ++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index fbfb1e677d..0c3481ba98 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -16,50 +16,50 @@ class TestNaming(unittest.TestCase): todo_doctype.autoname = 'hash' todo_doctype.save() - # def test_append_number_if_name_exists(self): - # ''' - # Append number to name based on existing values - # if Bottle exists - # Bottle -> Bottle-1 - # if Bottle-1 exists - # Bottle -> Bottle-2 - # ''' + def test_append_number_if_name_exists(self): + ''' + Append number to name based on existing values + if Bottle exists + Bottle -> Bottle-1 + if Bottle-1 exists + Bottle -> Bottle-2 + ''' - # note = frappe.new_doc('Note') - # note.title = 'Test' - # note.insert() + note = frappe.new_doc('Note') + note.title = 'Test' + note.insert() - # title2 = append_number_if_name_exists('Note', 'Test') - # self.assertEqual(title2, 'Test-1') + title2 = append_number_if_name_exists('Note', 'Test') + self.assertEqual(title2, 'Test-1') - # title2 = append_number_if_name_exists('Note', 'Test', 'title', '_') - # self.assertEqual(title2, 'Test_1') + title2 = append_number_if_name_exists('Note', 'Test', 'title', '_') + self.assertEqual(title2, 'Test_1') - # def test_format_autoname(self): - # ''' - # Test if braced params are replaced in format autoname - # ''' - # doctype = 'ToDo' + def test_format_autoname(self): + ''' + Test if braced params are replaced in format autoname + ''' + doctype = 'ToDo' - # todo_doctype = frappe.get_doc('DocType', doctype) - # todo_doctype.autoname = 'format:TODO-{MM}-{status}-{##}' - # todo_doctype.save() + todo_doctype = frappe.get_doc('DocType', doctype) + todo_doctype.autoname = 'format:TODO-{MM}-{status}-{##}' + todo_doctype.save() - # description = 'Format' + description = 'Format' - # todo = frappe.new_doc(doctype) - # todo.description = description - # todo.insert() + todo = frappe.new_doc(doctype) + todo.description = description + todo.insert() - # series = getseries('', 2) + series = getseries('', 2) - # series = str(int(series)-1) + series = str(int(series)-1) - # if len(series) < 2: - # series = '0' + series + if len(series) < 2: + series = '0' + series - # self.assertEqual(todo.name, 'TODO-{month}-{status}-{series}'.format( - # month=now_datetime().strftime('%m'), status=todo.status, series=series)) + self.assertEqual(todo.name, 'TODO-{month}-{status}-{series}'.format( + month=now_datetime().strftime('%m'), status=todo.status, series=series)) def test_revert_series(self): from datetime import datetime From f46c3b59a6385f3d470b03712e00ce0ad5cf289c Mon Sep 17 00:00:00 2001 From: shariquerik Date: Thu, 6 May 2021 17:26:54 +0530 Subject: [PATCH 16/31] fix: sider fix --- frappe/tests/test_naming.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 0c3481ba98..f808d06db3 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -106,7 +106,7 @@ class TestNaming(unittest.TestCase): self.assertEqual(count.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) - + series = '' key = '.#####.-2021-22' name = '00003-2021-22' From c48a8e933f4017e41a175ba5a9a6e1b2207f758f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 6 May 2021 18:39:17 +0530 Subject: [PATCH 17/31] fix: remove six.moves.input and use builtin.input reason: py2 support no longer required and six makes mocking/testing complicated. --- frappe/utils/boilerplate.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index ba20562544..b935bfbf4f 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -3,8 +3,6 @@ from __future__ import unicode_literals, print_function -from six.moves import input - import frappe, os, re, git from frappe.utils import touch_file, cstr From 4210299ab424aa77c61269fb41f1b29d95cdee94 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 6 May 2021 19:12:19 +0530 Subject: [PATCH 18/31] test: add test for creating new app --- frappe/tests/test_boilerplate.py | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 frappe/tests/test_boilerplate.py diff --git a/frappe/tests/test_boilerplate.py b/frappe/tests/test_boilerplate.py new file mode 100644 index 0000000000..14741dc29e --- /dev/null +++ b/frappe/tests/test_boilerplate.py @@ -0,0 +1,67 @@ +import os +import unittest +from unittest.mock import patch + +import frappe +from frappe.utils.boilerplate import make_boilerplate + + +class TestBoilerPlate(unittest.TestCase): + def test_create_app(self): + title = "Test App" + description = "Test app for unit testing" + publisher = "Test Publisher" + email = "example@example.org" + icon = "" # empty -> default + color = "" + app_license = "MIT" + + user_input = [ + title, + description, + publisher, + email, + icon, + color, + app_license, + ] + + bench_path = frappe.utils.get_bench_path() + apps_dir = os.path.join(bench_path, "apps") + app_name = "test_app" + + with patch("builtins.input", side_effect=user_input): + make_boilerplate(apps_dir, app_name) + + root_paths = [ + app_name, + "requirements.txt", + "README.md", + "setup.py", + "license.txt", + ".git", + ] + paths_inside_app = [ + "__init__.py", + "hooks.py", + "patches.txt", + "templates", + "www", + "config", + "modules.txt", + "public", + app_name, + ] + + new_app_dir = os.path.join(bench_path, apps_dir, app_name) + + all_paths = list() + + for path in root_paths: + all_paths.append(os.path.join(new_app_dir, path)) + + for path in paths_inside_app: + all_paths.append(os.path.join(new_app_dir, app_name, path)) + + for path in all_paths: + self.assertTrue(os.path.exists(path), msg=f"{path} should exist in new app") From 835ed181b5d57ed26b163403bdd8eb3872a8212a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 7 May 2021 10:34:17 +0530 Subject: [PATCH 19/31] test: delete created test_app in tearDownClass --- frappe/tests/test_boilerplate.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frappe/tests/test_boilerplate.py b/frappe/tests/test_boilerplate.py index 14741dc29e..7dd704aad1 100644 --- a/frappe/tests/test_boilerplate.py +++ b/frappe/tests/test_boilerplate.py @@ -1,4 +1,5 @@ import os +import shutil import unittest from unittest.mock import patch @@ -7,6 +8,14 @@ from frappe.utils.boilerplate import make_boilerplate class TestBoilerPlate(unittest.TestCase): + @classmethod + def tearDownClass(cls): + + bench_path = frappe.utils.get_bench_path() + test_app_dir = os.path.join(bench_path, "apps", "test_app") + if os.path.exists(test_app_dir): + shutil.rmtree(test_app_dir) + def test_create_app(self): title = "Test App" description = "Test app for unit testing" From e9a3569bf78792cc9b03fe2027a7c8f3ae396311 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 7 May 2021 10:34:46 +0530 Subject: [PATCH 20/31] test: check if created python files are parsable --- frappe/tests/test_boilerplate.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/tests/test_boilerplate.py b/frappe/tests/test_boilerplate.py index 7dd704aad1..4ae78c94de 100644 --- a/frappe/tests/test_boilerplate.py +++ b/frappe/tests/test_boilerplate.py @@ -1,3 +1,5 @@ +import ast +import glob import os import shutil import unittest @@ -74,3 +76,13 @@ class TestBoilerPlate(unittest.TestCase): for path in all_paths: self.assertTrue(os.path.exists(path), msg=f"{path} should exist in new app") + + # check if python files are parsable + python_files = glob.glob(new_app_dir + "**/*.py", recursive=True) + + for python_file in python_files: + with open(python_file) as p: + try: + ast.parse(p.read()) + except Exception as e: + self.fail(f"Can't parse python file in new app: {python_file}\n" + str(e)) From 926d13e69ef88309f409446d2dc1c2e3f65ab0f3 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 7 May 2021 18:02:25 +0530 Subject: [PATCH 21/31] fix: Skip field-method conflicts validation on new docs --- frappe/core/doctype/doctype/doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 2cdccda8e1..e0b9d15114 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -70,7 +70,6 @@ class DocType(Document): validate_series(self) self.validate_document_type() validate_fields(self) - self.validate_field_name_conflicts() if not self.istable: validate_permissions(self) @@ -84,6 +83,7 @@ class DocType(Document): if not self.is_new(): self.before_update = frappe.get_doc('DocType', self.name) self.setup_fields_to_fetch() + self.validate_field_name_conflicts() check_email_append_to(self) From e7552413b4212e1daf47660b72838d1713c6a9bd Mon Sep 17 00:00:00 2001 From: shariquerik Date: Fri, 7 May 2021 18:10:59 +0530 Subject: [PATCH 22/31] refactor: minor fix --- frappe/model/naming.py | 1 + frappe/tests/test_naming.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 1cfcd56350..dbe77562d0 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -203,6 +203,7 @@ def revert_series_if_last(key, name, doc=None): prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: key = key.rsplit(".") + # get the hash part from the key hash = list(filter(re.compile(".*#").match, key))[0] if not hash: return diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index f808d06db3..66d48e3612 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -70,9 +70,9 @@ class TestNaming(unittest.TestCase): name = 'TEST-{}-00001'.format(year) frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 1)""", (series,)) revert_series_if_last(key, name) - count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] - self.assertEqual(count.get('current'), 0) + self.assertEqual(current_index.get('current'), 0) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) series = 'TEST-{}-'.format(year) @@ -80,9 +80,9 @@ class TestNaming(unittest.TestCase): name = 'TEST-{}-00002'.format(year) frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 2)""", (series,)) revert_series_if_last(key, name) - count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] - self.assertEqual(count.get('current'), 1) + self.assertEqual(current_index.get('current'), 1) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) series = 'TEST-' @@ -91,9 +91,9 @@ class TestNaming(unittest.TestCase): frappe.db.sql("DELETE FROM `tabSeries` WHERE `name`=%s", series) frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 3)""", (series,)) revert_series_if_last(key, name) - count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] - self.assertEqual(count.get('current'), 2) + self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) series = 'TEST1-' @@ -102,9 +102,9 @@ class TestNaming(unittest.TestCase): frappe.db.sql("DELETE FROM `tabSeries` WHERE `name`=%s", series) frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 3)""", (series,)) revert_series_if_last(key, name) - count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] - self.assertEqual(count.get('current'), 2) + self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) series = '' @@ -113,7 +113,7 @@ class TestNaming(unittest.TestCase): frappe.db.sql("DELETE FROM `tabSeries` WHERE `name`=%s", series) frappe.db.sql("""INSERT INTO `tabSeries` (name, current) values (%s, 3)""", (series,)) revert_series_if_last(key, name) - count = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] + current_index = frappe.db.sql("""SELECT current from `tabSeries` where name = %s""", series, as_dict=True)[0] - self.assertEqual(count.get('current'), 2) + self.assertEqual(current_index.get('current'), 2) frappe.db.sql("""delete from `tabSeries` where name = %s""", series) From c84feb16e9e456cdd040f20a6ac154d3a1d760c0 Mon Sep 17 00:00:00 2001 From: gavin Date: Sat, 8 May 2021 12:39:27 +0530 Subject: [PATCH 23/31] fix: Avoid possible whitespace bug * Handles semgrep warning * Changed "" to None as a precaution against future whitespace bugs via human error Co-authored-by: Ankush Menat --- frappe/core/doctype/doctype/doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index e0b9d15114..6eef5a4023 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -123,7 +123,7 @@ class DocType(Document): } for docfield in self.get("fields") or []: - conflict_type = "" + conflict_type = None field = docfield.fieldname field_label = docfield.label or docfield.fieldname From bf33a80042cc76e2cbc36991330d7dbbd7a33db4 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 3 May 2021 17:14:45 +0530 Subject: [PATCH 24/31] fix(setup): do not show messsage when exception is handled (cherry picked from commit ac9fe71733af5fe944cbca62dd688d2e49254497) --- frappe/translate.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 4baf4bdd89..5c5480d0a2 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -540,8 +540,12 @@ def extract_messages_from_code(code): try: code = frappe.as_unicode(render_include(code)) - except (TemplateError, ImportError, InvalidIncludePath, IOError): - # Exception will occur when it encounters John Resig's microtemplating code + + # Exception will occur when it encounters John Resig's microtemplating code + except (TemplateError, ImportError, InvalidIncludePath, IOError) as e: + if isinstance(e, InvalidIncludePath): + frappe.clear_last_message() + pass messages = [] From 0bbf7c89bfdb8919c93b9da48ba45ee36bf81177 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 8 May 2021 17:38:38 +0550 Subject: [PATCH 25/31] bumped to version 13.2.2 --- frappe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 12346d78c3..1a495d8ce1 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -34,7 +34,7 @@ if PY2: reload(sys) sys.setdefaultencoding("utf-8") -__version__ = '13.2.1' +__version__ = '13.2.2' __title__ = "Frappe Framework" From 8995e8e9db83b40e9df19433b77ec7ee853eaf18 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Sun, 9 May 2021 06:24:20 +0530 Subject: [PATCH 26/31] refactor: remove unnecessary code --- frappe/model/naming.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index dbe77562d0..ade4eaad95 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -202,13 +202,13 @@ def revert_series_if_last(key, name, doc=None): if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: - key = key.rsplit(".") + key = key.split(".") # get the hash part from the key hash = list(filter(re.compile(".*#").match, key))[0] if not hash: return name = name.replace(hashes, "") - prefix, hashes = key[:key.index(hash)+1] + prefix = prefix.replace(".{}".format(hash), "") else: prefix = key From 36d6d224df87e03e6247afc4ec873fb8058ba000 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 10 May 2021 12:18:37 +0530 Subject: [PATCH 27/31] fix: Do not skip text in save while using shortcut --- frappe/public/js/frappe/desk.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 6bcd20c494..d6cb7f5507 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -474,14 +474,19 @@ frappe.Application = Class.extend({ $('').appendTo("head"); }, trigger_primary_action: function() { - if(window.cur_dialog && cur_dialog.display) { - // trigger primary - cur_dialog.get_primary_btn().trigger("click"); - } else if(cur_frm && cur_frm.page.btn_primary.is(':visible')) { - cur_frm.page.btn_primary.trigger('click'); - } else if(frappe.container.page.save_action) { - frappe.container.page.save_action(); - } + // to trigger change event on active input before triggering primary action + $(document.activeElement).blur(); + // wait for possible JS validations triggered after blur (it might change primary button) + setTimeout(() => { + if (window.cur_dialog && cur_dialog.display) { + // trigger primary + cur_dialog.get_primary_btn().trigger("click"); + } else if (cur_frm && cur_frm.page.btn_primary.is(':visible')) { + cur_frm.page.btn_primary.trigger('click'); + } else if (frappe.container.page.save_action) { + frappe.container.page.save_action(); + } + }, 100); }, set_rtl: function() { From e0efefd9e5756fb3e17fe2891964c9a6d0831607 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Mon, 10 May 2021 12:54:04 +0530 Subject: [PATCH 28/31] refactor: Used re.search also added examples --- frappe/model/naming.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index ade4eaad95..c5b2775ec5 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -199,16 +199,39 @@ def getseries(key, digits): def revert_series_if_last(key, name, doc=None): - if ".#" in key: + """ + Reverts the series for particular naming series: + * key is naming series - SINV-.YYYY-.#### + * name is actual name - SINV-2021-0001 + + 1. This function split the key into two parts prefix (SINV-YYYY) & hashes (####). + 2. Use prefix to get the current index of that naming series from Series table + 3. Then revert the current index. + + *For custom naming series:* + 1. hash can exist anywhere, if it exist in hashes then it take normal flow. + 2. If hash doesn't exit in hashes, we get the hash from prefix, then update name and prefix accordingly. + + *Example:* + 1. key = SINV-.YYYY.- + * If key doesn't have hash it will add hash at the end + * prefix will be SINV-YYYY based on this will get current index from Series table. + 2. key = SINV-.####.-2021 + * now prefix = SINV-#### and hashes = 2021 (hash doesn't exist) + * will search hash in key then accordingly get prefix = SINV- + 3. key = ####.-2021 + * prefix = #### and hashes = 2021 (hash doesn't exist) + * will search hash in key then accordingly get prefix = "" + """ + if ".#" in key: prefix, hashes = key.rsplit(".", 1) if "#" not in hashes: - key = key.split(".") # get the hash part from the key - hash = list(filter(re.compile(".*#").match, key))[0] + hash = re.search("#+", key) if not hash: return name = name.replace(hashes, "") - prefix = prefix.replace(".{}".format(hash), "") + prefix = prefix.replace(hash.group(), "") else: prefix = key From 9919ddff2a937555881f43b83b22cd517555a94c Mon Sep 17 00:00:00 2001 From: shariquerik Date: Mon, 10 May 2021 12:56:15 +0530 Subject: [PATCH 29/31] fix: sider fix --- frappe/model/naming.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index c5b2775ec5..359b8e2367 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -214,7 +214,7 @@ def revert_series_if_last(key, name, doc=None): *Example:* 1. key = SINV-.YYYY.- - * If key doesn't have hash it will add hash at the end + * If key doesn't have hash it will add hash at the end * prefix will be SINV-YYYY based on this will get current index from Series table. 2. key = SINV-.####.-2021 * now prefix = SINV-#### and hashes = 2021 (hash doesn't exist) From 67ceab88ff827c19e2880f03271e3ba8c254f50f Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 11 May 2021 08:58:15 +0200 Subject: [PATCH 30/31] fix: translations (#12942) * fix: get_messages_from_include_files * feat: include labels of navbar items * refactor: strip -> lstrip Co-authored-by: gavin --- frappe/translate.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index aeca758a9d..1d8b1234c7 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -98,6 +98,7 @@ def get_dict(fortype, name=None): translation_assets = cache.hget("translation_assets", frappe.local.lang, shared=True) or {} if not asset_key in translation_assets: + messages = [] if fortype=="doctype": messages = get_messages_from_doctype(name) elif fortype=="page": @@ -109,14 +110,12 @@ def get_dict(fortype, name=None): elif fortype=="jsfile": messages = get_messages_from_file(name) elif fortype=="boot": - messages = [] apps = frappe.get_all_apps(True) for app in apps: messages.extend(get_server_messages(app)) - messages = deduplicate_messages(messages) - messages += frappe.db.sql("""select 'navbar', item_label from `tabNavbar Item` where item_label is not null""") - messages = get_messages_from_include_files() + messages += get_messages_from_navbar() + messages += get_messages_from_include_files() messages += frappe.db.sql("select 'Print Format:', name from `tabPrint Format`") messages += frappe.db.sql("select 'DocType:', name from tabDocType") messages += frappe.db.sql("select 'Role:', name from tabRole") @@ -124,6 +123,7 @@ def get_dict(fortype, name=None): messages += frappe.db.sql("select '', format from `tabWorkspace Shortcut` where format is not null") messages += frappe.db.sql("select '', title from `tabOnboarding Step`") + messages = deduplicate_messages(messages) message_dict = make_dict_from_messages(messages, load_user_translation=False) message_dict.update(get_dict_from_hooks(fortype, name)) # remove untranslated @@ -320,10 +320,22 @@ def get_messages_for_app(app, deduplicate=True): # server_messages messages.extend(get_server_messages(app)) + + # messages from navbar settings + messages.extend(get_messages_from_navbar()) + if deduplicate: messages = deduplicate_messages(messages) + return messages + +def get_messages_from_navbar(): + """Return all labels from Navbar Items, as specified in Navbar Settings.""" + labels = frappe.get_all('Navbar Item', filters={'item_label': ('is', 'set')}, pluck='item_label') + return [('Navbar:', label, 'Label of a Navbar Item') for label in labels] + + def get_messages_from_doctype(name): """Extract all translatable messages for a doctype. Includes labels, Python code, Javascript code, html templates""" @@ -490,8 +502,14 @@ def get_server_messages(app): def get_messages_from_include_files(app_name=None): """Returns messages from js files included at time of boot like desk.min.js for desk and web""" messages = [] - for file in (frappe.get_hooks("app_include_js", app_name=app_name) or []) + (frappe.get_hooks("web_include_js", app_name=app_name) or []): - messages.extend(get_messages_from_file(os.path.join(frappe.local.sites_path, file))) + app_include_js = frappe.get_hooks("app_include_js", app_name=app_name) or [] + web_include_js = frappe.get_hooks("web_include_js", app_name=app_name) or [] + include_js = app_include_js + web_include_js + + for js_path in include_js: + relative_path = os.path.join(frappe.local.sites_path, js_path.lstrip('/')) + messages_from_file = get_messages_from_file(relative_path) + messages.extend(messages_from_file) return messages From 17928f2d280ae09f8cec197647e606eb583e253e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 11 May 2021 15:30:55 +0530 Subject: [PATCH 31/31] fix: Clear web page cache after web template is updated --- .../website/doctype/web_template/web_template.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frappe/website/doctype/web_template/web_template.py b/frappe/website/doctype/web_template/web_template.py index 3c61807099..2fd5bfa179 100644 --- a/frappe/website/doctype/web_template/web_template.py +++ b/frappe/website/doctype/web_template/web_template.py @@ -9,6 +9,7 @@ from shutil import rmtree import frappe from frappe.model.document import Document +from frappe.website.render import clear_cache from frappe import _ from frappe.modules.export_file import ( write_document_file, @@ -37,6 +38,19 @@ class WebTemplate(Document): if was_standard and not self.standard: self.import_from_files() + def on_update(self): + """Clear cache for all Web Pages in which this template is used""" + routes = frappe.db.get_all( + "Web Page", + filters=[ + ["Web Page Block", "web_template", "=", self.name], + ["Web Page", "published", "=", 1], + ], + pluck="route", + ) + for route in routes: + clear_cache(route) + def on_trash(self): if frappe.conf.developer_mode and self.standard: # delete template html and json files