Skip to content

Commit e63f36f

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 #7566 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 34d4c64 commit e63f36f

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;
@@ -52,7 +51,6 @@ const dateGranularities = [
5251
pivotRegistry.add("SPREADSHEET", {
5352
ui: SpreadsheetPivot,
5453
definition: SpreadsheetPivotRuntimeDefinition,
55-
externalData: false,
5654
dateGranularities: [...dateGranularities],
5755
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
5856
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
@@ -327,18 +327,19 @@ export class PivotCorePlugin extends CorePlugin<CoreState> implements CoreState
327327
if (!pivot) {
328328
continue;
329329
}
330-
for (const measure of pivot.definition.measures) {
330+
const def = deepCopy(pivot.definition);
331+
332+
for (const measure of def.measures) {
331333
if (measure.computedBy?.formula === formulaString) {
332-
const measureIndex = pivot.definition.measures.indexOf(measure);
333-
this.history.update(
334-
"pivots",
335-
pivotId,
336-
"definition",
337-
"measures",
338-
measureIndex,
339-
"computedBy",
340-
{ formula: newFormulaString, sheetId }
341-
);
334+
const measureIndex = def.measures.indexOf(measure);
335+
if (measureIndex !== -1) {
336+
def.measures[measureIndex].computedBy = {
337+
formula: newFormulaString,
338+
sheetId,
339+
};
340+
}
341+
342+
this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
342343
}
343344
}
344345
}

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/formula_formatter";
33
import { toScalar } from "../../functions/helper_matrices";
44
import { toBoolean } from "../../functions/helpers";
5-
import { deepEquals, getUniqueText } from "../../helpers";
5+
import { deepCopy, deepEquals, getUniqueText } from "../../helpers";
66
import {
77
getFirstPivotFunction,
88
getNumberOfPivotFunctions,
@@ -71,9 +71,7 @@ export class PivotUIPlugin extends CoreViewPlugin {
7171
handle(cmd: Command) {
7272
if (invalidateEvaluationCommands.has(cmd.type)) {
7373
for (const pivotId of this.getters.getPivotIds()) {
74-
if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
75-
this.setupPivot(pivotId, { recreate: true });
76-
}
74+
this.setupPivot(pivotId, { recreate: true });
7775
}
7876
}
7977
switch (cmd.type) {
@@ -326,7 +324,7 @@ export class PivotUIPlugin extends CoreViewPlugin {
326324
}
327325

328326
setupPivot(pivotId: UID, { recreate } = { recreate: false }) {
329-
const definition = this.getters.getPivotCoreDefinition(pivotId);
327+
const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
330328
if (!(pivotId in this.pivots)) {
331329
const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
332330
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";
@@ -1024,7 +1023,6 @@ describe("Collaborative local history", () => {
10241023
});
10251024

10261025
test("remove pivot, new user joins, then undo", () => {
1027-
pivotRegistry.get("SPREADSHEET").externalData = true; // simulate external pivot
10281026
const network = new MockTransportService();
10291027
const data = {
10301028
revisionId: DEFAULT_REVISION_ID,
@@ -1072,7 +1070,6 @@ describe("Collaborative local history", () => {
10721070
const bob = new Model(data, configBob, messages);
10731071
undo(alice);
10741072
expect(getEvaluatedCell(bob, "B3").value).toEqual(10);
1075-
pivotRegistry.get("SPREADSHEET").externalData = false;
10761073
});
10771074

10781075
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
@@ -4,6 +4,7 @@ import { positions, toZone } from "../../../src/helpers";
44
import { resetMapValueDimensionDate } from "../../../src/helpers/pivot/spreadsheet_pivot/date_spreadsheet_pivot";
55
import { DEFAULT_LOCALES } from "../../../src/types/locale";
66
import {
7+
addRows,
78
createSheet,
89
deleteContent,
910
deleteSheet,
@@ -640,6 +641,22 @@ describe("Spreadsheet Pivot", () => {
640641
);
641642
});
642643

644+
test("Modifying a sheet structure adapts the pivot range", () => {
645+
const model = createModelWithPivot("A1:I5");
646+
setCellContent(model, "A26", `=pivot(1)`);
647+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
648+
expect(getEvaluatedCell(model, "A26").value).toEqual("My pivot");
649+
addRows(model, "before", 0, 1);
650+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
651+
expect(getEvaluatedCell(model, "A27").value).toEqual("My pivot");
652+
undo(model);
653+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
654+
expect(getEvaluatedCell(model, "A26").value).toEqual("My pivot");
655+
redo(model);
656+
expect(model.getters.getPivot("1").isValid()).toBeTruthy();
657+
expect(getEvaluatedCell(model, "A27").value).toEqual("My pivot");
658+
});
659+
643660
test("Sum with a field that contains a string should work", () => {
644661
const model = createModelWithPivot("A1:I5");
645662
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
@@ -735,11 +735,16 @@ describe("Spreadsheet pivot side panel", () => {
735735
test("Invalid pivot dimensions are displayed as such in the side panel", async () => {
736736
setCellContent(model, "A1", "ValidDimension");
737737
setCellContent(model, "A2", "10");
738-
addPivot(model, "A1:A2", {
739-
columns: [{ fieldName: "ValidDimension" }],
740-
rows: [{ fieldName: "InvalidDimension" }],
741-
});
742-
env.openSidePanel("PivotSidePanel", { pivotId: "1" });
738+
addPivot(
739+
model,
740+
"A1:A2",
741+
{
742+
columns: [{ fieldName: "ValidDimension" }],
743+
rows: [{ fieldName: "InvalidDimension" }],
744+
},
745+
"2"
746+
);
747+
env.openSidePanel("PivotSidePanel", { pivotId: "2" });
743748
await nextTick();
744749
const pivotDimensionEls = fixture.querySelectorAll<HTMLElement>(".pivot-dimension")!;
745750
const validDimensionEl = pivotDimensionEls[0];

0 commit comments

Comments
 (0)