diff --git a/frappe/core/page/dashboard/dashboard.css b/frappe/core/page/dashboard/dashboard.css index 3657374c8b..51db4bc78a 100644 --- a/frappe/core/page/dashboard/dashboard.css +++ b/frappe/core/page/dashboard/dashboard.css @@ -17,7 +17,7 @@ font-weight: bold; } -.chart-loading-state { +.chart-loading-state, .chart-empty-state { height: 100%; margin-top: 160px; text-align: center; diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js index 6b36908260..54e8bddfd5 100644 --- a/frappe/core/page/dashboard/dashboard.js +++ b/frappe/core/page/dashboard/dashboard.js @@ -226,23 +226,26 @@ class DashboardChart { "Line": "line", "Bar": "bar", }; - let chart_args = { - title: this.chart_doc.chart_name, - data: this.data, - type: chart_type_map[this.chart_doc.type], - truncateLegends: 1, - colors: [this.chart_doc.color || "light-blue"], - axisOptions: { - xIsSeries: this.chart_doc.timeseries, - shortenYAxisNumbers: 1 - } - }; - this.chart_container.find('.chart-loading-state').addClass('hide'); - if(!this.chart) { - this.chart = new frappe.Chart(this.chart_container.find(".chart-wrapper")[0], chart_args); + this.chart_container.find('.chart-loading-state').addClass('hide'); + if (!this.data) { + this.chart_container.find('.chart-empty-state').removeClass('hide'); } else { - this.chart.update(this.data); + let chart_args = { + title: this.chart_doc.chart_name, + data: this.data, + type: chart_type_map[this.chart_doc.type], + colors: [this.chart_doc.color || "light-blue"], + axisOptions: { + xIsSeries: this.chart_doc.timeseries, + shortenYAxisNumbers: 1 + } + }; + if (!this.chart) { + this.chart = new frappe.Chart(this.chart_container.find(".chart-wrapper")[0], chart_args); + } else { + this.chart.update(this.data); + } } } diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js index f42faea0e5..99ba49bc4f 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js @@ -23,6 +23,8 @@ frappe.ui.form.on('Dashboard Chart', { // set timeseries based on chart type if (['Count', 'Average', 'Sum'].includes(frm.doc.chart_type)) { frm.set_value('timeseries', 1); + } else { + frm.set_value('timeseries', 0); } frm.set_value('document_type', ''); }, @@ -38,6 +40,7 @@ frappe.ui.form.on('Dashboard Chart', { timespan: function(frm) { const time_interval_options = { + "Select Date Range": ["Quarterly", "Monthly", "Weekly", "Daily"], "Last Year": ["Quarterly", "Monthly", "Weekly", "Daily"], "Last Quarter": ["Monthly", "Weekly", "Daily"], "Last Month": ["Weekly", "Daily"], @@ -55,10 +58,14 @@ frappe.ui.form.on('Dashboard Chart', { {label: __('Last Modified On'), value: 'modified'} ]; let value_fields = []; + let group_by_fields = []; + let aggregate_function_fields = []; let update_form = function() { // update select options frm.set_df_property('based_on', 'options', date_fields); frm.set_df_property('value_based_on', 'options', value_fields); + frm.set_df_property('group_by_based_on', 'options', group_by_fields); + frm.set_df_property('aggregate_function_based_on', 'options', aggregate_function_fields); frm.trigger("show_filters"); } @@ -72,6 +79,10 @@ frappe.ui.form.on('Dashboard Chart', { } if (['Int', 'Float', 'Currency', 'Percent'].includes(df.fieldtype)) { value_fields.push({label: df.label, value: df.fieldname}); + aggregate_function_fields.push({label: df.label, value: df.fieldname}); + } + if (['Link', 'Select'].includes(df.fieldtype)) { + group_by_fields.push({label: df.label, value: df.fieldname}); } }); update_form(); diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json index 1f6941c955..200e191ae1 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json @@ -1,703 +1,212 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, "allow_rename": 1, "autoname": "field:chart_name", - "beta": 0, "creation": "2019-01-10 12:28:06.282875", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "chart_name", + "chart_type", + "source", + "document_type", + "based_on", + "value_based_on", + "group_by_type", + "group_by_based_on", + "aggregate_function_based_on", + "number_of_groups", + "column_break_6", + "timespan", + "from_date", + "to_date", + "time_interval", + "timeseries", + "filters_section", + "filters_json", + "chart_options_section", + "type", + "width", + "column_break_2", + "color", + "section_break_10", + "last_synced_on" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "chart_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Chart Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "chart_type", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Chart Type", - "length": 0, - "no_copy": 0, - "options": "Count\nSum\nAverage\nCustom", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Count\nSum\nAverage\nCustom\nGroup By" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.chart_type === 'Custom'", - "fetch_if_empty": 0, "fieldname": "source", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Chart Source", - "length": 0, - "no_copy": 0, - "options": "Dashboard Chart Source", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Dashboard Chart Source" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval: doc.chart_type !== 'Custom'", - "fetch_if_empty": 0, "fieldname": "document_type", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Document Type", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "DocType" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.chart_type !== 'Custom'", - "fetch_if_empty": 0, + "depends_on": "eval: ['Count', 'Sum', 'Average'].includes(doc.chart_type)", "fieldname": "based_on", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Series Based On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Time Series Based On" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: ['Sum', 'Average'].includes(doc.chart_type)", - "fetch_if_empty": 0, + "depends_on": "eval: ['Sum', 'Average'].includes(doc.chart_type)\n", "fieldname": "value_based_on", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Value Based On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Value Based On" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "timeseries", - "fetch_if_empty": 0, "fieldname": "timespan", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Timespan", - "length": 0, - "no_copy": 0, - "options": "Last Year\nLast Quarter\nLast Month\nLast Week", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Last Year\nLast Quarter\nLast Month\nLast Week\nSelect Date Range" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "timeseries", - "fetch_if_empty": 0, "fieldname": "time_interval", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Time Interval", - "length": 0, - "no_copy": 0, - "options": "Quarterly\nMonthly\nWeekly\nDaily", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Quarterly\nMonthly\nWeekly\nDaily" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", + "depends_on": "eval:doc.chart_type !== 'Group By'", "fieldname": "timeseries", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Series", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Time Series" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "filters_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Filters", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Filters" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "filters_json", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Filters JSON", - "length": 0, - "no_copy": 0, "options": "JSON", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "chart_options_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Chart Options", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Chart Options" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "type", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Type", - "length": 0, - "no_copy": 0, "options": "Line\nBar", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "width", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Width", - "length": 0, - "no_copy": 0, "options": "Half\nFull", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "color", "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Color" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "last_synced_on", "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Last Synced On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 + }, + { + "depends_on": "eval:doc.chart_type === 'Group By'", + "fieldname": "group_by_based_on", + "fieldtype": "Select", + "label": "Group By Based On" + }, + { + "default": "Count", + "depends_on": "eval:doc.chart_type === 'Group By'", + "fieldname": "group_by_type", + "fieldtype": "Select", + "label": "Group By Type", + "options": "Count\nSum\nAverage" + }, + { + "depends_on": "eval: ['Sum', 'Average'].includes(doc.group_by_type)", + "fieldname": "aggregate_function_based_on", + "fieldtype": "Select", + "label": "Aggregate Function Based On" + }, + { + "depends_on": "eval:doc.chart_type === 'Group By'", + "fieldname": "number_of_groups", + "fieldtype": "Int", + "label": "Number of Groups" + }, + { + "depends_on": "eval:doc.timespan === 'Choose Time Span'", + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, + { + "depends_on": "eval:doc.timespan === 'Choose Time Span'", + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" } ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-09 15:04:02.434131", + "modified": "2019-10-29 02:26:14.838057", "modified_by": "Administrator", "module": "Desk", "name": "Dashboard Chart", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py index bf8d3020a5..845eaa9055 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.py @@ -12,19 +12,30 @@ from frappe.model.document import Document @frappe.whitelist() @cache_source -def get(chart_name = None, chart = None, no_cache = None, from_date=None, to_date=None, refresh = None): +def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_date = None, refresh = None): if chart_name: chart = frappe.get_doc('Dashboard Chart', chart_name) else: chart = frappe._dict(frappe.parse_json(chart)) timespan = chart.timespan + from_date = chart.from_date + to_date = chart.to_date timegrain = chart.time_interval filters = frappe.parse_json(chart.filters_json) # don't include cancelled documents filters['docstatus'] = ('<', 2) + if chart.chart_type == 'Group By': + chart_config = get_group_by_chart_config(chart, filters) + else: + chart_config = get_chart_config(chart, filters, timespan, timegrain, from_date, to_date) + + return chart_config + + +def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date): if not from_date: from_date = get_from_date_from_timespan(to_date, timespan) if not to_date: @@ -32,7 +43,6 @@ def get(chart_name = None, chart = None, no_cache = None, from_date=None, to_dat # get conditions from filters conditions, values = frappe.db.build_conditions(filters) - # query will return year, unit and aggregate value data = frappe.db.sql(''' select @@ -63,7 +73,7 @@ def get(chart_name = None, chart = None, no_cache = None, from_date=None, to_dat # add missing data points for periods where there was no result result = add_missing_values(result, timegrain, from_date, to_date) - return { + chart_config = { "labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result], "datasets": [{ "name": chart.name, @@ -71,11 +81,53 @@ def get(chart_name = None, chart = None, no_cache = None, from_date=None, to_dat }] } + return chart_config + + +def get_group_by_chart_config(chart, filters): + conditions, values = frappe.db.build_conditions(filters) + data = frappe.db.sql(''' + select + {aggregate_function}({value_field}) as count, + {group_by_field} as name + from `tab{doctype}` + where {conditions} + group by {group_by_field} + order by count desc + '''.format( + aggregate_function = get_aggregate_function(chart.group_by_type), + value_field = chart.aggregate_function_based_on or '1', + field = chart.aggregate_function_based_on or chart.group_by_based_on, + group_by_field = chart.group_by_based_on, + doctype = chart.document_type, + conditions = conditions, + ), values, as_dict = True) + + if data: + if chart.number_of_groups and chart.number_of_groups < len(data): + other_count = 0 + for i in range(chart.number_of_groups - 1, len(data)): + other_count += data[i]['count'] + data = data[0: chart.number_of_groups - 1] + data.append({'name': 'Other', 'count': other_count}) + + chart_config = { + "labels": [item['name'] if item['name'] else 'Not Specified' for item in data], + "datasets": [{ + "name": chart.name, + "values": [item['count'] for item in data] + }] + } + return chart_config + else: + return None + + def get_aggregate_function(chart_type): return { "Sum": "SUM", "Count": "COUNT", - "Average": "AVG" + "Average": "AVG", }[chart_type] def convert_to_dates(data, timegrain): @@ -210,7 +262,14 @@ class DashboardChart(Document): self.check_required_field() def check_required_field(self): - if not self.based_on: - frappe.throw(_("Time series based on is required to create a dashboard chart")) if not self.document_type: - frappe.throw(_("Document type is required to create a dashboard chart")) + frappe.throw(_("Document type is required to create a dashboard chart")) + + if self.chart_type == 'Group By': + if not self.group_by_based_on: + frappe.throw(_("Group By field is required to create a dashboard chart")) + if self.group_by_type in ['Sum', 'Average'] and not self.aggregate_function_based_on: + frappe.throw(_("Aggregate Function field is required to create a dashboard chart")) + else: + if not self.based_on: + frappe.throw(_("Time series based on is required to create a dashboard chart")) diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py index 35e468fc3c..fc74448d10 100644 --- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py +++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -129,3 +129,25 @@ class TestDashboardChart(unittest.TestCase): self.assertEqual(result.get('datasets')[0].get('values')[2], 0) frappe.db.rollback() + + def test_group_by_chart_type(self): + if frappe.db.exists('Dashboard Chart', 'Test Group By Dashboard Chart'): + frappe.delete_doc('Dashboard Chart', 'Test Group By Dashboard Chart') + + frappe.get_doc({"doctype":"ToDo", "description": "test"}).insert() + + frappe.get_doc(dict( + doctype = 'Dashboard Chart', + chart_name = 'Test Group By Dashboard Chart', + chart_type = 'Group By', + document_type = 'ToDo', + group_by_based_on = 'status', + filters_json = '{}', + )).insert() + + result = get(chart_name ='Test Group By Dashboard Chart', refresh = 1) + todo_status_count = frappe.db.count('ToDo', {'status': result.get('labels')[0]}) + + self.assertEqual(result.get('datasets')[0].get('values')[0], todo_status_count) + + frappe.db.rollback() \ No newline at end of file