odoo.define('web.PivotRenderer', function (require) { "use strict"; var AbstractRenderer = require('web.AbstractRenderer'); var core = require('web.core'); var field_utils = require('web.field_utils'); var config = require('web.config'); var QWeb = core.qweb; var _t = core._t; var PivotRenderer = AbstractRenderer.extend({ tagName: 'table', className: 'table-hover table-sm table-bordered', events: _.extend({}, AbstractRenderer.prototype.events, { 'hover td': '_onTdHover', }), /** * @overide * * @param {Widget} parent * @param {Object} state * @param {Object} params */ init: function (parent, state, params) { this._super.apply(this, arguments); this.compare = state.compare; this.fieldWidgets = params.widgets || {}; this.timeRangeDescription = params.timeRangeDescription; this.comparisonTimeRangeDescription = params.comparisonTimeRangeDescription; this.paddingLeftHeaderTabWidth = config.device.isMobile ? 5 : 30; }, //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- /** * @override * @param {Object} state * @param {Object} params */ updateState: function (state, params) { if (params.context !== undefined) { var timeRangeMenuData = params.context.timeRangeMenuData; if (timeRangeMenuData) { this.timeRangeDescription = timeRangeMenuData.timeRangeDescription; this.comparisonTimeRangeDescription = timeRangeMenuData.comparisonTimeRangeDescription; } else { this.timeRangeDescription = undefined; this.comparisonTimeRangeDescription = undefined; } } this.compare = state.compare; return this._super.apply(this, arguments); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * Used to determine whether or not to display the no content helper. * * @private * @returns {boolean} */ _hasContent: function () { return this.state.has_data && this.state.measures.length; }, /** * @override * @private * @returns {Promise} */ _render: function () { if (!this._hasContent()) { // display the nocontent helper this._replaceElement(QWeb.render('PivotView.nodata')); return this._super.apply(this, arguments); } if (!this.$el.is('table')) { // coming from the no content helper, so the root element has to be // re-rendered before rendering and appending its content this.renderElement(); } var $fragment = $(document.createDocumentFragment()); var $table = $('
| ') .text(cell.title) .attr('rowspan', cell.height + 1) .attr('colspan', cell.width); } else { $cell = $(' | ') .text(cell.title) .attr('rowspan', cell.height) .attr('colspan', (cell.width && self.compare) ? 3 * cell.width : cell.width || (self.compare && cell.measure && 3)); } if (i > 0) { $cell.attr('title', groupbyLabels[i-1]); } if (cell.expanded !== undefined) { $cell.addClass(cell.expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed'); $cell.data('id', cell.id); } if (cell.measure) { measure = cell.measure; measureCellIds.push(cell.id); $cell.addClass('o_pivot_measure_row text-muted') .text(this.state.fields[measure].string); $cell.data('id', cell.id).data('measure', measure); if (cell.id === this.state.sortedColumn.id && measure === this.state.sortedColumn.measure) { $cell.addClass('o_pivot_measure_row_sorted_' + this.state.sortedColumn.order); if (this.state.sortedColumn.order == 'asc') { $cell.attr('aria-sorted', 'ascending'); } else { $cell.attr('aria-sorted', 'descending'); } } } $row.append($cell); $cell.toggleClass('d-none d-md-table-cell', (cell.expanded !== undefined) || (cell.measure !== undefined && j < headers[i].length - this.state.measures.length)); if (cell.height > 1) { $cell.css('padding', 0); } } $thead.append($row); } if (this.compare) { var colLabels = [this.timeRangeDescription, this.comparisonTimeRangeDescription, _t('Variation')]; var dataTypes = ['data', 'comparisonData', 'variation']; $row = $(' | ||
|---|---|---|---|
| ') .addClass('o_pivot_measure_row text-muted') .data('data_type', dataType) .data('id', id) .data('measure', measure) .text(colLabels[i % 3]) .attr('rowspan', 1) .attr('colspan', 1); if (dataType === this.state.sortedColumn.dataType && id === this.state.sortedColumn.id && measure === this.state.sortedColumn.measure) { $cell.addClass('o_pivot_measure_row_sorted_' + this.state.sortedColumn.order); if (this.state.sortedColumn.order === 'asc') { $cell.attr('aria-sorted', 'ascending'); } else { $cell.attr('aria-sorted', 'descending'); } } $row.append($cell); } $thead.append($row); } }, /** * @private * @param {jQueryElement} $tbody * @param {jQueryElement} rows */ _renderRows: function ($tbody, rows) { var self = this; var i, j, value, measure, name, formatter, $row, $cell, $header; var nbrMeasures = this.state.measures.length; var length = rows[0].values.length; var shouldDisplayTotal = this.state.mainColWidth > 1; var groupbyLabels = _.map(this.state.rowGroupBys, function (gb) { return self.state.fields[gb.split(':')[0]].string; }); var measureTypes = this.state.measures.map(function (name) { var type = self.state.fields[name].type; return type === 'many2one' ? 'integer' : type; }); for (i = 0; i < rows.length; i++) { $row = $(' | |||
| ') .text(rows[i].title) .data('id', rows[i].id) .css('padding-left', (5 + rows[i].indent * self.paddingLeftHeaderTabWidth) + 'px') .addClass(rows[i].expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed'); if (rows[i].indent > 0) $header.attr('title', groupbyLabels[rows[i].indent - 1]); $header.appendTo($row); for (j = 0; j < length; j++) { value = rows[i].values[j]; name = this.state.measures[j % nbrMeasures]; formatter = field_utils.format[this.fieldWidgets[name] || measureTypes[j % nbrMeasures]]; measure = this.state.fields[name]; if (this.compare) { if (value instanceof Object) { for (var origin in value) { $cell = $(' | ')
.data('id', rows[i].id)
.data('col_id', rows[i].col_ids[Math.floor(j / nbrMeasures)])
.data('type' , origin)
.toggleClass('o_empty', false)
.addClass('o_pivot_cell_value text-right');
if (origin === 'data') {
$cell.append($(' ', {class: 'o_value'}).html(formatter(
value[origin],
measure
)));
} else if (origin === 'comparisonData') {
$cell.append($(' ', {class: 'o_comparison_value'}).html(formatter(
value[origin],
measure
)));
} else {
$cell.append($(' ', {class: 'o_variation' + value[origin].signClass}).html(
field_utils.format.percentage(
value[origin].magnitude,
measure
)
));
}
if (((j >= length - this.state.measures.length) && shouldDisplayTotal) || i === 0){
$cell.css('font-weight', 'bold');
}
$cell.toggleClass('d-none d-md-table-cell', j < length - nbrMeasures);
$row.append($cell);
}
} else {
for (var l=0; l < 3; l++) {
$cell = $(' ')
.data('id', rows[i].id)
.toggleClass('o_empty', true)
.addClass('o_pivot_cell_value text-right');
$row.append($cell);
}
}
} else {
$cell = $(' | ')
.data('id', rows[i].id)
.data('col_id', rows[i].col_ids[Math.floor(j / nbrMeasures)])
.toggleClass('o_empty', !value)
.addClass('o_pivot_cell_value text-right');
if (value !== undefined) {
$cell.append($(' | ', {class: 'o_value'}).html(formatter(value, measure)));
}
if (((j >= length - this.state.measures.length) && shouldDisplayTotal) || i === 0){
$cell.css('font-weight', 'bold');
}
$row.append($cell);
$cell.toggleClass('d-none d-md-table-cell', j < length - nbrMeasures);
}
}
$tbody.append($row);
}
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {MouseEvent} ev
*/
_onTdHover: function (ev) {
var $td = $(event.target);
$td.closest('table').find('col:eq(' + $td.index()+')').toggleClass('hover');
},
});
return PivotRenderer;
});
|