summaryrefslogtreecommitdiff
path: root/addons/web/static/tests/views/pivot_tests.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/tests/views/pivot_tests.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web/static/tests/views/pivot_tests.js')
-rw-r--r--addons/web/static/tests/views/pivot_tests.js3294
1 files changed, 3294 insertions, 0 deletions
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
new file mode 100644
index 00000000..9760cfbc
--- /dev/null
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -0,0 +1,3294 @@
+odoo.define('web.pivot_tests', function (require) {
+"use strict";
+
+var core = require('web.core');
+var PivotView = require('web.PivotView');
+const PivotController = require("web.PivotController");
+var testUtils = require('web.test_utils');
+var testUtilsDom = require('web.test_utils_dom');
+
+var _t = core._t;
+const cpHelpers = testUtils.controlPanel;
+var createActionManager = testUtils.createActionManager;
+var createView = testUtils.createView;
+var patchDate = testUtils.mock.patchDate;
+
+/**
+ * Helper function that returns, given a pivot instance, the values of the
+ * table, separated by ','.
+ *
+ * @returns {string}
+ */
+var getCurrentValues = function (pivot) {
+ return pivot.$('.o_pivot_cell_value div').map(function () {
+ return $(this).text();
+ }).get().join();
+};
+
+
+QUnit.module('Views', {
+ beforeEach: function () {
+ this.data = {
+ partner: {
+ fields: {
+ foo: {string: "Foo", type: "integer", searchable: true, group_operator: 'sum'},
+ bar: {string: "bar", type: "boolean", store: true, sortable: true},
+ date: {string: "Date", type: "date", store: true, sortable: true},
+ product_id: {string: "Product", type: "many2one", relation: 'product', store: true},
+ other_product_id: {string: "Other Product", type: "many2one", relation: 'product', store: true},
+ non_stored_m2o: {string: "Non Stored M2O", type: "many2one", relation: 'product'},
+ customer: {string: "Customer", type: "many2one", relation: 'customer', store: true},
+ computed_field: {string: "Computed and not stored", type: 'integer', compute: true, group_operator: 'sum'},
+ company_type: {
+ string: "Company Type", type: "selection",
+ selection: [["company", "Company"], ["individual", "individual"]],
+ searchable: true, sortable: true, store: true,
+ },
+ },
+ records: [
+ {
+ id: 1,
+ foo: 12,
+ bar: true,
+ date: '2016-12-14',
+ product_id: 37,
+ customer: 1,
+ computed_field: 19,
+ company_type: 'company',
+ }, {
+ id: 2,
+ foo: 1,
+ bar: true,
+ date: '2016-10-26',
+ product_id: 41,
+ customer: 2,
+ computed_field: 23,
+ company_type: 'individual',
+ }, {
+ id: 3,
+ foo: 17,
+ bar: true,
+ date: '2016-12-15',
+ product_id: 41,
+ customer: 2,
+ computed_field: 26,
+ company_type: 'company',
+ }, {
+ id: 4,
+ foo: 2,
+ bar: false,
+ date: '2016-04-11',
+ product_id: 41,
+ customer: 1,
+ computed_field: 19,
+ company_type: 'individual',
+ },
+ ]
+ },
+ product: {
+ fields: {
+ name: {string: "Product Name", type: "char"}
+ },
+ records: [{
+ id: 37,
+ display_name: "xphone",
+ }, {
+ id: 41,
+ display_name: "xpad",
+ }]
+ },
+ customer: {
+ fields: {
+ name: {string: "Customer Name", type: "char"}
+ },
+ records: [{
+ id: 1,
+ display_name: "First",
+ }, {
+ id: 2,
+ display_name: "Second",
+ }]
+ },
+ };
+ },
+}, function () {
+ QUnit.module('PivotView');
+
+ QUnit.test('simple pivot rendering', async function (assert) {
+ assert.expect(3);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function (route, args) {
+ assert.strictEqual(args.kwargs.lazy, false,
+ "the read_group should be done with the lazy=false option");
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.hasClass(pivot.$('table'), 'o_enable_linking',
+ "table should have classname 'o_enable_linking'");
+ assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(32)').length, 1,
+ "should contain a pivot cell with the sum of all records");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot rendering with widget', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '<field name="foo" type="measure" widget="float_time"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(32:00)').length, 1,
+ "should contain a pivot cell with the sum of all records");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot rendering with string attribute on field', async function (assert) {
+ assert.expect(1);
+
+ this.data.partner.fields.foo = {string: "Foo", type: "integer", store: true, group_operator: 'sum'};
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '<field name="foo" string="BAR" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual(pivot.$('.o_pivot_measure_row').text(), "BAR",
+ "the displayed name should be the one set in the string attribute");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot rendering with string attribute on non stored field', async function (assert) {
+ assert.expect(1);
+
+ this.data.partner.fields.fubar = {string: "Fubar", type: "integer", store: false, group_operator: 'sum'};
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '<field name="fubar" string="fubar" type="measure"/>' +
+ '</pivot>',
+ });
+ assert.containsOnce(pivot, '.o_pivot', 'Non stored fields can have a string attribute');
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot rendering with invisible attribute on field', async function (assert) {
+ assert.expect(3);
+ // when invisible, a field should neither be an active measure,
+ // nor be a selectable measure.
+ _.extend(this.data.partner.fields, {
+ foo: {string: "Foo", type: "integer", store: true, group_operator: 'sum'},
+ foo2: {string: "Foo2", type: "integer", store: true, group_operator: 'sum'}
+ });
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '<field name="foo" type="measure"/>' +
+ '<field name="foo2" type="measure" invisible="True"/>' +
+ '</pivot>',
+ });
+
+ // there should be only one displayed measure as the other one is invisible
+ assert.containsOnce(pivot, '.o_pivot_measure_row');
+ // there should be only one measure besides count, as the other one is invisible
+ assert.containsN(pivot, '.o_cp_bottom_left .dropdown-item', 2);
+ // the invisible field souldn't be in the groupable fields neither
+ await testUtils.dom.click(pivot.$('.o_pivot_header_cell_closed:first'));
+ assert.containsNone(pivot, '.o_pivot_field_menu a[data-field="foo2"]');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view without "string" attribute', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ // this is important for export functionality.
+ assert.strictEqual(pivot.title, _t("Untitled"), "should have a valid title");
+ pivot.destroy();
+ });
+
+ QUnit.test('group headers should have a tooltip', async function (assert) {
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="col"/>' +
+ '<field name="date" type="row"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:first').attr('data-original-title'), 'Date');
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:first').attr('data-original-title'), 'Product');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view add computed fields explicitly defined as measure', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="computed_field" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.ok(pivot.measures.computed_field, "measures contains the field 'computed_field'");
+ pivot.destroy();
+ });
+
+ QUnit.test('clicking on a cell triggers a do_action', async function (assert) {
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ intercepts: {
+ do_action: function (ev) {
+ assert.deepEqual(ev.data.action, {
+ context: {someKey: true, userContextKey: true},
+ domain: [['product_id', '=', 37]],
+ name: 'Partners',
+ res_model: 'partner',
+ target: 'current',
+ type: 'ir.actions.act_window',
+ view_mode: 'list',
+ views: [[false, 'list'], [2, 'form']],
+ }, "should trigger do_action with the correct args");
+ },
+ },
+ session: {
+ user_context: {userContextKey: true},
+ },
+ viewOptions: {
+ action: {
+ views: [
+ { viewID: 2, type: 'form' },
+ { viewID: 5, type: 'kanban' },
+ { viewID: false, type: 'list' },
+ { viewID: false, type: 'pivot' },
+ ],
+ },
+ context: {someKey: true, search_default_test: 3},
+ title: 'Partners',
+ }
+ });
+
+ assert.hasClass(pivot.$('table'), 'o_enable_linking',
+ "table should have classname 'o_enable_linking'");
+ await testUtils.dom.click(pivot.$('.o_pivot_cell_value:contains(12)')); // should trigger a do_action
+
+ pivot.destroy();
+ });
+
+ QUnit.test('row and column are highlighted when hovering a cell', async function (assert) {
+ assert.expect(11);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '<field name="foo" type="col"/>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ });
+
+ // check row highlighting
+ assert.hasClass(pivot.$('table'), 'table-hover',
+ "with className 'table-hover', rows are highlighted (bootstrap)");
+
+ // check column highlighting
+ // hover third measure
+ await testUtils.dom.triggerEvents(pivot.$('th.o_pivot_measure_row:nth(2)'), 'mouseover');
+ assert.containsN(pivot, '.o_cell_hover', 3);
+ for (var i = 0; i < 3; i++) {
+ assert.hasClass(pivot.$('tbody tr:nth(' + i + ') td:nth(2)'), 'o_cell_hover');
+ }
+ await testUtils.dom.triggerEvents(pivot.$('th.o_pivot_measure_row:nth(2)'), 'mouseout');
+ assert.containsNone(pivot, '.o_cell_hover');
+
+ // hover second cell, second row
+ await testUtils.dom.triggerEvents(pivot.$('tbody tr:nth(1) td:nth(1)'), 'mouseover');
+ assert.containsN(pivot, '.o_cell_hover', 3);
+ for (i = 0; i < 3; i++) {
+ assert.hasClass(pivot.$('tbody tr:nth(' + i + ') td:nth(1)'), 'o_cell_hover');
+ }
+ await testUtils.dom.triggerEvents(pivot.$('tbody tr:nth(1) td:nth(1)'), 'mouseout');
+ assert.containsNone(pivot, '.o_cell_hover');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view with disable_linking="True"', async function (assert) {
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot disable_linking="True">' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ intercepts: {
+ do_action: function () {
+ assert.ok(false, "should not trigger do_action");
+ },
+ },
+ });
+
+ assert.doesNotHaveClass(pivot.$('table'), 'o_enable_linking',
+ "table should not have classname 'o_enable_linking'");
+ assert.containsOnce(pivot, '.o_pivot_cell_value',
+ "should have one cell");
+ await testUtils.dom.click(pivot.$('.o_pivot_cell_value')); // should not trigger a do_action
+
+ pivot.destroy();
+ });
+
+ QUnit.test('clicking on the "Total" Cell with time range activated gives the right action domain', async function (assert) {
+ assert.expect(2);
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot/>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
+ </search>
+ `,
+ },
+ intercepts: {
+ do_action: function (ev) {
+ assert.deepEqual(
+ ev.data.action.domain,
+ ["&",["date",">=","2016-12-01"],["date","<=","2016-12-31"]],
+ "should trigger do_action with the correct action domain"
+ );
+ },
+ },
+ viewOptions: {
+ context: { search_default_date_filter: true, },
+ title: 'Partners',
+ },
+ });
+
+ assert.hasClass(pivot.$('table'), 'o_enable_linking',
+ "root node should have classname 'o_enable_linking'");
+ await testUtilsDom.click(pivot.$('.o_pivot_cell_value'));
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('clicking on a fake cell value ("empty group") in comparison mode gives an action domain equivalent to [[0,"=",1]]', async function (assert) {
+ assert.expect(3);
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+
+ this.data.partner.records[0].date = '2016-11-15';
+ this.data.partner.records[1].date = '2016-11-17';
+ this.data.partner.records[2].date = '2016-11-22';
+ this.data.partner.records[3].date = '2016-11-03';
+
+ var first_do_action = true;
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ intercepts: {
+ do_action: function (ev) {
+ if (first_do_action) {
+ assert.deepEqual(
+ ev.data.action.domain,
+ ["&",["date",">=","2016-12-01"],["date","<=","2016-12-31"]],
+ "should trigger do_action with the correct action domain"
+ );
+ } else {
+ assert.deepEqual(
+ ev.data.action.domain,
+ [[0, "=", 1]],
+ "should trigger do_action with the correct action domain"
+ );
+ }
+ first_do_action = false;
+ },
+ },
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ context: { search_default_date_filter: true, },
+ title: 'Partners',
+ },
+ });
+
+ await cpHelpers.toggleComparisonMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ assert.hasClass(pivot.$('table'), 'o_enable_linking',
+ "root node should have classname 'o_enable_linking'");
+ // here we click on the group corresponding to Total/Total/This Month
+ pivot.$('.o_pivot_cell_value').eq(1).click(); // should trigger a do_action with appropriate domain
+ // here we click on the group corresponding to xphone/Total/This Month
+ pivot.$('.o_pivot_cell_value').eq(4).click(); // should trigger a do_action with appropriate domain
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view grouped by date field', async function (assert) {
+ assert.expect(2);
+
+ var data = this.data;
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function (route, params) {
+ var wrong_fields = _.filter(params.kwargs.fields, function (field) {
+ return !(field.split(':')[0] in data.partner.fields);
+ });
+ assert.ok(!wrong_fields.length, 'fields given to read_group should exist on the model');
+ return this._super.apply(this, arguments);
+ },
+ });
+ pivot.destroy();
+ });
+
+ QUnit.test('without measures, pivot view uses __count by default', async function (assert) {
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot></pivot>',
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group') {
+ assert.deepEqual(args.kwargs.fields, ['__count'],
+ "should make a read_group with no valid fields");
+ }
+ return this._super(route, args);
+ }
+ });
+
+ var $countMeasure = pivot.$buttons.find('.dropdown-item[data-field=__count]:first');
+ assert.hasClass($countMeasure, 'selected', "The count measure should be activated");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view can be reloaded', async function (assert) {
+ assert.expect(4);
+ var readGroupCount = 0;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot></pivot>',
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group') {
+ readGroupCount++;
+ }
+ return this._super(route, args);
+ }
+ });
+
+ assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(4)').length, 1,
+ "should contain a pivot cell with the number of all records");
+ assert.strictEqual(readGroupCount, 1, "should have done 1 rpc");
+
+ await testUtils.pivot.reload(pivot, {domain: [['foo', '>', 10]]});
+ assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(2)').length, 1,
+ "should contain a pivot cell with the number of remaining records");
+ assert.strictEqual(readGroupCount, 2, "should have done 2 rpcs");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view grouped by many2one field', async function (assert) {
+ assert.expect(3);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.containsOnce(pivot, '.o_pivot_header_cell_opened',
+ "should have one opened header");
+ assert.strictEqual(pivot.$('.o_pivot_header_cell_closed:contains(xphone)').length, 1,
+ "should display one header with 'xphone'");
+ assert.strictEqual(pivot.$('.o_pivot_header_cell_closed:contains(xpad)').length, 1,
+ "should display one header with 'xpad'");
+ pivot.destroy();
+ });
+
+ QUnit.test('basic folding/unfolding', async function (assert) {
+ assert.expect(7);
+
+ var rpcCount = 0;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function () {
+ rpcCount++;
+ return this._super.apply(this, arguments);
+ },
+ });
+ assert.containsN(pivot, 'tbody tr', 3,
+ "should have 3 rows: 1 for the opened header, and 2 for data");
+
+ // click on the opened header to close it
+ await testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened'));
+
+ assert.containsOnce(pivot, 'tbody tr', "should have 1 row");
+
+ // click on closed header to open dropdown
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed'));
+ assert.containsN(pivot, '.o_pivot_field_menu .dropdown-item[data-field="date"]', 6,
+ "should have the date field as proposition (Date, Day, Week, Month, Quarter and Year)");
+ assert.containsOnce(pivot, '.o_pivot_field_menu .dropdown-item[data-field="product_id"]',
+ "should have the product_id field as proposition");
+ assert.containsNone(pivot, '.o_pivot_field_menu .dropdown-item[data-field="non_stored_m2o"]',
+ "should not have the non_stored_m2o field as proposition");
+
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:first'));
+
+ assert.containsN(pivot, 'tbody tr', 4,
+ "should have 4 rows: one for header, 3 for data");
+ assert.strictEqual(rpcCount, 3,
+ "should have done 3 rpcs (initial load) + open header with different groupbys");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('more folding/unfolding', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ // open dropdown to zoom into first row
+ await testUtils.dom.clickFirst(pivot.$('tbody .o_pivot_header_cell_closed'));
+ // click on date by day
+ pivot.$('.dropdown-menu.show .o_inline_dropdown .dropdown-menu').toggle(); // unfold inline dropdown
+ await testUtils.nextTick();
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:contains("Day")'));
+
+ // open dropdown to zoom into second row
+ await testUtils.dom.clickLast(pivot.$('tbody th.o_pivot_header_cell_closed'));
+
+ assert.containsN(pivot, 'tbody tr', 7,
+ "should have 7 rows (1 for total, 1 for xphone, 1 for xpad, 4 for data)");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('fold and unfold header group', async function (assert) {
+ assert.expect(3);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.containsN(pivot, 'thead tr', 3);
+
+ // fold opened col group
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_opened'));
+ assert.containsN(pivot, 'thead tr', 2);
+
+ // unfold it
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]'));
+ assert.containsN(pivot, 'thead tr', 3);
+
+ pivot.destroy();
+ });
+
+ QUnit.test('unfold second header group', async function (assert) {
+ assert.expect(4);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.containsN(pivot, 'thead tr', 3);
+ var values = ['12', '20', '32'];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ // unfold it
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="company_type"]'));
+ assert.containsN(pivot, 'thead tr', 4);
+ values = ['12', '3', '17', '32'];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ pivot.destroy();
+ });
+
+ QUnit.test('can toggle extra measure', async function (assert) {
+ assert.expect(8);
+
+ var rpcCount = 0;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function () {
+ rpcCount++;
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.containsN(pivot, '.o_pivot_cell_value', 3,
+ "should have 3 cells: 1 for the open header, and 2 for data");
+ assert.doesNotHaveClass(pivot.$buttons.find('.dropdown-item[data-field=__count]:first'), 'selected',
+ "the __count measure should not be selected");
+
+ rpcCount = 0;
+ await testUtils.pivot.toggleMeasuresDropdown(pivot);
+ await testUtils.pivot.clickMeasure(pivot, '__count');
+
+ assert.hasClass(pivot.$buttons.find('.dropdown-item[data-field=__count]:first'), 'selected',
+ "the __count measure should be selected");
+ assert.containsN(pivot, '.o_pivot_cell_value', 6,
+ "should have 6 cells: 2 for the open header, and 4 for data");
+ assert.strictEqual(rpcCount, 2,
+ "should have done 2 rpcs to reload data");
+
+ await testUtils.pivot.clickMeasure(pivot, '__count');
+
+ assert.doesNotHaveClass(pivot.$buttons.find('.dropdown-item[data-field=__count]:first'), 'selected',
+ "the __count measure should not be selected");
+ assert.containsN(pivot, '.o_pivot_cell_value', 3,
+ "should have 3 cells: 1 for the open header, and 2 for data");
+ assert.strictEqual(rpcCount, 2,
+ "should not have done any extra rpcs");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('no content helper when no active measure', async function (assert) {
+ assert.expect(4);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '</pivot>',
+ });
+
+ assert.containsNone(pivot, '.o_view_nocontent',
+ "should not have a no_content_helper");
+ assert.containsOnce(pivot, 'table',
+ "should have a table in DOM");
+
+ await testUtils.pivot.toggleMeasuresDropdown(pivot);
+ await testUtils.pivot.clickMeasure(pivot, '__count');
+
+ assert.containsOnce(pivot, '.o_view_nocontent',
+ "should have a no_content_helper");
+ assert.containsNone(pivot, 'table',
+ "should not have a table in DOM");
+ pivot.destroy();
+ });
+
+ QUnit.test('no content helper when no data', async function (assert) {
+ assert.expect(4);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners">' +
+ '</pivot>',
+ });
+
+ assert.containsNone(pivot, '.o_view_nocontent',
+ "should not have a no_content_helper");
+ assert.containsOnce(pivot, 'table',
+ "should have a table in DOM");
+
+ await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]});
+
+ assert.containsOnce(pivot, '.o_view_nocontent',
+ "should have a no_content_helper");
+ assert.containsNone(pivot, 'table',
+ "should not have a table in DOM");
+ pivot.destroy();
+ });
+
+ QUnit.test('no content helper when no data, part 2', async function (assert) {
+ assert.expect(1);
+
+ this.data.partner.records = [];
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners"></pivot>',
+ });
+
+ assert.containsOnce(pivot, '.o_view_nocontent',
+ "should have a no_content_helper");
+ pivot.destroy();
+ });
+
+ QUnit.test('no content helper when no data, part 3', async function (assert) {
+ assert.expect(4);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot string="Partners"></pivot>',
+ viewOptions: {
+ domain: [['foo', '=', 12345]]
+ },
+ });
+
+ assert.containsOnce(pivot, '.o_view_nocontent',
+ "should have a no_content_helper");
+ await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]});
+ assert.containsOnce(pivot, '.o_view_nocontent',
+ "should still have a no_content_helper");
+ await testUtils.pivot.reload(pivot, {domain: []});
+ assert.containsNone(pivot, '.o_view_nocontent',
+ "should not have a no_content_helper");
+
+ // tries to open a field selection menu, to make sure it was not
+ // removed from the dom.
+ await testUtils.dom.clickFirst(pivot.$('.o_pivot_header_cell_closed'));
+ assert.containsOnce(pivot, '.o_pivot_field_menu',
+ "the field selector menu exists");
+ pivot.destroy();
+ });
+
+ QUnit.test('tries to restore previous state after domain change', async function (assert) {
+ assert.expect(5);
+
+ var rpcCount = 0;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function () {
+ rpcCount++;
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.containsN(pivot, '.o_pivot_cell_value', 3,
+ "should have 3 cells: 1 for the open header, and 2 for data");
+ assert.strictEqual(pivot.$('.o_pivot_measure_row:contains(Foo)').length, 1,
+ "should have 1 row for measure Foo");
+
+ await testUtils.pivot.reload(pivot, {domain: [['foo', '=', 12345]]});
+
+ rpcCount = 0;
+ await testUtils.pivot.reload(pivot, {domain: []});
+
+ assert.equal(rpcCount, 2, "should have reloaded data");
+ assert.containsN(pivot, '.o_pivot_cell_value', 3,
+ "should still have 3 cells: 1 for the open header, and 2 for data");
+ assert.strictEqual(pivot.$('.o_pivot_measure_row:contains(Foo)').length, 1,
+ "should still have 1 row for measure Foo");
+ pivot.destroy();
+ });
+
+ QUnit.test('can be grouped with the update function', async function (assert) {
+ assert.expect(4);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.containsOnce(pivot, '.o_pivot_cell_value',
+ "should have only 1 cell");
+ assert.containsOnce(pivot, 'tbody tr',
+ "should have 1 rows");
+
+ await testUtils.pivot.reload(pivot, {groupBy: ['product_id']});
+
+ assert.containsN(pivot, '.o_pivot_cell_value', 3,
+ "should have 3 cells");
+ assert.containsN(pivot, 'tbody tr', 3,
+ "should have 3 rows");
+ pivot.destroy();
+ });
+
+ QUnit.test('can sort data in a column by clicking on header', async function (assert) {
+ assert.expect(3);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual($('td.o_pivot_cell_value').text(), "321220",
+ "should have proper values in cells (total, result 1, result 2");
+
+ await testUtils.dom.click(pivot.$('th.o_pivot_measure_row'));
+
+ assert.strictEqual($('td.o_pivot_cell_value').text(), "321220",
+ "should have proper values in cells (total, result 1, result 2");
+
+ await testUtils.dom.click(pivot.$('th.o_pivot_measure_row'));
+
+ assert.strictEqual($('td.o_pivot_cell_value').text(), "322012",
+ "should have proper values in cells (total, result 2, result 1");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('can expand all rows', async function (assert) {
+ assert.expect(7);
+
+ var nbReadGroups = 0;
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group') {
+ nbReadGroups++;
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.strictEqual(nbReadGroups, 2, "should have done 2 read_group RPCS");
+ assert.strictEqual(pivot.$('td.o_pivot_cell_value').text(), "321220",
+ "should have proper values in cells (total, result 1, result 2)");
+
+ // expand on date:days, product
+ nbReadGroups = 0;
+ await testUtils.pivot.reload(pivot, {groupBy: ['date:days', 'product_id']});
+
+ assert.strictEqual(nbReadGroups, 3, "should have done 3 read_group RPCS");
+ assert.containsN(pivot, 'tbody tr', 8,
+ "should have 7 rows (total + 3 for December and 2 for October and April)");
+
+ // collapse the last two rows
+ await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened'));
+ await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened'));
+
+ assert.containsN(pivot, 'tbody tr', 6,
+ "should have 6 rows now");
+
+ // expand all
+ nbReadGroups = 0;
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button'));
+
+ assert.strictEqual(nbReadGroups, 3, "should have done 3 read_group RPCS");
+ assert.containsN(pivot, 'tbody tr', 8,
+ "should have 8 rows again");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('expand all with a delay', async function (assert) {
+ assert.expect(3);
+
+ var def;
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'read_group') {
+ return Promise.resolve(def).then(_.constant(result));
+ }
+ return result;
+ },
+ });
+
+ // expand on date:days, product
+ await testUtils.pivot.reload(pivot, {groupBy: ['date:days', 'product_id']});
+ assert.containsN(pivot, 'tbody tr', 8,
+ "should have 7 rows (total + 3 for December and 2 for October and April)");
+
+ // collapse the last two rows
+ await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened'));
+ await testUtils.dom.clickLast(pivot.$('.o_pivot_header_cell_opened'));
+
+ assert.containsN(pivot, 'tbody tr', 6,
+ "should have 6 rows now");
+
+ // expand all
+ def = testUtils.makeTestPromise();
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_expand_button'));
+ await testUtils.nextTick();
+ def.resolve();
+ // await testUtils.returnAfterNextAnimationFrame();
+ await testUtils.nextTick();
+ assert.containsN(pivot, 'tbody tr', 8,
+ "should have 8 rows again");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('can download a file', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ session: {
+ get_file: function (args) {
+ assert.strictEqual(args.url, '/web/pivot/export_xlsx',
+ "should call get_file with correct parameters");
+ args.complete();
+ },
+ },
+ });
+
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_download'));
+ pivot.destroy();
+ });
+
+ QUnit.test('download a file with single measure, measure row displayed in table', async function (assert) {
+ assert.expect(1);
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ session: {
+ get_file: function (args) {
+ const data = JSON.parse(args.data.data);
+ assert.strictEqual(data.measure_headers.length, 4,
+ "should have measure_headers in data");
+ args.complete();
+ },
+ },
+ });
+
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_download'));
+ pivot.destroy();
+ });
+
+ QUnit.test('download button is disabled when there is no data', async function (assert) {
+ assert.expect(1);
+
+ this.data.partner.records = [];
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.hasAttrValue(pivot.$buttons.find('.o_pivot_download'), 'disabled', 'disabled',
+ "download button should be disabled");
+ pivot.destroy();
+ });
+
+ QUnit.test('getOwnedQueryParams correctly returns measures and groupbys', async function (assert) {
+ assert.expect(3);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['date:day'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: [],
+ },
+ }, "context should be correct");
+
+ // expand header on field customer
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first'));
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['date:day', 'customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: [],
+ },
+ }, "context should be correct");
+
+ // expand row on field product_id
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first'));
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['date:day', 'customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly remove pivot_ keys from the context', async function (assert) {
+ assert.expect(5);
+
+ this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'};
+
+ // Equivalent to loading with default filter
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="col"/>' +
+ '<field name="amount" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ },
+ });
+
+ // Equivalent to unload the filter
+ var reloadParams = {
+ context: {},
+ };
+ await pivot.reload(reloadParams);
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ // Let's get rid of the rows groupBy
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_opened'));
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: [],
+ },
+ }, "context should be correct");
+
+ // And now, get rid of the col groupby
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_opened'));
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: [],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: [],
+ },
+ }, "context should be correct");
+
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first'));
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: [],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]:first'));
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ }, "context should be correct");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('Unload Filter, reset display, load another filter', async function (assert) {
+ assert.expect(18);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ },
+ });
+
+ // Check Columns
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1,
+ 'The column should be grouped');
+ assert.strictEqual(pivot.$('thead tr:contains("First")').length, 1,
+ 'There should be a column "First"');
+ assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 1,
+ 'There should be a column "Second"');
+
+ // Check Rows
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1,
+ 'The row should be grouped');
+ assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 1,
+ 'There should be a row "xphone"');
+ assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 1,
+ 'There should be a row "xpad"');
+
+ // Equivalent to unload the filter
+ var reloadParams = {
+ context: {},
+ };
+ await pivot.reload(reloadParams);
+ // collapse all headers
+ await testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened:first'));
+ await testUtils.dom.click(pivot.$('.o_pivot_header_cell_opened'));
+
+ // Check Columns
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed').length, 1,
+ 'The column should not be grouped');
+ assert.strictEqual(pivot.$('thead tr:contains("First")').length, 0,
+ 'There should not be a column "First"');
+ assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 0,
+ 'There should not be a column "Second"');
+
+ // Check Rows
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed').length, 1,
+ 'The row should not be grouped');
+ assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 0,
+ 'There should not be a row "xphone"');
+ assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 0,
+ 'There should not be a row "xpad"');
+
+ // Equivalent to load another filter
+ reloadParams = {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ };
+ await pivot.reload(reloadParams);
+
+ // Check Columns
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1,
+ 'The column should be grouped');
+ assert.strictEqual(pivot.$('thead tr:contains("First")').length, 1,
+ 'There should be a column "First"');
+ assert.strictEqual(pivot.$('thead tr:contains("Second")').length, 1,
+ 'There should be a column "Second"');
+
+ // Check Rows
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1,
+ 'The row should be grouped');
+ assert.strictEqual(pivot.$('tbody tr:contains("xphone")').length, 1,
+ 'There should be a row "xphone"');
+ assert.strictEqual(pivot.$('tbody tr:contains("xpad")').length, 1,
+ 'There should be a row "xpad"');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('Reload, group by columns, reload', async function (assert) {
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot/>',
+ });
+
+ // Set a column groupby
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=customer]'));
+
+ // Set a domain
+ await pivot.update({domain: [['product_id', '=', 37]], groupBy: [], context: {}});
+
+ var expectedContext = {pivot_column_groupby: ['customer'],
+ pivot_measures: ['__count'],
+ pivot_row_groupby: []};
+
+ // Check that column groupbys were not lost
+ assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
+ 'Column groupby not lost after first reload');
+
+ // Set a column groupby
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]'));
+
+ // Set a domain
+ await pivot.update({domain: [['product_id', '=', 41]], groupBy: [], context: {}});
+
+ expectedContext = {pivot_column_groupby: ['customer', 'product_id'],
+ pivot_measures: ['__count'],
+ pivot_row_groupby: []};
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
+ 'Column groupby not lost after second reload');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('folded groups remain folded at reload', async function (assert) {
+ assert.expect(5);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '<field name="company_type" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ var values = [
+ "29", "3", "32",
+ "12", "12",
+ "17", "3", "20",
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ // expand a col group
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="customer"]'));
+
+ values = [
+ "29", "1", "2", "32",
+ "12", "12",
+ "17", "1", "2", "20",
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ // expand a row group
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed:nth(1)'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="other_product_id"]'));
+
+ values = [
+ "29", "1", "2", "32",
+ "12", "12",
+ "17", "1", "2", "20",
+ "17", "1", "2", "20",
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ // reload (should keep folded groups folded as col/row groupbys didn't change)
+ await testUtils.pivot.reload(pivot, {context: {}, domain: [], groupBy: []});
+
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ await testUtils.dom.click(pivot.$('.o_pivot_expand_button'));
+
+ // sanity check of what the table should look like if all groups are
+ // expanded, to ensure that the former asserts are pertinent
+ values = [
+ "12", "17", "1", "2", "32",
+ "12", "12",
+ "12", "12",
+ "17", "1", "2", "20",
+ "17", "1", "2", "20",
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+
+ pivot.destroy();
+ });
+
+ QUnit.test('Empty results keep groupbys', async function (assert) {
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot/>',
+ });
+
+ // Set a column groupby
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=customer]'));
+
+ // Set a domain for empty results
+ await pivot.update({domain: [['id', '=', false]]});
+
+ var expectedContext = {pivot_column_groupby: ['customer'],
+ pivot_measures: ['__count'],
+ pivot_row_groupby: []};
+ assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
+ 'Column groupby not lost after empty results');
+
+ // Set a domain for not empty results
+ await pivot.update({domain: [['product_id', '=', 37]]});
+
+ assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
+ 'Column groupby not lost after reload after empty results');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly uses pivot_ keys from the context', async function (assert) {
+ assert.expect(7);
+
+ this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'};
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="col"/>' +
+ '<field name="amount" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ },
+ });
+
+ assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened',
+ "column: should have one opened header");
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1,
+ "column: should display one closed header with 'First'");
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1,
+ "column: should display one closed header with 'Second'");
+
+ assert.containsOnce(pivot, 'tbody .o_pivot_header_cell_opened',
+ "row: should have one opened header");
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1,
+ "row: should display one closed header with 'xphone'");
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1,
+ "row: should display one closed header with 'xpad'");
+
+ assert.strictEqual(pivot.$('tbody tr:first td:nth(2)').text(), '32',
+ "selected measure should be foo, with total 32");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('clear table cells data after closeGroup', async function (assert) {
+ assert.expect(2);
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot/>',
+ groupBy: ['product_id'],
+ });
+
+ await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.el.querySelectorAll('.o_pivot_field_menu .dropdown-item[data-field="date"]')[0]);
+
+ // close and reopen row groupings after changing value
+ this.data.partner.records.find(r => r.product_id === 37).date = '2016-10-27';
+ await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_opened'));
+ await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.el.querySelector('.o_pivot_field_menu .dropdown-item[data-field="product_id"]'));
+ assert.strictEqual(pivot.el.querySelectorAll('.o_pivot_cell_value')[4].innerText, ''); // xphone December 2016
+
+ // invert axis, and reopen column groupings
+ await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons .o_pivot_flip_button'));
+ await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_opened'));
+ await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.el.querySelector('.o_pivot_field_menu .dropdown-item[data-field="product_id"]'));
+ assert.strictEqual(pivot.el.querySelectorAll('.o_pivot_cell_value')[3].innerText, ''); // December 2016 xphone
+
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly group data after flip (1)', async function (assert) {
+ assert.expect(4);
+ const actionManager = await createActionManager({
+ data: this.data,
+ archs: {
+ 'partner,false,pivot': "<pivot/>",
+ 'partner,false,search': `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`,
+ 'partner,false,list': '<tree><field name="foo"/></tree>',
+ 'partner,false,form': '<form><field name="foo"/></form>',
+ },
+ });
+
+ await actionManager.doAction({
+ res_model: 'partner',
+ type: 'ir.actions.act_window',
+ views: [[false, 'pivot']],
+ context: { group_by: ["product_id"] },
+ });
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // flip axis
+ await testUtils.dom.click(actionManager.el.querySelector('.o_cp_buttons .o_pivot_flip_button'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ ]
+ );
+
+ // select filter "Bayou" in control panel
+ await cpHelpers.toggleFilterMenu(actionManager);
+ await cpHelpers.toggleMenuItem(actionManager, "Bayou");
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // close row header "Total"
+ await testUtils.dom.click(actionManager.el.querySelector('tbody .o_pivot_header_cell_opened'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total"
+ ]
+ );
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('correctly group data after flip (2)', async function (assert) {
+ assert.expect(5);
+ const actionManager = await createActionManager({
+ data: this.data,
+ archs: {
+ 'partner,false,pivot': "<pivot/>",
+ 'partner,false,search': `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`,
+ 'partner,false,list': '<tree><field name="foo"/></tree>',
+ 'partner,false,form': '<form><field name="foo"/></form>',
+ },
+ });
+
+ await actionManager.doAction({
+ res_model: 'partner',
+ type: 'ir.actions.act_window',
+ views: [[false, 'pivot']],
+ context: { group_by: ["product_id"] },
+ });
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // select filter "Bayou" in control panel
+ await cpHelpers.toggleFilterMenu(actionManager);
+ await cpHelpers.toggleMenuItem(actionManager, "Bayou");
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // flip axis
+ await testUtils.dom.click(actionManager.el.querySelector('.o_cp_buttons .o_pivot_flip_button'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total"
+ ]
+ );
+
+ // unselect filter "Bayou" in control panel
+ await cpHelpers.toggleFilterMenu(actionManager);
+ await cpHelpers.toggleMenuItem(actionManager, "Bayou");
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // close row header "Total"
+ await testUtils.dom.click(actionManager.el.querySelector('tbody .o_pivot_header_cell_opened'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...actionManager.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total"
+ ]
+ );
+
+ actionManager.destroy();
+ });
+
+ QUnit.test('correctly group data after flip (3))', async function (assert) {
+ assert.expect(10);
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: `
+ <pivot>
+ <field name="product_id" type="row"/>
+ <field name="company_type" type="col"/>
+ </pivot>
+ `,
+ archs: {
+ 'partner,false,search': `<search><filter name="bayou" string="Bayou" domain="[(1,'=',1)]"/></search>`,
+ }
+ });
+
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText),
+ [
+ "", "Total", "",
+ "Company", "individual",
+ "Count", "Count", "Count"
+ ]
+ );
+
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // close col header "Total"
+ await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_opened'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText),
+ [
+ "", "Total",
+ "Count"
+ ]
+ );
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // flip axis
+ await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons .o_pivot_flip_button'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText),
+ [
+ "", "Total", "",
+ "xphone", "xpad",
+ "Count", "Count", "Count"
+ ]
+ );
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total"
+ ]
+ );
+
+ // select filter "Bayou" in control panel
+ await cpHelpers.toggleFilterMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, "Bayou");
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText),
+ [
+ "", "Total", "",
+ "xphone", "xpad",
+ "Count", "Count", "Count"
+ ]
+ );
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total",
+ "xphone",
+ "xpad"
+ ]
+ );
+
+ // close row header "Total"
+ await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_opened'));
+ await testUtils.nextTick();
+
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("thead th")].map(e => e.innerText),
+ [
+ "", "Total", "",
+ "xphone", "xpad",
+ "Count", "Count", "Count"
+ ]
+ );
+ assert.deepEqual(
+ [...pivot.el.querySelectorAll("tbody th")].map(e => e.innerText),
+ [
+ "Total"
+ ]
+ );
+
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly uses pivot_ keys from the context (at reload)', async function (assert) {
+ assert.expect(8);
+
+ this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'};
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="col"/>' +
+ '<field name="amount" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual(pivot.$('tbody tr:first td.o_pivot_cell_value:last').text(), '0.00',
+ "the active measure should be amount");
+
+ var reloadParams = {
+ context: {
+ pivot_measures: ['foo'],
+ pivot_column_groupby: ['customer'],
+ pivot_row_groupby: ['product_id'],
+ },
+ };
+ await testUtils.pivot.reload(pivot, reloadParams);
+
+ assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened',
+ "column: should have one opened header");
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1,
+ "column: should display one closed header with 'First'");
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1,
+ "column: should display one closed header with 'Second'");
+
+ assert.containsOnce(pivot, 'tbody .o_pivot_header_cell_opened',
+ "row: should have one opened header");
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1,
+ "row: should display one closed header with 'xphone'");
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1,
+ "row: should display one closed header with 'xpad'");
+
+ assert.strictEqual(pivot.$('tbody tr:first td:nth(2)').text(), '32',
+ "selected measure should be foo, with total 32");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly use group_by key from the context', async function (assert) {
+ assert.expect(7);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="customer" type="col" />' +
+ '<field name="foo" type="measure" />' +
+ '</pivot>',
+ groupBy: ['product_id'],
+ });
+
+ assert.containsOnce(pivot, 'thead .o_pivot_header_cell_opened',
+ 'column: should have one opened header');
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1,
+ 'column: should display one closed header with "First"');
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1,
+ 'column: should display one closed header with "Second"');
+
+ assert.containsOnce(pivot, 'tbody .o_pivot_header_cell_opened',
+ 'row: should have one opened header');
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1,
+ 'row: should display one closed header with "xphone"');
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1,
+ 'row: should display one closed header with "xpad"');
+
+ assert.strictEqual(pivot.$('tbody tr:first td:nth(2)').text(), '32',
+ 'selected measure should be foo, with total 32');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly uses pivot_row_groupby key with default groupBy from the context', async function (assert) {
+ assert.expect(6);
+
+ this.data.partner.fields.amount = {string: "Amount", type: "float", group_operator: 'sum'};
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="customer" type="col"/>' +
+ '<field name="date" interval="day" type="row"/>' +
+ '</pivot>',
+ groupBy: ['customer'],
+ viewOptions: {
+ context: {
+ pivot_row_groupby: ['product_id'],
+ },
+ },
+ });
+
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_opened').length, 1,
+ "column: should have one opened header");
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(First)').length, 1,
+ "column: should display one closed header with 'First'");
+ assert.strictEqual(pivot.$('thead .o_pivot_header_cell_closed:contains(Second)').length, 1,
+ "column: should display one closed header with 'Second'");
+
+ // With pivot_row_groupby, groupBy customer should replace and eventually display product_id
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_opened').length, 1,
+ "row: should have one opened header");
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xphone)').length, 1,
+ "row: should display one closed header with 'xphone'");
+ assert.strictEqual(pivot.$('tbody .o_pivot_header_cell_closed:contains(xpad)').length, 1,
+ "row: should display one closed header with 'xpad'");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot still handles __count__ measure', async function (assert) {
+ // for retro-compatibility reasons, the pivot view still handles
+ // '__count__' measure.
+ assert.expect(2);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot></pivot>',
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group') {
+ assert.deepEqual(args.kwargs.fields, ['__count'],
+ "should make a read_group with field __count");
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ context: {
+ pivot_measures: ['__count__'],
+ },
+ },
+ });
+
+ var $countMeasure = pivot.$buttons.find('.dropdown-item[data-field=__count]:first');
+ assert.hasClass($countMeasure,'selected', "The count measure should be activated");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('not use a many2one as a measure by default', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id"/>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '</pivot>',
+ });
+ assert.notOk(pivot.measures.product_id,
+ "should not have product_id as measure");
+ pivot.destroy();
+ });
+
+ QUnit.test('use a many2one as a measure with specified additional measure', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id"/>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '</pivot>',
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ },
+ });
+ assert.ok(pivot.measures.product_id,
+ "should have product_id as measure");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view with many2one field as a measure', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="measure"/>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual(pivot.$('table tbody tr').text().trim(), "Total2112",
+ "should display product_id count as measure");
+ pivot.destroy();
+ });
+
+ QUnit.test('m2o as measure, drilling down into data', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="measure"/>' +
+ '</pivot>',
+ });
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed'));
+ // click on date by month
+ pivot.$('.dropdown-menu.show .o_inline_dropdown .dropdown-menu').toggle(); // unfold inline dropdown
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="date"]:contains("Month")'));
+
+ assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '2211',
+ 'should have loaded the proper data');
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view with same many2one field as a measure and grouped by', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ },
+ });
+
+ await testUtils.pivot.toggleMeasuresDropdown(pivot);
+ await testUtils.pivot.clickMeasure(pivot, 'product_id');
+ assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '421131',
+ 'should have loaded the proper data');
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view with same many2one field as a measure and grouped by (and drill down)', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="measure"/>' +
+ '</pivot>',
+ });
+
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed'));
+
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field="product_id"]:first'));
+
+ assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '211',
+ 'should have loaded the proper data');
+ pivot.destroy();
+ });
+
+ QUnit.test('Row and column groupbys plus a domain', async function (assert) {
+ assert.expect(3);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ // Set a column groupby
+ await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=customer]:first'));
+
+ // Set a Row groupby
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first'));
+
+ // Set a domain
+ await testUtils.pivot.reload(pivot, {domain: [['product_id', '=', 41]]});
+
+ var expectedContext = {
+ context: {
+ pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id'],
+ },
+ };
+
+ // Mock 'save as favorite'
+ assert.deepEqual(pivot.getOwnedQueryParams(), expectedContext,
+ 'The pivot view should have the right context');
+
+ var $xpadHeader = pivot.$('tbody .o_pivot_header_cell_closed[data-original-title=Product]');
+ assert.equal($xpadHeader.length, 1,
+ 'There should be only one product line because of the domain');
+
+ assert.equal($xpadHeader.text(), 'xpad',
+ 'The product should be the right one');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('parallel data loading should discard all but the last one', async function (assert) {
+ assert.expect(2);
+
+ var def;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'read_group') {
+ return Promise.resolve(def).then(_.constant(result));
+ }
+ return result;
+ },
+ });
+
+ def = testUtils.makeTestPromise();
+ testUtils.pivot.reload(pivot, {groupBy: ['product_id']});
+ testUtils.pivot.reload(pivot, {groupBy: ['product_id', 'customer']});
+ await def.resolve();
+ await testUtils.nextTick();
+ assert.containsN(pivot, '.o_pivot_cell_value', 6,
+ "should have 6 cells");
+ assert.containsN(pivot, 'tbody tr', 6,
+ "should have 6 rows");
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot measures should be alphabetically sorted', async function (assert) {
+ assert.expect(2);
+
+ var data = this.data;
+ // It's important to compare capitalized and lowercased words
+ // to be sure the sorting is effective with both of them
+ data.partner.fields.bouh = {string: "bouh", type: "integer", group_operator: 'sum'};
+ data.partner.fields.modd = {string: "modd", type: "integer", group_operator: 'sum'};
+ data.partner.fields.zip = {string: "Zip", type: "integer", group_operator: 'sum'};
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: data,
+ arch: '<pivot>' +
+ '<field name="zip" type="measure"/>' +
+ '<field name="foo" type="measure"/>' +
+ '<field name="bouh" type="measure"/>' +
+ '<field name="modd" type="measure"/>' +
+ '</pivot>',
+ });
+ assert.strictEqual(pivot.$buttons.find('.o_pivot_measures_list .dropdown-item:first').data('field'), 'bouh',
+ "Bouh should be the first measure");
+ assert.strictEqual(pivot.$buttons.find('.o_pivot_measures_list .dropdown-item:last').data('field'), '__count',
+ "Count should be the last measure");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view should use default order for auto sorting', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot default_order="foo asc">' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.hasClass(pivot.$('thead tr:last th:last'), 'o_pivot_sort_order_asc',
+ "Last thead should be sorted in ascending order");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot view can be flipped', async function (assert) {
+ assert.expect(5);
+
+ var rpcCount = 0;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="product_id" type="row"/>' +
+ '</pivot>',
+ mockRPC: function () {
+ rpcCount++;
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ assert.containsN(pivot, 'tbody tr', 3,
+ "should have 3 rows: 1 for the open header, and 2 for data");
+ var values = [
+ "4",
+ "1",
+ "3"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ rpcCount = 0;
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button'));
+
+ assert.strictEqual(rpcCount, 0, "should not have done any rpc");
+ assert.containsOnce(pivot, 'tbody tr',
+ "should have 1 rows: 1 for the main header");
+
+ values = [
+ "1", "3", "4"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ pivot.destroy();
+ });
+
+ QUnit.test('rendering of pivot view with comparison', async function (assert) {
+ assert.expect(8);
+
+ this.data.partner.records[0].date = '2016-12-15';
+ this.data.partner.records[1].date = '2016-12-17';
+ this.data.partner.records[2].date = '2016-11-22';
+ this.data.partner.records[3].date = '2016-11-03';
+
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='last_year'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ context: { search_default_date_filter: 1 },
+ },
+ mockRPC: function () {
+ return this._super.apply(this, arguments);
+ },
+ env: {
+ dataManager: {
+ create_filter: async function (filter) {
+ assert.deepEqual(filter.context, {
+ pivot_measures: ['__count'],
+ pivot_column_groupby: [],
+ pivot_row_groupby: ['product_id'],
+ group_by: [],
+ comparison: {
+ comparisonId: "previous_period",
+ comparisonRange: "[\"&\", [\"date\", \">=\", \"2016-11-01\"], [\"date\", \"<=\", \"2016-11-30\"]]",
+ comparisonRangeDescription: "November 2016",
+ fieldDescription: "Date",
+ fieldName: "date",
+ range: "[\"&\", [\"date\", \">=\", \"2016-12-01\"], [\"date\", \"<=\", \"2016-12-31\"]]",
+ rangeDescription: "December 2016"
+ },
+ });
+ }
+ }
+ },
+ });
+
+ // with no data
+ await cpHelpers.toggleComparisonMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ assert.strictEqual(pivot.$('.o_pivot p.o_view_nocontent_empty_folder').length, 1);
+
+ await cpHelpers.toggleFilterMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date');
+ await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'December');
+ await cpHelpers.toggleMenuItemOption(pivot, 'Date', '2016');
+ await cpHelpers.toggleMenuItemOption(pivot, 'Date', '2015');
+
+ assert.containsN(pivot, '.o_pivot thead tr:last th', 9,
+ "last header row should contains 9 cells (3*[December 2016, November 2016, Variation]");
+ var values = [
+ "19", "0", "-100%", "0", "13", "100%", "19", "13", "-31.58%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // with data, with row groupby
+ await testUtils.dom.click(pivot.$('.o_pivot .o_pivot_header_cell_closed').eq(2));
+ await testUtils.dom.click(pivot.$('.o_pivot .o_pivot_field_menu a[data-field="product_id"]'));
+ values = [
+ "19", "0", "-100%", "0", "13", "100%", "19", "13", "-31.58%",
+ "19", "0", "-100%", "0", "1" , "100%", "19", "1", "-94.74%",
+ "0", "12", "100%", "0" , "12", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ await testUtils.pivot.toggleMeasuresDropdown(pivot);
+ await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="foo"]'));
+ await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]'));
+ values = [
+ "1", "0", "-100%", "0", "2", "100%", "1", "2", "100%",
+ "1", "0", "-100%", "0", "1", "100%", "1", "1", "0%",
+ "0", "1", "100%", "0", "1", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="__count"]'));
+ await testUtils.dom.click(pivot.$('.o_control_panel div.o_pivot_measures_list a[data-field="product_id"]'));
+ values = [
+ "2", "0", "-100%", "0", "2", "100%", "2", "2", "0%",
+ "2", "0", "-100%", "0", "1", "100%", "2", "1", "-50%",
+ "0", "1", "100%", "0", "1", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ await testUtils.dom.clickFirst(pivot.$('.o_pivot .o_pivot_header_cell_opened'));
+ values = [
+ "2", "2", "0%",
+ "2", "1", "-50%",
+ "0", "1", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ await cpHelpers.toggleFavoriteMenu(pivot);
+ await cpHelpers.toggleSaveFavorite(pivot);
+ await cpHelpers.editFavoriteName(pivot, 'Fav');
+ await cpHelpers.saveFavorite(pivot);
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('export data in excel with comparison', async function (assert) {
+ assert.expect(11);
+
+ this.data.partner.records[0].date = '2016-12-15';
+ this.data.partner.records[1].date = '2016-12-17';
+ this.data.partner.records[2].date = '2016-11-22';
+ this.data.partner.records[3].date = '2016-11-03';
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="month" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='antepenultimate_month'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ context: { search_default_date_filter: 1 },
+ },
+ session: {
+ get_file: function (args) {
+ var data = JSON.parse(args.data.data);
+ _.each(data.col_group_headers, function (l) {
+ var titles = l.map(function (o) {
+ return o.title;
+ });
+ assert.step(JSON.stringify(titles));
+ });
+ var measures = data.measure_headers.map(function (o) {
+ return o.title;
+ });
+ assert.step(JSON.stringify(measures));
+ var origins = data.origin_headers.map(function (o) {
+ return o.title;
+ });
+ assert.step(JSON.stringify(origins));
+ assert.step(String(data.measure_count));
+ assert.step(String(data.origin_count));
+ var valuesLength = data.rows.map(function (o) {
+ return o.values.length;
+ });
+ assert.step(JSON.stringify(valuesLength));
+ assert.strictEqual(args.url, '/web/pivot/export_xlsx',
+ "should call get_file with correct parameters");
+ args.complete();
+ },
+ },
+ });
+
+ // open comparison menu
+ await cpHelpers.toggleComparisonMenu(pivot);
+ // compare October 2016 to September 2016
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ // With the data above, the time ranges contain no record.
+ assert.strictEqual(pivot.$('.o_pivot p.o_view_nocontent_empty_folder').length, 1, "there should be no data");
+ // export data should be impossible since the pivot buttons
+ // are deactivated (exception: the 'Measures' button).
+ assert.ok(pivot.$('.o_control_panel button.o_pivot_download').prop('disabled'));
+
+ await cpHelpers.toggleFilterMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date');
+ await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'December');
+ await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'October');
+
+ // With the data above, the time ranges contain some records.
+ // export data. Should execute 'get_file'
+ await testUtils.dom.click(pivot.$('.o_control_panel button.o_pivot_download'));
+
+ assert.verifySteps([
+ // col group headers
+ '["Total",""]',
+ '["November 2016","December 2016"]',
+ // measure headers
+ '["Foo","Foo","Foo"]',
+ // origin headers
+ '["November 2016","December 2016","Variation","November 2016","December 2016"' +
+ ',"Variation","November 2016","December 2016","Variation"]',
+ // number of 'measures'
+ '1',
+ // number of 'origins'
+ '2',
+ // rows values length
+ '[9]',
+ ]);
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('rendering of pivot view with comparison and count measure', async function (assert) {
+ assert.expect(2);
+
+ var mockMock = false;
+ var nbReadGroup = 0;
+
+ this.data.partner.records[0].date = '2016-12-15';
+ this.data.partner.records[1].date = '2016-12-17';
+ this.data.partner.records[2].date = '2016-12-22';
+ this.data.partner.records[3].date = '2016-12-03';
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot><field name="customer" type="row"/></pivot>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ context: { search_default_date_filter: 1 },
+ },
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'read_group' && mockMock) {
+ nbReadGroup++;
+ if (nbReadGroup === 4) {
+ // this modification is necessary because mockReadGroup does not
+ // properly reflect the server response when there is no record
+ // and a groupby list of length at least one.
+ return Promise.resolve([{}]);
+ }
+ }
+ return result;
+ },
+ });
+
+ mockMock = true;
+
+ // compare December 2016 to November 2016
+ await cpHelpers.toggleComparisonMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ var values = [
+ "0", "4", "100%",
+ "0", "2", "100%",
+ "0", "2", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join(','));
+ assert.strictEqual(pivot.$('.o_pivot_header_cell_closed').length, 3,
+ "there should be exactly three closed header ('Total','First', 'Second')");
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('can sort a pivot view with comparison by clicking on header', async function (assert) {
+ assert.expect(6);
+
+ this.data.partner.records[0].date = '2016-12-15';
+ this.data.partner.records[1].date = '2016-12-17';
+ this.data.partner.records[2].date = '2016-11-22';
+ this.data.partner.records[3].date = '2016-11-03';
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="row"/>' +
+ '<field name="company_type" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ context: { search_default_date_filter: 1 },
+ },
+ });
+
+ // compare December 2016 to November 2016
+ await cpHelpers.toggleComparisonMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ // initial sanity check
+ var values = [
+ "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "17", "0", "-100%",
+ "2", "0", "-100%", "2", "0", "-100%",
+ "0", "12" , "100%", "0", "12" , "100%",
+ "0", "1", "100%" , "0" , "1" , "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // click on 'Foo' in column Total/Company (should sort by the period of interest, ASC)
+ await testUtils.dom.click(pivot.$('.o_pivot_measure_row').eq(0));
+ values = [
+ "17", "12", "-29.41%", "2", "1", "-50%" , "19", "13", "-31.58%",
+ "2", "0", "-100%", "2", "0", "-100%",
+ "0", "12", "100%", "0", "12", "100%",
+ "0", "1", "100%", "0", "1", "100%",
+ "17", "0", "-100%", "17", "0", "-100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // click again on 'Foo' in column Total/Company (should sort by the period of interest, DESC)
+ await testUtils.dom.click(pivot.$('.o_pivot_measure_row').eq(0));
+ values = [
+ "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "17", "0", "-100%",
+ "2", "0", "-100%", "2", "0", "-100%",
+ "0", "12", "100%", "0", "12", "100%",
+ "0", "1", "100%", "0", "1", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // click on 'This Month' in column Total/Individual/Foo
+ await testUtils.dom.click(pivot.$('.o_pivot_origin_row').eq(3));
+ values = [
+ "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "17", "0", "-100%",
+ "0", "12", "100%", "0", "12" , "100%",
+ "0", "1", "100%", "0", "1", "100%",
+ "2", "0", "-100%", "2", "0", "-100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // click on 'Previous Period' in column Total/Individual/Foo
+ await testUtils.dom.click(pivot.$('.o_pivot_origin_row').eq(4));
+ values = [
+ "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "17", "0", "-100%",
+ "2", "0", "-100%", "2", "0", "-100%",
+ "0", "12", "100%", "0", "12", "100%",
+ "0", "1", "100%", "0", "1", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // click on 'Variation' in column Total/Foo
+ await testUtils.dom.click(pivot.$('.o_pivot_origin_row').eq(8));
+ values = [
+ "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "17", "0", "-100%",
+ "2", "0", "-100%", "2" , "0" , "-100%",
+ "0", "12", "100%", "0", "12" , "100%",
+ "0", "1", "100%", "0" , "1", "100%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('Click on the measure list but not on a menu item', async function (assert) {
+ assert.expect(2);
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: `<pivot/>`,
+ });
+
+ // open the "Measures" menu
+ await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons button'));
+
+ // click on the divider in the "Measures" menu does not crash
+ await testUtils.dom.click(pivot.el.querySelector('.o_pivot_measures_list .dropdown-divider'));
+ // the menu should still be open
+ assert.isVisible(pivot.el.querySelector('.o_pivot_measures_list'));
+
+ // click on the measure list but not on a menu item or the separator
+ await testUtils.dom.click(pivot.el.querySelector('.o_pivot_measures_list'));
+ // the menu should still be open
+ assert.isVisible(pivot.el.querySelector('.o_pivot_measures_list'));
+
+ pivot.destroy();
+ });
+
+ QUnit.test('Navigation list view for a group and back with breadcrumbs', async function (assert) {
+ assert.expect(16);
+ // create an action manager to test the interactions with the search view
+ var readGroupCount = 0;
+
+ var actionManager = await createActionManager({
+ data: this.data,
+ archs: {
+ 'partner,false,pivot': '<pivot>' +
+ '<field name="customer" type="row"/>' +
+ '</pivot>',
+ 'partner,false,search': '<search><filter name="bayou" string="Bayou" domain="[(\'foo\',\'=\', 12)]"/></search>',
+ 'partner,false,list': '<tree><field name="foo"/></tree>',
+ 'partner,false,form': '<form><field name="foo"/></form>',
+ },
+ intercepts: {
+ do_action: function (event) {
+ var action = event.data.action;
+ actionManager.doAction(action);
+ }
+ },
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group') {
+ assert.step('read_group');
+ const domain = args.kwargs.domain;
+ if ([0,1].indexOf(readGroupCount) !== -1) {
+ assert.deepEqual(domain, [], 'domain empty');
+ } else if ([2,3,4,5].indexOf(readGroupCount) !== -1) {
+ assert.deepEqual(domain, [['foo', '=', 12]],
+ 'domain conserved when back with breadcrumbs');
+ }
+ readGroupCount++;
+ }
+ if (route === '/web/dataset/search_read') {
+ assert.step('search_read');
+ const domain = args.domain;
+ assert.deepEqual(domain, ['&', ['customer', '=', 1], ['foo', '=', 12]],
+ 'list domain is correct');
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ await actionManager.doAction({
+ res_model: 'partner',
+ type: 'ir.actions.act_window',
+ views: [[false, 'pivot']],
+ });
+
+
+ await cpHelpers.toggleFilterMenu(actionManager);
+ await cpHelpers.toggleMenuItem(actionManager, 0);
+ await testUtils.nextTick();
+
+ await testUtilsDom.click(actionManager.$('.o_pivot_cell_value:nth(1)'));
+ await testUtils.nextTick();
+
+ assert.containsOnce(actionManager, '.o_list_view');
+
+ await testUtilsDom.click(actionManager.$('.o_control_panel ol.breadcrumb li.breadcrumb-item').eq(0));
+
+ assert.verifySteps([
+ 'read_group', 'read_group',
+ 'read_group', 'read_group',
+ 'search_read',
+ 'read_group', 'read_group']);
+ actionManager.destroy();
+ });
+
+ QUnit.test('Cell values are kept when flippin a pivot view in comparison mode', async function (assert) {
+ assert.expect(2);
+
+ this.data.partner.records[0].date = '2016-12-15';
+ this.data.partner.records[1].date = '2016-12-17';
+ this.data.partner.records[2].date = '2016-11-22';
+ this.data.partner.records[3].date = '2016-11-03';
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="row"/>' +
+ '<field name="company_type" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ context: { search_default_date_filter: 1 },
+ },
+ });
+
+ // compare December 2016 to November 2016
+ await cpHelpers.toggleComparisonMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ // initial sanity check
+ var values = [
+ "17", "12", "-29.41%", "2", "1", "-50%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "17", "0", "-100%",
+ "2", "0", "-100%", "2", "0", "-100%",
+ "0", "12", "100%", "0", "12", "100%",
+ "0", "1", "100%", "0", "1", "100%",
+
+
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ // flip table
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button'));
+
+ values = [
+ "17", "0", "-100%", "2", "0", "-100%", "0", "12", "100%", "0", "1", "100%", "19", "13", "-31.58%",
+ "17", "0", "-100%", "0", "12", "100%", "17", "12", "-29.41%",
+ "2", "0", "-100%", "0", "1", "100%", "2", "1", "-50%"
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('Flip then compare, table col groupbys are kept', async function (assert) {
+ assert.expect(6);
+
+ this.data.partner.records[0].date = '2016-12-15';
+ this.data.partner.records[1].date = '2016-12-17';
+ this.data.partner.records[2].date = '2016-11-22';
+ this.data.partner.records[3].date = '2016-11-03';
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" interval="day" type="row"/>' +
+ '<field name="company_type" type="col"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ archs: {
+ 'partner,false,search': `
+ <search>
+ <filter name="date_filter" date="date" domain="[]" default_period='this_month'/>
+ </search>
+ `,
+ },
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ },
+ });
+
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 5).text(),
+ [
+ '', 'Total', '',
+ 'Company', 'individual',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(8).text(),
+ [
+ 'Total',
+ '2016-12-15',
+ '2016-12-17',
+ '2016-11-22',
+ '2016-11-03'
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+ // flip
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button'));
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 7).text(),
+ [
+ '', 'Total', '',
+ '2016-12-15', '2016-12-17', '2016-11-22', '2016-11-03',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(12).text(),
+ [
+ 'Total',
+ 'Company',
+ 'individual'
+
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+ // Filter on December 2016
+ await cpHelpers.toggleFilterMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date');
+ await cpHelpers.toggleMenuItemOption(pivot, 'Date', 'December');
+
+ // compare December 2016 to November 2016
+ await cpHelpers.toggleComparisonMenu(pivot);
+ await cpHelpers.toggleMenuItem(pivot, 'Date: Previous period');
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 7).text(),
+ [
+ '', 'Total', '',
+ '2016-11-22', '2016-11-03', '2016-12-15', '2016-12-17',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(27).text(),
+ [
+ 'Total',
+ 'Company',
+ 'individual'
+
+ ].join(''),
+ "The row headers should be as expected"
+ );
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('correctly compute group domain when a date field has false value', async function (assert) {
+ assert.expect(1);
+
+ this.data.partner.records.forEach(r => r.date = false);
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot o_enable_linking="1">' +
+ '<field name="date" interval="day" type="row"/>' +
+ '</pivot>',
+ intercepts: {
+ do_action: function (ev) {
+ assert.deepEqual(ev.data.action.domain, [['date', '=', false]]);
+ },
+ },
+ });
+
+ await testUtils.dom.click($('div .o_value')[1]);
+
+ unpatchDate();
+ pivot.destroy();
+ });
+ QUnit.test('Does not identify "false" with false as keys when creating group trees', async function (assert) {
+ assert.expect(2);
+
+ this.data.partner.fields.favorite_animal = {string: "Favorite animal", type: "char", store: true};
+ this.data.partner.records[0].favorite_animal = 'Dog';
+ this.data.partner.records[1].favorite_animal = 'false';
+ this.data.partner.records[2].favorite_animal = 'Undefined';
+
+ var unpatchDate = patchDate(2016, 11, 20, 1, 0, 0);
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot o_enable_linking="1">' +
+ '<field name="favorite_animal" type="row"/>' +
+ '</pivot>',
+
+ });
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 2).text(),
+ [
+ '', 'Total', '',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(3).text(),
+ [
+ 'Total',
+ 'Dog',
+ 'false',
+ 'Undefined',
+ 'Undefined'
+
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+ unpatchDate();
+ pivot.destroy();
+ });
+
+ QUnit.test('group bys added via control panel and expand Header do not stack', async function (assert) {
+ assert.expect(8);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ },
+ });
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 2).text(),
+ [
+ '', 'Total',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(3).text(),
+ [
+ 'Total',
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+
+ // open group by menu and add new groupby
+ await cpHelpers.toggleGroupByMenu(pivot);
+ await cpHelpers.toggleAddCustomGroup(pivot);
+ await cpHelpers.applyGroup(pivot);
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 2).text(),
+ [
+ '', 'Total',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(3).text(),
+ [
+ 'Total',
+ 'Company',
+ 'individual'
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+ // Set a Row groupby
+ await testUtils.dom.click(pivot.$('tbody .o_pivot_header_cell_closed').eq(0));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first'));
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 2).text(),
+ [
+ '', 'Total',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(3).text(),
+ [
+ 'Total',
+ 'Company',
+ 'xphone',
+ 'xpad',
+ 'individual'
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+ // open groupby menu generator and add a new groupby
+ await cpHelpers.toggleGroupByMenu(pivot);
+ await cpHelpers.toggleAddCustomGroup(pivot);
+ await cpHelpers.selectGroup(pivot, 'bar');
+ await cpHelpers.applyGroup(pivot);
+
+ assert.strictEqual(
+ pivot.$('th').slice(0, 2).text(),
+ [
+ '', 'Total',
+ ].join(''),
+ "The col headers should be as expected"
+ );
+ assert.strictEqual(
+ pivot.$('th').slice(3).text(),
+ [
+ 'Total',
+ 'Company',
+ 'true',
+ 'individual',
+ 'true',
+ 'Undefined'
+ ].join(''),
+ "The row headers should be as expected"
+ );
+
+ pivot.destroy();
+ });
+
+ QUnit.test('display only one dropdown menu', async function (assert) {
+ assert.expect(1);
+
+ var pivot = await createView({
+ View: PivotView,
+ model: 'partner',
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ viewOptions: {
+ additionalMeasures: ['product_id'],
+ },
+ });
+ await testUtils.dom.clickFirst(pivot.$('th.o_pivot_header_cell_closed'));
+ await testUtils.dom.click(pivot.$('.o_pivot_field_menu .dropdown-item[data-field=product_id]:first'));
+
+ // Click on the two dropdown
+ await testUtils.dom.click(pivot.$('th.o_pivot_header_cell_closed')[0]);
+ await testUtils.dom.click(pivot.$('th.o_pivot_header_cell_closed')[1]);
+
+ assert.containsOnce(pivot, '.o_pivot_field_menu', 'Only one dropdown should be displayed at a time');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('Server order is kept by default', async function (assert) {
+ assert.expect(1);
+
+ let isSecondReadGroup = false;
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="customer" type="row"/>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ mockRPC: function (route, args) {
+ if (args.method === 'read_group' && isSecondReadGroup) {
+ return Promise.resolve([
+ {
+ customer: [2, 'Second'],
+ foo: 18,
+ __count: 2,
+ __domain :[["customer", "=", 2]],
+ },
+ {
+ customer: [1, 'First'],
+ foo: 14,
+ __count: 2,
+ __domain :[["customer", "=", 1]],
+ }
+ ]);
+ }
+ var result = this._super.apply(this, arguments);
+ isSecondReadGroup = true;
+ return result;
+ },
+ });
+
+ const values = [
+ "32", // Total Value
+ "18", // Second
+ "14", // First
+ ];
+ assert.strictEqual(getCurrentValues(pivot), values.join());
+
+ pivot.destroy();
+ });
+
+ QUnit.test('pivot rendering with boolean field', async function (assert) {
+ assert.expect(4);
+
+ this.data.partner.fields.bar = {string: "bar", type: "boolean", store: true, searchable: true, group_operator: 'bool_or'};
+ this.data.partner.records = [{id: 1, bar: true, date: '2019-12-14'}, {id: 2, bar: false, date: '2019-05-14'}];
+
+ var pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" type="row" interval="day"/>' +
+ '<field name="bar" type="col"/>' +
+ '<field name="bar" string="SLA status Failed" type="measure"/>' +
+ '</pivot>',
+ });
+
+ assert.strictEqual(pivot.$('tbody tr:contains("2019-12-14")').length, 1, 'There should be a first column');
+ assert.ok(pivot.$('tbody tr:contains("2019-12-14") [type="checkbox"]').is(':checked'), 'first column contains checkbox and value should be ticked');
+ assert.strictEqual(pivot.$('tbody tr:contains("2019-05-14")').length, 1, 'There should be a second column');
+ assert.notOk(pivot.$('tbody tr:contains("2019-05-14") [type="checkbox"]').is(':checked'), "second column should have checkbox that is not checked by default");
+
+ pivot.destroy();
+ });
+
+ QUnit.test('Allow to add behaviour to buttons on pivot', async function (assert) {
+ assert.expect(2);
+
+ let _testButtons = (ev) => {
+ if ($(ev.target).hasClass("o_pivot_flip_button")) {
+ assert.step("o_pivot_flip_button")
+ }
+ }
+
+ PivotController.include({
+ _addIncludedButtons: async function (ev) {
+ await this._super(...arguments);
+ _testButtons(ev);
+ },
+ });
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="date" type="row" interval="day"/>' +
+ '<field name="bar" type="col"/>' +
+ '</pivot>',
+ });
+ await testUtils.dom.click(pivot.$buttons.find('.o_pivot_flip_button'));
+ assert.verifySteps(["o_pivot_flip_button"]);
+ _testButtons = () => true;
+ pivot.destroy();
+ });
+
+ QUnit.test('empty pivot view with action helper', async function (assert) {
+ assert.expect(4);
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: `
+ <pivot>
+ <field name="product_id" type="measure"/>
+ <field name="date" interval="month" type="col"/>
+ </pivot>`,
+ domain: [['id', '<', 0]],
+ viewOptions: {
+ action: {
+ help: '<p class="abc">click to add a foo</p>'
+ }
+ },
+ });
+
+ assert.containsOnce(pivot, '.o_view_nocontent .abc');
+ assert.containsNone(pivot, 'table');
+
+ await pivot.reload({ domain: [] });
+
+ assert.containsNone(pivot, '.o_view_nocontent .abc');
+ assert.containsOnce(pivot, 'table');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('empty pivot view with sample data', async function (assert) {
+ assert.expect(7);
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: `
+ <pivot sample="1">
+ <field name="product_id" type="measure"/>
+ <field name="date" interval="month" type="col"/>
+ </pivot>`,
+ domain: [['id', '<', 0]],
+ viewOptions: {
+ action: {
+ help: '<p class="abc">click to add a foo</p>'
+ }
+ },
+ });
+
+ assert.hasClass(pivot.el, 'o_view_sample_data');
+ assert.containsOnce(pivot, '.o_view_nocontent .abc');
+ assert.containsOnce(pivot, 'table.o_sample_data_disabled');
+
+ await pivot.reload({ domain: [] });
+
+ assert.doesNotHaveClass(pivot.el, 'o_view_sample_data');
+ assert.containsNone(pivot, '.o_view_nocontent .abc');
+ assert.containsOnce(pivot, 'table');
+ assert.doesNotHaveClass(pivot.$('table'), 'o_sample_data_disabled');
+
+ pivot.destroy();
+ });
+
+ QUnit.test('non empty pivot view with sample data', async function (assert) {
+ assert.expect(7);
+
+ const pivot = await createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: `
+ <pivot sample="1">
+ <field name="product_id" type="measure"/>
+ <field name="date" interval="month" type="col"/>
+ </pivot>`,
+ viewOptions: {
+ action: {
+ help: '<p class="abc">click to add a foo</p>'
+ }
+ },
+ });
+
+ assert.doesNotHaveClass(pivot.el, 'o_view_sample_data');
+ assert.containsNone(pivot, '.o_view_nocontent .abc');
+ assert.containsOnce(pivot, 'table');
+ assert.doesNotHaveClass(pivot.$('table'), 'o_sample_data_disabled');
+
+ await pivot.reload({ domain: [['id', '<', 0]] });
+
+ assert.doesNotHaveClass(pivot.el, 'o_view_sample_data');
+ assert.containsOnce(pivot, '.o_view_nocontent .abc');
+ assert.containsNone(pivot, 'table');
+
+ pivot.destroy();
+ });
+});
+});