/** @odoo-module */ import { registry } from "@web/core/registry"; import { fillTemporalService } from "@crm/views/fill_temporal_service"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { click, dragAndDrop, getFixture, patchDate, } from '@web/../tests/helpers/utils'; const serviceRegistry = registry.category("services"); let target; QUnit.module('Crm Forecast Model Extension', { beforeEach: async function () { serviceRegistry.add("fillTemporalService", fillTemporalService); this.testKanbanView = { arch: `
`, searchViewArch: ` `, serverData: { models: { 'crm.lead': { fields: { name: {string: 'Name', type: 'char'}, date_deadline: {string: "Expected closing", type: 'date'}, date_closed: {string: "Closed Date", type: 'datetime'}, }, records: [ {id: 1, name: 'Lead 1', date_deadline: '2021-01-01', date_closed: '2021-01-01 00:00:00'}, {id: 2, name: 'Lead 2', date_deadline: '2021-01-20', date_closed: '2021-01-20 00:00:00'}, {id: 3, name: 'Lead 3', date_deadline: '2021-02-01', date_closed: '2021-02-01 00:00:00'}, {id: 4, name: 'Lead 4', date_deadline: '2021-02-20', date_closed: '2021-02-20 00:00:00'}, {id: 5, name: 'Lead 5', date_deadline: '2021-03-01', date_closed: '2021-03-01 00:00:00'}, {id: 6, name: 'Lead 6', date_deadline: '2021-03-20', date_closed: '2021-03-20 00:00:00'}, ], }, }, views: {}, }, resModel: 'crm.lead', type: "kanban", context: { search_default_forecast: true, search_default_groupby_date_deadline: true, forecast_field: 'date_deadline', }, groupBy: ['date_deadline'], }; target = getFixture(); setupViewRegistries(); patchDate(2021, 1, 10, 0, 0, 0); }, }, function () { QUnit.test("filter out every records before the start of the current month with forecast_filter for a date field", async function (assert) { // the filter is used by the forecast model extension, and applies the forecast_filter context key, // which adds a domain constraint on the field marked in the other context key forecast_field assert.expect(7); await makeView(this.testKanbanView); // the filter is active assert.containsN(target, '.o_kanban_group', 2, "There should be 2 columns"); assert.containsN(target, '.o_kanban_group:nth-child(1) .o_kanban_record', 2, "1st column February should contain 2 record"); assert.containsN(target, '.o_kanban_group:nth-child(2) .o_kanban_record', 2, "2nd column March should contain 2 records"); // remove the filter await click($(target).find(".o_searchview_facet:contains(Forecast)")[0], ".o_facet_remove"); assert.containsN(target, '.o_kanban_group', 3, "There should be 3 columns"); assert.containsN(target, '.o_kanban_group:nth-child(1) .o_kanban_record', 2, "1st column January should contain 2 record"); assert.containsN(target, '.o_kanban_group:nth-child(2) .o_kanban_record', 2, "2nd column February should contain 2 records"); assert.containsN(target, '.o_kanban_group:nth-child(3) .o_kanban_record', 2, "3nd column March should contain 2 records"); }); QUnit.test("filter out every records before the start of the current month with forecast_filter for a datetime field", async function (assert) { // same tests as for the date field assert.expect(7); await makeView({ ...this.testKanbanView, context: { search_default_forecast: true, search_default_groupby_date_closed: true, forecast_field: 'date_closed', }, groupBy: ['date_closed'], }); // with the filter assert.containsN(target, '.o_kanban_group', 2, "There should be 2 columns"); assert.containsN(target, '.o_kanban_group:nth-child(1) .o_kanban_record', 2, "1st column February should contain 2 record"); assert.containsN(target, '.o_kanban_group:nth-child(2) .o_kanban_record', 2, "2nd column March should contain 2 records"); // remove the filter await click($(target).find(".o_searchview_facet:contains(Forecast)")[0], ".o_facet_remove"); assert.containsN(target, '.o_kanban_group', 3, "There should be 3 columns"); assert.containsN(target, '.o_kanban_group:nth-child(1) .o_kanban_record', 2, "1st column January should contain 2 record"); assert.containsN(target, '.o_kanban_group:nth-child(2) .o_kanban_record', 2, "2nd column February should contain 2 records"); assert.containsN(target, '.o_kanban_group:nth-child(3) .o_kanban_record', 2, "3nd column March should contain 2 records"); }); }); QUnit.module('Crm Fill Temporal Service', { /** * Remark: -> the filter with the groupBy is needed for the model_extension to access the groupby * when created with makeView. Not needed in production. * -> testKanbanView.groupBy is still needed to apply the groupby on the view */ beforeEach: async function () { serviceRegistry.add("fillTemporalService", fillTemporalService); this.testKanbanView = { arch: `
`, searchViewArch: ` ` , serverData: { models: { 'crm.lead': { fields: { name: {string: 'Name', type: 'char'}, date_deadline: {string: "Expected Closing", type: 'date'}, }, }, }, views: {}, }, resModel: 'crm.lead', type: "kanban", context: { search_default_forecast: true, search_default_groupby_date_deadline: true, forecast_field: 'date_deadline', }, groupBy: ['date_deadline'], }; target = getFixture(); setupViewRegistries(); patchDate(2021, 9, 10, 0, 0, 0); }, }, function () { /** * Since mock_server does not support fill_temporal, * we only check the domain and the context sent to the read_group, as well * as the end value of the FillTemporal Service after the read_group (which should have been updated in the model) */ QUnit.test("Forecast on months, until the end of the year of the latest data", async function (assert) { assert.expect(3); this.testKanbanView.serverData.models['crm.lead'].records = [ {id: 1, name: 'Lead 1', date_deadline: '2021-01-01'}, {id: 2, name: 'Lead 2', date_deadline: '2021-02-01'}, {id: 3, name: 'Lead 3', date_deadline: '2021-11-01'}, {id: 4, name: 'Lead 4', date_deadline: '2022-01-01'}, ]; const kanban = await makeView({ ...this.testKanbanView, mockRPC: function (route, args) { if (route === '/web/dataset/call_kw/crm.lead/web_read_group') { assert.deepEqual(args.kwargs.context.fill_temporal, { fill_from: "2021-10-01", min_groups: 4, }); assert.deepEqual(args.kwargs.domain, [ "&", "|", ["date_deadline", "=", false], ["date_deadline", ">=", "2021-10-01"], "|", ["date_deadline", "=", false], ["date_deadline", "<", "2023-01-01"], ]); } }, }); assert.strictEqual(kanban.env.services.fillTemporalService.getFillTemporalPeriod({ modelName: 'crm.lead', field: { name: 'date_deadline', type: 'date', }, granularity: 'month', }).end.toFormat('yyyy-MM-dd'), '2022-02-01'); }); /** * Since mock_server does not support fill_temporal, * we only check the domain and the context sent to the read_group, as well * as the end value of the FillTemporal Service after the read_group (which should have been updated in the model) */ QUnit.test("Forecast on years, until the end of the year of the latest data", async function (assert) { assert.expect(3); this.testKanbanView.serverData.models['crm.lead'].records = [ {id: 1, name: 'Lead 1', date_deadline: '2021-01-01'}, {id: 2, name: 'Lead 2', date_deadline: '2022-02-01'}, {id: 3, name: 'Lead 3', date_deadline: '2027-11-01'}, ]; const kanban = await makeView({ ...this.testKanbanView, groupBy: ['date_deadline:year'], searchViewArch: this.testKanbanView.searchViewArch.replace("'date_deadline'", "'date_deadline:year'"), mockRPC: function (route, args) { if (route === '/web/dataset/call_kw/crm.lead/web_read_group') { assert.deepEqual(args.kwargs.context.fill_temporal, { fill_from: "2021-01-01", min_groups: 4, }); assert.deepEqual(args.kwargs.domain, [ "&", "|", ["date_deadline", "=", false], ["date_deadline", ">=", "2021-01-01"], "|", ["date_deadline", "=", false], ["date_deadline", "<", "2025-01-01"], ]); } }, }); assert.strictEqual(kanban.env.services.fillTemporalService.getFillTemporalPeriod({ modelName: 'crm.lead', field: { name: 'date_deadline', type: 'date', }, granularity: 'year', }).end.toFormat('yyyy-MM-dd'), '2023-01-01'); }); }); QUnit.module('Crm Forecast main flow with progressBars', (hooks) => { function getCounters() { return [...target.querySelectorAll(".o_animated_number")].map((counter) => counter.innerText); } function getProgressBarsColors() { return [...target.querySelectorAll(".o_column_progress")].map(columnProgressEl => { return [...columnProgressEl.querySelectorAll(".progress-bar")].map(progressBarEl => { return [...progressBarEl.classList.values()].filter(htmlClass => { return htmlClass.startsWith('bg-'); })[0]; }) }); } hooks.beforeEach(() => { serviceRegistry.add("fillTemporalService", fillTemporalService); this.testKanbanView = { arch: `
`, searchViewArch: ` ` , serverData: { models: { 'crm.lead': { fields: { color: {string: 'Color', type: 'string'}, date_deadline: {string: 'Expected Closing', type: 'date'}, int_field: {string: 'Value', type: 'integer', sortable: true}, name: {string: 'Name', type: 'char'}, }, }, }, views: {}, }, resModel: 'crm.lead', type: "kanban", context: { search_default_forecast: true, search_default_groupby_date_deadline: true, forecast_field: 'date_deadline', }, groupBy: ['date_deadline'], }; target = getFixture(); setupViewRegistries(); // first september 2023 patchDate(2023, 8, 1, 0, 0, 0); }); QUnit.test("Forecast drag&drop and add column", async (assert) => { this.testKanbanView.serverData.models['crm.lead'].records = [ {id: 1, int_field: 7, color: 'd', name: 'Lead 1', date_deadline: '2023-09-03'}, {id: 2, int_field: 20, color: 'w', name: 'Lead 2', date_deadline: '2023-09-05'}, {id: 3, int_field: 300, color: 's', name: 'Lead 3', date_deadline: '2023-10-10'}, ]; await makeView({ ...this.testKanbanView, async mockRPC(route, args) { assert.step(args.method || route); }, }); assert.deepEqual(getCounters(), ["27", "300"]); assert.deepEqual(getProgressBarsColors(), [["bg-warning", "bg-danger"], ["bg-success"]]); await dragAndDrop( ".o_kanban_group:first-child .o_kanban_record", ".o_kanban_group:nth-child(2)" ); assert.deepEqual(getCounters(), ["20", "307"]); assert.deepEqual(getProgressBarsColors(), [["bg-warning"], ["bg-success", "bg-danger"]]); await click(target, ".o_kanban_add_column"); // Counters and progressbars should be unchanged after adding a column. assert.deepEqual(getCounters(), ["20", "307"]); assert.deepEqual(getProgressBarsColors(), [["bg-warning"], ["bg-success", "bg-danger"]]); assert.verifySteps([ // makeView "get_views", "read_progress_bar", "web_read_group", "web_search_read", "web_search_read", // drag&drop "web_save", "read_progress_bar", "web_read_group", // add column "read_progress_bar", "web_read_group", "web_search_read", "web_search_read", ]); }); });