From 30f4a40e207308e67a1eb0dac5c29bfcfdeca1f5 Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Mon, 31 Jan 2022 16:10:56 +0000 Subject: [PATCH] [FIX] web: compute aggregate value with valid data The footer of some lists may contain undesirable values (such as `NaN`) To reproduce the issue: 1. Install mrp with demo data 2. In Settings, enable "Work Orders" 3. Open the list view of MOs 4. Group by product Error: The total value for Expected Duration and Real Duration is `NaN:NaN` (The below explanation is about Expected Duration (`production_duration_expected`). This is exactly the same behavior for Real Duration) As defined on the list view, if there are several rows on the list, there must be a sum of all `production_duration_expected`: https://github.com/odoo/odoo/blob/cddbb76e02c3848819ac1f06948b4e7ab8df4bdd/addons/mrp/views/mrp_production_views.xml#L35 This is correctly working when consulting a standard list of MOs (step 3). However, this is different when using the Group-By feature. When rendering the list, at some point, `_computeColumnAggregates` is called to compute the sum of all `production_duration_expected`: https://github.com/odoo/odoo/blob/4e213f0b93f59cf377271b18eae9765ff9cfbe4f/addons/web/static/src/legacy/js/views/list/list_renderer.js#L178-L185 Here is the issue: the rows in the list `data` are not records (there are results of `read_group`) and these rows don't have any aggregate value for `production_duration_expected`, which means that `d.aggregateValues[attrs.name]` returns `undefined`. As a result, when performing the sum of `0` and `undefined`, it gives `NaN`. Some detailed explanations: `aggregateValues` is completed when processing the result of the `read_group` RPC: https://github.com/odoo/odoo/blob/bc79a329a1e1edf964f97886943c0688db2087fd/addons/web/static/src/legacy/js/views/basic/basic_model.js#L4697-L4703 However, on PY-side, `read_group` doesn't provide any value for `production_duration_expected`. For a `read_group` to provide some aggregate values, it needs the field to be stored and the `group_operator` attribute to be defined on this field: https://github.com/odoo/odoo/blob/5691b126f06c2b200ae507bd6edd8f8f7e68d5f5/odoo/models.py#L2521-L2528 Except that none of the conditions are met: https://github.com/odoo/odoo/blob/d584ad282178deb40bb036c48f971617b0a41f88/addons/mrp/models/mrp_production.py#L147 OPW-2735181 closes odoo/odoo#84035 X-original-commit: 8bc1c582b626aedb680d59f858c92db9dfe4f357 Signed-off-by: Simon Genin (ges@odoo) --- .../src/legacy/js/views/list/list_renderer.js | 17 +++++++---- .../static/tests/legacy/views/list_tests.js | 30 ++++++++++++++++++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/legacy/js/views/list/list_renderer.js b/addons/web/static/src/legacy/js/views/list/list_renderer.js index 6e9647e6ac7..f648e21880c 100644 --- a/addons/web/static/src/legacy/js/views/list/list_renderer.js +++ b/addons/web/static/src/legacy/js/views/list/list_renderer.js @@ -176,8 +176,11 @@ var ListRenderer = BasicRenderer.extend({ aggregateValue = Infinity; } _.each(data, function (d) { - count += 1; var value = (d.type === 'record') ? d.data[attrs.name] : d.aggregateValues[attrs.name]; + if (Number(value) !== value) { + return; + } + count += 1; if (func === 'avg') { aggregateValue += value; } else if (func === 'sum') { @@ -191,10 +194,14 @@ var ListRenderer = BasicRenderer.extend({ if (func === 'avg') { aggregateValue = count ? aggregateValue / count : aggregateValue; } - column.aggregate = { - help: attrs[func], - value: aggregateValue, - }; + if (count) { + column.aggregate = { + help: attrs[func], + value: aggregateValue, + }; + } else { + delete column.aggregate; + } } }, /** diff --git a/addons/web/static/tests/legacy/views/list_tests.js b/addons/web/static/tests/legacy/views/list_tests.js index f3c763a0ab2..1dfb107d356 100644 --- a/addons/web/static/tests/legacy/views/list_tests.js +++ b/addons/web/static/tests/legacy/views/list_tests.js @@ -2405,7 +2405,7 @@ QUnit.module('Views', { // Let's update the view to dislay NO records await list.update({domain: ['&', ['bar', '=', false], ['int_field', '>', 0]]}); - assert.strictEqual(list.$('tfoot td:nth(2)').text(), "0", "total should have been recomputed to 0"); + assert.strictEqual(list.$('tfoot td:nth(2)').text(), "", "No records, so no total."); list.destroy(); }); @@ -2438,6 +2438,34 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('hide aggregated value in grouped lists when no data provided by RPC call', async function(assert) { + assert.expect(1); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + groupBy: ['bar'], + arch: '', + mockRPC: function (route, args) { + return this._super.apply(this, arguments).then(result => { + // On PY-side, a read_group won't provide any aggregate value + // if the field does not have a `group_operator` attribute. + // (see 5691b126f06/odoo/models.py#L2521-L2528) + // Here, since `qux` does not have such an attribute, we + // remove the value provided by `_mockReadGroup` + _.each(result.groups, function(group) { + delete group.qux + }) + return Promise.resolve(result) + }); + }, + }); + + assert.strictEqual(list.$('tfoot td:nth(2)').text(), "", "There isn't any aggregated value"); + list.destroy(); + }) + QUnit.test('aggregates are updated when a line is edited', async function (assert) { assert.expect(2);