Skip to content

Commit 30800f6

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 #7567 Task: 5358213 X-original-commit: 246af93 Signed-off-by: Lucas Lefèvre (lul) <lul@odoo.com> Signed-off-by: Rémi Rahir (rar) <rar@odoo.com>
1 parent ba344c3 commit 30800f6

File tree

8 files changed

+57
-29
lines changed

8 files changed

+57
-29
lines changed

packages/o-spreadsheet-engine/src/helpers/pivot/pivot_registry.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ type PivotDefinitionConstructor = new (
3030
export interface PivotRegistryItem {
3131
ui: PivotUIConstructor;
3232
definition: PivotDefinitionConstructor;
33-
externalData: boolean;
3433
dateGranularities: string[];
3534
datetimeGranularities: string[];
3635
isMeasureCandidate: (field: PivotField) => boolean;
@@ -55,7 +54,6 @@ const dateGranularities = [
5554
pivotRegistry.add("SPREADSHEET", {
5655
ui: SpreadsheetPivot,
5756
definition: SpreadsheetPivotRuntimeDefinition,
58-
externalData: false,
5957
dateGranularities: [...dateGranularities],
6058
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
6159
isMeasureCandidate: (field: PivotField) => field.type !== "boolean",

packages/o-spreadsheet-engine/src/plugins/core/pivot.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -322,18 +322,19 @@ export class PivotCorePlugin extends CorePlugin<CoreState> implements CoreState
322322
if (!pivot) {
323323
continue;
324324
}
325-
for (const measure of pivot.definition.measures) {
325+
const def = deepCopy(pivot.definition);
326+
327+
for (const measure of def.measures) {
326328
if (measure.computedBy?.formula === formulaString) {
327-
const measureIndex = pivot.definition.measures.indexOf(measure);
328-
this.history.update(
329-
"pivots",
330-
pivotId,
331-
"definition",
332-
"measures",
333-
measureIndex,
334-
"computedBy",
335-
{ formula: newFormulaString, sheetId }
336-
);
329+
const measureIndex = def.measures.indexOf(measure);
330+
if (measureIndex !== -1) {
331+
def.measures[measureIndex].computedBy = {
332+
formula: newFormulaString,
333+
sheetId,
334+
};
335+
}
336+
337+
this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
337338
}
338339
}
339340
}

packages/o-spreadsheet-engine/src/plugins/core/spreadsheet_pivot.ts

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

48+
if (adaptedRange === range) {
49+
return;
50+
}
51+
4852
const dataSet = adaptedRange && {
4953
sheetId: adaptedRange.sheetId,
5054
zone: adaptedRange.zone,

packages/o-spreadsheet-engine/src/plugins/ui_core_views/pivot_ui.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { astToFormula } from "../../formulas/formula_formatter";
22
import { Token } from "../../formulas/tokenizer";
33
import { toScalar } from "../../functions/helper_matrices";
4+
import { deepCopy } from "../../helpers";
45
import { deepEquals, getUniqueText } from "../../helpers/misc";
56
import {
67
getFirstPivotFunction,
@@ -65,9 +66,7 @@ export class PivotUIPlugin extends CoreViewPlugin {
6566
handle(cmd: Command) {
6667
if (invalidateEvaluationCommands.has(cmd.type)) {
6768
for (const pivotId of this.getters.getPivotIds()) {
68-
if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
69-
this.setupPivot(pivotId, { recreate: true });
70-
}
69+
this.setupPivot(pivotId, { recreate: true });
7170
}
7271
}
7372
switch (cmd.type) {
@@ -320,7 +319,7 @@ export class PivotUIPlugin extends CoreViewPlugin {
320319
}
321320

322321
setupPivot(pivotId: UID, { recreate } = { recreate: false }) {
323-
const definition = this.getters.getPivotCoreDefinition(pivotId);
322+
const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
324323
if (!(pivotId in this.pivots)) {
325324
const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
326325
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,5 +1,4 @@
11
import { DEFAULT_REVISION_ID, MESSAGE_VERSION } from "@odoo/o-spreadsheet-engine/constants";
2-
import { pivotRegistry } from "@odoo/o-spreadsheet-engine/helpers/pivot/pivot_registry";
32
import { LineChartDefinition } from "@odoo/o-spreadsheet-engine/types/chart/line_chart";
43
import { StateUpdateMessage } from "@odoo/o-spreadsheet-engine/types/collaborative/transport_service";
54
import { Model } from "../../src";
@@ -1025,7 +1024,6 @@ describe("Collaborative local history", () => {
10251024
});
10261025

10271026
test("remove pivot, new user joins, then undo", () => {
1028-
pivotRegistry.get("SPREADSHEET").externalData = true; // simulate external pivot
10291027
const network = new MockTransportService();
10301028
const data = {
10311029
revisionId: DEFAULT_REVISION_ID,
@@ -1073,7 +1071,6 @@ describe("Collaborative local history", () => {
10731071
const bob = new Model(data, configBob, messages);
10741072
undo(alice);
10751073
expect(getEvaluatedCell(bob, "B3").value).toEqual(10);
1076-
pivotRegistry.get("SPREADSHEET").externalData = false;
10771074
});
10781075

10791076
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
@@ -10,6 +10,7 @@ import {
1010
} from "../../../src";
1111
import { positions, toZone } from "../../../src/helpers";
1212
import {
13+
addRows,
1314
createSheet,
1415
deleteContent,
1516
deleteSheet,
@@ -645,6 +646,22 @@ describe("Spreadsheet Pivot", () => {
645646
);
646647
});
647648

649+
test("Modifying a sheet structure adapts the pivot range", () => {
650+
const model = createModelWithPivot("A1:I5");
651+
setCellContent(model, "A26", `=pivot(1)`);
652+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
653+
expect(getEvaluatedCell(model, "A26").value).toEqual("My pivot");
654+
addRows(model, "before", 0, 1);
655+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
656+
expect(getEvaluatedCell(model, "A27").value).toEqual("My pivot");
657+
undo(model);
658+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
659+
expect(getEvaluatedCell(model, "A26").value).toEqual("My pivot");
660+
redo(model);
661+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
662+
expect(getEvaluatedCell(model, "A27").value).toEqual("My pivot");
663+
});
664+
648665
test("Sum with a field that contains a string should work", () => {
649666
const model = createModelWithPivot("A1:I5");
650667
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
@@ -743,11 +743,16 @@ describe("Spreadsheet pivot side panel", () => {
743743
test("Invalid pivot dimensions are displayed as such in the side panel", async () => {
744744
setCellContent(model, "A1", "ValidDimension");
745745
setCellContent(model, "A2", "10");
746-
addPivot(model, "A1:A2", {
747-
columns: [{ fieldName: "ValidDimension" }],
748-
rows: [{ fieldName: "InvalidDimension" }],
749-
});
750-
env.openSidePanel("PivotSidePanel", { pivotId: "1" });
746+
addPivot(
747+
model,
748+
"A1:A2",
749+
{
750+
columns: [{ fieldName: "ValidDimension" }],
751+
rows: [{ fieldName: "InvalidDimension" }],
752+
},
753+
"2"
754+
);
755+
env.openSidePanel("PivotSidePanel", { pivotId: "2" });
751756
await nextTick();
752757
const pivotDimensionEls = fixture.querySelectorAll<HTMLElement>(".pivot-dimension")!;
753758
const validDimensionEl = pivotDimensionEls[0];

0 commit comments

Comments
 (0)