diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js index 9abfa4861b6..3bc256611d9 100644 --- a/addons/web/static/src/js/views/pivot/pivot_model.js +++ b/addons/web/static/src/js/views/pivot/pivot_model.js @@ -791,10 +791,25 @@ var PivotModel = AbstractModel.extend({ return header; }, /** - * @param {Object} data - * @param {Object} comparisonData - * @param {(string[])[]} groupBys - * @returns {Object} + * Here data and comparisonData are arrays of arrays of objects. + * Each one of those objects is called a dataPoint and represents a group of records + * (determined by some groupbys values) together with measure values aggregated. + * An exception: if a many2one is selected as a measure and a groupby simultaneously, + * we have only the corresponding groupby value which is the of the form ['id', 'name_get'] + * but the measure value can be inferred, it is indeed 1. + * In case 'this.data.compare' is true (a comparison is required), + * the dataPoint are transformed in such a way that the measures values become objects of the form + * + * {'data': 'some value', 'comparisonData': 'other value', 'variation': 'yet another value'}. + * + * In case two dataPoints have the same associated group + * (they then come necessarily from 'data' and 'comparisonData'), + * they are merged into a single dataPoint of the form above. + * + * @param {Object[][]} data + * @param {Object[][]} comparisonData + * @param {string[][]} groupBys + * @returns {Object[][]} */ _mergeData: function (data, comparisonData, groupBys) { if (!this.data.compare) { @@ -805,6 +820,9 @@ var PivotModel = AbstractModel.extend({ var value, groupIdentifier, dataPoint, m, measureName, measureValue, measureComparisonValue; for (var index = 0; index < groupBys.length; index++) { dataPoints = {}; + // Consider dataPoints comming from 'data'. The dataPoint measure values are objects with + // zeros values for the 'comparisonData' key since we don't know at this stage + // if the group is represented in the 'comparisonData'. if (data.length) { for (var k = 0; k < data[index].length; k++) { dataPoint = data[index][k]; @@ -816,6 +834,9 @@ var PivotModel = AbstractModel.extend({ if (typeof measureValue === 'boolean') { measureValue = measureValue ? 1 : 0; } + if (measureValue === null) { + measureValue = 0; + } if (!(measureValue instanceof Array) && measureName !== '__count') { dataPoint[measureName] = { data: measureValue, @@ -831,11 +852,11 @@ var PivotModel = AbstractModel.extend({ }; } } - dataPoint.__count = { + dataPoint.__count = dataPoint.__count ? { data: dataPoint.__count, comparisonData: 0, variation: computeVariation(dataPoint.__count, 0) - }; + } : undefined; dataPoints[groupIdentifier] = dataPoint; } } @@ -845,12 +866,16 @@ var PivotModel = AbstractModel.extend({ value = this._getValue(dataPoint, groupBys[index]); groupIdentifier = value.join(); if (!dataPoints[groupIdentifier]) { + // Here we know that the group is not represented in 'data'. for (m=0; m < this.data.measures.length; m++) { measureName = this.data.measures[m]; measureComparisonValue = dataPoint[measureName]; if (typeof(measureComparisonValue) === 'boolean') { measureComparisonValue = measureComparisonValue ? 1 : 0; } + if (measureComparisonValue === null) { + measureComparisonValue = 0; + } if (!(measureComparisonValue instanceof Array) && measureName !== '__count') { dataPoint[measureName] = { data: 0, @@ -866,21 +891,27 @@ var PivotModel = AbstractModel.extend({ }; } } - dataPoint.__count = { + dataPoint.__count = dataPoint.__count ? { data: 0, comparisonData: dataPoint.__count, variation: computeVariation(0, dataPoint.__count) - }; + } : undefined; dataPoint.__comparisonCount = dataPoint.__count; dataPoint.__comparisonDomain = dataPoint.__domain; dataPoints[groupIdentifier] = _.omit(dataPoint, '__domain'); } else { + // Here we know that the group is represented in 'data'. + // Therefore we modify the corresonding dataPoint: + // we modify the key 'comparisonData' and recompute 'variation'. for (m=0; m < this.data.measures.length; m++) { measureName = this.data.measures[m]; measureComparisonValue = dataPoint[measureName]; if (typeof(measureComparisonValue) === 'boolean') { measureComparisonValue = measureComparisonValue ? 1 : 0; } + if (measureComparisonValue === null) { + measureComparisonValue = 0; + } if (!(measureComparisonValue instanceof Array) && measureName !== '__count') { dataPoints[groupIdentifier][measureName].comparisonData = measureComparisonValue; dataPoints[groupIdentifier][measureName].variation = computeVariation( @@ -897,11 +928,14 @@ var PivotModel = AbstractModel.extend({ } } - dataPoints[groupIdentifier].__count.comparisonData = dataPoint.__count; - dataPoints[groupIdentifier].__count.variation = computeVariation( - dataPoints[groupIdentifier].__count.data, - dataPoint.__count - ); + if (dataPoint.__count) { + dataPoints[groupIdentifier].__count.data = dataPoints[groupIdentifier].__count.data || 0; + dataPoints[groupIdentifier].__count.comparisonData = dataPoint.__count; + dataPoints[groupIdentifier].__count.variation = computeVariation( + dataPoints[groupIdentifier].__count.data, + dataPoint.__count + ); + } dataPoints[groupIdentifier].__comparisonCount = dataPoint.__count; dataPoints[groupIdentifier].__comparisonDomain = dataPoint.__domain; } @@ -912,7 +946,7 @@ var PivotModel = AbstractModel.extend({ return allData; }, /** - * @param {Object} data + * @param {Object[][]} data */ _prepareData: function (data) { var self = this; @@ -942,9 +976,11 @@ var PivotModel = AbstractModel.extend({ value: self._getValue(dataPoint, groupBys), domain: dataPoint.__domain, comparisonDomain: dataPoint.__comparisonDomain, - length: dataPoint.__count.data ? dataPoint.__count.data : dataPoint.__count, - comparisonLength: dataPoint.__count.comparisonData || 0 }; + if (dataPoint.__count) { + attrs.length = dataPoint.__count instanceof Object ? dataPoint.__count.data : dataPoint.__count; + attrs.comparisonLength = dataPoint.__comparisonCount; + } if (j === 0) { row = this._makeHeader(attrs.value, attrs.domain, attrs.comparisonDomain, attrs.length, attrs.comparisonLength, main_row_header, 0, i); diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js index 3bad47347ec..fe318efd9e2 100644 --- a/addons/web/static/tests/views/pivot_tests.js +++ b/addons/web/static/tests/views/pivot_tests.js @@ -1225,8 +1225,8 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.test('rendering of pivot view with comparison active', function (assert) { - assert.expect(1); + QUnit.test('rendering of pivot view with comparison', function (assert) { + assert.expect(91); this.data.partner.records[0].date = '2016-12-15'; this.data.partner.records[1].date = '2016-12-17'; @@ -1243,6 +1243,15 @@ QUnit.module('Views', { var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + var results, i, length; + + + function checkCellValues (results) { + length = results.length; + for (i = 0; i < length; i++) { + assert.strictEqual($('.o_pivot .o_pivot_cell_value div').eq(i).text().trim(), results.shift()); + } + } // create an action manager to test the interactions with the search view var actionManager = createActionManager({ @@ -1260,15 +1269,71 @@ QUnit.module('Views', { res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], + flags: { + pivot: { + additionalMeasures: ['product_id'], + } + } }); + + // with no data + $('.o_time_range_menu_button').click(); - $('.o_time_range_menu .custom-control-label').click(); + $('.o_time_range_menu .o_comparison_checkbox').click(); + $('.o_time_range_selector').val('today'); $('.o_time_range_menu .o_apply_range').click(); - // Test to modify and extend assert.strictEqual($('.o_pivot p.o_view_nocontent_empty_folder').length, 1); + // with data, no row groupby + $('.o_time_range_menu_button').click(); + $('.o_time_range_selector').val('this_month'); + $('.o_time_range_menu .o_apply_range').click(); + results = [ + "13", "0", "100%", "0", "19", "-100%", "13", "19", "-31.58%" + ]; + checkCellValues(results); + + // with data, with row groupby + + $('.o_pivot .o_pivot_header_cell_closed').eq(2).click(); + $('.o_pivot .o_field_selection a[data-field="product_id"]').click(); + results = [ + "13", "0", "100%", "0", "19", "-100%", "13", "19", "-31.58%" , + "12", "0", "100%", "12", "0" , "100%" , + "1" , "0", "100%", "0", "19", "-100%", "1" , "19" , "-94.74%" + ]; + checkCellValues(results); + + $('.o_control_panel button.btn-primary').eq(0).click(); + $('.o_control_panel div.o_pivot_measures_list a[data-field="foo"').click(); + $('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"').click(); + results = [ + "2", "0", "100%", "0", "1", "-100%", "2", "1", "100%" , + "1", "0", "100%", "1", "0" , "100%" , + "1" , "0", "100%", "0", "1", "-100%", "1" , "1" , "100%" + ]; + checkCellValues(results); + + $('.o_control_panel button.btn-primary').eq(0).click(); + $('.o_control_panel div.o_pivot_measures_list a[data-field="__count"').click(); + $('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"').click(); + results = [ + "2", "0", "100%", "0", "2", "-100%", "2", "2", "0%" , + "1", "0", "100%", "1", "0" , "100%" , + "1" , "0", "100%", "0", "2", "-100%", "1" , "2" , "-50%" + ]; + checkCellValues(results); + + $('.o_pivot .o_pivot_header_cell_opened').eq(0).click(); + results = [ + "2", "2", "0%" , + "1", "0", "100%" , + "1", "2", "-50%" + ]; + checkCellValues(results); + unpatchDate(); actionManager.destroy(); }); @@ -1317,6 +1382,8 @@ QUnit.module('Views', { // open time range menu $('.o_control_panel .o_time_range_menu_button').click(); + // select 'Today' as range + $('.o_control_panel .o_time_range_selector').val('today'); // check checkbox 'Compare To' $('.o_control_panel .o_time_range_menu .o_comparison_checkbox').click(); // Click on 'Apply' button