19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:28 +01:00
parent 20ddc1b4a3
commit c0efcc53f5
1162 changed files with 125577 additions and 105287 deletions

View file

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta
from dateutil.parser import parse
import logging
@ -8,17 +9,17 @@ from freezegun import freeze_time
from odoo import Command
from odoo.addons.microsoft_calendar.models.microsoft_sync import MicrosoftSync
from odoo.addons.microsoft_calendar.models.microsoft_sync import MicrosoftCalendarSync
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.utils.event_id_storage import combine_ids
from odoo.addons.microsoft_calendar.models.res_users import ResUsers
from odoo.addons.microsoft_calendar.tests.common import TestCommon, mock_get_token, _modified_date_in_the_future, patch_api
from odoo.exceptions import UserError, ValidationError
_logger = logging.getLogger(__name__)
@patch.object(User, '_get_microsoft_calendar_token', mock_get_token)
@patch.object(ResUsers, '_get_microsoft_calendar_token', mock_get_token)
class TestUpdateEvents(TestCommon):
@patch_api
@ -68,8 +69,8 @@ class TestUpdateEvents(TestCommon):
# assert
self.assertTrue(res)
mock_patch.assert_called_once_with(
self.simple_event.ms_organizer_event_id,
{"subject": "my new simple event"},
self.simple_event.microsoft_id,
{"subject": "my new simple event", "isOnlineMeeting": False},
token=mock_get_token(self.organizer_user),
timeout=ANY,
)
@ -92,8 +93,8 @@ class TestUpdateEvents(TestCommon):
# assert
self.assertTrue(res)
mock_patch.assert_called_once_with(
self.simple_event.ms_organizer_event_id,
{"subject": "my new simple event"},
self.simple_event.microsoft_id,
{"subject": "my new simple event", "isOnlineMeeting": False},
token=mock_get_token(self.organizer_user),
timeout=ANY,
)
@ -123,7 +124,7 @@ class TestUpdateEvents(TestCommon):
# assert
self.assertTrue(res)
mock_patch.assert_called_once_with(
self.recurrent_events[modified_event_id].ms_organizer_event_id,
self.recurrent_events[modified_event_id].microsoft_id,
{'seriesMasterId': 'REC123', 'type': 'exception', "subject": new_name},
token=mock_get_token(self.organizer_user),
timeout=ANY,
@ -157,7 +158,7 @@ class TestUpdateEvents(TestCommon):
# assert
self.assertTrue(res)
mock_patch.assert_called_once_with(
self.recurrent_events[modified_event_id].ms_organizer_event_id,
self.recurrent_events[modified_event_id].microsoft_id,
{
'seriesMasterId': 'REC123',
'type': 'exception',
@ -228,7 +229,7 @@ class TestUpdateEvents(TestCommon):
# assert
self.assertTrue(res)
mock_patch.assert_called_once_with(
self.recurrent_events[modified_event_id].ms_organizer_event_id,
self.recurrent_events[modified_event_id].microsoft_id,
{'seriesMasterId': 'REC123', 'type': 'exception', "subject": new_name},
token=mock_get_token(self.organizer_user),
timeout=ANY,
@ -266,7 +267,7 @@ class TestUpdateEvents(TestCommon):
self.assertEqual(mock_patch.call_count, self.recurrent_events_count - modified_event_id)
for i in range(modified_event_id, self.recurrent_events_count):
mock_patch.assert_any_call(
self.recurrent_events[i].ms_organizer_event_id,
self.recurrent_events[i].microsoft_id,
{'seriesMasterId': 'REC123', 'type': 'exception', "subject": new_name},
token=mock_get_token(self.organizer_user),
timeout=ANY,
@ -304,7 +305,7 @@ class TestUpdateEvents(TestCommon):
existing_recurrences = self.env["calendar.recurrence"].search([])
expected_deleted_event_ids = [
r.ms_organizer_event_id
r.microsoft_id
for i, r in enumerate(self.recurrent_events)
if i in range(modified_event_id + 1, self.recurrent_events_count)
]
@ -337,7 +338,7 @@ class TestUpdateEvents(TestCommon):
# the base event should have been modified
mock_patch.assert_called_once_with(
self.recurrent_events[modified_event_id].ms_organizer_event_id,
self.recurrent_events[modified_event_id].microsoft_id,
{
'seriesMasterId': 'REC123',
'type': 'exception',
@ -373,14 +374,14 @@ class TestUpdateEvents(TestCommon):
existing_recurrences = self.env["calendar.recurrence"].search([])
expected_deleted_event_ids = [
r.ms_organizer_event_id
r.microsoft_id
for i, r in enumerate(self.recurrent_events)
if i in range(modified_event_id + 1, self.recurrent_events_count)
]
# as the test overlap the previous event of the updated event, this previous event
# should be removed too
expected_deleted_event_ids += [self.recurrent_events[modified_event_id - 1].ms_organizer_event_id]
expected_deleted_event_ids += [self.recurrent_events[modified_event_id - 1].microsoft_id]
# act
res = self.recurrent_events[modified_event_id].with_user(self.organizer_user).write({
@ -410,7 +411,7 @@ class TestUpdateEvents(TestCommon):
# the base event should have been modified
mock_patch.assert_called_once_with(
self.recurrent_events[modified_event_id].ms_organizer_event_id,
self.recurrent_events[modified_event_id].microsoft_id,
{
'seriesMasterId': 'REC123',
'type': 'exception',
@ -445,7 +446,7 @@ class TestUpdateEvents(TestCommon):
existing_recurrences = self.env["calendar.recurrence"].search([])
expected_deleted_event_ids = [
r.ms_organizer_event_id
r.microsoft_id
for i, r in enumerate(self.recurrent_events)
if i in range(modified_event_id + 1, self.recurrent_events_count)
]
@ -478,7 +479,7 @@ class TestUpdateEvents(TestCommon):
# the base event should have been modified
mock_patch.assert_called_once_with(
self.recurrent_events[modified_event_id].ms_organizer_event_id,
self.recurrent_events[modified_event_id].microsoft_id,
{
'seriesMasterId': 'REC123',
'type': 'exception',
@ -525,7 +526,7 @@ class TestUpdateEvents(TestCommon):
self.assertEqual(mock_patch.call_count, self.recurrent_events_count)
for i in range(self.recurrent_events_count):
mock_patch.assert_any_call(
self.recurrent_events[i].ms_organizer_event_id,
self.recurrent_events[i].microsoft_id,
{'seriesMasterId': 'REC123', 'type': 'exception', "subject": new_name},
token=mock_get_token(self.organizer_user),
timeout=ANY,
@ -548,7 +549,7 @@ class TestUpdateEvents(TestCommon):
new_date = datetime(2021, 9, 25, 10, 0, 0)
existing_recurrences = self.env["calendar.recurrence"].search([])
expected_deleted_event_ids = [
r.ms_organizer_event_id
r.microsoft_id
for i, r in enumerate(self.recurrent_events)
if i in range(1, self.recurrent_events_count)
]
@ -571,7 +572,7 @@ class TestUpdateEvents(TestCommon):
self.assertEqual(len(new_recurrences.calendar_event_ids), self.recurrent_events_count)
mock_patch.assert_called_once_with(
self.recurrent_events[0].ms_organizer_event_id,
self.recurrent_events[0].microsoft_id,
{
'seriesMasterId': 'REC123',
'type': 'exception',
@ -612,7 +613,7 @@ class TestUpdateEvents(TestCommon):
new_date = datetime(2021, 9, 25, 10, 0, 0)
existing_recurrences = self.env["calendar.recurrence"].search([])
expected_deleted_event_ids = [
r.ms_organizer_event_id
r.microsoft_id
for i, r in enumerate(self.recurrent_events)
if i in range(1, self.recurrent_events_count)
]
@ -635,7 +636,7 @@ class TestUpdateEvents(TestCommon):
self.assertEqual(len(new_recurrences.calendar_event_ids), self.recurrent_events_count)
mock_patch.assert_called_once_with(
self.recurrent_events[0].ms_organizer_event_id,
self.recurrent_events[0].microsoft_id,
{
'seriesMasterId': 'REC123',
'type': 'exception',
@ -739,7 +740,7 @@ class TestUpdateEvents(TestCommon):
self.organizer_user.with_user(self.organizer_user).sudo()._sync_microsoft_calendar()
# assert
updated_event = self.env["calendar.event"].search([('ms_organizer_event_id', '=', ms_event_id)])
updated_event = self.env["calendar.event"].search([('microsoft_id', '=', ms_event_id)])
self.assertEqual(updated_event.name, new_name)
self.assertEqual(updated_event.follow_recurrence, False)
@ -767,7 +768,7 @@ class TestUpdateEvents(TestCommon):
self.organizer_user.with_user(self.organizer_user).sudo()._sync_microsoft_calendar()
# assert
updated_event = self.env["calendar.event"].search([('ms_organizer_event_id', '=', ms_event_id)])
updated_event = self.env["calendar.event"].search([('microsoft_id', '=', ms_event_id)])
self.assertEqual(updated_event.start, new_date)
self.assertEqual(updated_event.follow_recurrence, False)
@ -797,7 +798,7 @@ class TestUpdateEvents(TestCommon):
self.organizer_user.with_user(self.organizer_user).sudo()._sync_microsoft_calendar()
# assert
updated_event = self.env["calendar.event"].search([('ms_organizer_event_id', '=', ms_event_id)])
updated_event = self.env["calendar.event"].search([('microsoft_id', '=', ms_event_id)])
self.assertEqual(updated_event.start, new_date)
self.assertEqual(updated_event.follow_recurrence, False)
@ -829,10 +830,10 @@ class TestUpdateEvents(TestCommon):
# assert
updated_events = self.env["calendar.event"].search([
('ms_organizer_event_id', 'in', tuple(ms_event_ids.keys()))
('microsoft_id', 'in', tuple(ms_event_ids.keys()))
])
for e in updated_events:
self.assertEqual(e.name, ms_event_ids[e.ms_organizer_event_id])
self.assertEqual(e.name, ms_event_ids[e.microsoft_id])
@patch.object(MicrosoftCalendarService, 'get_events')
def test_update_start_of_one_event_and_future_of_recurrence_from_outlook_organizer_calendar(self, mock_get_events):
@ -941,14 +942,15 @@ class TestUpdateEvents(TestCommon):
# new recurrence
self.assertEqual(len(new_recurrences), 1)
self.assertEqual(len(new_events), new_recurrence_event_count)
self.assertEqual(new_recurrences.ms_organizer_event_id, "REC123_new")
self.assertEqual(new_recurrences.microsoft_id, "REC123_new")
self.assertEqual(new_recurrences.ms_universal_event_id, "REC456_new")
for i, e in enumerate(sorted(new_events, key=lambda e: e.id)):
self.assert_odoo_event(e, {
"start": new_rec_first_event_start_date + timedelta(days=i * self.recurrent_event_interval),
"stop": new_rec_first_event_end_date + timedelta(days=i * self.recurrent_event_interval),
"microsoft_id": combine_ids(f'REC123_new_{i+1}', f'REC456_new_{i+1}'),
"microsoft_id": f'REC123_new_{i+1}',
"ms_universal_event_id": f'REC456_new_{i+1}',
"recurrence_id": new_recurrences,
"follow_recurrence": True,
})
@ -1060,14 +1062,15 @@ class TestUpdateEvents(TestCommon):
# new recurrence
self.assertEqual(len(new_recurrences), 1)
self.assertEqual(len(new_events), new_recurrence_event_count)
self.assertEqual(new_recurrences.ms_organizer_event_id, "REC123_new")
self.assertEqual(new_recurrences.microsoft_id, "REC123_new")
self.assertEqual(new_recurrences.ms_universal_event_id, "REC456_new")
for i, e in enumerate(sorted(new_events, key=lambda e: e.id)):
self.assert_odoo_event(e, {
"start": new_rec_first_event_start_date + timedelta(days=i * self.recurrent_event_interval),
"stop": new_rec_first_event_end_date + timedelta(days=i * self.recurrent_event_interval),
"microsoft_id": combine_ids(f'REC123_new_{i+1}', f'REC456_new_{i+1}'),
"microsoft_id": f"REC123_new_{i+1}",
"ms_universal_event_id": f"REC456_new_{i+1}",
"recurrence_id": new_recurrences,
"follow_recurrence": True,
})
@ -1098,10 +1101,10 @@ class TestUpdateEvents(TestCommon):
# assert
updated_events = self.env["calendar.event"].search([
('ms_organizer_event_id', 'in', tuple(ms_events_to_update.keys()))
('microsoft_id', 'in', tuple(ms_events_to_update.keys()))
])
for e in updated_events:
self.assertEqual(e.name, ms_events_to_update[e.ms_organizer_event_id])
self.assertEqual(e.name, ms_events_to_update[e.microsoft_id])
self.assertEqual(e.follow_recurrence, True)
def _prepare_outlook_events_for_all_events_start_date_update(self, nb_of_events):
@ -1182,12 +1185,12 @@ class TestUpdateEvents(TestCommon):
# ----------- ASSERT -----------
updated_events = self.env["calendar.event"].search([
('ms_organizer_event_id', 'in', tuple(ms_events_to_update.keys()))
('microsoft_id', 'in', tuple(ms_events_to_update.keys()))
])
for e in updated_events:
self.assertEqual(
e.start.strftime("%Y-%m-%dT%H:%M:%S.0000000"),
ms_events_to_update[e.ms_organizer_event_id]["dateTime"]
ms_events_to_update[e.microsoft_id]["dateTime"]
)
@patch.object(MicrosoftCalendarService, 'get_events')
@ -1210,14 +1213,13 @@ class TestUpdateEvents(TestCommon):
self.organizer_user.with_user(self.organizer_user).sudo()._sync_microsoft_calendar()
# ----------- ASSERT -----------
updated_events = self.env["calendar.event"].search([
('ms_organizer_event_id', 'in', tuple(ms_events_to_update.keys()))
('microsoft_id', 'in', tuple(ms_events_to_update.keys()))
])
for e in updated_events:
self.assertEqual(
e.start.strftime("%Y-%m-%dT%H:%M:%S.0000000"),
ms_events_to_update[e.ms_organizer_event_id]["dateTime"]
ms_events_to_update[e.microsoft_id]["dateTime"]
)
@patch.object(MicrosoftCalendarService, 'get_events')
@ -1242,12 +1244,12 @@ class TestUpdateEvents(TestCommon):
# ----------- ASSERT -----------
updated_events = self.env["calendar.event"].search([
('ms_organizer_event_id', 'in', tuple(ms_events_to_update.keys()))
('microsoft_id', 'in', tuple(ms_events_to_update.keys()))
])
for e in updated_events:
self.assertEqual(
e.start.strftime("%Y-%m-%dT%H:%M:%S.0000000"),
ms_events_to_update[e.ms_organizer_event_id]["dateTime"]
ms_events_to_update[e.microsoft_id]["dateTime"]
)
@patch.object(MicrosoftCalendarService, 'get_events')
@ -1287,12 +1289,12 @@ class TestUpdateEvents(TestCommon):
# ----------- ASSERT -----------
updated_events = self.env["calendar.event"].search([
('ms_organizer_event_id', 'in', tuple(ms_events_to_update.keys()))
('microsoft_id', 'in', tuple(ms_events_to_update.keys()))
])
for e in updated_events:
self.assertEqual(
e.start.strftime("%Y-%m-%dT%H:%M:%S.0000000"),
ms_events_to_update[e.ms_organizer_event_id]["dateTime"]
ms_events_to_update[e.microsoft_id]["dateTime"]
)
@patch.object(MicrosoftCalendarService, 'patch')
@ -1326,6 +1328,27 @@ class TestUpdateEvents(TestCommon):
# Assert that no patch call was made due to the recurrence update forbiddance.
mock_patch.assert_not_called()
@patch.object(MicrosoftCalendarService, 'patch')
def test_update_synced_event_with_sync_config_paused(self, mock_patch):
"""
Updates an event with the synchronization paused, the event must have its field 'need_sync_m' as True
for later synchronizing it with Outlook Calendar.
"""
# Set user synchronization configuration as active and pause it.
self.organizer_user.microsoft_synchronization_stopped = False
self.organizer_user.pause_microsoft_synchronization()
# Try to update a simple event in Odoo Calendar.
self.simple_event.with_user(self.organizer_user).write({"name": "updated simple event"})
self.call_post_commit_hooks()
self.simple_event.invalidate_recordset()
# Ensure that synchronization is paused, delete wasn't called and record is waiting to be synced again.
self.assertFalse(self.organizer_user.microsoft_synchronization_stopped)
self.assertEqual(self.organizer_user._get_microsoft_sync_status(), "sync_paused")
self.assertTrue(self.simple_event.need_sync_m, "Sync variable must be true for updating event when sync re-activates")
mock_patch.assert_not_called()
@patch.object(MicrosoftCalendarService, 'get_events')
@patch.object(MicrosoftCalendarService, 'delete')
@patch.object(MicrosoftCalendarService, 'insert')
@ -1340,6 +1363,9 @@ class TestUpdateEvents(TestCommon):
self.simple_event_values['user_id'] = self.organizer_user.id
self.simple_event_values['partner_ids'] = [Command.set([self.organizer_user.partner_id.id])]
event = self.env['calendar.event'].with_user(self.organizer_user).create(self.simple_event_values)
# Simulate sync where the api update the microsoft_id field
event.ms_universal_event_id = "test_id_for_event"
event.microsoft_id = "test_id_for_organizer"
# Deactivate user B's calendar synchronization. Try changing the event organizer to user B.
# A ValidationError must be thrown because user B's calendar is not synced.
@ -1360,8 +1386,6 @@ class TestUpdateEvents(TestCommon):
mock_get_events.return_value = ([], None)
# Change the event organizer: user B (the organizer) is synced and now listed as an attendee.
event.ms_universal_event_id = "test_id_for_event"
event.ms_organizer_event_id = "test_id_for_organizer"
event.with_user(self.organizer_user).write({
'user_id': self.attendee_user.id,
'partner_ids': [Command.set([self.organizer_user.partner_id.id, self.attendee_user.partner_id.id])]
@ -1372,7 +1396,7 @@ class TestUpdateEvents(TestCommon):
# Ensure that the event was deleted and recreated with the new organizer and the organizer listed as attendee.
mock_delete.assert_any_call(
event.ms_organizer_event_id,
event.microsoft_id,
token=mock_get_token(self.attendee_user),
timeout=ANY,
)
@ -1388,12 +1412,16 @@ class TestUpdateEvents(TestCommon):
""" Ensure that sync restart is not blocked when there are recurrence outliers in Odoo database. """
# Stop synchronization, set recurrent events as outliers and restart sync with Outlook.
self.organizer_user.stop_microsoft_synchronization()
self.recurrent_events.with_user(self.organizer_user).write({'microsoft_id': False, 'follow_recurrence': False})
self.recurrent_events.with_user(self.organizer_user).write({
'microsoft_id': False,
'ms_universal_event_id': False,
'follow_recurrence': False
})
self.attendee_user.with_user(self.attendee_user).restart_microsoft_synchronization()
self.organizer_user.with_user(self.organizer_user).restart_microsoft_synchronization()
self.assertTrue(all(ev.need_sync_m for ev in self.recurrent_events))
@patch.object(MicrosoftSync, '_write_from_microsoft')
@patch.object(MicrosoftCalendarSync, '_write_from_microsoft')
@patch.object(MicrosoftCalendarService, 'get_events')
def test_update_old_event_synced_with_outlook(self, mock_get_events, mock_write_from_microsoft):
"""