From e152fa579ddf930f660f4ea5f02ac76447967165 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Thu, 14 Mar 2024 23:15:46 +0530 Subject: [PATCH 1/5] feat: add eval support for link field filters --- frappe/public/js/form_builder/components/Field.vue | 6 +----- frappe/public/js/frappe/form/form.js | 8 +++++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frappe/public/js/form_builder/components/Field.vue b/frappe/public/js/form_builder/components/Field.vue index 7e5ab99fa3..a4e2f79468 100644 --- a/frappe/public/js/form_builder/components/Field.vue +++ b/frappe/public/js/form_builder/components/Field.vue @@ -97,6 +97,7 @@ function make_dialog(frm) { }); props.field.df.link_filters = JSON.stringify(filters); + store.form.selected_field = props.field.df; frm.dialog.hide(); }, primary_action_label: __("Apply"), @@ -133,11 +134,6 @@ function make_dialog(frm) { } }); } - - // Setting selected field in store because when we click on the dialog the selected field is set to null - frm.dialog.$wrapper.on("click", () => { - store.form.selected_field = props.field.df; - }); } function make_filter_area(frm, doctype) { diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 69a7c6e366..2d9afa7c7a 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -292,13 +292,19 @@ frappe.ui.form.Form = class FrappeForm { for (const d of data) { for (const condition of d) { let [doctype, field, operator, value] = condition; + if (value.includes("eval")) { + // if condition type of 'like' is used then remove % from value + value = value.replace(/%/g, ""); + // get the value to calculate + value = value.split("eval:")[1]; + value = frappe.utils.eval(value, { doc: this.doc, frappe }); + } doctype = doctype.fieldname; if (!parsed_data[doctype]) { parsed_data[doctype] = { filters: {}, }; } - if (!parsed_data[doctype].filters[field]) { parsed_data[doctype].filters[field] = [operator, value]; } From 5756713851b22393bd09f62a5c9481955bbb4549 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Fri, 15 Mar 2024 16:26:00 +0530 Subject: [PATCH 2/5] fix: support for adding filters for child table --- frappe/public/js/frappe/form/form.js | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 2d9afa7c7a..644364a846 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -284,6 +284,34 @@ frappe.ui.form.Form = class FrappeForm { const filters = fields_with_filters[link_field]; this.set_query(link_field, () => filters); } + + // for child tables + let table_fields = frappe + .get_meta(this.doctype) + .fields.filter((field) => field.fieldtype === "Table"); + // get meta of table fields + let table_field_map = {}; + + table_fields.forEach((field) => { + table_field_map[field.fieldname] = frappe.get_meta(field.options); + }); + // now in table_field_map we have the filter field where link_filters are present + let i = {}; + for (let table_field in table_field_map) { + let filters = table_field_map[table_field].fields + .filter((field) => field.link_filters) + .map((field) => JSON.parse(field.link_filters)); + // console.log(filters) + i[table_field] = this.parse_filters(filters); + } + + for (let parent_fieldname in i) { + let child_field = i[parent_fieldname]; + for (let child_fieldname in child_field) { + let filters = child_field[child_fieldname]; + this.set_query(child_fieldname, parent_fieldname, () => filters); + } + } } parse_filters(data) { @@ -292,7 +320,7 @@ frappe.ui.form.Form = class FrappeForm { for (const d of data) { for (const condition of d) { let [doctype, field, operator, value] = condition; - if (value.includes("eval")) { + if (String(value).startsWith("eval:")) { // if condition type of 'like' is used then remove % from value value = value.replace(/%/g, ""); // get the value to calculate From f1c4f88843e5e0ee658ed11bcc7ba95dee8e6d4a Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Tue, 19 Mar 2024 12:02:17 +0530 Subject: [PATCH 3/5] fix: eval support for child table --- .../js/form_builder/components/Field.vue | 6 +- frappe/public/js/frappe/form/controls/link.js | 32 +++++++++ frappe/public/js/frappe/form/form.js | 70 ------------------- 3 files changed, 33 insertions(+), 75 deletions(-) diff --git a/frappe/public/js/form_builder/components/Field.vue b/frappe/public/js/form_builder/components/Field.vue index a4e2f79468..2cfb944bbb 100644 --- a/frappe/public/js/form_builder/components/Field.vue +++ b/frappe/public/js/form_builder/components/Field.vue @@ -92,7 +92,7 @@ function make_dialog(frm) { filter.pop(); // filter_group component requires options and frm.set_query requires fieldname so storing both - filter[0] = { fieldname, field_option }; + filter[0] = field_option; return filter; }); @@ -147,10 +147,6 @@ function make_filter_area(frm, doctype) { function add_existing_filter(frm, df) { if (df.link_filters) { let filters = JSON.parse(df.link_filters); - filters.map((filter) => { - // filter_group component requires options and frm.set_query requires fieldname - filter[0] = filter[0].field_option; - }); if (filters) { frm.filter_group.add_filters_to_filter_group(filters); } diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 51eb63e88d..6221441333 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -509,6 +509,12 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat }); return obj; }; + + // apply link field filters + if (this.df.link_filters && !!this.df.link_filters.length) { + this.apply_link_field_filters(); + } + if (this.get_query || this.df.get_query) { var get_query = this.get_query || this.df.get_query; if ($.isPlainObject(get_query)) { @@ -571,6 +577,32 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat $.extend(args.filters, this.df.filters); } } + + apply_link_field_filters() { + let link_filters = JSON.parse(this.df.link_filters); + let filters = {}; + link_filters.forEach((filter) => { + filter.shift(); + let filter_value = String(filter[2]).replace(/%/g, ""); + if (filter_value.startsWith("eval:")) { + // get the value to calculate + filter_value = filter_value.split("eval:")[1]; + let context = { + doc: this.doc, + parent: this.doc.parenttype ? this.frm.doc : null, + frappe, + }; + filter_value = frappe.utils.eval(filter_value, context); + filter[2] = filter_value; + } + filters[filter[0]] = [filter[1], filter[2]]; + }); + this.get_query = function (doc) { + // take filters from the link field and add to the query + return { filters }; + }; + } + validate(value) { // validate the value just entered if (this._validated || this.df.options == "[Select]" || this.df.ignore_link_validation) { diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 644364a846..b17c84df3d 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -107,7 +107,6 @@ 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({ @@ -273,75 +272,6 @@ 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); - } - - // for child tables - let table_fields = frappe - .get_meta(this.doctype) - .fields.filter((field) => field.fieldtype === "Table"); - // get meta of table fields - let table_field_map = {}; - - table_fields.forEach((field) => { - table_field_map[field.fieldname] = frappe.get_meta(field.options); - }); - // now in table_field_map we have the filter field where link_filters are present - let i = {}; - for (let table_field in table_field_map) { - let filters = table_field_map[table_field].fields - .filter((field) => field.link_filters) - .map((field) => JSON.parse(field.link_filters)); - // console.log(filters) - i[table_field] = this.parse_filters(filters); - } - - for (let parent_fieldname in i) { - let child_field = i[parent_fieldname]; - for (let child_fieldname in child_field) { - let filters = child_field[child_fieldname]; - this.set_query(child_fieldname, parent_fieldname, () => filters); - } - } - } - - parse_filters(data) { - const parsed_data = {}; - - for (const d of data) { - for (const condition of d) { - let [doctype, field, operator, value] = condition; - if (String(value).startsWith("eval:")) { - // if condition type of 'like' is used then remove % from value - value = value.replace(/%/g, ""); - // get the value to calculate - value = value.split("eval:")[1]; - value = frappe.utils.eval(value, { doc: this.doc, frappe }); - } - 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; From 237572c7fbd0cbb252232f59b1686e5c3eb52a74 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Fri, 22 Mar 2024 00:54:33 +0530 Subject: [PATCH 4/5] chore: code cleanup --- frappe/public/js/frappe/form/controls/link.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 6221441333..43d116568c 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -580,6 +580,16 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat apply_link_field_filters() { let link_filters = JSON.parse(this.df.link_filters); + let filters = this.parse_filters(link_filters); + // take filters from the link field and add to the query + this.get_query = function () { + return { + filters, + }; + }; + } + + parse_filters(link_filters) { let filters = {}; link_filters.forEach((filter) => { filter.shift(); @@ -597,10 +607,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat } filters[filter[0]] = [filter[1], filter[2]]; }); - this.get_query = function (doc) { - // take filters from the link field and add to the query - return { filters }; - }; + return filters; } validate(value) { From d3981107a3e05bc5d4d73acf75b56c6d2c110ae7 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Fri, 22 Mar 2024 01:03:02 +0530 Subject: [PATCH 5/5] chore: code cleanup --- frappe/public/js/frappe/form/controls/link.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 43d116568c..7cae8684e8 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -592,20 +592,19 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat parse_filters(link_filters) { let filters = {}; link_filters.forEach((filter) => { - filter.shift(); - let filter_value = String(filter[2]).replace(/%/g, ""); - if (filter_value.startsWith("eval:")) { + let [_, fieldname, operator, value] = filter; + value = String(value).replace(/%/g, ""); + if (value.startsWith("eval:")) { // get the value to calculate - filter_value = filter_value.split("eval:")[1]; + value = value.split("eval:")[1]; let context = { doc: this.doc, parent: this.doc.parenttype ? this.frm.doc : null, frappe, }; - filter_value = frappe.utils.eval(filter_value, context); - filter[2] = filter_value; + value = frappe.utils.eval(value, context); } - filters[filter[0]] = [filter[1], filter[2]]; + filters[fieldname] = [operator, value]; }); return filters; }