diff --git a/addons/web/static/src/views/graph/graph_search_model.js b/addons/web/static/src/views/graph/graph_search_model.js
new file mode 100644
index 00000000000..ab76cf1831c
--- /dev/null
+++ b/addons/web/static/src/views/graph/graph_search_model.js
@@ -0,0 +1,21 @@
+/** @odoo-module **/
+
+import { SearchModel } from "@web/search/search_model";
+
+export class GraphSearchModel extends SearchModel {
+ _getIrFilterDescription() {
+ this.preparingIrFilterDescription = true;
+ const result = super._getIrFilterDescription(...arguments);
+ this.preparingIrFilterDescription = false;
+ return result;
+ }
+
+ _getSearchItemGroupBys(activeItem) {
+ const { searchItemId } = activeItem;
+ const { context, type } = this.searchItems[searchItemId];
+ if (!this.preparingIrFilterDescription && type === "favorite" && context.graph_groupbys) {
+ return context.graph_groupbys;
+ }
+ return super._getSearchItemGroupBys(...arguments);
+ }
+}
diff --git a/addons/web/static/src/views/graph/graph_view.js b/addons/web/static/src/views/graph/graph_view.js
index 088ae71a40f..9d93e085ab8 100644
--- a/addons/web/static/src/views/graph/graph_view.js
+++ b/addons/web/static/src/views/graph/graph_view.js
@@ -6,6 +6,7 @@ import { GraphArchParser } from "./graph_arch_parser";
import { GraphModel } from "./graph_model";
import { GraphController } from "./graph_controller";
import { GraphRenderer } from "./graph_renderer";
+import { GraphSearchModel } from "./graph_search_model";
const viewRegistry = registry.category("views");
@@ -18,6 +19,7 @@ export const graphView = {
Renderer: GraphRenderer,
Model: GraphModel,
ArchParser: GraphArchParser,
+ SearchModel: GraphSearchModel,
searchMenuTypes: ["filter", "groupBy", "comparison", "favorite"],
buttonTemplate: "web.GraphView.Buttons",
diff --git a/addons/web/static/src/views/pivot/pivot_search_model.js b/addons/web/static/src/views/pivot/pivot_search_model.js
new file mode 100644
index 00000000000..ffe46a6bb6b
--- /dev/null
+++ b/addons/web/static/src/views/pivot/pivot_search_model.js
@@ -0,0 +1,25 @@
+/** @odoo-module **/
+
+import { SearchModel } from "@web/search/search_model";
+
+export class PivotSearchModel extends SearchModel {
+ _getIrFilterDescription() {
+ this.preparingIrFilterDescription = true;
+ const result = super._getIrFilterDescription(...arguments);
+ this.preparingIrFilterDescription = false;
+ return result;
+ }
+
+ _getSearchItemGroupBys(activeItem) {
+ const { searchItemId } = activeItem;
+ const { context, type } = this.searchItems[searchItemId];
+ if (
+ !this.preparingIrFilterDescription &&
+ type === "favorite" &&
+ context.pivot_row_groupby
+ ) {
+ return context.pivot_row_groupby;
+ }
+ return super._getSearchItemGroupBys(...arguments);
+ }
+}
diff --git a/addons/web/static/src/views/pivot/pivot_view.js b/addons/web/static/src/views/pivot/pivot_view.js
index d8ea9e95521..0bf46ca0a18 100644
--- a/addons/web/static/src/views/pivot/pivot_view.js
+++ b/addons/web/static/src/views/pivot/pivot_view.js
@@ -6,6 +6,7 @@ import { PivotArchParser } from "@web/views/pivot/pivot_arch_parser";
import { PivotController } from "./pivot_controller";
import { PivotModel } from "@web/views/pivot/pivot_model";
import { PivotRenderer } from "@web/views/pivot/pivot_renderer";
+import { PivotSearchModel } from "./pivot_search_model";
const viewRegistry = registry.category("views");
@@ -18,6 +19,7 @@ export const pivotView = {
Renderer: PivotRenderer,
Model: PivotModel,
ArchParser: PivotArchParser,
+ SearchModel: PivotSearchModel,
searchMenuTypes: ["filter", "groupBy", "comparison", "favorite"],
buttonTemplate: "web.PivotView.Buttons",
diff --git a/addons/web/static/tests/views/graph_view_tests.js b/addons/web/static/tests/views/graph_view_tests.js
index a472dd8cf64..0ea43c26c76 100644
--- a/addons/web/static/tests/views/graph_view_tests.js
+++ b/addons/web/static/tests/views/graph_view_tests.js
@@ -3676,4 +3676,43 @@ QUnit.module("Views", (hooks) => {
});
checkLabels(assert, graph, ["January 2016", "March 2016", "May 2016", "April 2016"]);
});
+
+ QUnit.test("graph_groupbys should be also used after first load", async function (assert) {
+ const graph = await makeView({
+ serverData,
+ type: "graph",
+ resModel: "foo",
+ groupBy: ["date:quarter"],
+ arch: ``,
+ irFilters: [
+ {
+ user_id: [2, "Mitchell Admin"],
+ name: "Favorite",
+ id: 1,
+ context: `{
+ "group_by": [],
+ "graph_measure": "revenue",
+ "graph_mode": "bar",
+ "graph_groupbys": ["color_id"],
+ }`,
+ sort: "[]",
+ domain: "",
+ is_default: false,
+ model_id: "foo",
+ action_id: false,
+ },
+ ],
+ });
+
+ checkModeIs(assert, graph, "bar");
+ checkLabels(assert, graph, ["Q1 2016", "Q2 2016", "Undefined"]);
+ checkLegend(assert, graph, "Count");
+
+ await toggleFavoriteMenu(target);
+ await toggleMenuItem(target, "Favorite");
+
+ checkModeIs(assert, graph, "bar");
+ checkLabels(assert, graph, ["Undefined", "red"]);
+ checkLegend(assert, graph, "Revenue");
+ });
});
diff --git a/addons/web/static/tests/views/pivot_view_tests.js b/addons/web/static/tests/views/pivot_view_tests.js
index 870ec97919b..e2dea0351e7 100644
--- a/addons/web/static/tests/views/pivot_view_tests.js
+++ b/addons/web/static/tests/views/pivot_view_tests.js
@@ -5383,4 +5383,164 @@ QUnit.module("Views", (hooks) => {
assert.strictEqual(getCurrentValues(target), values.join());
}
);
+
+ QUnit.test("pivot_row_groupby should be also used after first load", async function (assert) {
+ const ids = [1, 2];
+ const expectedContexts = [
+ {
+ group_by: ["bar"],
+ pivot_column_groupby: [],
+ pivot_measures: ["__count"],
+ pivot_row_groupby: ["product_id"],
+ },
+ {
+ group_by: ["bar", "customer"],
+ pivot_column_groupby: [],
+ pivot_measures: ["__count"],
+ pivot_row_groupby: ["customer"],
+ },
+ ];
+
+ await makeView({
+ type: "pivot",
+ resModel: "partner",
+ serverData,
+ arch: ``,
+ searchViewArch: `
+
+
+
+
+ `,
+ groupBy: ["bar"],
+ mockRPC(route, args) {
+ if (args.method === "create_or_replace") {
+ assert.deepEqual(args.args[0].context, expectedContexts.shift());
+ return ids.shift();
+ }
+ },
+ });
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "No", "Yes"],
+ "The row headers should be as expected"
+ );
+
+ await click(target.querySelector("tbody th")); // click on row header "Total"
+ await click(target.querySelector("tbody th")); // click on row header "Total"
+ await click(target.querySelector("tbody .o_group_by_menu .o_menu_item")); // select "Product"
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "xphone", "xpad"],
+ "The row headers should be as expected"
+ );
+
+ await toggleFavoriteMenu(target);
+ await toggleSaveFavorite(target);
+ await editFavoriteName(target, "Favorite");
+ await saveFavorite(target);
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "xphone", "xpad"],
+ "The row headers should be as expected"
+ );
+
+ await removeFacet(target);
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "No", "Yes"],
+ "The row headers should be as expected"
+ );
+
+ await toggleFavoriteMenu(target);
+ await toggleMenuItem(target, "Favorite");
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "xphone", "xpad"],
+ "The row headers should be as expected"
+ );
+
+ await toggleGroupByMenu(target);
+ await toggleMenuItem(target, "Customer");
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "xphone", "First", "xpad", "First", "Second"],
+ "The row headers should be as expected"
+ );
+
+ await click(target.querySelector("tbody th")); // click on row header "Total"
+ await click(target.querySelector("tbody th")); // click on row header "Total"
+ await click(target.querySelectorAll("tbody .o_group_by_menu .o_menu_item")[1]); // select "Customer"
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "First", "Second"],
+ "The row headers should be as expected"
+ );
+
+ await toggleFavoriteMenu(target);
+ await toggleSaveFavorite(target);
+ await editFavoriteName(target, "Favorite 2");
+ await saveFavorite(target);
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "First", "Second"],
+ "The row headers should be as expected"
+ );
+ });
+
+ QUnit.test(
+ "pivot_row_groupby should be also used after first load (2)",
+ async function (assert) {
+ await makeView({
+ serverData,
+ type: "pivot",
+ resModel: "partner",
+ groupBy: ["product_id"],
+ arch: ``,
+ irFilters: [
+ {
+ user_id: [2, "Mitchell Admin"],
+ name: "Favorite",
+ id: 1,
+ context: `
+ {
+ "group_by": [],
+ "pivot_row_groupby": ["customer"],
+ "pivot_col_groupby": [],
+ "pivot_measures": ["foo"],
+ }
+ `,
+ sort: "[]",
+ domain: "",
+ is_default: false,
+ model_id: "foo",
+ action_id: false,
+ },
+ ],
+ });
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "xphone", "xpad"],
+ "The row headers should be as expected"
+ );
+
+ await toggleFavoriteMenu(target);
+ await toggleMenuItem(target, "Favorite");
+
+ assert.deepEqual(
+ [...target.querySelectorAll("th")].slice(3).map((el) => el.innerText),
+ ["Total", "First", "Second"],
+ "The row headers should be as expected"
+ );
+ }
+ );
});