[IMP] web: prepare calendar view to ease modifications
The calendar renderer used a template for events. Now moved as a configurable template to ease futur calendar changes. This commit adds also a custom event to the calendar renderer so that custom implementations have a way to re-render the event items within the calendar after making internal changes to the event records. We introduce also a new option for a field "filter_field" which allows to specify the field of the model in which we will save the status of a filter. This is preliminary changes in order to make the calendar view able to modify the attendance status of a meeting and refresh the events to visually display if the user is attending or not. Task ID 2196775 COM PR: odoo/odoo#55190 ENT PR: odoo/enterprise#12196 UPG PR: odoo/upgrade#1532 Co-authored-by: Simon Genin (ges) <ges@odoo.com> Co-authored-by: jeh-odoo <jeh@odoo.com>
This commit is contained in:
committed by
Jérémy Hennecart
co-authored by
jeh-odoo
parent
55eb75d949
commit
0774459f5d
@@ -246,7 +246,22 @@ var CalendarController = AbstractController.extend({
|
||||
* @private
|
||||
* @param {OdooEvent} event
|
||||
*/
|
||||
_onChangeFilter: function (event) {
|
||||
_onChangeFilter: async function (event) {
|
||||
if (event.data.value !== "all" && event.target.filter_field) {
|
||||
const domain = [['user_id', '=', this.getSession().uid], [event.target.write_field, '=', parseInt(event.data.value)]];
|
||||
const contact = await this._rpc({
|
||||
model: event.target.write_model,
|
||||
method: 'search',
|
||||
args: [domain],
|
||||
});
|
||||
let val = {};
|
||||
val[event.target.filter_field] = event.data.active;
|
||||
await this._rpc({
|
||||
model: event.target.write_model,
|
||||
method: 'write',
|
||||
args: [contact, val],
|
||||
});
|
||||
}
|
||||
if (this.model.changeFilter(event.data) && !event.data.no_reload) {
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@@ -537,11 +537,15 @@ return AbstractModel.extend({
|
||||
}
|
||||
|
||||
var field = this.fields[filter.fieldName];
|
||||
var fields = [filter.write_field];
|
||||
if (filter.filter_field) {
|
||||
fields.push(filter.filter_field);
|
||||
}
|
||||
return this._rpc({
|
||||
model: filter.write_model,
|
||||
method: 'search_read',
|
||||
domain: [["user_id", "=", session.uid]],
|
||||
fields: [filter.write_field],
|
||||
fields: fields,
|
||||
})
|
||||
.then(function (res) {
|
||||
var records = _.map(res, function (record) {
|
||||
@@ -554,7 +558,7 @@ return AbstractModel.extend({
|
||||
'id': record.id,
|
||||
'value': value,
|
||||
'label': formater(_value, field),
|
||||
'active': f && f.active,
|
||||
'active': (f && f.active) || (filter.filter_field && record[filter.filter_field]),
|
||||
};
|
||||
});
|
||||
records.sort(function (f1,f2) {
|
||||
|
||||
@@ -46,6 +46,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
|
||||
this.fieldName = options.fieldName;
|
||||
this.write_model = options.write_model;
|
||||
this.write_field = options.write_field;
|
||||
this.filter_field = options.filter_field;
|
||||
this.avatar_field = options.avatar_field;
|
||||
this.avatar_model = options.avatar_model;
|
||||
this.filters = options.filters;
|
||||
@@ -161,10 +162,12 @@ return AbstractRenderer.extend({
|
||||
template: "CalendarView",
|
||||
config: {
|
||||
CalendarPopover: CalendarPopover,
|
||||
eventTemplate: 'calendar-box',
|
||||
},
|
||||
custom_events: _.extend({}, AbstractRenderer.prototype.custom_events || {}, {
|
||||
edit_event: '_onEditEvent',
|
||||
delete_event: '_onDeleteEvent',
|
||||
render_event: '_renderEvents',
|
||||
}),
|
||||
|
||||
/**
|
||||
@@ -356,7 +359,7 @@ return AbstractRenderer.extend({
|
||||
if (_.isEmpty(qweb_context.record)) {
|
||||
return '';
|
||||
} else {
|
||||
return qweb.render("calendar-box", qweb_context);
|
||||
return qweb.render(this.config.eventTemplate, qweb_context);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -112,6 +112,7 @@ var CalendarView = AbstractView.extend({
|
||||
};
|
||||
filters[fieldName].write_model = child.attrs.write_model;
|
||||
filters[fieldName].write_field = child.attrs.write_field; // can't use a x2many fields
|
||||
filters[fieldName].filter_field = child.attrs.filter_field;
|
||||
|
||||
modelFilters.push(fields[fieldName].relation);
|
||||
}
|
||||
|
||||
@@ -116,11 +116,12 @@ QUnit.module('Views', {
|
||||
id: {string: "ID", type: "integer"},
|
||||
user_id: {string: "user", type: "many2one", relation: 'user'},
|
||||
partner_id: {string: "partner", type: "many2one", relation: 'partner'},
|
||||
partner_checked: {string: "checked", type: "boolean"}
|
||||
},
|
||||
records: [
|
||||
{id: 1, user_id: session.uid, partner_id: 1},
|
||||
{id: 2, user_id: session.uid, partner_id: 2},
|
||||
{id: 3, user_id: 4, partner_id: 3}
|
||||
{id: 1, user_id: session.uid, partner_id: 1, partner_checked: true},
|
||||
{id: 2, user_id: session.uid, partner_id: 2, partner_checked: true},
|
||||
{id: 3, user_id: 4, partner_id: 3, partner_checked: true}
|
||||
]
|
||||
},
|
||||
};
|
||||
@@ -2140,6 +2141,40 @@ QUnit.module('Views', {
|
||||
assert.strictEqual($('#ui-datepicker-div:empty').length, 0, "should have a clean body");
|
||||
});
|
||||
|
||||
QUnit.test('check filters with filter_field specified', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const calendar = await createCalendarView({
|
||||
View: CalendarView,
|
||||
model: 'event',
|
||||
data: this.data,
|
||||
arch: `
|
||||
<calendar class="o_calendar_test"
|
||||
event_open_popup="true"
|
||||
date_start="start"
|
||||
date_stop="stop"
|
||||
all_day="allday"
|
||||
mode="week"
|
||||
color="partner_id">
|
||||
<field name="partner_ids" write_model="filter_partner" write_field="partner_id" filter_field="partner_checked"/>
|
||||
</calendar>`,
|
||||
});
|
||||
|
||||
assert.containsOnce(calendar, '.o_calendar_filter_item[data-id="2"] input:checked',
|
||||
"checkbox should be checked");
|
||||
await testUtils.dom.click(calendar.$('.o_calendar_filter_item[data-id="2"] input'));
|
||||
assert.containsNone(calendar, '.o_calendar_filter_item[data-id="2"] input:checked',
|
||||
"checkbox should not be checked");
|
||||
assert.strictEqual(this.data.filter_partner.records.find(r => r.id === 2).partner_checked, false,
|
||||
"the status of this filter should now be false");
|
||||
await calendar.reload();
|
||||
assert.containsNone(calendar, '.o_calendar_filter_item[data-id="2"] input:checked',
|
||||
"checkbox should not be checked after the reload");
|
||||
assert.strictEqual(this.data.filter_partner.records.find(r => r.id === 2).partner_checked, false,
|
||||
"the status of this filter should still be false after the reload");
|
||||
calendar.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('"all" filter', async function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
|
||||
@@ -454,10 +454,11 @@ calendar view are:
|
||||
* ``avatar_field``
|
||||
only for x2many field, to display the avatar instead of the display_name
|
||||
in the cards
|
||||
* ``write_model`` and ``write_field``
|
||||
* ``write_model`` and ``write_field`` and ``filter_field``
|
||||
|
||||
you can add a filter and save the result in the defined model, the
|
||||
filter is added in the sidebar
|
||||
filter is added in the sidebar. The ``filter_field`` is optionnal and allows
|
||||
you to specify the field that will hold the status of the filter.
|
||||
* ``filter`` and ``color``
|
||||
|
||||
use "True" to add this field in filter in the sidebar. You can specify
|
||||
|
||||
@@ -265,6 +265,7 @@
|
||||
<rng:optional><rng:attribute name="avatar_field" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="write_model" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="write_field" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="filter_field" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="text" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="optional" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="decoration-bf"/></rng:optional>
|
||||
|
||||
Reference in New Issue
Block a user