2013-11-08 15:41:14 +00:00
|
|
|
|
2013-11-12 08:38:27 +00:00
|
|
|
/* jshint undef: false */
|
|
|
|
|
2014-01-14 12:51:42 +00:00
|
|
|
(function () {
|
2013-11-13 10:50:59 +00:00
|
|
|
'use strict';
|
2014-01-14 12:51:42 +00:00
|
|
|
var QWeb = openerp.web.qweb;
|
2014-01-28 11:10:22 +00:00
|
|
|
var _lt = openerp.web._lt;
|
|
|
|
var _t = openerp.web._t;
|
|
|
|
|
2014-01-14 13:01:41 +00:00
|
|
|
nv.dev = false; // sets nvd3 library in production mode
|
2011-06-17 13:25:50 +00:00
|
|
|
|
2014-01-21 14:30:34 +00:00
|
|
|
openerp.web_graph.Graph = openerp.web.Widget.extend({
|
2013-12-24 11:22:24 +00:00
|
|
|
template: 'GraphWidget',
|
2013-12-18 13:40:40 +00:00
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Init stuff
|
|
|
|
// ----------------------------------------------------------------------
|
2014-01-10 11:35:57 +00:00
|
|
|
init: function(parent, model, domain, options) {
|
2013-12-18 13:40:40 +00:00
|
|
|
this._super(parent);
|
|
|
|
this.model = model;
|
2014-01-10 11:35:57 +00:00
|
|
|
this.domain = domain;
|
2014-01-16 14:00:58 +00:00
|
|
|
this.mode = options.mode || 'pivot'; // pivot, bar, pie, line
|
|
|
|
this.heatmap_mode = options.heatmap_mode || 'none';
|
2014-01-10 11:35:57 +00:00
|
|
|
this.visible_ui = options.visible_ui || true;
|
2014-01-13 15:23:16 +00:00
|
|
|
this.bar_ui = options.bar_ui || 'group';
|
2014-01-21 14:30:34 +00:00
|
|
|
this.graph_view = options.graph_view || null;
|
2014-01-10 11:35:57 +00:00
|
|
|
this.pivot_options = options;
|
2014-02-05 13:14:06 +00:00
|
|
|
this.title = options.title || 'Data';
|
2013-12-18 15:07:56 +00:00
|
|
|
},
|
2013-11-08 15:20:48 +00:00
|
|
|
|
2013-12-18 13:40:40 +00:00
|
|
|
start: function() {
|
|
|
|
var self = this;
|
2014-01-24 15:16:35 +00:00
|
|
|
this.table = $('<table>');
|
2013-12-18 13:40:40 +00:00
|
|
|
this.$('.graph_main_content').append(this.table);
|
2014-01-16 14:00:58 +00:00
|
|
|
|
2014-01-24 15:16:35 +00:00
|
|
|
var indexes = {'pivot': 0, 'bar': 1, 'line': 2, 'chart': 3};
|
|
|
|
this.$('.graph_mode_selection label').eq(indexes[this.mode]).addClass('active');
|
2014-01-16 14:00:58 +00:00
|
|
|
|
|
|
|
if (this.mode !== 'pivot') {
|
|
|
|
this.$('.graph_heatmap label').addClass('disabled');
|
2014-02-26 14:39:19 +00:00
|
|
|
this.$('.graph_main_content').addClass('graph_chart_mode');
|
2014-02-26 11:17:10 +00:00
|
|
|
} else {
|
2014-02-26 14:39:19 +00:00
|
|
|
this.$('.graph_main_content').addClass('graph_pivot_mode');
|
2014-01-16 14:00:58 +00:00
|
|
|
}
|
|
|
|
|
2014-04-30 08:49:27 +00:00
|
|
|
// get search view
|
|
|
|
var parent = this.getParent();
|
|
|
|
while (!(parent instanceof openerp.web.ViewManager)) {
|
|
|
|
parent = parent.getParent();
|
|
|
|
}
|
|
|
|
this.search_view = parent.searchview;
|
|
|
|
|
2014-02-03 11:33:32 +00:00
|
|
|
openerp.session.rpc('/web_graph/check_xlwt').then(function (result) {
|
2014-04-22 14:59:18 +00:00
|
|
|
self.$('.graph_options_selection label').last().toggle(result);
|
2014-02-03 11:33:32 +00:00
|
|
|
});
|
|
|
|
|
2014-11-27 15:03:14 +00:00
|
|
|
return this.model.call('fields_get', {
|
|
|
|
context: this.graph_view.dataset.context
|
|
|
|
}).then(function (f) {
|
2014-01-10 11:35:57 +00:00
|
|
|
self.fields = f;
|
2014-10-21 12:57:52 +00:00
|
|
|
self.fields.__count = {field:'__count', type: 'integer', string:_t('Count')};
|
2014-07-25 12:35:42 +00:00
|
|
|
self.groupby_fields = self.get_groupby_fields();
|
2014-01-10 11:35:57 +00:00
|
|
|
self.measure_list = self.get_measures();
|
|
|
|
self.add_measures_to_options();
|
2014-01-21 14:30:34 +00:00
|
|
|
self.pivot_options.row_groupby = self.create_field_values(self.pivot_options.row_groupby || []);
|
|
|
|
self.pivot_options.col_groupby = self.create_field_values(self.pivot_options.col_groupby || []);
|
2014-10-21 12:57:52 +00:00
|
|
|
self.pivot_options.measures = self.create_field_values(self.pivot_options.measures || [{field:'__count', type: 'integer', string:'Count'}]);
|
2014-01-10 11:35:57 +00:00
|
|
|
self.pivot = new openerp.web_graph.PivotTable(self.model, self.domain, self.fields, self.pivot_options);
|
2014-01-21 14:30:34 +00:00
|
|
|
self.pivot.update_data().then(function () {
|
|
|
|
self.display_data();
|
|
|
|
if (self.graph_view) {
|
|
|
|
self.graph_view.register_groupby(self.pivot.rows.groupby, self.pivot.cols.groupby);
|
|
|
|
}
|
|
|
|
});
|
2014-05-09 09:09:35 +00:00
|
|
|
openerp.web.bus.on('click', self, function (event) {
|
2014-01-10 11:35:57 +00:00
|
|
|
if (self.dropdown) {
|
2014-05-09 09:09:35 +00:00
|
|
|
self.$row_clicked = $(event.target).closest('tr');
|
2014-01-10 11:35:57 +00:00
|
|
|
self.dropdown.remove();
|
|
|
|
self.dropdown = null;
|
|
|
|
}
|
2013-12-11 13:11:48 +00:00
|
|
|
});
|
2014-01-17 14:43:03 +00:00
|
|
|
self.put_measure_checkmarks();
|
2014-01-10 11:35:57 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2014-07-25 12:35:42 +00:00
|
|
|
get_groupby_fields: function () {
|
|
|
|
var search_fields = this.get_search_fields(),
|
|
|
|
search_field_names = _.pluck(search_fields, 'field'),
|
|
|
|
other_fields = [],
|
|
|
|
groupable_types = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
|
|
|
|
|
|
|
|
_.each(this.fields, function (val, key) {
|
|
|
|
if (!_.contains(search_field_names, key) &&
|
|
|
|
_.contains(groupable_types, val.type) &&
|
|
|
|
val.store === true) {
|
|
|
|
other_fields.push({
|
|
|
|
field: key,
|
|
|
|
string: val.string,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return search_fields.concat(other_fields);
|
|
|
|
},
|
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// this method gets the fields that appear in the search view, under the
|
|
|
|
// 'Groupby' heading
|
2014-01-10 11:35:57 +00:00
|
|
|
get_search_fields: function () {
|
2014-04-30 08:49:27 +00:00
|
|
|
var self = this;
|
2014-01-10 11:35:57 +00:00
|
|
|
|
2014-05-20 14:18:03 +00:00
|
|
|
var groupbygroups = _(this.search_view.drawer.inputs).select(function (g) {
|
2014-01-21 14:30:34 +00:00
|
|
|
return g instanceof openerp.web.search.GroupbyGroup;
|
|
|
|
});
|
|
|
|
|
2014-01-27 15:23:24 +00:00
|
|
|
var filters = _.flatten(_.pluck(groupbygroups, 'filters'), true),
|
|
|
|
groupbys = _.flatten(_.map(filters, function (filter) {
|
|
|
|
var groupby = py.eval(filter.attrs.context).group_by;
|
|
|
|
if (!(groupby instanceof Array)) { groupby = [groupby]; }
|
2014-02-04 11:31:35 +00:00
|
|
|
return _.map(groupby, function(g) {
|
|
|
|
return {field: g, filter: filter};
|
2014-01-27 15:23:24 +00:00
|
|
|
});
|
|
|
|
}));
|
2014-01-21 14:30:34 +00:00
|
|
|
|
2014-01-27 15:23:24 +00:00
|
|
|
return _.uniq(_.map(groupbys, function (groupby) {
|
|
|
|
var field = groupby.field,
|
|
|
|
filter = groupby.filter,
|
2014-01-24 12:59:44 +00:00
|
|
|
raw_field = field.split(':')[0],
|
2014-01-24 15:16:35 +00:00
|
|
|
string = (field === raw_field) ? filter.attrs.string : self.fields[raw_field].string;
|
|
|
|
|
|
|
|
filter = (field === raw_field) ? filter : undefined;
|
2014-01-24 12:59:44 +00:00
|
|
|
|
|
|
|
return { field: raw_field, string: string, filter: filter };
|
|
|
|
}), false, function (filter) {return filter.field;});
|
2014-01-10 11:35:57 +00:00
|
|
|
},
|
2013-12-11 13:11:48 +00:00
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// Extracts the integer/float fields which are not 'id'
|
2014-01-17 14:43:03 +00:00
|
|
|
get_measures: function() {
|
2014-01-24 15:16:35 +00:00
|
|
|
return _.compact(_.map(this.fields, function (f, id) {
|
2014-06-13 11:32:24 +00:00
|
|
|
if (((f.type === 'integer') || (f.type === 'float')) &&
|
|
|
|
(id !== 'id') &&
|
|
|
|
(f.store !== false)) {
|
2014-01-24 15:16:35 +00:00
|
|
|
return {field:id, type: f.type, string: f.string};
|
2013-12-18 13:40:40 +00:00
|
|
|
}
|
2014-01-24 15:16:35 +00:00
|
|
|
}));
|
2012-05-07 08:19:08 +00:00
|
|
|
},
|
2013-11-08 15:20:48 +00:00
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
add_measures_to_options: function() {
|
2014-01-24 15:16:35 +00:00
|
|
|
this.$('.graph_measure_selection').append(
|
|
|
|
_.map(this.measure_list, function (measure) {
|
|
|
|
return $('<li>').append($('<a>').attr('data-choice', measure.field)
|
2014-01-15 09:37:49 +00:00
|
|
|
.attr('href', '#')
|
2014-01-24 15:16:35 +00:00
|
|
|
.text(measure.string));
|
|
|
|
}));
|
2014-01-15 09:37:49 +00:00
|
|
|
},
|
2014-01-10 11:35:57 +00:00
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Configuration methods
|
|
|
|
// ----------------------------------------------------------------------
|
2014-08-21 07:03:12 +00:00
|
|
|
set: function (domain, row_groupby, col_groupby, measures_groupby) {
|
2014-01-21 14:30:34 +00:00
|
|
|
if (!this.pivot) {
|
2014-01-10 14:08:37 +00:00
|
|
|
this.pivot_options.domain = domain;
|
|
|
|
this.pivot_options.row_groupby = row_groupby;
|
|
|
|
this.pivot_options.col_groupby = col_groupby;
|
2014-08-21 07:03:12 +00:00
|
|
|
this.pivot_options.measures_groupby = measures_groupby;
|
2014-01-21 14:30:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
var row_gbs = this.create_field_values(row_groupby),
|
|
|
|
col_gbs = this.create_field_values(col_groupby),
|
2014-08-21 07:03:12 +00:00
|
|
|
measures_gbs = this.create_field_values(measures_groupby),
|
2014-01-21 14:30:34 +00:00
|
|
|
dom_changed = !_.isEqual(this.pivot.domain, domain),
|
2014-01-22 16:07:07 +00:00
|
|
|
row_gb_changed = !_.isEqual(row_gbs, this.pivot.rows.groupby),
|
|
|
|
col_gb_changed = !_.isEqual(col_gbs, this.pivot.cols.groupby),
|
2014-08-21 07:03:12 +00:00
|
|
|
measures_gb_changed = !_.isEqual(measures_gbs, this.pivot.measures),
|
2014-01-21 14:30:34 +00:00
|
|
|
row_reduced = is_strict_beginning_of(row_gbs, this.pivot.rows.groupby),
|
2014-08-21 07:03:12 +00:00
|
|
|
col_reduced = is_strict_beginning_of(col_gbs, this.pivot.cols.groupby),
|
|
|
|
measures_reduced = is_strict_beginning_of(measures_gbs, this.pivot.measures);
|
2014-01-21 14:30:34 +00:00
|
|
|
|
2014-08-21 07:03:12 +00:00
|
|
|
if (!dom_changed && row_reduced && !col_gb_changed && !measures_gb_changed) {
|
2014-01-21 14:30:34 +00:00
|
|
|
this.pivot.fold_with_depth(this.pivot.rows, row_gbs.length);
|
|
|
|
this.display_data();
|
|
|
|
return;
|
|
|
|
}
|
2014-08-21 07:03:12 +00:00
|
|
|
if (!dom_changed && col_reduced && !row_gb_changed && !measures_gb_changed) {
|
2014-01-21 14:30:34 +00:00
|
|
|
this.pivot.fold_with_depth(this.pivot.cols, col_gbs.length);
|
|
|
|
this.display_data();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 07:03:12 +00:00
|
|
|
if (!dom_changed && col_reduced && row_reduced && !measures_gb_changed) {
|
2014-02-25 10:42:33 +00:00
|
|
|
this.pivot.fold_with_depth(this.pivot.rows, row_gbs.length);
|
|
|
|
this.pivot.fold_with_depth(this.pivot.cols, col_gbs.length);
|
|
|
|
this.display_data();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 07:03:12 +00:00
|
|
|
if (dom_changed || row_gb_changed || col_gb_changed || measures_gb_changed) {
|
|
|
|
this.pivot.set(domain, row_gbs, col_gbs, measures_gbs).then(this.proxy('display_data'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (measures_gb_changed) {
|
|
|
|
this.put_measure_checkmarks();
|
2014-01-10 11:35:57 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
set_mode: function (mode) {
|
2014-01-10 14:08:37 +00:00
|
|
|
this.mode = mode;
|
2014-01-16 14:00:58 +00:00
|
|
|
|
|
|
|
if (mode === 'pivot') {
|
|
|
|
this.$('.graph_heatmap label').removeClass('disabled');
|
2014-02-26 14:39:19 +00:00
|
|
|
this.$('.graph_main_content').removeClass('graph_chart_mode').addClass('graph_pivot_mode');
|
2014-01-16 14:00:58 +00:00
|
|
|
} else {
|
|
|
|
this.$('.graph_heatmap label').addClass('disabled');
|
2014-02-26 14:39:19 +00:00
|
|
|
this.$('.graph_main_content').removeClass('graph_pivot_mode').addClass('graph_chart_mode');
|
2014-01-17 14:43:03 +00:00
|
|
|
}
|
2014-01-16 14:00:58 +00:00
|
|
|
this.display_data();
|
|
|
|
},
|
|
|
|
|
|
|
|
set_heatmap_mode: function (mode) { // none, row, col, all
|
|
|
|
this.heatmap_mode = mode;
|
|
|
|
if (mode === 'none') {
|
|
|
|
this.$('.graph_heatmap label').removeClass('disabled');
|
|
|
|
this.$('.graph_heatmap label').removeClass('active');
|
|
|
|
}
|
2014-01-10 14:08:37 +00:00
|
|
|
this.display_data();
|
2014-01-10 11:35:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-21 14:30:34 +00:00
|
|
|
create_field_value: function (f) {
|
2014-01-22 16:07:07 +00:00
|
|
|
var field = (_.contains(f, ':')) ? f.split(':')[0] : f,
|
2014-07-25 12:35:42 +00:00
|
|
|
groupby_field = _.findWhere(this.groupby_fields, {field:field}),
|
2014-01-22 16:07:07 +00:00
|
|
|
string = groupby_field ? groupby_field.string : this.fields[field].string,
|
|
|
|
result = {field: f, string: string, type: this.fields[field].type };
|
2014-01-21 14:30:34 +00:00
|
|
|
|
2014-01-22 16:07:07 +00:00
|
|
|
if (groupby_field) {
|
|
|
|
result.filter = groupby_field.filter;
|
2014-01-22 13:34:52 +00:00
|
|
|
}
|
2014-01-22 16:07:07 +00:00
|
|
|
|
2014-01-21 14:30:34 +00:00
|
|
|
return result;
|
2014-01-10 11:35:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-21 14:30:34 +00:00
|
|
|
create_field_values: function (field_ids) {
|
2014-01-22 16:07:07 +00:00
|
|
|
return _.map(field_ids, this.proxy('create_field_value'));
|
2014-01-21 14:30:34 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
|
2014-01-22 16:07:07 +00:00
|
|
|
get_col_groupbys: function () {
|
|
|
|
return _.pluck(this.pivot.cols.groupby, 'field');
|
|
|
|
},
|
|
|
|
|
2014-08-21 07:03:12 +00:00
|
|
|
get_current_measures: function () {
|
|
|
|
return _.pluck(this.pivot.measures, 'field');
|
|
|
|
},
|
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// UI code
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
events: {
|
2014-01-16 14:00:58 +00:00
|
|
|
'click .graph_mode_selection label' : 'mode_selection',
|
2014-01-15 09:37:49 +00:00
|
|
|
'click .graph_measure_selection li' : 'measure_selection',
|
2014-01-16 14:00:58 +00:00
|
|
|
'click .graph_options_selection label' : 'option_selection',
|
|
|
|
'click .graph_heatmap label' : 'heatmap_mode_selection',
|
2014-01-15 09:37:49 +00:00
|
|
|
'click .web_graph_click' : 'header_cell_clicked',
|
|
|
|
'click a.field-selection' : 'field_selection',
|
2013-11-22 13:59:27 +00:00
|
|
|
},
|
|
|
|
|
2013-12-05 10:07:10 +00:00
|
|
|
mode_selection: function (event) {
|
|
|
|
event.preventDefault();
|
2014-01-24 15:16:35 +00:00
|
|
|
var mode = event.currentTarget.getAttribute('data-mode');
|
2014-01-10 14:08:37 +00:00
|
|
|
this.set_mode(mode);
|
2013-12-05 10:07:10 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
measure_selection: function (event) {
|
|
|
|
event.preventDefault();
|
2014-01-17 15:24:13 +00:00
|
|
|
event.stopPropagation();
|
2014-01-24 15:16:35 +00:00
|
|
|
var measure_field = event.target.getAttribute('data-choice');
|
2014-01-21 14:30:34 +00:00
|
|
|
var measure = {
|
|
|
|
field: measure_field,
|
|
|
|
type: this.fields[measure_field].type,
|
|
|
|
string: this.fields[measure_field].string
|
|
|
|
};
|
|
|
|
|
|
|
|
this.pivot.toggle_measure(measure).then(this.proxy('display_data'));
|
2014-01-17 14:43:03 +00:00
|
|
|
this.put_measure_checkmarks();
|
|
|
|
},
|
|
|
|
|
|
|
|
put_measure_checkmarks: function () {
|
|
|
|
var self = this,
|
|
|
|
measures_li = this.$('.graph_measure_selection a');
|
|
|
|
measures_li.removeClass('oe_selected');
|
|
|
|
_.each(this.measure_list, function (measure, index) {
|
|
|
|
if (_.findWhere(self.pivot.measures, measure)) {
|
|
|
|
measures_li.eq(index).addClass('oe_selected');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-12-05 10:07:10 +00:00
|
|
|
},
|
|
|
|
|
2013-12-12 10:55:33 +00:00
|
|
|
option_selection: function (event) {
|
2013-12-05 10:07:10 +00:00
|
|
|
event.preventDefault();
|
2014-01-24 15:16:35 +00:00
|
|
|
switch (event.currentTarget.getAttribute('data-choice')) {
|
2013-12-12 10:55:33 +00:00
|
|
|
case 'swap_axis':
|
2014-01-21 14:30:34 +00:00
|
|
|
this.swap_axis();
|
2013-12-05 10:07:10 +00:00
|
|
|
break;
|
2014-04-28 09:02:21 +00:00
|
|
|
case 'expand_all':
|
|
|
|
this.pivot.expand_all().then(this.proxy('display_data'));
|
|
|
|
break;
|
2013-12-05 10:07:10 +00:00
|
|
|
case 'update_values':
|
2014-01-21 14:30:34 +00:00
|
|
|
this.pivot.update_data().then(this.proxy('display_data'));
|
2013-12-05 10:07:10 +00:00
|
|
|
break;
|
2014-02-03 11:33:32 +00:00
|
|
|
case 'export_data':
|
|
|
|
this.export_xls();
|
|
|
|
break;
|
2013-12-05 10:07:10 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-01-16 14:00:58 +00:00
|
|
|
heatmap_mode_selection: function (event) {
|
|
|
|
event.preventDefault();
|
2014-01-24 15:16:35 +00:00
|
|
|
var mode = event.currentTarget.getAttribute('data-mode');
|
2014-01-16 14:00:58 +00:00
|
|
|
if (this.heatmap_mode === mode) {
|
|
|
|
event.stopPropagation();
|
|
|
|
this.set_heatmap_mode('none');
|
|
|
|
} else {
|
|
|
|
this.set_heatmap_mode(mode);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-12-18 13:40:40 +00:00
|
|
|
header_cell_clicked: function (event) {
|
2013-12-05 10:07:10 +00:00
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2014-01-24 15:16:35 +00:00
|
|
|
var id = event.target.getAttribute('data-id'),
|
2013-12-18 13:40:40 +00:00
|
|
|
header = this.pivot.get_header(id),
|
2013-12-24 11:22:24 +00:00
|
|
|
self = this;
|
2013-12-18 13:40:40 +00:00
|
|
|
|
2014-01-10 11:35:57 +00:00
|
|
|
if (header.expanded) {
|
2014-05-08 12:40:02 +00:00
|
|
|
if (header.root === this.pivot.rows) {
|
|
|
|
this.fold_row(header, event);
|
|
|
|
} else {
|
|
|
|
this.fold_col(header);
|
|
|
|
}
|
2014-02-10 09:17:16 +00:00
|
|
|
return;
|
2014-04-22 15:02:09 +00:00
|
|
|
}
|
2014-02-10 09:17:16 +00:00
|
|
|
if (header.path.length < header.root.groupby.length) {
|
2014-05-09 09:09:35 +00:00
|
|
|
this.$row_clicked = $(event.target).closest('tr');
|
2014-02-10 09:17:16 +00:00
|
|
|
this.expand(id);
|
|
|
|
return;
|
2014-04-22 15:02:09 +00:00
|
|
|
}
|
2014-07-25 12:35:42 +00:00
|
|
|
if (!this.groupby_fields.length) {
|
2014-02-10 09:17:16 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-01-24 12:59:44 +00:00
|
|
|
|
2014-07-25 12:35:42 +00:00
|
|
|
var fields = _.map(this.groupby_fields, function (field) {
|
2014-02-10 09:17:16 +00:00
|
|
|
return {id: field.field, value: field.string, type:self.fields[field.field.split(':')[0]].type};
|
|
|
|
});
|
|
|
|
if (this.dropdown) {
|
|
|
|
this.dropdown.remove();
|
2013-12-18 13:40:40 +00:00
|
|
|
}
|
2014-02-10 09:17:16 +00:00
|
|
|
this.dropdown = $(QWeb.render('field_selection', {fields:fields, header_id:id}));
|
|
|
|
$(event.target).after(this.dropdown);
|
2014-08-22 10:52:58 +00:00
|
|
|
this.dropdown.css({
|
|
|
|
position:'absolute',
|
|
|
|
left:event.originalEvent.layerX,
|
|
|
|
});
|
|
|
|
this.$('.field-selection').next('.dropdown-menu').first().toggle();
|
2013-12-05 10:07:10 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
field_selection: function (event) {
|
2014-01-24 15:16:35 +00:00
|
|
|
var id = event.target.getAttribute('data-id'),
|
|
|
|
field_id = event.target.getAttribute('data-field-id'),
|
2014-01-22 16:07:07 +00:00
|
|
|
interval,
|
|
|
|
groupby = this.create_field_value(field_id);
|
2013-12-05 10:07:10 +00:00
|
|
|
event.preventDefault();
|
2014-01-13 13:38:00 +00:00
|
|
|
if (this.fields[field_id].type === 'date' || this.fields[field_id].type === 'datetime') {
|
2014-01-24 15:28:21 +00:00
|
|
|
interval = event.target.getAttribute('data-interval');
|
2014-01-22 16:07:07 +00:00
|
|
|
groupby.field = groupby.field + ':' + interval;
|
2014-01-13 13:38:00 +00:00
|
|
|
}
|
2014-01-22 16:07:07 +00:00
|
|
|
this.expand(id, groupby);
|
2013-11-22 13:59:27 +00:00
|
|
|
},
|
|
|
|
|
2014-01-21 14:30:34 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Pivot Table integration
|
|
|
|
// ----------------------------------------------------------------------
|
2014-01-22 16:07:07 +00:00
|
|
|
expand: function (header_id, groupby) {
|
2014-01-21 14:30:34 +00:00
|
|
|
var self = this,
|
|
|
|
header = this.pivot.get_header(header_id),
|
2014-01-24 15:16:35 +00:00
|
|
|
update_groupby = !!groupby;
|
|
|
|
|
|
|
|
groupby = groupby || header.root.groupby[header.path.length];
|
2014-01-21 14:30:34 +00:00
|
|
|
|
|
|
|
this.pivot.expand(header_id, groupby).then(function () {
|
2014-05-09 09:09:35 +00:00
|
|
|
if (header.root === self.pivot.rows) {
|
|
|
|
// expanding rows can be done by only inserting in the dom
|
|
|
|
// console.log(event.target);
|
|
|
|
var rows = self.build_rows(header.children);
|
|
|
|
var doc_fragment = $(document.createDocumentFragment());
|
|
|
|
rows.map(function (row) {
|
2014-09-16 08:11:47 +00:00
|
|
|
doc_fragment.append(self.draw_row(row, 0));
|
2014-05-09 09:09:35 +00:00
|
|
|
});
|
|
|
|
self.$row_clicked.after(doc_fragment);
|
|
|
|
} else {
|
|
|
|
// expanding cols will redraw the full table
|
|
|
|
self.display_data();
|
|
|
|
}
|
2014-01-21 14:30:34 +00:00
|
|
|
if (update_groupby && self.graph_view) {
|
|
|
|
self.graph_view.register_groupby(self.pivot.rows.groupby, self.pivot.cols.groupby);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
2014-05-08 12:40:02 +00:00
|
|
|
fold_row: function (header, event) {
|
|
|
|
var rows_before = this.pivot.rows.headers.length,
|
|
|
|
update_groupby = this.pivot.fold(header),
|
|
|
|
rows_after = this.pivot.rows.headers.length,
|
|
|
|
rows_removed = rows_before - rows_after;
|
|
|
|
|
|
|
|
if (rows_after === 1) {
|
|
|
|
// probably faster to redraw the unique row instead of removing everything
|
|
|
|
this.display_data();
|
|
|
|
} else {
|
|
|
|
var $row = $(event.target).parent().parent();
|
|
|
|
$row.nextAll().slice(0,rows_removed).remove();
|
|
|
|
}
|
|
|
|
if (update_groupby && this.graph_view) {
|
|
|
|
this.graph_view.register_groupby(this.pivot.rows.groupby, this.pivot.cols.groupby);
|
|
|
|
}
|
|
|
|
},
|
2014-01-21 14:30:34 +00:00
|
|
|
|
2014-05-08 12:40:02 +00:00
|
|
|
fold_col: function (header) {
|
|
|
|
var update_groupby = this.pivot.fold(header);
|
|
|
|
|
2014-01-21 14:30:34 +00:00
|
|
|
this.display_data();
|
|
|
|
if (update_groupby && this.graph_view) {
|
|
|
|
this.graph_view.register_groupby(this.pivot.rows.groupby, this.pivot.cols.groupby);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
swap_axis: function () {
|
|
|
|
this.pivot.swap_axis();
|
|
|
|
this.display_data();
|
|
|
|
this.graph_view.register_groupby(this.pivot.rows.groupby, this.pivot.cols.groupby);
|
|
|
|
},
|
|
|
|
|
2014-02-04 11:31:35 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Convert Pivot data structure into table structure :
|
|
|
|
// compute rows, cols, colors, cell width, cell height, ...
|
|
|
|
// ----------------------------------------------------------------------
|
2014-04-04 08:55:04 +00:00
|
|
|
build_table: function(raw) {
|
2014-02-04 11:31:35 +00:00
|
|
|
return {
|
|
|
|
headers: this.build_headers(),
|
|
|
|
measure_row: this.build_measure_row(),
|
2014-05-09 09:09:35 +00:00
|
|
|
rows: this.build_rows(this.pivot.rows.headers,raw),
|
2014-02-05 13:14:06 +00:00
|
|
|
nbr_measures: this.pivot.measures.length,
|
|
|
|
title: this.title,
|
2014-02-04 11:31:35 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
build_headers: function () {
|
|
|
|
var pivot = this.pivot,
|
|
|
|
nbr_measures = pivot.measures.length,
|
2014-07-25 12:35:42 +00:00
|
|
|
height = _.max(_.map(pivot.cols.headers, function(g) {return g.path.length;})) + 1,
|
2014-02-04 11:31:35 +00:00
|
|
|
rows = [];
|
|
|
|
|
|
|
|
_.each(pivot.cols.headers, function (col) {
|
|
|
|
var cell_width = nbr_measures * (col.expanded ? pivot.get_ancestor_leaves(col).length : 1),
|
2014-07-25 12:35:42 +00:00
|
|
|
cell_height = col.expanded ? 1 : height - col.path.length,
|
2014-02-04 11:31:35 +00:00
|
|
|
cell = {width: cell_width, height: cell_height, title: col.title, id: col.id, expanded: col.expanded};
|
2014-07-25 12:35:42 +00:00
|
|
|
if (rows[col.path.length]) {
|
|
|
|
rows[col.path.length].push(cell);
|
2014-02-04 11:31:35 +00:00
|
|
|
} else {
|
2014-07-25 12:35:42 +00:00
|
|
|
rows[col.path.length] = [cell];
|
2014-02-04 11:31:35 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (pivot.get_cols_leaves().length > 1) {
|
2014-07-25 12:35:42 +00:00
|
|
|
rows[0].push({width: nbr_measures, height: height, title: ' ', id: pivot.main_col().id });
|
2014-02-04 11:31:35 +00:00
|
|
|
}
|
|
|
|
if (pivot.cols.headers.length === 1) {
|
2014-02-05 13:14:06 +00:00
|
|
|
rows = [[{width: nbr_measures, height: 1, title: _t('Total'), id: pivot.main_col().id, expanded: false}]];
|
2014-02-04 11:31:35 +00:00
|
|
|
}
|
|
|
|
return rows;
|
|
|
|
},
|
|
|
|
|
|
|
|
build_measure_row: function () {
|
|
|
|
var nbr_leaves = this.pivot.get_cols_leaves().length,
|
|
|
|
nbr_cols = nbr_leaves + ((nbr_leaves > 1) ? 1 : 0),
|
|
|
|
result = [],
|
|
|
|
add_total = this.pivot.get_cols_leaves().length > 1,
|
|
|
|
i, m;
|
|
|
|
for (i = 0; i < nbr_cols; i++) {
|
|
|
|
for (m = 0; m < this.pivot.measures.length; m++) {
|
|
|
|
result.push({
|
|
|
|
text:this.pivot.measures[m].string,
|
|
|
|
is_bold: add_total && (i === nbr_cols - 1)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
2014-04-04 08:55:04 +00:00
|
|
|
make_cell: function (row, col, value, index, raw) {
|
2014-04-04 15:17:39 +00:00
|
|
|
var formatted_value = raw && !_.isUndefined(value) ? value : openerp.web.format_value(value, {type:this.pivot.measures[index].type}),
|
2014-02-05 09:58:08 +00:00
|
|
|
cell = {value:formatted_value};
|
|
|
|
|
2014-02-04 11:31:35 +00:00
|
|
|
if (this.heatmap_mode === 'none') { return cell; }
|
|
|
|
var total = (this.heatmap_mode === 'both') ? this.pivot.get_total()[index]
|
|
|
|
: (this.heatmap_mode === 'row') ? this.pivot.get_total(row)[index]
|
|
|
|
: this.pivot.get_total(col)[index];
|
2014-02-05 09:58:08 +00:00
|
|
|
var color = Math.floor(90 + 165*(total - Math.abs(value))/total);
|
2014-02-04 11:31:35 +00:00
|
|
|
if (color < 255) {
|
|
|
|
cell.color = color;
|
|
|
|
}
|
|
|
|
return cell;
|
|
|
|
},
|
|
|
|
|
2014-05-09 09:09:35 +00:00
|
|
|
build_rows: function (headers, raw) {
|
2014-02-04 11:31:35 +00:00
|
|
|
var self = this,
|
|
|
|
pivot = this.pivot,
|
2014-04-30 07:47:33 +00:00
|
|
|
m, i, j, k, cell, row;
|
|
|
|
|
|
|
|
var rows = [];
|
|
|
|
var cells, pivot_cells, values;
|
|
|
|
|
2014-05-09 09:09:35 +00:00
|
|
|
var nbr_of_rows = headers.length;
|
2014-04-30 07:47:33 +00:00
|
|
|
var col_headers = pivot.get_cols_leaves();
|
|
|
|
|
|
|
|
for (i = 0; i < nbr_of_rows; i++) {
|
2014-05-09 09:09:35 +00:00
|
|
|
row = headers[i];
|
2014-04-30 07:47:33 +00:00
|
|
|
cells = [];
|
|
|
|
pivot_cells = [];
|
|
|
|
for (j = 0; j < pivot.cells.length; j++) {
|
|
|
|
if (pivot.cells[j].x == row.id || pivot.cells[j].y == row.id) {
|
|
|
|
pivot_cells.push(pivot.cells[j]);
|
|
|
|
}
|
2014-04-09 09:39:20 +00:00
|
|
|
}
|
2014-04-30 07:47:33 +00:00
|
|
|
|
|
|
|
for (j = 0; j < col_headers.length; j++) {
|
|
|
|
values = undefined;
|
|
|
|
for (k = 0; k < pivot_cells.length; k++) {
|
|
|
|
if (pivot_cells[k].x == col_headers[j].id || pivot_cells[k].y == col_headers[j].id) {
|
|
|
|
values = pivot_cells[k].values;
|
2014-04-09 09:39:20 +00:00
|
|
|
break;
|
2014-04-30 07:47:33 +00:00
|
|
|
}
|
2014-04-09 09:39:20 +00:00
|
|
|
}
|
|
|
|
if (!values) { values = new Array(pivot.measures.length);}
|
2014-02-04 11:31:35 +00:00
|
|
|
for (m = 0; m < pivot.measures.length; m++) {
|
2014-04-30 07:47:33 +00:00
|
|
|
cells.push(self.make_cell(row,col_headers[j],values[m], m, raw));
|
2014-02-04 11:31:35 +00:00
|
|
|
}
|
2014-04-30 07:47:33 +00:00
|
|
|
}
|
|
|
|
if (col_headers.length > 1) {
|
2014-02-04 11:31:35 +00:00
|
|
|
var totals = pivot.get_total(row);
|
|
|
|
for (m = 0; m < pivot.measures.length; m++) {
|
2014-04-30 07:47:33 +00:00
|
|
|
cell = self.make_cell(row, pivot.cols.headers[0], totals[m], m, raw);
|
2014-02-05 09:58:08 +00:00
|
|
|
cell.is_bold = 'true';
|
|
|
|
cells.push(cell);
|
2014-02-04 11:31:35 +00:00
|
|
|
}
|
|
|
|
}
|
2014-04-30 07:47:33 +00:00
|
|
|
rows.push({
|
2014-02-04 11:31:35 +00:00
|
|
|
id: row.id,
|
|
|
|
indent: row.path.length,
|
|
|
|
title: row.title,
|
|
|
|
expanded: row.expanded,
|
|
|
|
cells: cells,
|
2014-04-30 07:47:33 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return rows;
|
2014-02-04 11:31:35 +00:00
|
|
|
},
|
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Main display method
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
display_data: function () {
|
2014-04-22 15:03:16 +00:00
|
|
|
var scroll = $(window).scrollTop();
|
2014-01-15 09:37:49 +00:00
|
|
|
this.$('.graph_main_content svg').remove();
|
|
|
|
this.$('.graph_main_content div').remove();
|
|
|
|
this.table.empty();
|
2014-02-03 11:33:32 +00:00
|
|
|
this.table.toggleClass('heatmap', this.heatmap_mode !== 'none');
|
2014-04-22 14:59:55 +00:00
|
|
|
this.$('.graph_options_selection label').last().toggleClass('disabled', this.pivot.no_data);
|
2014-01-21 14:30:34 +00:00
|
|
|
this.width = this.$el.width();
|
|
|
|
this.height = Math.min(Math.max(document.documentElement.clientHeight - 116 - 60, 250), Math.round(0.8*this.$el.width()));
|
2014-01-15 09:37:49 +00:00
|
|
|
|
2014-01-24 15:16:35 +00:00
|
|
|
this.$('.graph_header').toggle(this.visible_ui);
|
2014-01-15 09:37:49 +00:00
|
|
|
if (this.pivot.no_data) {
|
|
|
|
this.$('.graph_main_content').append($(QWeb.render('graph_no_data')));
|
|
|
|
} else {
|
2014-01-16 14:00:58 +00:00
|
|
|
if (this.mode === 'pivot') {
|
2014-01-15 09:37:49 +00:00
|
|
|
this.draw_table();
|
2014-04-22 15:03:16 +00:00
|
|
|
$(window).scrollTop(scroll);
|
2014-01-15 09:37:49 +00:00
|
|
|
} else {
|
2014-01-24 15:16:35 +00:00
|
|
|
this.$('.graph_main_content').append($('<div><svg>'));
|
2014-01-15 09:37:49 +00:00
|
|
|
this.svg = this.$('.graph_main_content svg')[0];
|
|
|
|
this[this.mode]();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Drawing the table
|
|
|
|
// ----------------------------------------------------------------------
|
2013-11-22 07:43:27 +00:00
|
|
|
draw_table: function () {
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
var custom_gbs = this.graph_view.get_custom_filter_groupbys(),
|
|
|
|
frozen_rows = custom_gbs.groupby.length,
|
|
|
|
frozen_cols = custom_gbs.col_groupby.length;
|
|
|
|
|
2014-02-04 11:31:35 +00:00
|
|
|
var table = this.build_table();
|
2014-04-30 07:47:33 +00:00
|
|
|
var doc_fragment = $(document.createDocumentFragment());
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
this.draw_headers(table.headers, doc_fragment, frozen_cols);
|
2014-04-30 07:47:33 +00:00
|
|
|
this.draw_measure_row(table.measure_row, doc_fragment);
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
this.draw_rows(table.rows, doc_fragment, frozen_rows);
|
2014-04-30 07:47:33 +00:00
|
|
|
this.table.append(doc_fragment);
|
2014-02-04 11:31:35 +00:00
|
|
|
},
|
|
|
|
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
make_header_cell: function (header, frozen) {
|
2014-02-04 11:31:35 +00:00
|
|
|
var cell = (_.has(header, 'cells') ? $('<td>') : $('<th>'))
|
|
|
|
.addClass('graph_border')
|
|
|
|
.attr('rowspan', header.height)
|
|
|
|
.attr('colspan', header.width);
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
var $content = $('<span>').attr('href','#')
|
2014-02-04 11:31:35 +00:00
|
|
|
.text(' ' + (header.title || _t('Undefined')))
|
2014-05-08 11:00:27 +00:00
|
|
|
.css('margin-left', header.indent*30 + 'px')
|
2014-02-04 11:31:35 +00:00
|
|
|
.attr('data-id', header.id);
|
|
|
|
if (_.has(header, 'expanded')) {
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
if (('indent' in header) && header.indent >= frozen) {
|
|
|
|
$content.addClass(header.expanded ? 'fa fa-minus-square' : 'fa fa-plus-square');
|
|
|
|
$content.addClass('web_graph_click');
|
|
|
|
}
|
|
|
|
if (!('indent' in header) && header.lvl >= frozen) {
|
|
|
|
$content.addClass(header.expanded ? 'fa fa-minus-square' : 'fa fa-plus-square');
|
|
|
|
$content.addClass('web_graph_click');
|
|
|
|
}
|
2013-11-22 09:43:22 +00:00
|
|
|
} else {
|
2014-05-08 11:13:11 +00:00
|
|
|
$content.css('font-weight', 'bold');
|
2013-11-22 09:43:22 +00:00
|
|
|
}
|
2014-05-08 11:13:11 +00:00
|
|
|
return cell.append($content);
|
2013-11-18 15:26:20 +00:00
|
|
|
},
|
|
|
|
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
draw_headers: function (headers, doc_fragment, frozen_cols) {
|
2014-02-04 11:31:35 +00:00
|
|
|
var make_cell = this.make_header_cell,
|
2014-05-08 11:13:11 +00:00
|
|
|
$empty_cell = $('<th>').attr('rowspan', headers.length),
|
|
|
|
$thead = $('<thead>');
|
2014-01-03 14:22:15 +00:00
|
|
|
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
_.each(headers, function (row, lvl) {
|
2014-05-08 11:13:11 +00:00
|
|
|
var $row = $('<tr>');
|
2014-02-04 11:31:35 +00:00
|
|
|
_.each(row, function (header) {
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
header.lvl = lvl;
|
|
|
|
$row.append(make_cell(header, frozen_cols));
|
2014-02-04 11:31:35 +00:00
|
|
|
});
|
2014-05-08 11:13:11 +00:00
|
|
|
$thead.append($row);
|
2014-01-03 14:22:15 +00:00
|
|
|
});
|
2014-05-08 11:13:11 +00:00
|
|
|
$thead.children(':first').prepend($empty_cell);
|
|
|
|
doc_fragment.append($thead);
|
|
|
|
this.$thead = $thead;
|
2014-02-04 11:31:35 +00:00
|
|
|
},
|
|
|
|
|
2014-05-09 09:09:35 +00:00
|
|
|
draw_measure_row: function (measure_row) {
|
2014-05-08 11:13:11 +00:00
|
|
|
var $row = $('<tr>').append('<th>');
|
2014-02-04 11:31:35 +00:00
|
|
|
_.each(measure_row, function (cell) {
|
2014-05-08 11:13:11 +00:00
|
|
|
var $cell = $('<th>').addClass('measure_row').text(cell.text);
|
|
|
|
if (cell.is_bold) {$cell.css('font-weight', 'bold');}
|
|
|
|
$row.append($cell);
|
2014-02-04 11:31:35 +00:00
|
|
|
});
|
2014-05-08 11:13:11 +00:00
|
|
|
this.$thead.append($row);
|
2014-02-04 11:31:35 +00:00
|
|
|
},
|
|
|
|
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
draw_row: function (row, frozen_rows) {
|
2014-05-09 09:09:35 +00:00
|
|
|
var $row = $('<tr>')
|
|
|
|
.attr('data-indent', row.indent)
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
.append(this.make_header_cell(row, frozen_rows));
|
2014-04-30 07:47:33 +00:00
|
|
|
|
2014-05-09 09:09:35 +00:00
|
|
|
var cells_length = row.cells.length;
|
|
|
|
var cells_list = [];
|
|
|
|
var cell, hcell;
|
|
|
|
|
|
|
|
for (var j = 0; j < cells_length; j++) {
|
|
|
|
cell = row.cells[j];
|
|
|
|
hcell = '<td';
|
|
|
|
if (cell.is_bold || cell.color) {
|
|
|
|
hcell += ' style="';
|
|
|
|
if (cell.is_bold) hcell += 'font-weight: bold;';
|
|
|
|
if (cell.color) hcell += 'background-color:' + $.Color(255, cell.color, cell.color) + ';';
|
|
|
|
hcell += '"';
|
|
|
|
}
|
|
|
|
hcell += '>' + cell.value + '</td>';
|
|
|
|
cells_list[j] = hcell;
|
|
|
|
}
|
|
|
|
return $row.append(cells_list.join(''));
|
|
|
|
},
|
|
|
|
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
draw_rows: function (rows, doc_fragment, frozen_rows) {
|
2014-05-09 09:09:35 +00:00
|
|
|
var rows_length = rows.length,
|
|
|
|
$tbody = $('<tbody>');
|
2014-05-08 11:27:23 +00:00
|
|
|
|
|
|
|
doc_fragment.append($tbody);
|
2014-05-09 09:09:35 +00:00
|
|
|
for (var i = 0; i < rows_length; i++) {
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
$tbody.append(this.draw_row(rows[i], frozen_rows));
|
2014-04-30 07:47:33 +00:00
|
|
|
}
|
2013-11-27 14:45:05 +00:00
|
|
|
},
|
2013-12-18 13:40:40 +00:00
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Drawing charts code
|
|
|
|
// ----------------------------------------------------------------------
|
2014-01-10 14:08:37 +00:00
|
|
|
bar: function () {
|
2013-12-18 15:07:56 +00:00
|
|
|
var self = this,
|
|
|
|
dim_x = this.pivot.rows.groupby.length,
|
|
|
|
dim_y = this.pivot.cols.groupby.length,
|
2014-01-17 14:50:56 +00:00
|
|
|
show_controls = (this.width > 400 && this.height > 300 && dim_x + dim_y >=2),
|
2013-12-18 15:49:49 +00:00
|
|
|
data;
|
2013-12-18 15:07:56 +00:00
|
|
|
|
2014-01-15 09:37:49 +00:00
|
|
|
// No groupby
|
2013-12-18 15:07:56 +00:00
|
|
|
if ((dim_x === 0) && (dim_y === 0)) {
|
2014-01-28 11:10:22 +00:00
|
|
|
data = [{key: _t('Total'), values:[{
|
|
|
|
x: _t('Total'),
|
2014-04-22 15:04:01 +00:00
|
|
|
y: this.pivot.get_total()[0],
|
2013-12-18 15:07:56 +00:00
|
|
|
}]}];
|
2014-01-15 09:37:49 +00:00
|
|
|
// Only column groupbys
|
2013-12-18 15:07:56 +00:00
|
|
|
} else if ((dim_x === 0) && (dim_y >= 1)){
|
2014-01-16 14:00:58 +00:00
|
|
|
data = _.map(this.pivot.get_cols_with_depth(1), function (header) {
|
2013-12-18 15:07:56 +00:00
|
|
|
return {
|
|
|
|
key: header.title,
|
2014-01-16 14:00:58 +00:00
|
|
|
values: [{x:header.title, y: self.pivot.get_total(header)[0]}]
|
2013-12-18 15:07:56 +00:00
|
|
|
};
|
|
|
|
});
|
2014-01-15 09:37:49 +00:00
|
|
|
// Just 1 row groupby
|
2013-12-18 15:07:56 +00:00
|
|
|
} else if ((dim_x === 1) && (dim_y === 0)) {
|
2014-01-10 11:35:57 +00:00
|
|
|
data = _.map(this.pivot.main_row().children, function (pt) {
|
2014-04-22 15:04:01 +00:00
|
|
|
var value = self.pivot.get_total(pt)[0],
|
2014-01-28 11:10:22 +00:00
|
|
|
title = (pt.title !== undefined) ? pt.title : _t('Undefined');
|
2013-12-18 15:49:49 +00:00
|
|
|
return {x: title, y: value};
|
2013-12-18 15:07:56 +00:00
|
|
|
});
|
2014-01-10 11:35:57 +00:00
|
|
|
data = [{key: self.pivot.measures[0].string, values:data}];
|
2014-01-15 09:37:49 +00:00
|
|
|
// 1 row groupby and some col groupbys
|
2013-12-18 15:07:56 +00:00
|
|
|
} else if ((dim_x === 1) && (dim_y >= 1)) {
|
2014-01-10 11:35:57 +00:00
|
|
|
data = _.map(this.pivot.get_cols_with_depth(1), function (colhdr) {
|
|
|
|
var values = _.map(self.pivot.get_rows_with_depth(1), function (header) {
|
2013-12-18 15:07:56 +00:00
|
|
|
return {
|
2014-01-28 11:10:22 +00:00
|
|
|
x: header.title || _t('Undefined'),
|
2014-01-13 15:23:16 +00:00
|
|
|
y: self.pivot.get_values(header.id, colhdr.id)[0] || 0
|
2013-12-18 15:07:56 +00:00
|
|
|
};
|
|
|
|
});
|
2014-01-28 11:10:22 +00:00
|
|
|
return {key: colhdr.title || _t('Undefined'), values: values};
|
2013-12-18 15:07:56 +00:00
|
|
|
});
|
2014-01-15 09:37:49 +00:00
|
|
|
// At least two row groupby
|
2013-12-18 15:07:56 +00:00
|
|
|
} else {
|
2014-01-10 11:35:57 +00:00
|
|
|
var keys = _.uniq(_.map(this.pivot.get_rows_with_depth(2), function (hdr) {
|
2014-01-28 11:10:22 +00:00
|
|
|
return hdr.title || _t('Undefined');
|
2013-12-18 15:07:56 +00:00
|
|
|
}));
|
|
|
|
data = _.map(keys, function (key) {
|
2014-01-10 11:35:57 +00:00
|
|
|
var values = _.map(self.pivot.get_rows_with_depth(1), function (hdr) {
|
2013-12-18 15:07:56 +00:00
|
|
|
var subhdr = _.find(hdr.children, function (child) {
|
2014-01-28 11:10:22 +00:00
|
|
|
return ((child.title === key) || ((child.title === undefined) && (key === _t('Undefined'))));
|
2013-12-18 15:07:56 +00:00
|
|
|
});
|
|
|
|
return {
|
2014-01-28 11:10:22 +00:00
|
|
|
x: hdr.title || _t('Undefined'),
|
2014-01-03 14:22:15 +00:00
|
|
|
y: (subhdr) ? self.pivot.get_total(subhdr)[0] : 0
|
2013-12-18 15:07:56 +00:00
|
|
|
};
|
|
|
|
});
|
|
|
|
return {key:key, values: values};
|
|
|
|
});
|
|
|
|
}
|
2013-12-18 15:49:49 +00:00
|
|
|
|
|
|
|
nv.addGraph(function () {
|
|
|
|
var chart = nv.models.multiBarChart()
|
2013-12-27 14:28:14 +00:00
|
|
|
.reduceXTicks(false)
|
2013-12-20 11:02:43 +00:00
|
|
|
.stacked(self.bar_ui === 'stack')
|
2014-01-17 14:50:56 +00:00
|
|
|
.showControls(show_controls);
|
2013-12-18 15:49:49 +00:00
|
|
|
|
2013-12-27 15:07:19 +00:00
|
|
|
if (self.width / data[0].values.length < 80) {
|
2013-12-27 14:28:14 +00:00
|
|
|
chart.rotateLabels(-15);
|
|
|
|
chart.reduceXTicks(true);
|
|
|
|
chart.margin({bottom:40});
|
|
|
|
}
|
2013-12-24 14:09:42 +00:00
|
|
|
|
2013-12-18 15:49:49 +00:00
|
|
|
d3.select(self.svg)
|
|
|
|
.datum(data)
|
|
|
|
.attr('width', self.width)
|
|
|
|
.attr('height', self.height)
|
|
|
|
.call(chart);
|
|
|
|
|
|
|
|
nv.utils.windowResize(chart.update);
|
|
|
|
return chart;
|
|
|
|
});
|
|
|
|
|
2013-12-18 15:07:56 +00:00
|
|
|
},
|
|
|
|
|
2014-01-10 14:08:37 +00:00
|
|
|
line: function () {
|
2013-12-18 15:07:56 +00:00
|
|
|
var self = this,
|
|
|
|
dim_x = this.pivot.rows.groupby.length,
|
|
|
|
dim_y = this.pivot.cols.groupby.length;
|
|
|
|
|
2014-04-22 15:04:41 +00:00
|
|
|
var rows = this.pivot.get_rows_with_depth(dim_x),
|
|
|
|
labels = _.pluck(rows, 'title');
|
|
|
|
|
2013-12-18 15:07:56 +00:00
|
|
|
var data = _.map(this.pivot.get_cols_leaves(), function (col) {
|
2014-04-22 15:04:41 +00:00
|
|
|
var values = _.map(rows, function (row, index) {
|
|
|
|
return {x: index, y: self.pivot.get_values(row.id,col.id)[0] || 0};
|
2013-12-18 15:07:56 +00:00
|
|
|
});
|
|
|
|
var title = _.map(col.path, function (p) {
|
2014-01-28 11:10:22 +00:00
|
|
|
return p || _t('Undefined');
|
2013-12-18 15:07:56 +00:00
|
|
|
}).join('/');
|
|
|
|
if (dim_y === 0) {
|
2014-01-10 11:35:57 +00:00
|
|
|
title = self.pivot.measures[0].string;
|
2013-12-18 15:07:56 +00:00
|
|
|
}
|
|
|
|
return {values: values, key: title};
|
|
|
|
});
|
|
|
|
|
|
|
|
nv.addGraph(function () {
|
|
|
|
var chart = nv.models.lineChart()
|
2014-04-22 15:04:01 +00:00
|
|
|
.x(function (d,u) { return u; });
|
2013-12-18 15:07:56 +00:00
|
|
|
|
2014-04-22 15:04:41 +00:00
|
|
|
chart.xAxis.tickFormat(function (d,u) {return labels[d];});
|
2013-12-18 15:07:56 +00:00
|
|
|
|
|
|
|
d3.select(self.svg)
|
|
|
|
.attr('width', self.width)
|
|
|
|
.attr('height', self.height)
|
|
|
|
.datum(data)
|
|
|
|
.call(chart);
|
|
|
|
|
|
|
|
return chart;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2014-01-10 14:08:37 +00:00
|
|
|
pie: function () {
|
2013-12-18 15:07:56 +00:00
|
|
|
var self = this,
|
2013-12-24 11:22:24 +00:00
|
|
|
dim_x = this.pivot.rows.groupby.length;
|
2013-12-18 15:07:56 +00:00
|
|
|
var data = _.map(this.pivot.get_rows_leaves(), function (row) {
|
|
|
|
var title = _.map(row.path, function (p) {
|
2014-01-28 11:10:22 +00:00
|
|
|
return p || _t('Undefined');
|
2013-12-18 15:07:56 +00:00
|
|
|
}).join('/');
|
|
|
|
if (dim_x === 0) {
|
|
|
|
title = self.measure_label;
|
|
|
|
}
|
2014-04-22 15:04:01 +00:00
|
|
|
return {x: title, y: self.pivot.get_total(row)[0]};
|
2013-12-18 15:07:56 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
nv.addGraph(function () {
|
|
|
|
var chart = nv.models.pieChart()
|
|
|
|
.width(self.width)
|
2014-04-22 15:04:01 +00:00
|
|
|
.height(self.height)
|
|
|
|
.color(d3.scale.category10().range());
|
2013-12-18 15:07:56 +00:00
|
|
|
|
|
|
|
d3.select(self.svg)
|
|
|
|
.datum(data)
|
|
|
|
.transition().duration(1200)
|
|
|
|
.attr('width', self.width)
|
|
|
|
.attr('height', self.height)
|
|
|
|
.call(chart);
|
|
|
|
|
|
|
|
nv.utils.windowResize(chart.update);
|
|
|
|
return chart;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2014-02-03 11:33:32 +00:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Controller stuff...
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
export_xls: function() {
|
2014-02-05 09:58:08 +00:00
|
|
|
var c = openerp.webclient.crashmanager;
|
2014-02-04 11:31:35 +00:00
|
|
|
openerp.web.blockUI();
|
2014-02-03 11:33:32 +00:00
|
|
|
this.session.get_file({
|
|
|
|
url: '/web_graph/export_xls',
|
2014-04-04 08:55:04 +00:00
|
|
|
data: {data: JSON.stringify(this.build_table(true))},
|
2014-02-03 11:33:32 +00:00
|
|
|
complete: openerp.web.unblockUI,
|
2014-02-05 09:58:08 +00:00
|
|
|
error: c.rpc_error.bind(c)
|
2014-02-03 11:33:32 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-11-22 07:43:27 +00:00
|
|
|
});
|
2014-01-21 14:30:34 +00:00
|
|
|
|
|
|
|
// Utility function: returns true if the beginning of array2 is array1 and
|
|
|
|
// if array1 is not array2
|
|
|
|
function is_strict_beginning_of (array1, array2) {
|
|
|
|
if (array1.length >= array2.length) { return false; }
|
|
|
|
var result = true;
|
|
|
|
for (var i = 0; i < array1.length; i++) {
|
2014-01-24 15:16:35 +00:00
|
|
|
if (!_.isEqual(array1[i], array2[i])) { return false;}
|
2014-01-21 14:30:34 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2014-01-14 12:51:42 +00:00
|
|
|
})();
|