[FIX] microsoft_calendar: issue when archiving several events

In the `write` method, accessing the `ms_organizer_event_id` field of
`self` leads to an exception if `self` represents several records.

So, the idea of this fix is to set the `need_sync_m` field of all modified
records to `True` when at least a field to sync with Outlook is modified
(see `_get_microsoft_synced_fields()`) and then, at the end of the `write`
method, really patch or delete records that are already linked to Outlook
(that means they already have their `ms_organizer_event_id` field set).

closes odoo/odoo#96761

X-original-commit: 1a66343327f9ea9f59ba25ea63e26331cabb65b5
Signed-off-by: Arnaud Joset <arj@odoo.com>
This commit is contained in:
Rémy Baranx (bar)
2022-07-27 19:20:15 +02:00
committed by Arnaud Joset
parent 1a77ace252
commit 77e89e893c
3 changed files with 45 additions and 23 deletions
@@ -78,30 +78,22 @@ class MicrosoftSync(models.AbstractModel):
def write(self, vals):
if 'ms_universal_event_id' in vals:
self._from_uids.clear_cache(self)
synced_fields = self._get_microsoft_synced_fields()
if (
'need_sync_m' not in vals and vals.keys() & synced_fields
and self.ms_organizer_event_id
and not self.env.user.microsoft_synchronization_stopped
):
fields_to_sync = [x for x in vals.keys() if x in synced_fields]
if fields_to_sync:
vals['need_sync_m'] = True
else:
fields_to_sync = [x for x in vals.keys() if x in synced_fields]
fields_to_sync = [x for x in vals.keys() if x in self._get_microsoft_synced_fields()]
if fields_to_sync and 'need_sync_m' not in vals and not self.env.user.microsoft_synchronization_stopped:
vals['need_sync_m'] = True
result = super().write(vals)
need_delete = 'active' in vals.keys() and not vals.get('active')
for record in self.filtered('need_sync_m'):
if record.ms_universal_event_id:
if need_delete:
# We need to delete the event. Cancel is not sufficant. Errors may occurs
record._microsoft_delete(record._get_organizer(), record.ms_organizer_event_id, timeout=3)
elif fields_to_sync:
values = record._microsoft_values(fields_to_sync)
if not values:
continue
record._microsoft_patch(record._get_organizer(), record.ms_organizer_event_id, values, timeout=3)
for record in self.filtered(lambda e: e.need_sync_m and e.ms_organizer_event_id):
if not vals.get('active', True):
# We need to delete the event. Cancel is not sufficant. Errors may occurs
record._microsoft_delete(record._get_organizer(), record.ms_organizer_event_id, timeout=3)
elif fields_to_sync:
values = record._microsoft_values(fields_to_sync)
if not values:
continue
record._microsoft_patch(record._get_organizer(), record.ms_organizer_event_id, values, timeout=3)
return result
+12
View File
@@ -423,6 +423,18 @@ class TestCommon(HttpCase):
)
)
# a group of events
self.several_events = self.env["calendar.event"].search([("name", "like", "event%")])
if not self.several_events:
self.several_events = self.env["calendar.event"].with_user(self.organizer_user).create([
dict(
self.simple_event_values,
name=f"event{i}",
microsoft_id=combine_ids(f"e{i}", f"u{i}"),
)
for i in range(1, 4)
])
# a recurrent event with 7 occurrences
self.recurrent_base_event = self.env["calendar.event"].search(
[("name", "=", "recurrent_event")],
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from unittest.mock import patch, ANY
from unittest.mock import patch, ANY, call
from odoo.addons.microsoft_calendar.utils.microsoft_calendar import MicrosoftCalendarService
from odoo.addons.microsoft_calendar.utils.microsoft_event import MicrosoftEvent
@@ -81,6 +81,24 @@ class TestDeleteEvents(TestCommon):
timeout=ANY
)
@patch.object(MicrosoftCalendarService, 'delete')
def test_archive_several_events_at_once(self, mock_delete):
"""
Archive several events at once should not produce any exception.
"""
# act
self.several_events.action_archive()
self.call_post_commit_hooks()
self.several_events.invalidate_recordset()
# assert
self.assertFalse(all(e.active for e in self.several_events))
mock_delete.assert_has_calls([
call(e.ms_organizer_event_id, token=ANY, timeout=ANY)
for e in self.several_events
])
@patch.object(MicrosoftCalendarService, 'get_events')
def test_cancel_simple_event_from_outlook_organizer_calendar(self, mock_get_events):
"""