Skip to content

Commit 4777025

Browse files
committed
[FIX] pivot: Ensure computed measure range adaptation
Currently, ODOO pivots computed measures are not properly updated upon sheet structure modification. To be precise, their definition, which is stored in the core plugin `PivotCorePlugin` is properly updated but the runtime definition, stored in `PivotUIPlugin`, is not. This occurs because the mecanism to invalidate the runtime definition explicitely ignores the ODOO pivots. histoically, this was set up to avoid useless reloading of ODOO pivots which could end up making server calls but this logic is properly handled in the function `onDefinitionChange`. We can see that in the case of spreadsheet pivots, we already notify all plugins of such a change, but by "pure accident", as we dispatch an "UPDATE_PIVOT" command at every range adaptation, regardless of whether it was necessary or not. This means that the spreadsheet pivots beneficiated of two mecanisms to update their runtime (in core, an UPDATE_PIVOT, and the `invalidateEvaluationCommands` mecanism) which means that invalidation work was done two times. The investigation also led to the discovery of a missing check on the command "ADD_PIVOT" which has been reported in https://www.odoo.com/odoo/2328/tasks/5360591 We also noted that there is a double handling of commands between the handling of `invalidateEvaluationCommands` and the specific command handlers in `PivotUIPlugin`. We could clean this up in master. Note that additional tests regarding the Odoo pivots will be added in Odoo repository to ensure the validity of the fix. closes #7564 Task: 5358213 X-original-commit: 246af93 Signed-off-by: Lucas Lefèvre (lul) <[email protected]> Signed-off-by: Rémi Rahir (rar) <[email protected]>
1 parent d52b590 commit 4777025

File tree

8 files changed

+57
-30
lines changed

8 files changed

+57
-30
lines changed

src/helpers/pivot/pivot_registry.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ type PivotDefinitionConstructor = new (
2828
export interface PivotRegistryItem {
2929
ui: PivotUIConstructor;
3030
definition: PivotDefinitionConstructor;
31-
externalData: boolean;
3231
dateGranularities: string[];
3332
datetimeGranularities: string[];
3433
isMeasureCandidate: (field: PivotField) => boolean;
@@ -51,7 +50,6 @@ const dateGranularities = [
5150
pivotRegistry.add("SPREADSHEET", {
5251
ui: SpreadsheetPivot,
5352
definition: SpreadsheetPivotRuntimeDefinition,
54-
externalData: false,
5553
dateGranularities: [...dateGranularities],
5654
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
5755
isMeasureCandidate: (field: PivotField) => field.type !== "boolean",

src/plugins/core/pivot.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -332,18 +332,19 @@ export class PivotCorePlugin extends CorePlugin<CoreState> implements CoreState
332332
if (!pivot) {
333333
continue;
334334
}
335-
for (const measure of pivot.definition.measures) {
335+
const def = deepCopy(pivot.definition);
336+
337+
for (const measure of def.measures) {
336338
if (measure.computedBy?.formula === formulaString) {
337-
const measureIndex = pivot.definition.measures.indexOf(measure);
338-
this.history.update(
339-
"pivots",
340-
pivotId,
341-
"definition",
342-
"measures",
343-
measureIndex,
344-
"computedBy",
345-
{ formula: newFormulaString, sheetId }
346-
);
339+
const measureIndex = def.measures.indexOf(measure);
340+
if (measureIndex !== -1) {
341+
def.measures[measureIndex].computedBy = {
342+
formula: newFormulaString,
343+
sheetId,
344+
};
345+
}
346+
347+
this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
347348
}
348349
}
349350
}

src/plugins/core/spreadsheet_pivot.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export class SpreadsheetPivotCorePlugin extends CorePlugin {
4848
const range = this.getters.getRangeFromZone(sheetId, zone);
4949
const adaptedRange = adaptPivotRange(range, applyChange);
5050

51+
if (adaptedRange === range) {
52+
return;
53+
}
54+
5155
const dataSet = adaptedRange && {
5256
sheetId: adaptedRange.sheetId,
5357
zone: adaptedRange.zone,

src/plugins/ui_core_views/pivot_ui.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Token } from "../../formulas";
22
import { astToFormula } from "../../formulas/parser";
33
import { toScalar } from "../../functions/helper_matrices";
44
import { toBoolean } from "../../functions/helpers";
5-
import { getUniqueText } from "../../helpers";
5+
import { deepCopy, getUniqueText } from "../../helpers";
66
import {
77
getFirstPivotFunction,
88
getNumberOfPivotFunctions,
@@ -67,9 +67,7 @@ export class PivotUIPlugin extends CoreViewPlugin {
6767
handle(cmd: Command) {
6868
if (invalidateEvaluationCommands.has(cmd.type)) {
6969
for (const pivotId of this.getters.getPivotIds()) {
70-
if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
71-
this.setupPivot(pivotId, { recreate: true });
72-
}
70+
this.setupPivot(pivotId, { recreate: true });
7371
}
7472
}
7573
switch (cmd.type) {
@@ -300,7 +298,7 @@ export class PivotUIPlugin extends CoreViewPlugin {
300298
}
301299

302300
setupPivot(pivotId: UID, { recreate } = { recreate: false }) {
303-
const definition = this.getters.getPivotCoreDefinition(pivotId);
301+
const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
304302
if (!(pivotId in this.pivots)) {
305303
const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
306304
this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });

tests/collaborative/collaborative_history.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Model } from "../../src";
22
import { DEFAULT_REVISION_ID, MESSAGE_VERSION } from "../../src/constants";
33
import { toZone } from "../../src/helpers";
4-
import { pivotRegistry } from "../../src/helpers/pivot/pivot_registry";
54
import { CommandResult, UpdateCellCommand } from "../../src/types";
65
import { LineChartDefinition } from "../../src/types/chart/line_chart";
76
import { StateUpdateMessage } from "../../src/types/collaborative/transport_service";
@@ -1019,7 +1018,6 @@ describe("Collaborative local history", () => {
10191018
});
10201019

10211020
test("remove pivot, new user joins, then undo", () => {
1022-
pivotRegistry.get("SPREADSHEET").externalData = true; // simulate external pivot
10231021
const network = new MockTransportService();
10241022
const data = {
10251023
revisionId: DEFAULT_REVISION_ID,
@@ -1067,7 +1065,6 @@ describe("Collaborative local history", () => {
10671065
const bob = new Model(data, configBob, messages);
10681066
undo(alice);
10691067
expect(getEvaluatedCell(bob, "B3").value).toEqual(10);
1070-
pivotRegistry.get("SPREADSHEET").externalData = false;
10711068
});
10721069

10731070
test("Concurrently undo a command on which another is based", () => {

tests/pivots/pivot_calculated_measure.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {
22
activateSheet,
3-
addColumns,
3+
addRows,
44
createSheet,
55
deleteSheet,
6+
redo,
67
setCellContent,
78
setFormat,
9+
undo,
810
} from "../test_helpers/commands_helpers";
911
import { getEvaluatedCell, getEvaluatedGrid } from "../test_helpers/getters_helpers";
1012
import { createModelFromGrid } from "../test_helpers/helpers";
@@ -1063,16 +1065,21 @@ describe("Pivot calculated measure", () => {
10631065
],
10641066
});
10651067
expect(getEvaluatedCell(model, "A4").value).toEqual(42);
1066-
addColumns(model, "before", "A", 1);
1068+
addRows(model, "before", 2, 1);
10671069
expect(model.getters.getPivotCoreDefinition("1").measures).toEqual([
10681070
{
10691071
id: "calculated",
10701072
fieldName: "calculated",
10711073
aggregator: "sum",
1072-
computedBy: { formula: "=B3", sheetId },
1074+
computedBy: { formula: "=A4", sheetId },
10731075
},
10741076
]);
1075-
expect(getEvaluatedCell(model, "B4").value).toEqual(42);
1077+
expect(getEvaluatedCell(model, "A5").value).toEqual(42);
1078+
1079+
undo(model);
1080+
expect(getEvaluatedCell(model, "A4").value).toEqual(42);
1081+
redo(model);
1082+
expect(getEvaluatedCell(model, "A5").value).toEqual(42);
10761083
});
10771084

10781085
test("references becomes invalid when sheet is deleted", () => {

tests/pivots/spreadsheet_pivot/spreadsheet_pivot.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CellErrorType, FunctionResultObject, Model } from "../../../src";
22
import { resetMapValueDimensionDate } from "../../../src/helpers/pivot/spreadsheet_pivot/date_spreadsheet_pivot";
33
import { DEFAULT_LOCALES } from "../../../src/types/locale";
44
import {
5+
addRows,
56
createSheet,
67
deleteContent,
78
deleteSheet,
@@ -650,6 +651,22 @@ describe("Spreadsheet Pivot", () => {
650651
);
651652
});
652653

654+
test("Modifying a sheet structure adapts the pivot range", () => {
655+
const model = createModelWithPivot("A1:I5");
656+
setCellContent(model, "A26", `=pivot(1)`);
657+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
658+
expect(getEvaluatedCell(model, "A26").value).toEqual("(#1) My pivot");
659+
addRows(model, "before", 0, 1);
660+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
661+
expect(getEvaluatedCell(model, "A27").value).toEqual("(#1) My pivot");
662+
undo(model);
663+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
664+
expect(getEvaluatedCell(model, "A26").value).toEqual("(#1) My pivot");
665+
redo(model);
666+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
667+
expect(getEvaluatedCell(model, "A27").value).toEqual("(#1) My pivot");
668+
});
669+
653670
test("Sum with a field that contains a string should work", () => {
654671
const model = createModelWithPivot("A1:I5");
655672
updatePivot(model, "1", {

tests/pivots/spreadsheet_pivot/spreadsheet_pivot_side_panel.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -729,11 +729,16 @@ describe("Spreadsheet pivot side panel", () => {
729729
test("Invalid pivot dimensions are displayed as such in the side panel", async () => {
730730
setCellContent(model, "A1", "ValidDimension");
731731
setCellContent(model, "A2", "10");
732-
addPivot(model, "A1:A2", {
733-
columns: [{ fieldName: "ValidDimension" }],
734-
rows: [{ fieldName: "InvalidDimension" }],
735-
});
736-
env.openSidePanel("PivotSidePanel", { pivotId: "1" });
732+
addPivot(
733+
model,
734+
"A1:A2",
735+
{
736+
columns: [{ fieldName: "ValidDimension" }],
737+
rows: [{ fieldName: "InvalidDimension" }],
738+
},
739+
"2"
740+
);
741+
env.openSidePanel("PivotSidePanel", { pivotId: "2" });
737742
await nextTick();
738743
const pivotDimensionEls = fixture.querySelectorAll<HTMLElement>(".pivot-dimension")!;
739744
const validDimensionEl = pivotDimensionEls[0];

0 commit comments

Comments
 (0)