diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 2e63f73544..bc6adff8ef 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -17,6 +17,7 @@ from six import string_types, iteritems from datetime import timedelta from frappe.utils.file_manager import get_file from frappe.utils import gzip_decompress +from frappe.model import no_value_fields def get_report_doc(report_name): doc = frappe.get_doc("Report", report_name) @@ -33,7 +34,7 @@ def get_report_doc(report_name): return doc -def generate_report_result(report, filters=None, user=None): +def generate_report_result(report, filters=None, user=None, custom_columns=None): status = None if not user: user = frappe.session.user @@ -83,10 +84,10 @@ def generate_report_result(report, filters=None, user=None): data_to_be_printed = res[4] if result: - result, columns = get_filtered_data(report.ref_doctype, columns, result, user, report.add_custom_fields_in_report) + result, columns = get_filtered_data(report.ref_doctype, columns, result, user, custom_columns) - # if cint(report.add_total_row) and result: - # result = add_total_row(result, columns) + if cint(report.add_total_row) and result: + result = add_total_row(result, columns) return { "result": result, @@ -160,7 +161,7 @@ def get_script(report_name): @frappe.whitelist() @frappe.read_only() -def run(report_name, filters=None, user=None): +def run(report_name, filters=None, user=None, custom_columns=None): report = get_report_doc(report_name) if not user: @@ -182,7 +183,7 @@ def run(report_name, filters=None, user=None): dn = "" result = get_prepared_report_result(report, filters, dn, user) else: - result = generate_report_result(report, filters, user) + result = generate_report_result(report, filters, user, custom_columns) result["add_total_row"] = report.add_total_row @@ -353,8 +354,27 @@ def add_total_row(result, columns, meta = None): result.append(total_row) return result +@frappe.whitelist() +def get_custom_fields(doctypes): -def get_filtered_data(ref_doctype, columns, data, user, add_custom_fields): + field_map = [] + + doclist = json.loads(doctypes) + + for d in doclist: + fieldlist = [f.label for f in frappe.get_meta(d).fields \ + if f.label and f.fieldname and f.fieldname not in no_value_fields + and f.fieldname not in ["naming_series"] + and f.fieldtype not in ["Section Break", "Column Break", "Table"]] + + field_map.append({ + "doctype": d, + "fields": fieldlist + }) + + return field_map + +def get_filtered_data(ref_doctype, columns, data, user, custom_columns): result = [] linked_doctypes = get_linked_doctypes(columns, data) match_filters_per_doctype = get_user_match_filters(linked_doctypes, user=user) @@ -364,45 +384,42 @@ def get_filtered_data(ref_doctype, columns, data, user, add_custom_fields): role_permissions = get_role_permissions(frappe.get_meta(ref_doctype), user) if_owner = role_permissions.get("if_owner", {}).get("report") - doc_fields_map = {} - custom_field_value_map = {} + if custom_columns: + custom_field_value_map = {} + fields = json.loads(custom_columns) + custom_field_list = [] - if add_custom_fields: + for doctype, field_list in iteritems(fields): + values = frappe.db.sql("select name, {fields} from `tab{doctype}` " + .format(fields = ", ".join(field_list), doctype=doctype), as_dict=1) - fields = frappe.db.sql(""" select dt, fieldname, fieldtype from `tabCustom Field` - where fieldtype not in ('Section Break', 'Column Break') and - dt in (%s)""" % ', '.join(['%s']* len(linked_doctypes)), tuple([doctype for doctype in linked_doctypes.keys()]), as_dict=1) - - for d in fields: - doc_fields_map.setdefault(d.dt, []) - doc_fields_map.get(d.dt).append(d.fieldname) - - columns.append({ - "label": frappe.unscrub(d.fieldname), - "fieldname": d.filedname, - "fieldtype": d.fieldtype, - "width": 100 + custom_field_list += field_list + for field in field_list: + columns.append({ + "label": frappe.unscrub(field), + "fieldname": field, + "fieldtype": "Data", + "width": 100 }) - for doctype in linked_doctypes.keys(): - if doc_fields_map.get(doctype): - values = frappe.db.sql("select name, {fields} from `tab{doctype}` " - .format(fields = ", ".join(doc_fields_map.get(doctype)), doctype=doctype), as_dict=1) + for value in values: + custom_field_value_map.setdefault(value.name, value) - for value in values: - custom_field_value_map.setdefault(value.name, value) + columns_dict = get_columns_dict(columns) + columns_idx_map = [columns_dict.get(i) for i in range(len(columns))] for row in data: - for index, column in enumerate(columns): - print(index,column) - if isinstance(row, dict) and column.get("fieldtype") == "Link": - fieldname = column.get("fieldname") - row.update({ "test_field": custom_field_value_map.get(row.get(fieldname),{}).get("test_field")}) - else: - print("$$$$$$$$$$") - custom_field_value_map.get(row[index],{}) - row.append(custom_field_value_map.get(row[index],{}).get("test_field")) - + for index, column in enumerate(columns_idx_map): + for d in custom_field_list: + if column.get("fieldtype") == "Link" or column.get("fieldtype") == "Dynamic Link": + if isinstance(row, dict): + fieldname = column.get("fieldname") + row.update({ fieldname: custom_field_value_map.get(row.get(fieldname),{}).get(d)}) + else: + fieldname = column.get("fieldname") + value = custom_field_value_map.get(row[index],{}).get(d) + if value: + row.append(value) if match_filters_per_doctype: for row in data: diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 596a6501ad..0beb56005b 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -269,7 +269,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.page.clear_fields(); } - refresh() { + refresh(values) { this.toggle_message(true); let filters = this.get_filter_values(true); let query = frappe.utils.get_query_string(frappe.get_route_str()); @@ -291,6 +291,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { args: { report_name: this.report_name, filters: filters, + custom_columns: values }, callback: resolve, always: () => this.page.btn_secondary.prop('disabled', false) @@ -418,7 +419,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.raw_data = data; this.columns = this.prepare_columns(data.columns); this.data = this.prepare_data(data.result); - + this.custom_fields = this.get_dialog_fields(); this.tree_report = this.data.some(d => 'indent' in d); } @@ -962,6 +963,22 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { action: () => frappe.set_route('List', 'Auto Email Report', {'report' : this.report_name}), standard: true }, + { + label: __('Add Custom Fields'), + action: () => { + const d = new frappe.ui.Dialog({ + title: __('Add Custom Fields'), + fields: this.custom_fields, + primary_action: (values) => { + this.refresh(values); + d.hide(); + } + }); + + d.show(); + }, + standard: true + }, { label: __('User Permissions'), action: () => frappe.set_route('List', 'User Permission', { @@ -979,6 +996,64 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { ]; } + get_linked_doctypes() { + + let doctypes = []; + let dynamic_links = []; + let dynamic_doctypes = new Set(); + + this.columns.forEach(df => { + if (df.fieldtype == "Link" && df.options) { + doctypes.push(df.options); + } + else if (df.fieldtype == "Dynamic Link" && df.options) { + dynamic_links.push(df.options); + } + }); + + this.data.forEach(row => { + dynamic_links.forEach(field => { + if (row[field]){ + dynamic_doctypes.add(row[field]); + } + }) + }) + + doctypes = doctypes.concat(Array.from(dynamic_doctypes)); + + return doctypes; + } + + get_dialog_fields() { + var dialog_fields = []; + const linked_doctypes = this.get_linked_doctypes(); + + frappe.call({ + method: "frappe.desk.query_report.get_custom_fields", + args: { + doctypes: linked_doctypes + }, + callback: function(r) { + r.message.forEach(df => { + dialog_fields.push({ + label: __(df.doctype), + fieldname: df.doctype, + fieldtype: 'MultiCheck', + columns: 2, + options: df.fields + .map(f => ({ + label: __(f), + value: f ? frappe.scrub(f) : null, + checked: 0 + })) + }); + }); + } + }); + + return dialog_fields; + } + setup_report_wrapper() { if (this.$report) return;