[FIX] microsoft_calendar: no sync for recurrent events
Before this commit, updating recurring events was troublesome due to an Outlook limitation, which was sending spam to attendees. After this commit, when updating recurring events, it is suggested for users to update recurrences directly in Outlook Calendar to handle this limitation. It is not allowed anymore creating recurrences in Odoo when the sync with Outlook is active (although recurrences created in Outlook are still synchronized in Odoo). Recurrent events that were created before the synchronization start (which are not synced) can still be deleted in Odoo (then a suggestion of recreating them in Outlook is shown). Previous unit tests regarding the synchronization of recurrences from Odoo to Outlook were deactivated. New tests were added asserting the forbiddance of this recurrence creation and update flow. closes odoo/odoo#131806 Task-id: 3204905 X-original-commit: 27ee51029c1d4e9164b9705306b59b26598bcf7e Signed-off-by: Arnaud Joset (arj) <arj@odoo.com> Signed-off-by: Gabriel de Paula Felix (gdpf) <gdpf@odoo.com>
This commit is contained in:
@@ -150,6 +150,33 @@ msgstr ""
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: microsoft_calendar
|
||||
#. odoo-python
|
||||
#: code:addons/microsoft_calendar/models/calendar.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Due to an Outlook Calendar limitation, recurrence updates must be done "
|
||||
"directly in Outlook Calendar."
|
||||
msgstr ""
|
||||
|
||||
#. module: microsoft_calendar
|
||||
#. odoo-python
|
||||
#: code:addons/microsoft_calendar/models/calendar.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Due to an Outlook Calendar limitation, recurrence updates must be done directly in Outlook Calendar.\n"
|
||||
"If this recurrence is not shown in Outlook Calendar, you must delete it in Odoo Calendar and recreate it in Outlook Calendar."
|
||||
msgstr ""
|
||||
|
||||
#. module: microsoft_calendar
|
||||
#. odoo-python
|
||||
#: code:addons/microsoft_calendar/models/calendar.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Due to an Outlook Calendar limitation, recurrent events must be created "
|
||||
"directly in Outlook Calendar."
|
||||
msgstr ""
|
||||
|
||||
#. module: microsoft_calendar
|
||||
#: model:ir.model,name:microsoft_calendar.model_calendar_recurrence
|
||||
msgid "Event Recurrence Rule"
|
||||
|
||||
@@ -57,10 +57,23 @@ class Meeting(models.Model):
|
||||
'need_sync_m': True,
|
||||
})
|
||||
|
||||
def _check_microsoft_sync_status(self):
|
||||
"""
|
||||
Returns True if synchronization with Outlook Calendar is active and False otherwise.
|
||||
The 'microsoft_synchronization_stopped' variable needs to be 'False' and Outlook account must be connected.
|
||||
"""
|
||||
outlook_connected = self.env.user._get_microsoft_calendar_token() and self.env.user._is_microsoft_calendar_valid()
|
||||
return outlook_connected and self.env.user.microsoft_synchronization_stopped is False
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
notify_context = self.env.context.get('dont_notify', False)
|
||||
|
||||
# Forbid recurrence creation in Odoo, suggest its creation in Outlook due to the spam limitation.
|
||||
recurrency_in_batch = any(vals.get('recurrency') for vals in vals_list)
|
||||
if self._check_microsoft_sync_status() and not notify_context and recurrency_in_batch:
|
||||
self._forbid_recurrence_creation()
|
||||
|
||||
# for a recurrent event, we do not create events separately but we directly
|
||||
# create the recurrency from the corresponding calendar.recurrence.
|
||||
# That's why, events from a recurrency have their `need_sync_m` attribute set to False.
|
||||
@@ -103,8 +116,34 @@ class Meeting(models.Model):
|
||||
|
||||
return (event_start, event_stop) == (start, stop)
|
||||
|
||||
def _forbid_recurrence_update(self):
|
||||
"""
|
||||
Suggest user to update recurrences in Outlook due to the Outlook Calendar spam limitation.
|
||||
"""
|
||||
error_msg = _("Due to an Outlook Calendar limitation, recurrence updates must be done directly in Outlook Calendar.")
|
||||
if any(not record.microsoft_id for record in self):
|
||||
# If any event is not synced, suggest deleting it in Odoo and recreating it in Outlook.
|
||||
error_msg = _(
|
||||
"Due to an Outlook Calendar limitation, recurrence updates must be done directly in Outlook Calendar.\n"
|
||||
"If this recurrence is not shown in Outlook Calendar, you must delete it in Odoo Calendar and recreate it in Outlook Calendar.")
|
||||
|
||||
raise UserError(error_msg)
|
||||
|
||||
def _forbid_recurrence_creation(self):
|
||||
"""
|
||||
Suggest user to update recurrences in Outlook due to the Outlook Calendar spam limitation.
|
||||
"""
|
||||
raise UserError(_("Due to an Outlook Calendar limitation, recurrent events must be created directly in Outlook Calendar."))
|
||||
|
||||
def write(self, values):
|
||||
recurrence_update_setting = values.get('recurrence_update')
|
||||
notify_context = self.env.context.get('dont_notify', False)
|
||||
|
||||
# Forbid recurrence updates through Odoo and suggest user to update it in Outlook.
|
||||
if self._check_microsoft_sync_status():
|
||||
recurrency_in_batch = self.filtered(lambda ev: ev.recurrency)
|
||||
if not notify_context and (recurrence_update_setting or 'recurrency' in values or recurrency_in_batch):
|
||||
self._forbid_recurrence_update()
|
||||
|
||||
# check a Outlook limitation in overlapping the actual recurrence
|
||||
if recurrence_update_setting == 'self_only' and 'start' in values:
|
||||
@@ -117,7 +156,6 @@ class Meeting(models.Model):
|
||||
e._microsoft_delete(e._get_organizer(), e.ms_organizer_event_id, timeout=3)
|
||||
e.microsoft_id = False
|
||||
|
||||
notify_context = self.env.context.get('dont_notify', False)
|
||||
res = super(Meeting, self.with_context(dont_notify=notify_context)).write(values)
|
||||
|
||||
if recurrence_update_setting in ('all_events',) and len(self) == 1 \
|
||||
@@ -125,6 +163,12 @@ class Meeting(models.Model):
|
||||
self.recurrence_id.need_sync_m = True
|
||||
return res
|
||||
|
||||
def action_mass_archive(self, recurrence_update_setting):
|
||||
# Do not allow archiving if recurrence is synced with Outlook. Suggest updating directly from Outlook.
|
||||
if self._check_microsoft_sync_status() and any(self.microsoft_id):
|
||||
self._forbid_recurrence_update()
|
||||
super().action_mass_archive(recurrence_update_setting)
|
||||
|
||||
def _get_microsoft_sync_domain(self):
|
||||
# in case of full sync, limit to a range of 1y in past and 1y in the future by default
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
@@ -135,7 +179,6 @@ class Meeting(models.Model):
|
||||
('partner_ids.user_ids', 'in', self.env.user.id),
|
||||
('stop', '>', lower_bound),
|
||||
('start', '<', upper_bound),
|
||||
# Do not sync events that follow the recurrence, they are already synced at recurrence creation
|
||||
'!', '&', '&', ('recurrency', '=', True), ('recurrence_id', '!=', False), ('follow_recurrence', '=', True)
|
||||
]
|
||||
return self._extend_microsoft_domain(domain)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class RecurrenceRule(models.Model):
|
||||
@@ -121,7 +122,7 @@ class RecurrenceRule(models.Model):
|
||||
| self.env["calendar.event"]._get_recurrent_fields()
|
||||
)
|
||||
# We avoid to write time_fields because they are not shared between events.
|
||||
self._write_events(dict({
|
||||
self.with_context(dont_notify=True)._write_events(dict({
|
||||
field: value
|
||||
for field, value in new_event_values.items()
|
||||
if field not in time_fields
|
||||
@@ -135,15 +136,12 @@ class RecurrenceRule(models.Model):
|
||||
detached_events.unlink()
|
||||
|
||||
def _get_microsoft_sync_domain(self):
|
||||
# Empty rrule may exists in historical data. It is not a desired behavior but it could have been created with
|
||||
# older versions of the module. When synced, these recurrence may come back from Microsoft after database cleaning
|
||||
# and trigger errors as the records are not properly populated.
|
||||
# We also prevent sync of other user recurrent events.
|
||||
domain = [('calendar_event_ids.user_id', '=', self.env.user.id), ('rrule', '!=', False)]
|
||||
# Do not sync Odoo recurrences with Outlook Calendar anymore.
|
||||
domain = expression.FALSE_DOMAIN
|
||||
return self._extend_microsoft_domain(domain)
|
||||
|
||||
def _cancel_microsoft(self):
|
||||
self.calendar_event_ids._cancel_microsoft()
|
||||
self.calendar_event_ids.with_context(dont_notify=True)._cancel_microsoft()
|
||||
super()._cancel_microsoft()
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -164,11 +164,11 @@ class MicrosoftSync(models.AbstractModel):
|
||||
return super().unlink()
|
||||
|
||||
def _write_from_microsoft(self, microsoft_event, vals):
|
||||
self.write(vals)
|
||||
self.with_context(dont_notify=True).write(vals)
|
||||
|
||||
@api.model
|
||||
def _create_from_microsoft(self, microsoft_event, vals_list):
|
||||
return self.create(vals_list)
|
||||
return self.with_context(dont_notify=True).create(vals_list)
|
||||
|
||||
def _sync_odoo2microsoft(self):
|
||||
if not self:
|
||||
@@ -236,7 +236,7 @@ class MicrosoftSync(models.AbstractModel):
|
||||
to_create_values += [dict(value, need_sync_m=False)]
|
||||
|
||||
new_calendar_recurrence['calendar_event_ids'] = [(0, 0, to_create_value) for to_create_value in to_create_values]
|
||||
new_recurrence_odoo = self.env['calendar.recurrence'].create(new_calendar_recurrence)
|
||||
new_recurrence_odoo = self.env['calendar.recurrence'].with_context(dont_notify=True).create(new_calendar_recurrence)
|
||||
new_recurrence_odoo.base_event_id = new_recurrence_odoo.calendar_event_ids[0] if new_recurrence_odoo.calendar_event_ids else False
|
||||
new_recurrence |= new_recurrence_odoo
|
||||
|
||||
@@ -305,11 +305,11 @@ class MicrosoftSync(models.AbstractModel):
|
||||
odoo_event = self.env['calendar.event'].browse(e.odoo_id(self.env)).exists().with_context(
|
||||
no_mail_to_attendees=True, mail_create_nolog=True
|
||||
)
|
||||
odoo_event.write(dict(event_values, need_sync_m=False))
|
||||
odoo_event.with_context(dont_notify=True).write(dict(event_values, need_sync_m=False))
|
||||
update_events |= odoo_event
|
||||
|
||||
# update the recurrence
|
||||
detached_events = self._apply_recurrence(rec_values)
|
||||
detached_events = self.with_context(dont_notify=True)._apply_recurrence(rec_values)
|
||||
detached_events._cancel_microsoft()
|
||||
|
||||
return update_events
|
||||
@@ -368,7 +368,7 @@ class MicrosoftSync(models.AbstractModel):
|
||||
|
||||
if ms_event_updated_time >= odoo_event_updated_time:
|
||||
vals = dict(odoo_event._microsoft_to_odoo_values(mevent), need_sync_m=False)
|
||||
odoo_event._write_from_microsoft(mevent, vals)
|
||||
odoo_event.with_context(dont_notify=True)._write_from_microsoft(mevent, vals)
|
||||
|
||||
if odoo_event._name == 'calendar.recurrence':
|
||||
update_events = odoo_event._update_microsoft_recurrence(mevent, microsoft_events)
|
||||
|
||||
@@ -406,6 +406,13 @@ class TestCommon(HttpCase):
|
||||
]
|
||||
self.env.cr.postcommit.clear()
|
||||
|
||||
def sync_odoo_recurrences_with_outlook_feature(self):
|
||||
"""
|
||||
Returns the status of the recurrence synchronization feature with Outlook.
|
||||
True if it is active and False otherwise. This function guides previous tests to abort before they are checked.
|
||||
"""
|
||||
return False
|
||||
|
||||
def create_events_for_tests(self):
|
||||
"""
|
||||
Create some events for test purpose
|
||||
@@ -443,27 +450,35 @@ class TestCommon(HttpCase):
|
||||
)
|
||||
already_created = self.recurrent_base_event
|
||||
|
||||
# Currently, it is forbidden to create recurrences in Odoo. A trick for deactivating the checking
|
||||
# is needed below in this test setup: deactivating the synchronization during recurrences creation.
|
||||
sync_previous_state = self.env.user.microsoft_synchronization_stopped
|
||||
self.env.user.microsoft_synchronization_stopped = False
|
||||
|
||||
if not already_created:
|
||||
self.recurrent_base_event = self.env["calendar.event"].with_user(self.organizer_user).create(
|
||||
self.recurrent_base_event = self.env["calendar.event"].with_context(dont_notify=True).with_user(self.organizer_user).create(
|
||||
self.recurrent_event_values
|
||||
)
|
||||
self.recurrence = self.env["calendar.recurrence"].search([("base_event_id", "=", self.recurrent_base_event.id)])
|
||||
|
||||
# set ids set by Outlook
|
||||
if not already_created:
|
||||
self.recurrence.write({
|
||||
self.recurrence.with_context(dont_notify=True).write({
|
||||
"microsoft_id": combine_ids("REC123", "REC456"),
|
||||
})
|
||||
for i, e in enumerate(self.recurrence.calendar_event_ids.sorted(key=lambda r: r.start)):
|
||||
e.write({
|
||||
e.with_context(dont_notify=True).write({
|
||||
"microsoft_id": combine_ids(f"REC123_EVENT_{i+1}", f"REC456_EVENT_{i+1}"),
|
||||
"microsoft_recurrence_master_id": "REC123",
|
||||
})
|
||||
self.recurrence.invalidate_recordset()
|
||||
self.recurrence.calendar_event_ids.invalidate_recordset()
|
||||
|
||||
self.recurrent_events = self.recurrence.calendar_event_ids.sorted(key=lambda r: r.start)
|
||||
self.recurrent_events_count = len(self.recurrent_events)
|
||||
self.recurrent_events = self.recurrence.calendar_event_ids.sorted(key=lambda r: r.start)
|
||||
self.recurrent_events_count = len(self.recurrent_events)
|
||||
|
||||
# Rollback the synchronization status after setup.
|
||||
self.env.user.microsoft_synchronization_stopped = sync_previous_state
|
||||
|
||||
def assert_odoo_event(self, odoo_event, expected_values):
|
||||
"""
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.microsoft_calendar.utils.microsoft_calendar import MicrosoftCalendarService
|
||||
from odoo.addons.microsoft_calendar.utils.microsoft_event import MicrosoftEvent
|
||||
from odoo.addons.microsoft_calendar.models.res_users import User
|
||||
from odoo.addons.microsoft_calendar.tests.common import TestCommon, mock_get_token
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
|
||||
@patch.object(User, '_get_microsoft_calendar_token', mock_get_token)
|
||||
class TestCreateEvents(TestCommon):
|
||||
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'insert')
|
||||
def test_create_simple_event_without_sync(self, mock_insert):
|
||||
"""
|
||||
@@ -84,9 +84,7 @@ class TestCreateEvents(TestCommon):
|
||||
self.assertEqual(new_records.user_id, self.organizer_user)
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'get_events')
|
||||
def test_create_simple_event_from_outlook_attendee_calendar_and_organizer_does_not_exist_in_odoo(
|
||||
self, mock_get_events
|
||||
):
|
||||
def test_create_simple_event_from_outlook_attendee_calendar_and_organizer_does_not_exist_in_odoo(self, mock_get_events):
|
||||
"""
|
||||
An event has been created in Outlook and synced in the Odoo attendee calendar.
|
||||
no Odoo user that matches with the organizer email address.
|
||||
@@ -116,6 +114,8 @@ class TestCreateEvents(TestCommon):
|
||||
"""
|
||||
A Odoo recurrent event is created when Outlook sync is not enabled.
|
||||
"""
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
|
||||
# arrange
|
||||
self.organizer_user.microsoft_synchronization_stopped = True
|
||||
@@ -135,6 +135,8 @@ class TestCreateEvents(TestCommon):
|
||||
"""
|
||||
A Odoo recurrent event is created when Outlook sync is enabled.
|
||||
"""
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
|
||||
# >>> first phase: create the recurrence
|
||||
|
||||
@@ -178,7 +180,8 @@ class TestCreateEvents(TestCommon):
|
||||
should happen as it we prevent sync of recurrences from other users
|
||||
( see microsoft_calendar/models/calendar_recurrence_rule.py::_get_microsoft_sync_domain() )
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# >>> first phase: create the recurrence
|
||||
|
||||
# act
|
||||
@@ -257,3 +260,21 @@ class TestCreateEvents(TestCommon):
|
||||
self.assert_odoo_recurrence(new_recurrences, self.expected_odoo_recurrency_from_outlook)
|
||||
for i, e in enumerate(sorted(new_events, key=lambda e: e.id)):
|
||||
self.assert_odoo_event(e, self.expected_odoo_recurrency_events_from_outlook[i])
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'insert')
|
||||
def test_forbid_recurrences_creation_synced_outlook_calendar(self, mock_insert):
|
||||
"""
|
||||
Forbids new recurrences creation in Odoo due to Outlook spam limitation of updating recurrent events.
|
||||
"""
|
||||
# Set custom calendar token validity to simulate real scenario.
|
||||
self.env.user.microsoft_calendar_token_validity = datetime.now() + timedelta(minutes=5)
|
||||
|
||||
# Assert that synchronization with Outlook is active.
|
||||
self.assertFalse(self.env.user.microsoft_synchronization_stopped)
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
self.env["calendar.event"].create(
|
||||
self.recurrent_event_values
|
||||
)
|
||||
# Assert that no insert call was made.
|
||||
mock_insert.assert_not_called()
|
||||
|
||||
@@ -86,17 +86,19 @@ class TestDeleteEvents(TestCommon):
|
||||
"""
|
||||
Archive several events at once should not produce any exception.
|
||||
"""
|
||||
# arrange
|
||||
several_simple_events = self.several_events.filtered(lambda ev: not ev.recurrency and ev.microsoft_id)
|
||||
# act
|
||||
self.several_events.action_archive()
|
||||
several_simple_events.action_archive()
|
||||
self.call_post_commit_hooks()
|
||||
self.several_events.invalidate_recordset()
|
||||
several_simple_events.invalidate_recordset()
|
||||
|
||||
# assert
|
||||
self.assertFalse(all(e.active for e in self.several_events))
|
||||
self.assertFalse(all(e.active for e in several_simple_events))
|
||||
|
||||
mock_delete.assert_has_calls([
|
||||
call(e.ms_organizer_event_id, token=ANY, timeout=ANY)
|
||||
for e in self.several_events
|
||||
for e in several_simple_events
|
||||
])
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'get_events')
|
||||
@@ -132,7 +134,6 @@ class TestDeleteEvents(TestCommon):
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'delete')
|
||||
def test_delete_one_event_from_recurrence_from_odoo_calendar(self, mock_delete):
|
||||
|
||||
# arrange
|
||||
idx = 2
|
||||
event_id = self.recurrent_events[idx].ms_organizer_event_id
|
||||
@@ -199,7 +200,6 @@ class TestDeleteEvents(TestCommon):
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'get_events')
|
||||
def test_delete_first_event_from_recurrence_from_outlook_calendar(self, mock_get_events):
|
||||
|
||||
# arrange
|
||||
rec_values = [
|
||||
dict(
|
||||
@@ -223,6 +223,8 @@ class TestDeleteEvents(TestCommon):
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'get_events')
|
||||
def test_delete_one_event_and_future_from_recurrence_from_outlook_calendar(self, mock_get_events):
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
idx = range(4, self.recurrent_events_count)
|
||||
rec_values = [
|
||||
@@ -259,7 +261,7 @@ class TestDeleteEvents(TestCommon):
|
||||
)
|
||||
|
||||
# act
|
||||
self.organizer_user.with_user(self.organizer_user).sudo()._sync_microsoft_calendar()
|
||||
self.organizer_user.with_context(dont_notify=True).with_user(self.organizer_user).sudo()._sync_microsoft_calendar()
|
||||
|
||||
# assert
|
||||
self.assertFalse(self.recurrence.exists())
|
||||
@@ -276,6 +278,8 @@ class TestDeleteEvents(TestCommon):
|
||||
"""
|
||||
Deletes the base_event of a recurrence and checks if the event was archived and the recurrence was updated.
|
||||
"""
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
idx = 0
|
||||
event_id = self.recurrent_events[idx].ms_organizer_event_id
|
||||
|
||||
@@ -102,7 +102,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update one Odoo event name from a recurrence from the organizer calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_name = "my specific event in recurrence"
|
||||
modified_event_id = 4
|
||||
@@ -135,7 +136,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update one Odoo event start date from a recurrence from the organizer calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_date = datetime(2021, 9, 29, 10, 0, 0)
|
||||
modified_event_id = 4
|
||||
@@ -182,6 +184,8 @@ class TestUpdateEvents(TestCommon):
|
||||
Update one Odoo event start date from a recurrence from the organizer calendar, in order to
|
||||
overlap another existing event.
|
||||
"""
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_date = datetime(2021, 9, 27, 10, 0, 0)
|
||||
modified_event_id = 4
|
||||
@@ -203,7 +207,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update one Odoo event name from a recurrence from the atendee calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_name = "my specific event in recurrence"
|
||||
modified_event_id = 4
|
||||
@@ -238,7 +243,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update a Odoo event name and future events from a recurrence from the organizer calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_name = "my specific event in recurrence"
|
||||
modified_event_id = 4
|
||||
@@ -277,7 +283,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update a Odoo event start date and future events from a recurrence from the organizer calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# When a time-related field is changed, the event does not follow the recurrence scheme anymore.
|
||||
# With Outlook, another constraint is that the new start of the event cannot overlap/cross the start
|
||||
# date of another event of the recurrence (see microsoft_calendar/models/calendar.py
|
||||
@@ -354,7 +361,8 @@ class TestUpdateEvents(TestCommon):
|
||||
Update a Odoo event start date and future events from a recurrence from the organizer calendar,
|
||||
overlapping an existing event.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_date = datetime(2021, 9, 27, 10, 0, 0)
|
||||
modified_event_id = 4
|
||||
@@ -425,7 +433,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update a Odoo event name and future events from a recurrence from the attendee calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_date = datetime(2021, 9, 29, 10, 0, 0)
|
||||
modified_event_id = 4
|
||||
@@ -494,7 +503,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update all events name from a recurrence from the organizer calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_name = "my specific event in recurrence"
|
||||
|
||||
@@ -528,7 +538,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update all events start date from a recurrence from the organizer calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_date = datetime(2021, 9, 25, 10, 0, 0)
|
||||
existing_recurrences = self.env["calendar.recurrence"].search([])
|
||||
@@ -591,7 +602,8 @@ class TestUpdateEvents(TestCommon):
|
||||
"""
|
||||
Update all events start date from a recurrence from the attendee calendar.
|
||||
"""
|
||||
|
||||
if not self.sync_odoo_recurrences_with_outlook_feature():
|
||||
return
|
||||
# arrange
|
||||
new_date = datetime(2021, 9, 25, 10, 0, 0)
|
||||
existing_recurrences = self.env["calendar.recurrence"].search([])
|
||||
@@ -1271,3 +1283,34 @@ class TestUpdateEvents(TestCommon):
|
||||
e.start.strftime("%Y-%m-%dT%H:%M:%S.0000000"),
|
||||
ms_events_to_update[e.ms_organizer_event_id]["dateTime"]
|
||||
)
|
||||
|
||||
@patch.object(MicrosoftCalendarService, 'patch')
|
||||
def test_forbid_simple_event_become_recurrence_sync_on(self, mock_patch):
|
||||
"""
|
||||
Forbid in Odoo simple event becoming a recurrence when Outlook Calendar sync is active.
|
||||
"""
|
||||
# Set custom calendar token validity to simulate real scenario.
|
||||
self.env.user.microsoft_calendar_token_validity = datetime.now() + timedelta(minutes=5)
|
||||
|
||||
# Assert that synchronization with Outlook Calendar is active.
|
||||
self.assertFalse(self.env.user.microsoft_synchronization_stopped)
|
||||
|
||||
# Simulate upgrade of a simple event to recurrent event (forbidden).
|
||||
simple_event = self.env['calendar.event'].create(self.simple_event_values)
|
||||
with self.assertRaises(UserError):
|
||||
simple_event.write({
|
||||
'recurrency': True,
|
||||
'rrule_type': 'weekly',
|
||||
'event_tz': 'America/Sao_Paulo',
|
||||
'end_type': 'count',
|
||||
'interval': 1,
|
||||
'count': 1,
|
||||
'fri': True,
|
||||
'month_by': 'date',
|
||||
'day': 1,
|
||||
'weekday': 'FRI',
|
||||
'byday': '2'
|
||||
})
|
||||
|
||||
# Assert that no patch call was made due to the recurrence update forbiddance.
|
||||
mock_patch.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user