From e5b41a63a4c366b84893901ad0c0d3aef137f03c Mon Sep 17 00:00:00 2001 From: "Thomas Lefebvre (thle)" Date: Thu, 20 Jul 2023 12:30:34 +0000 Subject: [PATCH] [FIX] calendar: add attendee_id for deletion Steps to reproduce: ------------------- - create an event in the calendar; - click on "Everybody's calendars" filter; - delete the event. Issue: ------ The event name is crossed out (declined) instead of being deleted, even if we are the organizer. Cause: ------ The value of `event.attendee_id` inside the `_onDeleteRecord` is `undefined`. The condition will therefore never be `true`. Solution: --------- Add the organizer's id to the event so that he can delete it in the case of "Everybody's calendars" filter. Note: ----- Correct decline event from calendar view. opw-3230254 closes odoo/odoo#133298 X-original-commit: 4ef44f403f1c93b62230307f5fa909b8a83f4eb8 Signed-off-by: Arnaud Joset (arj) Signed-off-by: Thomas Lefebvre (thle) --- .../attendee_calendar_controller.js | 2 +- .../attendee_calendar_model.js | 4 + .../static/tests/tours/calendar_tour.js | 82 +++++++++++++++++++ addons/calendar/tests/test_calendar.py | 75 ++++++++++++++++- 4 files changed, 161 insertions(+), 2 deletions(-) diff --git a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.js b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.js index 640e9e07442..ac8178187ef 100644 --- a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.js +++ b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.js @@ -79,7 +79,7 @@ export class AttendeeCalendarController extends CalendarController { this.orm.call( "calendar.attendee", "do_decline", - [record.attendeeId], + [record.calendarAttendeeId], ).then(this.model.load.bind(this.model)); } } diff --git a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js index ad5c10efab3..2558118e2e3 100644 --- a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js +++ b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js @@ -107,6 +107,7 @@ export class AttendeeCalendarModel extends CalendarModel { record.attendeeStatus = attendeeInfo.status; record.isAlone = attendeeInfo.is_alone; record.isCurrentPartner = attendeeInfo.id === currentPartnerId; + record.calendarAttendeeId = attendeeInfo.attendee_id; } const recordId = duplicatedRecords ? duplicatedRecordIdx-- : record.id; // Index in the records @@ -118,12 +119,15 @@ export class AttendeeCalendarModel extends CalendarModel { data.records = newRecords; } else { for (const event of Object.values(data.records)) { + const eventData = event.rawRecord; + event.attendeeId = eventData.partner_id && eventData.partner_id[0] const attendeeInfo = data.attendees.find(a => ( a.id === currentPartnerId && a.event_id === event.id )); if (attendeeInfo) { event.isAlone = attendeeInfo.is_alone; + event.calendarAttendeeId = attendeeInfo.attendee_id; } } } diff --git a/addons/calendar/static/tests/tours/calendar_tour.js b/addons/calendar/static/tests/tours/calendar_tour.js index 8068a70de16..f46b09b24a5 100644 --- a/addons/calendar/static/tests/tours/calendar_tour.js +++ b/addons/calendar/static/tests/tours/calendar_tour.js @@ -69,3 +69,85 @@ content: 'Check the event title', }, ]}); + + registry.category("web_tour.tours").add('test_calendar_delete_tour', { + test: true, + steps: () => [ + { + content: 'Select filter (everybody)', + trigger: 'div[data-value="all"] input', + }, + { + content: 'Click on the event (focus + waiting)', + trigger: 'a .fc-content:contains("Test Event")', + async run() { + $('a .fc-content:contains("Test Event")').click(); + await new Promise((r) => setTimeout(r, 1000)); + $('a .fc-content:contains("Test Event")').click(); + } + }, + { + content: 'Delete the event', + trigger: '.o_cw_popover_delete', + }, + { + content: 'Validate the deletion', + trigger:'button:contains("Delete")', + async run() { + $('button:contains("Delete")').click(); + await new Promise((r) => setTimeout(r, 1000)); + } + }, + ]}); + + registry.category("web_tour.tours").add('test_calendar_decline_tour', { + test: true, + steps: () => [ + { + content: 'Click on the event (focus + waiting)', + trigger: 'a .fc-content:contains("Test Event")', + async run() { + $('a .fc-content:contains("Test Event")').click(); + await new Promise((r) => setTimeout(r, 1000)); + $('a .fc-content:contains("Test Event")').click(); + } + }, + { + content: 'Delete the event', + trigger: '.o_cw_popover_delete', + }, + { + content: 'Wait declined status', + trigger: '.o_attendee_status_declined', + }, + ]}); + + registry.category("web_tour.tours").add('test_calendar_decline_with_everybody_filter_tour', { + test: true, + steps: () => [ + { + content: 'Select filter (everybody)', + trigger: 'div[data-value="all"] input', + }, + { + content: 'Click on the event (focus + waiting)', + trigger: 'a .fc-content:contains("Test Event")', + async run() { + $('a .fc-content:contains("Test Event")').click(); + await new Promise((r) => setTimeout(r, 1000)); + $('a .fc-content:contains("Test Event")').click(); + } + }, + { + content: 'Delete the event', + trigger: '.o_cw_popover_delete', + }, + { + content: 'Select filter (everybody)', + trigger: 'div[data-value="all"] input', + }, + { + content: 'Wait declined status', + trigger: '.o_attendee_status_declined', + }, + ]}); diff --git a/addons/calendar/tests/test_calendar.py b/addons/calendar/tests/test_calendar.py index b317acbd445..d0064c548e3 100644 --- a/addons/calendar/tests/test_calendar.py +++ b/addons/calendar/tests/test_calendar.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import datetime -from datetime import datetime, timedelta, time +from datetime import date, datetime, timedelta from odoo import fields, Command from odoo.tests import Form, HttpCase, tagged @@ -422,3 +422,76 @@ class TestCalendarTours(HttpCase): def test_calendar_month_view_start_hour_displayed(self): """ Test that the time is displayed in the month view. """ self.start_tour("/web", 'calendar_appointments_hour_tour', login="demo") + + def test_calendar_delete_tour(self): + """ + Check that we can delete events with the "Everybody's calendars" filter. + """ + user_admin = self.env.ref('base.user_admin') + start = datetime.combine(date.today(), datetime.min.time()).replace(hour=9) + stop = datetime.combine(date.today(), datetime.min.time()).replace(hour=12) + event = self.env['calendar.event'].with_user(user_admin).create({ + 'name': 'Test Event', + 'description': 'Test Description', + 'start': start.strftime("%Y-%m-%d %H:%M:%S"), + 'stop': stop.strftime("%Y-%m-%d %H:%M:%S"), + 'duration': 3, + 'location': 'Odoo S.A.', + 'privacy': 'public', + 'show_as': 'busy', + }) + action_id = self.env.ref('calendar.action_calendar_event') + url = "/web#action=" + str(action_id.id) + '&view_type=calendar' + self.start_tour(url, 'test_calendar_delete_tour', login='admin') + event = self.env['calendar.event'].search([('name', '=', 'Test Event')]) + self.assertFalse(event) # Check if the event has been correctly deleted + + def test_calendar_decline_tour(self): + """ + Check that we can decline events. + """ + user_admin = self.env.ref('base.user_admin') + user_demo = self.env.ref('base.user_demo') + start = datetime.combine(date.today(), datetime.min.time()).replace(hour=9) + stop = datetime.combine(date.today(), datetime.min.time()).replace(hour=12) + event = self.env['calendar.event'].with_user(user_admin).create({ + 'name': 'Test Event', + 'description': 'Test Description', + 'start': start.strftime("%Y-%m-%d %H:%M:%S"), + 'stop': stop.strftime("%Y-%m-%d %H:%M:%S"), + 'duration': 3, + 'location': 'Odoo S.A.', + 'privacy': 'public', + 'show_as': 'busy', + }) + event.partner_ids = [Command.link(user_demo.partner_id.id)] + action_id = self.env.ref('calendar.action_calendar_event') + url = "/web#action=" + str(action_id.id) + '&view_type=calendar' + self.start_tour(url, 'test_calendar_decline_tour', login='demo') + attendee = self.env['calendar.attendee'].search([('event_id', '=', event.id), ('partner_id', '=', user_demo.partner_id.id)]) + self.assertEqual(attendee.state, 'declined') # Check if the event has been correctly declined + + def test_calendar_decline_with_everybody_filter_tour(self): + """ + Check that we can decline events with the "Everybody's calendars" filter. + """ + user_admin = self.env.ref('base.user_admin') + user_demo = self.env.ref('base.user_demo') + start = datetime.combine(date.today(), datetime.min.time()).replace(hour=9) + stop = datetime.combine(date.today(), datetime.min.time()).replace(hour=12) + event = self.env['calendar.event'].with_user(user_admin).create({ + 'name': 'Test Event', + 'description': 'Test Description', + 'start': start.strftime("%Y-%m-%d %H:%M:%S"), + 'stop': stop.strftime("%Y-%m-%d %H:%M:%S"), + 'duration': 3, + 'location': 'Odoo S.A.', + 'privacy': 'public', + 'show_as': 'busy', + }) + event.partner_ids = [Command.link(user_demo.partner_id.id)] + action_id = self.env.ref('calendar.action_calendar_event') + url = "/web#action=" + str(action_id.id) + '&view_type=calendar' + self.start_tour(url, 'test_calendar_decline_with_everybody_filter_tour', login='demo') + attendee = self.env['calendar.attendee'].search([('event_id', '=', event.id), ('partner_id', '=', user_demo.partner_id.id)]) + self.assertEqual(attendee.state, 'declined') # Check if the event has been correctly declined