From eb55cbb2448c16fc4b3f09ccdef8174c84a77476 Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Thu, 23 Feb 2023 16:38:36 +0000 Subject: [PATCH] [FIX] google_calendar: Avoid unexpected recurring event duplication When following recurring meeting are imported through google calendar synchronisation, it happens that a meeting get unexpectedly duplicated in Odoo, what will in turn duplicate the event on google and send invitations to all the existing attendees. As calendar.recurrence.calendar_event_ids is a One2many field, the update using self was setting the base_event_id's recurrence_id field to the last record of self recordset. Due to this, the event will not be considered to filter out the existing_ranges in calendar.recurrence._reconcile_events function. Therefore, a calendar.event will in turn be created using the same start and end datetime as the base_event_id of the calendar.recurrence. OPW-3143680 closes odoo/odoo#114392 X-original-commit: d1587758ac642b0181e660ea48222385d01098e3 Signed-off-by: Arnaud Joset --- addons/calendar/models/calendar_recurrence.py | 2 +- .../tests/test_sync_google2odoo.py | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/addons/calendar/models/calendar_recurrence.py b/addons/calendar/models/calendar_recurrence.py index e714b45c029..afaba5356c4 100644 --- a/addons/calendar/models/calendar_recurrence.py +++ b/addons/calendar/models/calendar_recurrence.py @@ -217,7 +217,7 @@ class RecurrenceRule(models.Model): specific_values_creation = {} for recurrence in self.filtered('base_event_id'): - self.calendar_event_ids |= recurrence.base_event_id + recurrence.calendar_event_ids |= recurrence.base_event_id event = recurrence.base_event_id or recurrence._get_first_event(include_outliers=False) duration = event.stop - event.start if specific_values_creation: diff --git a/addons/google_calendar/tests/test_sync_google2odoo.py b/addons/google_calendar/tests/test_sync_google2odoo.py index 43627a966f2..a0e2bd681cd 100644 --- a/addons/google_calendar/tests/test_sync_google2odoo.py +++ b/addons/google_calendar/tests/test_sync_google2odoo.py @@ -742,6 +742,86 @@ class TestSyncGoogle2Odoo(TestSyncGoogle): self.assertEqual(recurrence.event_tz, 'Pacific/Auckland', "The Google event Timezone should be saved on the recurrency") self.assertGoogleAPINotCalled() + @patch_api + def test_recurrence_no_duplicate(self): + values = [ + { + "attendees": [ + { + "email": "myemail@exampl.com", + "responseStatus": "needsAction", + "self": True, + }, + {"email": "jane.doe@example.com", "responseStatus": "needsAction"}, + { + "email": "john.doe@example.com", + "organizer": True, + "responseStatus": "accepted", + }, + ], + "created": "2023-02-20T11:45:07.000Z", + "creator": {"email": "john.doe@example.com"}, + "end": {"dateTime": "2023-02-25T16:20:00+01:00", "timeZone": "Europe/Zurich"}, + "etag": '"4611038912699385"', + "eventType": "default", + "iCalUID": "9lxiofipomymx2yr1yt0hpep99@google.com", + "id": "9lxiofipomymx2yr1yt0hpep99", + "kind": "calendar#event", + "organizer": {"email": "john.doe@example.com"}, + "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=SA"], + "reminders": {"useDefault": True}, + "sequence": 0, + "start": {"dateTime": "2023-02-25T15:30:00+01:00", "timeZone": "Europe/Zurich"}, + "status": "confirmed", + "summary": "Weekly test", + "updated": "2023-02-20T11:45:08.547Z", + }, + { + "attendees": [ + { + "email": "myemail@exampl.com", + "responseStatus": "needsAction", + "self": True, + }, + { + "email": "jane.doe@example.com", + "organizer": True, + "responseStatus": "needsAction", + }, + {"email": "john.doe@example.com", "responseStatus": "accepted"}, + ], + "created": "2023-02-20T11:45:44.000Z", + "creator": {"email": "john.doe@example.com"}, + "end": {"dateTime": "2023-02-26T15:20:00+01:00", "timeZone": "Europe/Zurich"}, + "etag": '"5534851880843722"', + "eventType": "default", + "iCalUID": "hhb5t0cffjkndvlg7i22f7byn1@google.com", + "id": "hhb5t0cffjkndvlg7i22f7byn1", + "kind": "calendar#event", + "organizer": {"email": "jane.doe@example.com"}, + "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=SU"], + "reminders": {"useDefault": True}, + "sequence": 0, + "start": {"dateTime": "2023-02-26T14:30:00+01:00", "timeZone": "Europe/Zurich"}, + "status": "confirmed", + "summary": "Weekly test 2", + "updated": "2023-02-20T11:48:00.634Z", + }, + ] + google_events = GoogleEvent(values) + self.env['calendar.recurrence']._sync_google2odoo(google_events) + no_duplicate_gevent = google_events.filter(lambda e: e.id == "9lxiofipomymx2yr1yt0hpep99") + dt_start = datetime.fromisoformat(no_duplicate_gevent.start["dateTime"]).astimezone(pytz.utc).replace(tzinfo=None).replace(hour=0) + dt_end = datetime.fromisoformat(no_duplicate_gevent.end["dateTime"]).astimezone(pytz.utc).replace(tzinfo=None).replace(hour=23) + no_duplicate_event = self.env["calendar.event"].search( + [ + ("name", "=", no_duplicate_gevent.summary), + ("start", ">=", dt_start), + ("stop", "<=", dt_end,) + ] + ) + self.assertEqual(len(no_duplicate_event), 1) + @patch_api def test_simple_event_into_recurrency(self): """ Synched single events should be converted in recurrency without problems"""