From 14cc0ac29b23583de61c40ceea4a26dca0f98484 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Fri, 29 Sep 2023 18:14:41 +0200 Subject: [PATCH 01/62] feat(model): implement into-child table mapper --- frappe/model/mapper.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py index 3f43f2a5f2..ade401a83c 100644 --- a/frappe/model/mapper.py +++ b/frappe/model/mapper.py @@ -69,9 +69,25 @@ def get_mapped_doc( # main if not target_doc: - target_doc = frappe.new_doc(table_maps[from_doctype]["doctype"]) + target_doctype = table_maps[from_doctype]["doctype"] + if table_maps[from_doctype].get("on_parent"): + target_parent = table_maps[from_doctype].get("on_parent") + if isinstance(target_parent, str): + target_parent = frappe.get_doc(json.loads(target_parent)) + target_parentfield = target_parent.get_parentfield_of_doctype(target_doctype) + target_doc = frappe.new_doc( + target_doctype, parent_doc=target_parent, parentfield=target_parentfield + ) + target_parent.append(target_parentfield, target_doc) + ret_doc = target_parent + else: + target_doc = frappe.new_doc(target_doctype) + ret_doc = target_doc elif isinstance(target_doc, str): target_doc = frappe.get_doc(json.loads(target_doc)) + ret_doc = target_doc + else: + ret_doc = target_doc if ( not apply_strict_user_permissions @@ -147,16 +163,14 @@ def get_mapped_doc( if postprocess: postprocess(source_doc, target_doc) - target_doc.set_onload("load_after_mapping", True) + ret_doc.set_onload("load_after_mapping", True) if ( - apply_strict_user_permissions - and not ignore_permissions - and not target_doc.has_permission("create") + apply_strict_user_permissions and not ignore_permissions and not ret_doc.has_permission("create") ): - target_doc.raise_no_permission_to("create") + ret_doc.raise_no_permission_to("create") - return target_doc + return ret_doc def map_doc(source_doc, target_doc, table_map, source_parent=None): From de3be8687997562cc48d6fbe33500365e004398f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Oliver=20S=C3=BCnderhauf?= <46800703+bosue@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:34:28 +0200 Subject: [PATCH 02/62] refactor: Deprecate bolden_match_part and turn it into a wrapper. --- .../js/frappe/ui/toolbar/search_utils.js | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/frappe/public/js/frappe/ui/toolbar/search_utils.js b/frappe/public/js/frappe/ui/toolbar/search_utils.js index 658786f8dc..4577becf41 100644 --- a/frappe/public/js/frappe/ui/toolbar/search_utils.js +++ b/frappe/public/js/frappe/ui/toolbar/search_utils.js @@ -600,40 +600,11 @@ frappe.search.utils = { return { score, marked_string }; }, + /** + * @deprecated Use frappe.search.utils.fuzzy_search(subseq, str, true).marked_string instead. + */ bolden_match_part: function (str, subseq) { - if (fuzzy_match(subseq, str)[0] === false) { - return str; - } - if (str.indexOf(subseq) == 0) { - var tail = str.split(subseq)[1]; - return "" + subseq + "" + tail; - } - var rendered = ""; - var str_orig = str; - var str_len = str.length; - str = str.toLowerCase(); - subseq = subseq.toLowerCase(); - - outer: for (var i = 0, j = 0; i < subseq.length; i++) { - var sub_ch = subseq.charCodeAt(i); - while (j < str_len) { - if (str.charCodeAt(j) === sub_ch) { - var str_char = str_orig.charAt(j); - if (str_char === str_char.toLowerCase()) { - rendered += "" + subseq.charAt(i) + ""; - } else { - rendered += "" + subseq.charAt(i).toUpperCase() + ""; - } - j++; - continue outer; - } - rendered += str_orig.charAt(j); - j++; - } - return str_orig; - } - rendered += str_orig.slice(j); - return rendered; + return this.fuzzy_search(subseq, str, true).marked_string; }, get_executables(keywords) { From 79b67a4b833541da7e9d9b9df3f6d944933c5b84 Mon Sep 17 00:00:00 2001 From: zeel-codder Date: Tue, 24 Oct 2023 13:51:12 +0530 Subject: [PATCH 03/62] fix: Update email_message in the send_welcome_email function to use the use_html check for Email Template --- frappe/email/doctype/email_group/email_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index 7afd349804..3af69cda33 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -123,6 +123,6 @@ def send_welcome_email(welcome_email, email, email_group): return args = dict(email=email, email_group=email_group) - email_message = welcome_email.response or welcome_email.response_html + email_message = welcome_email.response_html if welcome_email.use_html else welcome_email.response message = frappe.render_template(email_message, args) frappe.sendmail(email, subject=welcome_email.subject, message=message) From ad121d11401a2218add346dec374b15616be8af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Oliver=20S=C3=BCnderhauf?= <46800703+bosue@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:23:45 +0200 Subject: [PATCH 04/62] refactor: Convert frappe.tags.utils to enhanced fuzzy_search. --- frappe/public/js/frappe/ui/toolbar/tag_utils.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/ui/toolbar/tag_utils.js b/frappe/public/js/frappe/ui/toolbar/tag_utils.js index ae1aa97670..ae07943f98 100644 --- a/frappe/public/js/frappe/ui/toolbar/tag_utils.js +++ b/frappe/public/js/frappe/ui/toolbar/tag_utils.js @@ -13,15 +13,14 @@ frappe.tags.utils = { return []; } - for (let i in frappe.tags.tags) { - let tag = frappe.tags.tags[i]; - let level = frappe.search.utils.fuzzy_search(txt, tag); - if (level) { + frappe.tags.tags.forEach((tag) => { + const search_result = frappe.search.utils.fuzzy_search(txt, tag, true); + if (search_result.score) { out.push({ type: "Tag", - label: __("#{0}", [frappe.search.utils.bolden_match_part(__(tag), txt)]), + label: __("#{0}", [search_result.marked_string]), value: __("#{0}", [__(tag)]), - index: 1 + level, + index: 1 + search_result.score, match: tag, onclick() { // Use Global Search Dialog for tag search too. @@ -29,8 +28,7 @@ frappe.tags.utils = { }, }); } - } - + }); return out; }, From bce95096f68027749885b685baf69a158546122e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 7 Nov 2023 19:28:00 +0530 Subject: [PATCH 05/62] chore: show fields table in customize form --- frappe/custom/doctype/customize_form/customize_form.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 32fd3302ec..d345698f2d 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -30,10 +30,10 @@ "links", "document_states_section", "states", - "form_tab", - "form_builder", "fields_section_break", "fields", + "form_tab", + "form_builder", "settings_tab", "form_settings_section", "image_field", @@ -180,7 +180,6 @@ "depends_on": "doc_type", "fieldname": "fields_section_break", "fieldtype": "Section Break", - "hidden": 1, "label": "Fields" }, { @@ -394,7 +393,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-10-31 02:04:25.955931", + "modified": "2023-11-07 19:25:32.656641", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", From aa55ccf88f8b870166a022fd9f34deef76992503 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 7 Nov 2023 19:32:15 +0530 Subject: [PATCH 06/62] fix: always show section header --- frappe/public/js/form_builder/components/Section.vue | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frappe/public/js/form_builder/components/Section.vue b/frappe/public/js/form_builder/components/Section.vue index 9266d5c8d3..a2f8cc831a 100644 --- a/frappe/public/js/form_builder/components/Section.vue +++ b/frappe/public/js/form_builder/components/Section.vue @@ -221,12 +221,8 @@ function move_sections_to_tab() { border-color: var(--primary); } - &.selected .section-header { - display: flex; - } - .section-header { - display: none; + display: flex; justify-content: space-between; align-items: center; padding-bottom: 0.75rem; From d3c534e0cf573ec3815623974cf9783cbae74ec4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 20:04:20 +0530 Subject: [PATCH 07/62] fix: attempt to not pass an empty string to `json.loads` (#23139) (#23141) * fix: attempt to not pass an empty string to `json.loads` * fix: ignore all falsy fields --------- Co-authored-by: Ankush Menat (cherry picked from commit 6a8cf0689ae8e70f99ffd6957603d6583b4bdf92) Co-authored-by: Arjun --- frappe/desk/reportview.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index fd299af819..8f2f7f8dca 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -215,12 +215,12 @@ def clean_params(data): def parse_json(data): - if isinstance(data.get("filters"), str): - data["filters"] = json.loads(data["filters"]) - if isinstance(data.get("or_filters"), str): - data["or_filters"] = json.loads(data["or_filters"]) - if isinstance(data.get("fields"), str): - data["fields"] = ["*"] if data["fields"] == "*" else json.loads(data["fields"]) + if (filters := data.get("filters")) and isinstance(filters, str): + data["filters"] = json.loads(filters) + if (or_filters := data.get("or_filters")) and isinstance(or_filters, str): + data["or_filters"] = json.loads(or_filters) + if (fields := data.get("fields")) and isinstance(fields, str): + data["fields"] = ["*"] if fields == "*" else json.loads(fields) if isinstance(data.get("docstatus"), str): data["docstatus"] = json.loads(data["docstatus"]) if isinstance(data.get("save_user_settings"), str): From e14349d656a0f3406f3b20550c4f6747566609a7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 7 Nov 2023 20:10:48 +0530 Subject: [PATCH 08/62] fix: newsletter indicator filters strings are all truthy. closes https://github.com/frappe/frappe/issues/23107 --- frappe/email/doctype/newsletter/newsletter_list.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/email/doctype/newsletter/newsletter_list.js b/frappe/email/doctype/newsletter/newsletter_list.js index 0921de02b4..71e9423b7e 100644 --- a/frappe/email/doctype/newsletter/newsletter_list.js +++ b/frappe/email/doctype/newsletter/newsletter_list.js @@ -2,11 +2,11 @@ frappe.listview_settings["Newsletter"] = { add_fields: ["subject", "email_sent", "schedule_sending"], get_indicator: function (doc) { if (doc.email_sent) { - return [__("Sent"), "green", "email_sent,=,Yes"]; + return [__("Sent"), "green", "email_sent,=,1"]; } else if (doc.schedule_sending) { - return [__("Scheduled"), "purple", "email_sent,=,No|schedule_sending,=,Yes"]; + return [__("Scheduled"), "purple", "email_sent,=,0|schedule_sending,=,1"]; } else { - return [__("Not Sent"), "gray", "email_sent,=,No"]; + return [__("Not Sent"), "gray", "email_sent,=,0"]; } }, }; From ec4bc971c5090e9744afc0ec9e31c57c786eda5f Mon Sep 17 00:00:00 2001 From: David Arnold Date: Tue, 7 Nov 2023 16:43:13 +0100 Subject: [PATCH 09/62] fix(integration): preserver data json formatting on update --- .../doctype/integration_request/integration_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/integrations/doctype/integration_request/integration_request.py b/frappe/integrations/doctype/integration_request/integration_request.py index c3cd8d061c..a81e702dfc 100644 --- a/frappe/integrations/doctype/integration_request/integration_request.py +++ b/frappe/integrations/doctype/integration_request/integration_request.py @@ -4,7 +4,7 @@ import json import frappe -from frappe.integrations.utils import json_handler +from frappe.integrations.utils import get_json, json_handler from frappe.model.document import Document @@ -45,7 +45,7 @@ class IntegrationRequest(Document): data = json.loads(self.data) data.update(params) - self.data = json.dumps(data) + self.data = get_json(data) self.status = status self.save(ignore_permissions=True) frappe.db.commit() From 5e660437df6b3017e7e55504fe8fe32a93492c5e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:08:20 +0100 Subject: [PATCH 10/62] fix: ignore case of filetype --- frappe/public/js/frappe/file_uploader/FileUploader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 7f8f1cedcb..91a4654bb9 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -337,7 +337,7 @@ function check_restrictions(file) { // otherwise this is likely an extension if (type[0] === '.') { - return file.name.endsWith(type); + return file.name.toLowerCase().endsWith(type.toLowerCase()); } return false; }); From 18f34c36f609b468051de00e754d50b390ca59e6 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Tue, 7 Nov 2023 16:40:42 +0100 Subject: [PATCH 11/62] fix(float): input formatting --- cypress/integration/control_float.js | 41 ++++++++++--------- .../public/js/frappe/form/controls/float.js | 13 ------ frappe/public/js/frappe/form/controls/int.js | 14 +++++-- frappe/templates/base.html | 2 +- frappe/website/utils.py | 2 +- 5 files changed, 35 insertions(+), 37 deletions(-) diff --git a/cypress/integration/control_float.js b/cypress/integration/control_float.js index e7d6b398f1..08b71eb870 100644 --- a/cypress/integration/control_float.js +++ b/cypress/integration/control_float.js @@ -32,10 +32,13 @@ context("Control Float", () => { cy.wait(200); cy.fill_field("float_number", d.input, "Float").blur(); cy.get_field("float_number", "Float").should("have.value", d.blur_expected); - + cy.wait(100); cy.get_field("float_number", "Float").focus(); + cy.wait(100); cy.get_field("float_number", "Float").blur(); + cy.wait(100); cy.get_field("float_number", "Float").focus(); + cy.wait(100); cy.get_field("float_number", "Float").should("have.value", d.focus_expected); }); }); @@ -49,17 +52,17 @@ context("Control Float", () => { { input: "364.87,334", blur_expected: "36.487,334", - focus_expected: "36487.334", + focus_expected: "36.487,334", }, { - input: "36487,334", - blur_expected: "36.487,334", - focus_expected: "36487.334", + input: "36487,335", + blur_expected: "36.487,335", + focus_expected: "36.487,335", }, { - input: "100", - blur_expected: "100,000", - focus_expected: "100", + input: "2*(2+47)+1,5+1", + blur_expected: "100,500", + focus_expected: "100,500", }, ], }, @@ -67,19 +70,19 @@ context("Control Float", () => { number_format: "#,###.##", values: [ { - input: "364,87.334", - blur_expected: "36,487.334", - focus_expected: "36487.334", + input: "464,87.334", + blur_expected: "46,487.334", + focus_expected: "46,487.334", }, { - input: "36487.334", - blur_expected: "36,487.334", - focus_expected: "36487.334", + input: "46487.335", + blur_expected: "46,487.335", + focus_expected: "46,487.335", }, { - input: "100", - blur_expected: "100.000", - focus_expected: "100", + input: "3*(2+47)+1.5+1", + blur_expected: "149.500", + focus_expected: "149.500", }, ], }, @@ -90,13 +93,13 @@ context("Control Float", () => { { input: "12.345", blur_expected: "12.345,000", - focus_expected: "12345", + focus_expected: "12.345,000", }, { // parseFloat would reduce 12,340 to 12,34 if this string was ever to be parsed input: "12.340", blur_expected: "12.340,000", - focus_expected: "12340", + focus_expected: "12.340,000", }, ], }, diff --git a/frappe/public/js/frappe/form/controls/float.js b/frappe/public/js/frappe/form/controls/float.js index 0066020a8f..5cb674d2f8 100644 --- a/frappe/public/js/frappe/form/controls/float.js +++ b/frappe/public/js/frappe/form/controls/float.js @@ -1,17 +1,4 @@ frappe.ui.form.ControlFloat = class ControlFloat extends frappe.ui.form.ControlInt { - make_input() { - super.make_input(); - const change_handler = (e) => { - if (this.change) this.change(e); - else { - let value = this.get_input_value(); - this.parse_validate_and_set_in_model(value, e); - this.refresh(); - } - }; - // convert to number format on focusout since focus converts it to flt. - this.$input.on("focusout", change_handler); - } parse(value) { value = this.eval_expression(value); return isNaN(parseFloat(value)) ? null : flt(value, this.get_precision()); diff --git a/frappe/public/js/frappe/form/controls/int.js b/frappe/public/js/frappe/form/controls/int.js index 42b243062d..ffd3fe5fc3 100644 --- a/frappe/public/js/frappe/form/controls/int.js +++ b/frappe/public/js/frappe/form/controls/int.js @@ -13,7 +13,6 @@ frappe.ui.form.ControlInt = class ControlInt extends frappe.ui.form.ControlData .on("focus", function () { setTimeout(function () { if (!document.activeElement) return; - document.activeElement.value = me.validate(document.activeElement.value); document.activeElement.select(); }, 100); return false; @@ -24,10 +23,19 @@ frappe.ui.form.ControlInt = class ControlInt extends frappe.ui.form.ControlData } eval_expression(value) { if (typeof value === "string") { - if (value.match(/^[0-9+\-/* ]+$/)) { + const parsed_components = value.match(/[^\d.,]+|[\d.,]+/g); + var parsed_value = value; + if (parsed_components !== null) { + parsed_value = parsed_components + .map((v) => { + return isNaN(parseFloat(v)) ? v : flt(v); + }) + .join(""); + } + if (parsed_value.match(/^[0-9+\-/*.() ]+$/)) { // If it is a string containing operators try { - return eval(value); + return eval(parsed_value); } catch (e) { // bad expression return value; diff --git a/frappe/templates/base.html b/frappe/templates/base.html index 55065924de..6f4c3a8ad7 100644 --- a/frappe/templates/base.html +++ b/frappe/templates/base.html @@ -1,6 +1,6 @@ - + diff --git a/frappe/website/utils.py b/frappe/website/utils.py index f5579d9adb..872b790cfc 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -166,7 +166,7 @@ def get_home_page_via_hooks(): def get_boot_data(): return { - "lang": "en", + "lang": frappe.local.lang or "en", "sysdefaults": { "float_precision": cint(frappe.get_system_settings("float_precision")) or 3, "date_format": frappe.get_system_settings("date_format") or "yyyy-mm-dd", From 268c851f858a8aec020c4454dbf654d402242298 Mon Sep 17 00:00:00 2001 From: "avazquez@ctgalega.com" Date: Wed, 8 Nov 2023 00:29:46 +0100 Subject: [PATCH 12/62] fix: description for columns property in docfield --- frappe/core/doctype/docfield/docfield.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 6b8469bd48..065e00c6aa 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -416,7 +416,7 @@ "width": "50px" }, { - "description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", + "description": "Number of columns for a field in a Grid (Total Columns should be less than 11)", "fieldname": "columns", "fieldtype": "Int", "label": "Columns" From 9baf5e6de481e24fbbf0ad93c76d61e59ea93317 Mon Sep 17 00:00:00 2001 From: "avazquez@ctgalega.com" Date: Wed, 8 Nov 2023 00:38:16 +0100 Subject: [PATCH 13/62] fix: typo accuracy --- frappe/core/doctype/docfield/docfield.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 065e00c6aa..42e8d21e34 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -416,7 +416,7 @@ "width": "50px" }, { - "description": "Number of columns for a field in a Grid (Total Columns should be less than 11)", + "description": "Number of columns for a field in a grid (total columns should be less than 11)", "fieldname": "columns", "fieldtype": "Int", "label": "Columns" From 88113b6c089d99a2f4547615614fe0c869fd81ef Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 8 Nov 2023 11:54:35 +0530 Subject: [PATCH 14/62] fix: added Dropdown component --- .../js/form_builder/components/Dropdown.vue | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 frappe/public/js/form_builder/components/Dropdown.vue diff --git a/frappe/public/js/form_builder/components/Dropdown.vue b/frappe/public/js/form_builder/components/Dropdown.vue new file mode 100644 index 0000000000..23e1a0443d --- /dev/null +++ b/frappe/public/js/form_builder/components/Dropdown.vue @@ -0,0 +1,129 @@ + + + + + From 3efbc302223693f5b1c134e07190d9f512e1d505 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 8 Nov 2023 11:55:38 +0530 Subject: [PATCH 15/62] fix: added all options to add/remove section/column in section header --- .../js/form_builder/components/Section.vue | 317 ++++++++++++------ 1 file changed, 214 insertions(+), 103 deletions(-) diff --git a/frappe/public/js/form_builder/components/Section.vue b/frappe/public/js/form_builder/components/Section.vue index a2f8cc831a..06eb7cb595 100644 --- a/frappe/public/js/form_builder/components/Section.vue +++ b/frappe/public/js/form_builder/components/Section.vue @@ -1,10 +1,87 @@ + + - +// column +function add_column() { + props.section.columns.push({ + fields: [], + df: store.get_df("Column Break"), + }); +} + +function remove_column() { + if (store.is_customize_form && column.value.df.is_custom_field == 0) { + frappe.msgprint(__("Cannot delete standard field. You can hide it if you want")); + throw "cannot delete standard field"; + } else if (column.value.fields.length == 0 || store.has_standard_field(column.value)) { + delete_column(); + } else { + confirm_dialog( + __("Delete Column", null, "Title of confirmation dialog"), + __( + "Are you sure you want to delete the column? All the fields in the column will be moved to the previous column.", + null, + "Confirmation dialog message" + ), + () => delete_column(), + __("Delete column", null, "Button text"), + () => delete_column(true), + __("Delete entire column with fields", null, "Button text") + ); + } +} + +function delete_column(with_children) { + // move all fields to previous column + let columns = props.section.columns; + let index = columns.length - 1; + + if (with_children && index == 0 && columns.length == 1) { + if (column.value.fields.length == 0) { + frappe.msgprint(__("Section must have at least one column")); + throw "section must have at least one column"; + } + + columns.unshift({ + df: store.get_df("Column Break"), + fields: [], + is_first: true, + }); + index++; + } + + if (!with_children) { + if (index > 0) { + let prev_column = columns[index - 1]; + prev_column.fields = [...prev_column.fields, ...column.value.fields]; + } else { + if (column.value.fields.length == 0) { + // set next column as first column + let next_column = columns[index + 1]; + if (next_column) { + next_column.is_first = true; + } else { + frappe.msgprint(__("Section must have at least one column")); + throw "section must have at least one column"; + } + } else { + // create a new column if current column has fields and push fields to it + columns.unshift({ + df: store.get_df("Column Break"), + fields: column.value.fields, + is_first: true, + }); + index++; + } + } + } + + // remove column + columns.splice(index, 1); + store.form.selected_field = null; +} + +const options = computed(() => { + let groups = [ + { + group: "Section", + items: [ + { label: "Add section above", onClick: add_section_above }, + { label: "Remove section", onClick: remove_section }, + ], + }, + { + group: "Column", + items: [{ label: "Add column", onClick: add_column }], + }, + ]; + + // add remove column option if there are more than one columns + if (props.section.columns.length > 1) { + groups[1].items.push({ + label: "Remove column", + tooltip: "Remove last column", + onClick: remove_column, + }); + } else if (props.section.columns[0].fields.length) { + // add remove all fields option if there is only one column and it has fields + groups[1].items.push({ + label: "Empty column", + tooltip: "Remove all fields in the column", + onClick: () => delete_column(true), + }); + } + + // add move to tab option if the current section is not the first section + if (props.tab.sections.indexOf(props.section) > 0) { + groups[0].items.push({ + label: "Move sections to new tab", + tooltip: "Move current and all subsequent sections to a new tab", + onClick: move_sections_to_tab, + }); + } + + return groups; +}); + diff --git a/frappe/public/js/form_builder/components/controls/CheckControl.vue b/frappe/public/js/form_builder/components/controls/CheckControl.vue index fbdb76396d..e8ec928763 100644 --- a/frappe/public/js/form_builder/components/controls/CheckControl.vue +++ b/frappe/public/js/form_builder/components/controls/CheckControl.vue @@ -20,7 +20,7 @@ let slots = useSlots(); type="checkbox" :checked="value" :disabled="read_only" - @change="event => $emit('update:modelValue', event.target.checked)" + @change="(event) => $emit('update:modelValue', event.target.checked)" /> {{ df.label }} @@ -31,7 +31,8 @@ let slots = useSlots(); diff --git a/frappe/public/js/form_builder/store.js b/frappe/public/js/form_builder/store.js index 7636c2575e..069cd10540 100644 --- a/frappe/public/js/form_builder/store.js +++ b/frappe/public/js/form_builder/store.js @@ -201,6 +201,18 @@ export const useStore = defineStore("form-builder-store", () => { get_field_data(df) ); } + + // check if link_filters format is correct or not + + if (df.link_filters) { + try { + let link_filters = JSON.parse(df.link_filters); + } catch (e) { + error_message = __( + `Invalid Filter Format. Try using filter icon on the field to set it correctly` + ); + } + } }); return error_message; diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 84276d20d8..46c17343f8 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -107,6 +107,7 @@ frappe.ui.form.Form = class FrappeForm { // 2 column layout this.setup_std_layout(); + this.setup_filters(); // client script must be called after "setup" - there are no fields_dict attached to the frm otherwise this.script_manager = new frappe.ui.form.ScriptManager({ @@ -272,6 +273,41 @@ frappe.ui.form.Form = class FrappeForm { }); } + setup_filters() { + let fields_with_filters = frappe + .get_meta(this.doctype) + .fields.filter((field) => field.link_filters) + .map((field) => JSON.parse(field.link_filters)); + if (fields_with_filters.length === 0) return; + fields_with_filters = this.parse_filters(fields_with_filters); + for (let link_field in fields_with_filters) { + const filters = fields_with_filters[link_field]; + this.set_query(link_field, () => filters); + } + } + + parse_filters(data) { + const parsed_data = {}; + + for (const d of data) { + for (const condition of d) { + let [doctype, field, operator, value] = condition; + doctype = doctype.fieldname; + if (!parsed_data[doctype]) { + parsed_data[doctype] = { + filters: {}, + }; + } + + if (!parsed_data[doctype].filters[field]) { + parsed_data[doctype].filters[field] = [operator, value]; + } + } + } + + return parsed_data; + } + watch_model_updates() { // watch model updates var me = this; diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 9fab23545b..bdf471cf1b 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -1,7 +1,7 @@ frappe.ui.FilterGroup = class { constructor(opts) { $.extend(this, opts); - this.filters = []; + this.filters = this.filters || []; window.fltr = this; if (!this.filter_button) { this.wrapper = this.parent; @@ -239,6 +239,7 @@ frappe.ui.FilterGroup = class { }, filter_list: this.base_list || this, }; + let filter = new frappe.ui.Filter(args); this.filters.push(filter); return filter; From 0ff08e4e0f61957890eb0f2c03e587c67d40490d Mon Sep 17 00:00:00 2001 From: David Arnold Date: Wed, 15 Nov 2023 13:53:42 +0100 Subject: [PATCH 46/62] build(python): restricted python goes up to 3.11 only (#23229) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4952b41e16..28eb0858b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ authors = [ { name = "Frappe Technologies Pvt Ltd", email = "developers@frappe.io"} ] description = "Metadata driven, full-stack low code web framework" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.12" readme = "README.md" dynamic = ["version"] dependencies = [ From 8cbe9196967f7706efb4c35b36bc7e04fb9024e8 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 11:16:53 +0530 Subject: [PATCH 47/62] fix: add submit role to system manager if doctype is submittable --- frappe/core/doctype/doctype/doctype_list.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/core/doctype/doctype/doctype_list.js b/frappe/core/doctype/doctype/doctype_list.js index 963e863380..56f345420f 100644 --- a/frappe/core/doctype/doctype/doctype_list.js +++ b/frappe/core/doctype/doctype/doctype_list.js @@ -101,6 +101,7 @@ frappe.listview_settings["DocType"] = { role: "System Manager", share: 1, write: 1, + submit: values.is_submittable ? 1 : 0, }, ], fields: [{ fieldtype: "Section Break" }], From 3341333a3a36dbfc592bcbadc4943fc9a1e9338e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 11:40:04 +0530 Subject: [PATCH 48/62] fix: combined details and settings tab in customize form --- .../customize_form/customize_form.json | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index b872ac73e5..aad7a59b37 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -7,6 +7,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "details_tab", "doc_type", "properties", "label", @@ -25,17 +26,6 @@ "naming_section", "naming_rule", "autoname", - "document_actions_section", - "actions", - "document_links_section", - "links", - "document_states_section", - "states", - "fields_section_break", - "fields", - "form_tab", - "form_builder", - "settings_tab", "form_settings_section", "image_field", "max_attachments", @@ -60,7 +50,17 @@ "section_break_8", "sort_field", "column_break_10", - "sort_order" + "sort_order", + "document_actions_section", + "actions", + "document_links_section", + "links", + "document_states_section", + "states", + "fields_section_break", + "fields", + "form_tab", + "form_builder" ], "fields": [ { @@ -372,11 +372,6 @@ "fieldtype": "Check", "label": "Is Calendar and Gantt" }, - { - "fieldname": "settings_tab", - "fieldtype": "Tab Break", - "label": "Settings" - }, { "fieldname": "form_builder", "fieldtype": "HTML", @@ -392,6 +387,11 @@ "fieldtype": "JSON", "hidden": 1, "label": "Link Filters" + }, + { + "fieldname": "details_tab", + "fieldtype": "Tab Break", + "label": "Details" } ], "hide_toolbar": 1, @@ -400,7 +400,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-07 19:25:32.656641", + "modified": "2023-11-16 11:23:06.427432", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", From 584d43aea5ef5afb433b8c47b8067312f3d38908 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 11:40:40 +0530 Subject: [PATCH 49/62] fix: change tab to form if doctype is set in customize form --- frappe/custom/doctype/customize_form/customize_form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index c2e2e83d7f..c64d690fbc 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -149,6 +149,7 @@ frappe.ui.form.on("Customize Form", { ); render_form_builder(frm); + frm.get_field("form_builder").tab.set_active(); }); } From 45aba69a062b5209829431037536225755765917 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 12:39:31 +0530 Subject: [PATCH 50/62] fix: set default state as 0 --- .../workflow_document_state.json | 16 +++++++++++----- .../workflow_document_state.py | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frappe/workflow/doctype/workflow_document_state/workflow_document_state.json b/frappe/workflow/doctype/workflow_document_state/workflow_document_state.json index d3d325adf4..9318cec308 100644 --- a/frappe/workflow/doctype/workflow_document_state/workflow_document_state.json +++ b/frappe/workflow/doctype/workflow_document_state/workflow_document_state.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-02-22 01:27:36", "description": "Represents the states allowed in one document and role assigned to change the state.", "doctype": "DocType", @@ -30,6 +31,7 @@ "width": "160px" }, { + "default": "0", "description": "0 - Draft; 1 - Submitted; 2 - Cancelled", "fieldname": "doc_status", "fieldtype": "Select", @@ -91,19 +93,23 @@ "fieldtype": "Section Break" }, { - "fieldname": "workflow_builder_id", - "fieldtype": "Data", - "hidden": 1, - "label": "Workflow Builder ID" + "fieldname": "workflow_builder_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Workflow Builder ID" } ], "idx": 1, "istable": 1, - "modified": "2019-06-14 11:43:20.271091", + "links": [], + "modified": "2023-11-16 12:10:15.472947", "modified_by": "Administrator", "module": "Workflow", "name": "Workflow Document State", "owner": "Administrator", "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py b/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py index 2484e8c49c..ed65665c87 100644 --- a/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py +++ b/frappe/workflow/doctype/workflow_document_state/workflow_document_state.py @@ -25,5 +25,6 @@ class WorkflowDocumentState(Document): state: DF.Link update_field: DF.Literal update_value: DF.Data | None + workflow_builder_id: DF.Data | None # end: auto-generated types pass From 41af595a9a2fb430d13b89faa9011571a94e5b94 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 12:39:56 +0530 Subject: [PATCH 51/62] fix: set default allow_self_approval as 1 --- frappe/public/js/workflow_builder/WorkflowBuilder.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/workflow_builder/WorkflowBuilder.vue b/frappe/public/js/workflow_builder/WorkflowBuilder.vue index 7702147cbb..25a4179de6 100644 --- a/frappe/public/js/workflow_builder/WorkflowBuilder.vue +++ b/frappe/public/js/workflow_builder/WorkflowBuilder.vue @@ -128,6 +128,7 @@ onConnect((edge) => { data: { action: "", allowed: "All", + allow_self_approval: 1, from: source_node.data.state, to: target_node.data.state, from_id: source_node.id, From 95311a986a7fd19dc3912762eba3cd49dbc1326b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 14:02:30 +0530 Subject: [PATCH 52/62] text: fixed failing Customize Form UI Test --- cypress/integration/customize_form.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cypress/integration/customize_form.js b/cypress/integration/customize_form.js index 07bf139da1..aa212ee0f9 100644 --- a/cypress/integration/customize_form.js +++ b/cypress/integration/customize_form.js @@ -4,6 +4,7 @@ context("Customize Form", () => { cy.visit("/app/customize-form"); }); it("Changing to naming rule should update autoname", () => { + cy.findByRole("tab", { name: "Details" }).click(); cy.fill_field("doc_type", "ToDo", "Link").blur(); cy.click_form_section("Naming"); const naming_rule_default_autoname_map = { @@ -15,6 +16,7 @@ context("Customize Form", () => { "By script": "", }; Cypress._.forOwn(naming_rule_default_autoname_map, (value, naming_rule) => { + cy.findByRole("tab", { name: "Details" }).click(); cy.fill_field("naming_rule", naming_rule, "Select"); cy.get_field("autoname", "Data").should("have.value", value); }); From ced6b033e15085169c01adeb74f22b98ddf8f87e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 14:03:27 +0530 Subject: [PATCH 53/62] text: fixed failing Grid Configuration UI Test --- cypress/integration/grid_configuration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/grid_configuration.js b/cypress/integration/grid_configuration.js index 3d49ed1503..b94028a42d 100644 --- a/cypress/integration/grid_configuration.js +++ b/cypress/integration/grid_configuration.js @@ -4,7 +4,7 @@ context("Grid Configuration", () => { cy.visit("/app/doctype/User"); }); it("Set user wise grid settings", () => { - cy.findByRole("tab", { name: "Form" }).click(); + cy.findByRole("tab", { name: "Settings" }).click(); cy.get('.form-section[data-fieldname="fields_section"]').click(); cy.wait(100); cy.get('.frappe-control[data-fieldname="fields"]').as("table"); From bb0a2ca4dff019f03f39584449a15e8d6c090267 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 14:57:31 +0530 Subject: [PATCH 54/62] fix: not able to edit private workspace --- .../js/frappe/views/workspace/workspace.js | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index bf677683c9..ba52bd587f 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -616,6 +616,8 @@ frappe.views.Workspace = class Workspace { "options", this.get_value() ? me.public_parent_pages : me.private_parent_pages ); + d.set_df_property("icon", "hidden", this.get_value() ? 0 : 1); + d.set_df_property("indicator_color", "hidden", this.get_value() ? 1 : 0); }, }, { @@ -625,24 +627,23 @@ frappe.views.Workspace = class Workspace { label: __("Icon"), fieldtype: "Icon", fieldname: "icon", - default: item.icon, - change: function () { - d.set_df_property("indicator_color", "hidden", this.get_value() ? 1 : 0); - }, + default: item.public && item.icon, + hidden: !item.public, }, { label: __("Indicator color"), fieldtype: "Select", fieldname: "indicator_color", options: this.indicator_colors, - default: item.indicator_color, + default: !item.public && item.indicator_color, + hidden: item.public, }, ], primary_action_label: __("Update"), primary_action: (values) => { values.title = frappe.utils.escape_html(values.title); let is_title_changed = values.title != old_item.title; - let is_section_changed = values.is_public != old_item.public; + let is_section_changed = Boolean(values.is_public) != Boolean(old_item.public); if ( (is_title_changed || is_section_changed) && !this.validate_page(values, old_item) @@ -943,6 +944,8 @@ frappe.views.Workspace = class Workspace { "options", this.get_value() ? me.public_parent_pages : me.private_parent_pages ); + d.set_df_property("icon", "hidden", this.get_value() ? 0 : 1); + d.set_df_property("indicator_color", "hidden", this.get_value() ? 1 : 0); }, }, { @@ -952,17 +955,16 @@ frappe.views.Workspace = class Workspace { label: __("Icon"), fieldtype: "Icon", fieldname: "icon", - default: new_page.icon, - change: function () { - d.set_df_property("indicator_color", "hidden", this.get_value() ? 1 : 0); - }, + default: new_page.public && new_page.icon, + hidden: !new_page.public, }, { label: __("Indicator color"), fieldtype: "Select", fieldname: "indicator_color", options: this.indicator_colors, - default: new_page.indicator_color, + hidden: new_page.public, + default: !new_page.public && new_page.indicator_color, }, ], primary_action_label: __("Duplicate"), @@ -1187,6 +1189,8 @@ frappe.views.Workspace = class Workspace { "options", this.get_value() ? me.public_parent_pages : me.private_parent_pages ); + d.set_df_property("icon", "hidden", this.get_value() ? 0 : 1); + d.set_df_property("indicator_color", "hidden", this.get_value() ? 1 : 0); }, }, { @@ -1196,9 +1200,7 @@ frappe.views.Workspace = class Workspace { label: __("Icon"), fieldtype: "Icon", fieldname: "icon", - change: function () { - d.set_df_property("indicator_color", "hidden", this.get_value() ? 1 : 0); - }, + hidden: 1, }, { label: __("Indicator color"), From 4e00c36f0d2c7424be242721b8829ff77fda8f5e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 14:57:52 +0530 Subject: [PATCH 55/62] test: fixed failing Web Form UI test --- cypress/integration/web_form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js index f6268b69cd..9db8b09ada 100644 --- a/cypress/integration/web_form.js +++ b/cypress/integration/web_form.js @@ -156,6 +156,7 @@ context("Web Form", () => { cy.findByRole("tab", { name: "Customization" }).click(); cy.fill_field("breadcrumbs", '[{"label": _("Notes"), "route":"note"}]', "Code"); + cy.wait(2000); cy.get(".form-tabs .nav-item .nav-link").contains("Customization").click(); cy.save(); From 1ae12170e935e48e4cc8826913dd95f350c4fa20 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 15:35:11 +0530 Subject: [PATCH 56/62] test: fixed failing Workspace UI test --- cypress/integration/workspace.js | 2 -- cypress/integration/workspace_blocks.js | 1 - 2 files changed, 3 deletions(-) diff --git a/cypress/integration/workspace.js b/cypress/integration/workspace.js index d52417b234..1499c8772e 100644 --- a/cypress/integration/workspace.js +++ b/cypress/integration/workspace.js @@ -20,7 +20,6 @@ context("Workspace 2.0", () => { cy.get(".codex-editor__redactor .ce-block"); cy.get('.custom-actions button[data-label="Create%20Workspace"]').click(); cy.fill_field("title", "Test Private Page", "Data"); - cy.fill_field("icon", "edit", "Icon"); cy.get_open_dialog().find(".modal-header").click(); cy.get_open_dialog().find(".btn-primary").click(); @@ -52,7 +51,6 @@ context("Workspace 2.0", () => { cy.get('.custom-actions button[data-label="Create%20Workspace"]').click(); cy.fill_field("title", "Test Child Page", "Data"); cy.fill_field("parent", "Test Private Page", "Select"); - cy.fill_field("icon", "edit", "Icon"); cy.get_open_dialog().find(".modal-header").click(); cy.get_open_dialog().find(".btn-primary").click(); diff --git a/cypress/integration/workspace_blocks.js b/cypress/integration/workspace_blocks.js index 3b75ffb8c1..7744bfd4da 100644 --- a/cypress/integration/workspace_blocks.js +++ b/cypress/integration/workspace_blocks.js @@ -20,7 +20,6 @@ context("Workspace Blocks", () => { cy.get(".codex-editor__redactor .ce-block"); cy.get('.custom-actions button[data-label="Create%20Workspace"]').click(); cy.fill_field("title", "Test Block Page", "Data"); - cy.fill_field("icon", "edit", "Icon"); cy.get_open_dialog().find(".modal-header").click(); cy.get_open_dialog().find(".btn-primary").click(); From 9adea285a37a77e2531d6729dfb1769b00dfa992 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Nov 2023 14:55:16 +0530 Subject: [PATCH 57/62] test: fixed failing Customize Form UI test --- cypress/integration/customize_form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/customize_form.js b/cypress/integration/customize_form.js index aa212ee0f9..bd8ca1d73b 100644 --- a/cypress/integration/customize_form.js +++ b/cypress/integration/customize_form.js @@ -4,8 +4,9 @@ context("Customize Form", () => { cy.visit("/app/customize-form"); }); it("Changing to naming rule should update autoname", () => { - cy.findByRole("tab", { name: "Details" }).click(); cy.fill_field("doc_type", "ToDo", "Link").blur(); + cy.wait(2000); + cy.findByRole("tab", { name: "Details" }).click(); cy.click_form_section("Naming"); const naming_rule_default_autoname_map = { "Set by user": "prompt", @@ -16,7 +17,6 @@ context("Customize Form", () => { "By script": "", }; Cypress._.forOwn(naming_rule_default_autoname_map, (value, naming_rule) => { - cy.findByRole("tab", { name: "Details" }).click(); cy.fill_field("naming_rule", naming_rule, "Select"); cy.get_field("autoname", "Data").should("have.value", value); }); From 0b4f06c3250be574512d6853906435dc51219618 Mon Sep 17 00:00:00 2001 From: Maharshi Patel <39730881+maharshivpatel@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:36:39 +0530 Subject: [PATCH 58/62] fix: remove quotes from IMAP Folder Name (#23242) Quotes were added around folder name as spaces would cause issue with IMAP. This extra quotes are also passed to query which sets the uidnext and uidvalidity. where condition is never met This values are used on subsequent pulls and without this values, it is considered first pull and all UNSEEN mails are synced. So even If user selected All in sync option it will always work as UNSEEN option. When UNSEEN is used it will mark all emails as seen on IMAP server when pulled. --- frappe/email/receive.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 0d1e7393c3..381d43a124 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -220,6 +220,9 @@ class EmailServer: ).where(Communication.email_account == self.settings.email_account).run() if self.settings.use_imap: + # Remove {"} quotes that are added to handle spaces in IMAP Folder names + if folder[0] == folder[-1] == '"': + folder = folder[1:-2] # new update for the IMAP Folder DocType IMAPFolder = frappe.qb.DocType("IMAP Folder") frappe.qb.update(IMAPFolder).set(IMAPFolder.uidvalidity, current_uid_validity).set( From 50129d32202726925ad84dfb7c1b0041d75bbe3d Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 16 Nov 2023 18:01:33 +0530 Subject: [PATCH 59/62] refactor(email_group): simplify logic to figure out which response to use Signed-off-by: Akhil Narang --- frappe/email/doctype/email_group/email_group.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index 3af69cda33..c866beebee 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -123,6 +123,5 @@ def send_welcome_email(welcome_email, email, email_group): return args = dict(email=email, email_group=email_group) - email_message = welcome_email.response_html if welcome_email.use_html else welcome_email.response - message = frappe.render_template(email_message, args) + message = frappe.render_template(welcome_email.response_, args) frappe.sendmail(email, subject=welcome_email.subject, message=message) From 0aab6160a4e6ab3bbf3f8f7bc93f78c82e4f0349 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 17 Nov 2023 00:31:04 +0530 Subject: [PATCH 60/62] chore: validate arg for rename_field() --- frappe/model/utils/rename_field.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/frappe/model/utils/rename_field.py b/frappe/model/utils/rename_field.py index c17d01183b..e3b0835ddd 100644 --- a/frappe/model/utils/rename_field.py +++ b/frappe/model/utils/rename_field.py @@ -8,19 +8,21 @@ from frappe.model.utils.user_settings import sync_user_settings, update_user_set from frappe.utils.password import rename_password_field -def rename_field(doctype, old_fieldname, new_fieldname): +def rename_field(doctype, old_fieldname, new_fieldname, validate=True): """This functions assumes that doctype is already synced""" meta = frappe.get_meta(doctype, cached=False) new_field = meta.get_field(new_fieldname) - if not new_field: - print("rename_field: " + (new_fieldname) + " not found in " + doctype) - return - if not meta.issingle and not frappe.db.has_column(doctype, old_fieldname): - print("rename_field: " + (old_fieldname) + " not found in table for: " + doctype) - # never had the field? - return + if validate: + if not new_field: + print("rename_field: " + (new_fieldname) + " not found in " + doctype) + return + + if not meta.issingle and not frappe.db.has_column(doctype, old_fieldname): + print("rename_field: " + (old_fieldname) + " not found in table for: " + doctype) + # never had the field? + return if new_field.fieldtype in table_fields: # change parentfield of table mentioned in options From ebccab06524eaff7ae074c238e3efd20cde269c9 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 17 Nov 2023 00:32:28 +0530 Subject: [PATCH 61/62] fix: copy any flags if required in rename_doc() only on validate --- frappe/model/rename_doc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 11821ffb19..e6cc83874d 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -201,8 +201,9 @@ def rename_doc( # call after_rename new_doc = frappe.get_doc(doctype, new) - # copy any flags if required - new_doc._local = getattr(old_doc, "_local", None) + if validate: + # copy any flags if required + new_doc._local = getattr(old_doc, "_local", None) new_doc.run_method("after_rename", old, new, merge) From 8f5a199d64981c406fbb230c6452d24ed1a631a2 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 17 Nov 2023 15:03:21 +0530 Subject: [PATCH 62/62] test: Remove cacheAcrossSpecs to avoid bulk permission failure --- cypress/support/commands.js | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index a2d0eb2698..50d86d42b1 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -37,22 +37,16 @@ Cypress.Commands.add("login", (email, password) => { // cy.session clears all localStorage on new login, so we need to retain the last route const session_last_route = window.localStorage.getItem("session_last_route"); return cy - .session( - [email, password] || "", - () => { - return cy.request({ - url: "/api/method/login", - method: "POST", - body: { - usr: email, - pwd: password, - }, - }); - }, - { - cacheAcrossSpecs: true, - } - ) + .session([email, password] || "", () => { + return cy.request({ + url: "/api/method/login", + method: "POST", + body: { + usr: email, + pwd: password, + }, + }); + }) .then(() => { if (session_last_route) { window.localStorage.setItem("session_last_route", session_last_route);