[FIX] spreadsheet: accumulate starting with past data

- Inserting a cumulated graph view inside spreadsheet
  (e.g. cumulated subscription MRR breakdown),
- apply a global filter to filter, let's say on the current
  month
=> the first data point does not include data from before
the current month (the accumulation starts at 0, even though
there is data before)

You can also check the MRR evolution subscription dashboard,
"MRR over time" chart.

Note: with this fix, we assume all "cumulated" charts are also
"cumulated_start". Which is true in practice (only one cumulated
graph view in the entire codebase)

closes odoo/odoo#149084

Task: 3680601
Related: odoo/enterprise#54146
Signed-off-by: Lucas Lefèvre (lul) <lul@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2024-01-12 14:27:47 +00:00
parent c113e96228
commit 0111317b75
4 changed files with 70 additions and 13 deletions
@@ -22,6 +22,7 @@ import { ChartDataSource } from "../data_source/chart_data_source";
* @property {string} title
* @property {string} background
* @property {string} legendPosition
* @property {boolean} cumulative
*
* @typedef OdooChartDefinitionDataSource
* @property {MetaData} metaData
@@ -38,7 +39,14 @@ export class OdooChart extends AbstractChart {
constructor(definition, sheetId, getters) {
super(definition, sheetId, getters);
this.type = definition.type;
this.metaData = definition.metaData;
this.metaData = {
...definition.metaData,
mode: this.type.replace("odoo_", ""),
cumulated: definition.cumulative,
// if a chart is cumulated, the first data point should take into
// account past data, even if a domain on a specific period is applied
cumulatedStart: definition.cumulative,
};
this.searchParams = definition.searchParams;
this.legendPosition = definition.legendPosition;
this.background = definition.background;
@@ -62,10 +70,7 @@ export class OdooChart extends AbstractChart {
*/
getDefinitionForDataSource() {
return {
metaData: {
...this.metaData,
mode: this.type.replace("odoo_", ""),
},
metaData: this.metaData,
searchParams: this.searchParams,
};
}
@@ -51,7 +51,7 @@ function createOdooChartRuntime(chart, getters) {
const locale = getters.getLocale();
const chartJsConfig = getLineConfiguration(chart, labels, locale);
const colors = new ChartColors();
for (let [index, { label, data }] of datasets.entries()) {
for (let [index, { label, data, cumulatedStart }] of datasets.entries()) {
const color = colors.next();
const backgroundRGBA = colorToRGBA(color);
if (chart.stacked) {
@@ -59,7 +59,7 @@ function createOdooChartRuntime(chart, getters) {
backgroundRGBA.a = LINE_FILL_TRANSPARENCY;
}
if (chart.cumulative) {
let accumulator = 0;
let accumulator = cumulatedStart;
data = data.map((value) => {
accumulator += value;
return accumulator;
@@ -12,6 +12,7 @@ import { THIS_YEAR_GLOBAL_FILTER } from "../../utils/global_filter";
import * as spreadsheet from "@odoo/o-spreadsheet";
import { makeServerError } from "@web/../tests/helpers/mock_server";
import { session } from "@web/session";
import { getBasicServerData } from "../../utils/data";
const { toZone } = spreadsheet.helpers;
@@ -523,6 +524,52 @@ QUnit.module("spreadsheet > odoo chart plugin", {}, () => {
);
});
QUnit.test("cumulative line chart with past data before domain period", async (assert) => {
const serverData = getBasicServerData();
serverData.models.partner.records = [
{ date: "2020-01-01", probability: 10 },
{ date: "2021-01-01", probability: 2 },
{ date: "2022-01-01", probability: 3 },
{ date: "2022-03-01", probability: 4 },
{ date: "2022-06-01", probability: 5 },
];
const { model } = await createSpreadsheetWithChart({
type: "odoo_line",
serverData,
definition: {
type: "odoo_line",
metaData: {
groupBy: ["date"],
measure: "probability",
order: null,
resModel: "partner",
},
searchParams: {
comparison: null,
context: {},
domain: [
["date", ">=", "2022-01-01"],
["date", "<=", "2022-12-31"],
],
groupBy: [],
orderBy: [],
},
cumulative: true,
title: "Partners",
dataSourceId: "42",
id: "42",
},
});
const sheetId = model.getters.getActiveSheetId();
const chartId = model.getters.getChartIds(sheetId)[0];
await waitForDataSourcesLoaded(model);
assert.deepEqual(
model.getters.getChartRuntime(chartId).chartJsConfig.data.datasets[0].data,
[15, 19, 24]
);
});
QUnit.test("Can insert odoo chart from a different model", async (assert) => {
const model = await createModelWithDataSource();
insertListInSpreadsheet(model, { model: "product", columns: ["name"] });
+11 -6
View File
@@ -10,9 +10,14 @@ const uuidGenerator = new spreadsheet.helpers.UuidGenerator();
/**
*
* @param {Model} model
* @param {string} type
* @param {import("@spreadsheet/chart/odoo_chart/odoo_chart").OdooChartDefinition} definition
*/
export function insertChartInSpreadsheet(model, type = "odoo_bar") {
const definition = getChartDefinition(type);
export function insertChartInSpreadsheet(
model,
type = "odoo_bar",
definition = getChartDefinition(type)
) {
model.dispatch("CREATE_CHART", {
sheetId: model.getters.getActiveSheetId(),
id: definition.id,
@@ -26,17 +31,17 @@ export function insertChartInSpreadsheet(model, type = "odoo_bar") {
/**
*
* @param {Object} params
* @param {function} [params.definition]
* @param {function} [params.mockRPC]
* @param {string} [params.type]
* @param {import("./data").ServerData} [params.serverData]
*
* @returns { Promise<{ model: Model, env: Object }>}
*/
export async function createSpreadsheetWithChart(params = {}) {
const model = await createModelWithDataSource({
mockRPC: params.mockRPC,
});
const model = await createModelWithDataSource(params);
insertChartInSpreadsheet(model, params.type);
insertChartInSpreadsheet(model, params.type, params.definition);
const env = model.config.custom.env;
env.model = model;