[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) <vsc@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2024-01-23 22:51:48 +00:00
parent 06920e4176
commit aae6b72823
4 changed files with 86 additions and 10 deletions
@@ -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)
);
@@ -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);
@@ -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);
},
};
}
@@ -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 */ `
<pivot>
<field name="date" interval="week" type="col"/>
<field name="foo" type="measure"/>
</pivot>`,
});
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 */ `
<pivot>
<field name="date" interval="month" type="col"/>
<field name="foo" type="measure"/>
</pivot>`,
});
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 */ `
<pivot>
<field name="date" interval="quarter" type="col"/>
<field name="foo" type="measure"/>
</pivot>`,
});
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 */ `
<pivot>
<field name="date" interval="year" type="col"/>
<field name="foo" type="measure"/>
</pivot>`,
});
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) {