From 0774459f5d2635c6e93a73266e98fd2da8673496 Mon Sep 17 00:00:00 2001 From: "Simon Genin (ges)" Date: Wed, 27 May 2020 10:05:31 +0000 Subject: [PATCH 1/5] [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) Co-authored-by: jeh-odoo --- .../js/views/calendar/calendar_controller.js | 17 +++++++- .../src/js/views/calendar/calendar_model.js | 8 +++- .../js/views/calendar/calendar_renderer.js | 5 ++- .../src/js/views/calendar/calendar_view.js | 1 + .../web/static/tests/views/calendar_tests.js | 41 +++++++++++++++++-- doc/reference/views.rst | 5 ++- odoo/addons/base/rng/common.rng | 1 + 7 files changed, 69 insertions(+), 9 deletions(-) diff --git a/addons/web/static/src/js/views/calendar/calendar_controller.js b/addons/web/static/src/js/views/calendar/calendar_controller.js index 6ec90393b7a..a4124405e2b 100644 --- a/addons/web/static/src/js/views/calendar/calendar_controller.js +++ b/addons/web/static/src/js/views/calendar/calendar_controller.js @@ -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(); } diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js index cb0eeec5143..26c490f4c6c 100644 --- a/addons/web/static/src/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/js/views/calendar/calendar_model.js @@ -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) { diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js index fed9a705d11..ef556066085 100644 --- a/addons/web/static/src/js/views/calendar/calendar_renderer.js +++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js @@ -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); } }, /** diff --git a/addons/web/static/src/js/views/calendar/calendar_view.js b/addons/web/static/src/js/views/calendar/calendar_view.js index 8894d0760e5..7ac7e1730c9 100644 --- a/addons/web/static/src/js/views/calendar/calendar_view.js +++ b/addons/web/static/src/js/views/calendar/calendar_view.js @@ -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); } diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 9f99431ae43..60994d3f713 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -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: ` + + + `, + }); + + 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); diff --git a/doc/reference/views.rst b/doc/reference/views.rst index 935c426bc72..4acbe4259af 100644 --- a/doc/reference/views.rst +++ b/doc/reference/views.rst @@ -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 diff --git a/odoo/addons/base/rng/common.rng b/odoo/addons/base/rng/common.rng index e2f2b05d39a..9c6d842d343 100644 --- a/odoo/addons/base/rng/common.rng +++ b/odoo/addons/base/rng/common.rng @@ -265,6 +265,7 @@ + From a901d655504dd0afa5317e19c765aa68f23c9f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Hennecart?= Date: Thu, 15 Apr 2021 14:15:44 +0000 Subject: [PATCH 2/5] [IMP] calendar: improve the quality of the calendar app - Remove organisers filter from the sidebar - Responsible becomes Organizer Task ID 2196775 COM PR: odoo/odoo#55190 ENT PR: odoo/enterprise#12196 UPG PR: odoo/upgrade#1532 Co-authored-by: Simon Genin (ges) Co-authored-by: jeh-odoo --- addons/calendar/models/calendar_event.py | 4 ++-- addons/calendar/views/calendar_views.xml | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/addons/calendar/models/calendar_event.py b/addons/calendar/models/calendar_event.py index a13afdd3a7d..56ba6d1d2c6 100644 --- a/addons/calendar/models/calendar_event.py +++ b/addons/calendar/models/calendar_event.py @@ -97,9 +97,9 @@ class Meeting(models.Model): # description name = fields.Char('Meeting Subject', required=True) description = fields.Text('Description') - user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user) + user_id = fields.Many2one('res.users', 'Organizer', default=lambda self: self.env.user) partner_id = fields.Many2one( - 'res.partner', string='Responsible Contact', related='user_id.partner_id', readonly=True) + 'res.partner', string='Scheduled by', related='user_id.partner_id', readonly=True) location = fields.Char('Location', tracking=True, help="Location of Event") videocall_location = fields.Char('Join Video Call', default=_default_videocall_location) # visibility diff --git a/addons/calendar/views/calendar_views.xml b/addons/calendar/views/calendar_views.xml index 0ec4a0bac11..b539e1a021a 100644 --- a/addons/calendar/views/calendar_views.xml +++ b/addons/calendar/views/calendar_views.xml @@ -163,8 +163,9 @@ + + -