From aae6b728239a4fc61b4006ec7e101815348c1182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Lef=C3=A8vre=20=28lul=29?= Date: Wed, 25 Oct 2023 10:43:08 +0200 Subject: [PATCH] [IMP] spreadsheet: allow to sort month pivot headers Currently, if you have a pivot grouped by a date field, aggregated by week month or quarter, sorting all the pivot cells does not work as expected when sorting based on the date header column. "April 2023" would end up being before "March 2020" just because "A" is before "M". Similarly, "W1 2023" is before "W2 2020" and "Q1 2023" is before "Q2 2020" With this commit, for months aggregates, the result of `=ODOO.PIVOT.HEADER(1,"create_date:month","04/2023")` is currently the string "April 2023". The result now becomes a real date just like any other date value in a spreadsheet. It's the number corresponding to the first day of the month. For week and quarter aggregates, we could move the year first ("W1 2023" becomes "2023 W1"). However, we decided not to do it to keep consistency: - with other places in odoo (pivot views) - with the way we talk/think (quarter/week comes first) closes odoo/odoo#139295 Task: 3570281 Related: odoo/enterprise#49300 Signed-off-by: Vincent Schippefilt (vsc) --- .../static/src/pivot/pivot_data_source.js | 2 +- .../static/src/pivot/pivot_model.js | 17 +++--- .../static/src/pivot/pivot_time_adapters.js | 22 ++++++++ .../tests/pivots/model/pivot_plugin_test.js | 55 +++++++++++++++++++ 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/addons/spreadsheet/static/src/pivot/pivot_data_source.js b/addons/spreadsheet/static/src/pivot/pivot_data_source.js index c66626f0e5b..584feef2ce0 100644 --- a/addons/spreadsheet/static/src/pivot/pivot_data_source.js +++ b/addons/spreadsheet/static/src/pivot/pivot_data_source.js @@ -77,7 +77,7 @@ export class PivotDataSource extends OdooViewsDataSource { if (domainArgs.at(-2) === "measure") { return this.getMeasureDisplayName(domainArgs.at(-1)); } - return this._model.getGroupByDisplayLabel( + return this._model.getGroupByCellValue( domainArgs.at(-2), this._model.getLastPivotGroupValue(domainArgs) ); diff --git a/addons/spreadsheet/static/src/pivot/pivot_model.js b/addons/spreadsheet/static/src/pivot/pivot_model.js index c45419e91f0..eb9963e612d 100644 --- a/addons/spreadsheet/static/src/pivot/pivot_model.js +++ b/addons/spreadsheet/static/src/pivot/pivot_model.js @@ -293,13 +293,16 @@ export class SpreadsheetPivotModel extends PivotModel { } /** - * Get the label the given field-value + * Get the value of a field + * + * @example + * getGroupByCellValue("stage_id", 42) // "Won" * * @param {string} groupFieldString Name of the field - * @param {string} groupValueString Value of the group by - * @returns {string} + * @param {string | number} groupValueString Value of the group by + * @returns {string | number} */ - getGroupByDisplayLabel(groupFieldString, groupValueString, locale = DEFAULT_LOCALE) { + getGroupByCellValue(groupFieldString, groupValueString) { if (groupValueString === NO_RECORD_AT_THIS_POSITION) { return ""; } @@ -307,12 +310,8 @@ export class SpreadsheetPivotModel extends PivotModel { const value = toNormalizedPivotValue(field, groupValueString, aggregateOperator); const undef = _t("None"); if (this._isDateField(field)) { - // TODO include this parsing to the pivot time adapters and extend it to other time periods - if (value && aggregateOperator === "day") { - return toNumber(value, DEFAULT_LOCALE); - } const adapter = pivotTimeAdapter(aggregateOperator); - return adapter.formatValue(value, locale); + return adapter.toCellValue(value); } if (field.relation) { const label = this.metadataRepository.getRecordDisplayName(field.relation, value); diff --git a/addons/spreadsheet/static/src/pivot/pivot_time_adapters.js b/addons/spreadsheet/static/src/pivot/pivot_time_adapters.js index 1231fc51ce3..9db870296a4 100644 --- a/addons/spreadsheet/static/src/pivot/pivot_time_adapters.js +++ b/addons/spreadsheet/static/src/pivot/pivot_time_adapters.js @@ -71,6 +71,7 @@ export function pivotTimeAdapter(groupAggregate) { * @property {(normalizedValue: string, step: number) => string} increment * @property {(normalizedValue: string, locale: Object) => string} formatValue * @property {(locale: Object) => string} getFormat + * @property {(normalizedValue: string) => string | number} toCellValue */ /** @@ -102,6 +103,9 @@ const dayAdapter = { const value = toNumber(normalizedValue, DEFAULT_LOCALE); return formatValue(value, { locale, format: this.getFormat(locale) }); }, + toCellValue(normalizedValue) { + return toNumber(normalizedValue, DEFAULT_LOCALE); + }, }; /** @@ -133,6 +137,9 @@ const weekAdapter = { const [week, year] = normalizedValue.split("/"); return sprintf(_t("W%(week)s %(year)s"), { week, year }); }, + toCellValue(normalizedValue) { + return this.formatValue(normalizedValue); + }, }; /** @@ -162,6 +169,9 @@ const monthAdapter = { const value = toNumber(normalizedValue, DEFAULT_LOCALE); return formatValue(value, { locale, format: this.getFormat(locale) }); }, + toCellValue(normalizedValue) { + return toNumber(normalizedValue, DEFAULT_LOCALE); + }, }; /** @@ -192,6 +202,9 @@ const quarterAdapter = { const [quarter, year] = normalizedValue.split("/"); return sprintf(_t("Q%(quarter)s %(year)s"), { quarter, year }); }, + toCellValue(normalizedValue) { + return this.formatValue(normalizedValue); + }, }; /** * @type {PivotTimeAdapter} @@ -212,6 +225,9 @@ const yearAdapter = { formatValue(normalizedValue, locale) { return formatValue(normalizedValue, { locale, format: "0" }); }, + toCellValue(normalizedValue) { + return normalizedValue; + }, }; /** @@ -246,6 +262,12 @@ function falseHandlerDecorator(adapter) { } return adapter.formatValue(normalizedValue, locale); }, + toCellValue(normalizedValue) { + if (normalizedValue === false) { + return _t("None"); + } + return adapter.toCellValue(normalizedValue); + }, }; } diff --git a/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js b/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js index 3f217acab09..c483f664d44 100644 --- a/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js +++ b/addons/spreadsheet/static/tests/pivots/model/pivot_plugin_test.js @@ -676,6 +676,61 @@ QUnit.module("spreadsheet > pivot plugin", {}, () => { assert.strictEqual(getEvaluatedCell(model, "B1").formattedValue, "4/14/2016"); }); + QUnit.test("PIVOT.HEADER week are correctly formatted at evaluation", async function (assert) { + const { model } = await createSpreadsheetWithPivot({ + arch: /* xml */ ` + + + + `, + }); + assert.strictEqual(getEvaluatedCell(model, "B1").format, undefined); + assert.strictEqual(getEvaluatedCell(model, "B1").value, "W15 2016"); + assert.strictEqual(getEvaluatedCell(model, "B1").formattedValue, "W15 2016"); + }); + + QUnit.test("PIVOT.HEADER month are correctly formatted at evaluation", async function (assert) { + const { model } = await createSpreadsheetWithPivot({ + arch: /* xml */ ` + + + + `, + }); + assert.strictEqual(getEvaluatedCell(model, "B1").format, "mmmm yyyy"); + assert.strictEqual(getEvaluatedCell(model, "B1").value, 42461); + assert.strictEqual(getEvaluatedCell(model, "B1").formattedValue, "April 2016"); + }); + + QUnit.test( + "PIVOT.HEADER quarter are correctly formatted at evaluation", + async function (assert) { + const { model } = await createSpreadsheetWithPivot({ + arch: /* xml */ ` + + + + `, + }); + assert.strictEqual(getEvaluatedCell(model, "B1").format, undefined); + assert.strictEqual(getEvaluatedCell(model, "B1").value, "Q2 2016"); + assert.strictEqual(getEvaluatedCell(model, "B1").formattedValue, "Q2 2016"); + } + ); + + QUnit.test("PIVOT.HEADER year are correctly formatted at evaluation", async function (assert) { + const { model } = await createSpreadsheetWithPivot({ + arch: /* xml */ ` + + + + `, + }); + assert.strictEqual(getEvaluatedCell(model, "B1").format, "0"); + assert.strictEqual(getEvaluatedCell(model, "B1").value, 2016); + assert.strictEqual(getEvaluatedCell(model, "B1").formattedValue, "2016"); + }); + QUnit.test( "PIVOT.HEADER formulas are correctly formatted at evaluation", async function (assert) {