382 lines
16 KiB
JavaScript
382 lines
16 KiB
JavaScript
|
/** @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: `
|
||
|
<kanban js_class="forecast_kanban">
|
||
|
<field name="date_deadline"/>
|
||
|
<field name="date_closed"/>
|
||
|
<templates>
|
||
|
<t t-name="kanban-box">
|
||
|
<div><field name="name"/></div>
|
||
|
</t>
|
||
|
</templates>
|
||
|
</kanban>`,
|
||
|
searchViewArch: `
|
||
|
<search>
|
||
|
<filter name="forecast" string="Forecast" context="{'forecast_filter':1}"/>
|
||
|
<filter name='groupby_date_deadline' context="{'group_by':'date_deadline'}"/>
|
||
|
<filter name='groupby_date_closed' context="{'group_by':'date_closed'}"/>
|
||
|
</search>`,
|
||
|
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: `
|
||
|
<kanban js_class="forecast_kanban">
|
||
|
<field name="date_deadline"/>
|
||
|
<templates>
|
||
|
<t t-name="kanban-box">
|
||
|
<div><field name="name"/></div>
|
||
|
</t>
|
||
|
</templates>
|
||
|
</kanban>`,
|
||
|
searchViewArch: `
|
||
|
<search>
|
||
|
<filter name="forecast" string="Forecast" context="{'forecast_filter':1}"/>
|
||
|
<filter name='groupby_date_deadline' context="{'group_by':'date_deadline'}"/>
|
||
|
</search>`
|
||
|
,
|
||
|
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: `
|
||
|
<kanban js_class="forecast_kanban">
|
||
|
<progressbar field="color" colors='{"s": "success", "w": "warning", "d": "danger"}' sum_field="int_field"/>
|
||
|
<field name="date_deadline"/>
|
||
|
<templates>
|
||
|
<t t-name="kanban-box">
|
||
|
<div>
|
||
|
<field name="name"/>
|
||
|
</div>
|
||
|
</t>
|
||
|
</templates>
|
||
|
</kanban>`,
|
||
|
searchViewArch: `
|
||
|
<search>
|
||
|
<filter name="forecast" string="Forecast" context="{'forecast_filter':1}"/>
|
||
|
<filter name='groupby_date_deadline' context="{'group_by':'date_deadline'}"/>
|
||
|
</search>`
|
||
|
,
|
||
|
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",
|
||
|
]);
|
||
|
});
|
||
|
});
|