caldav_sync: further fixes for organizer issues

This commit is contained in:
Marc Durepos 2025-04-08 16:11:45 -04:00
parent 3ebadcb969
commit 4b19f8262e
2 changed files with 44 additions and 14 deletions

View file

@ -611,7 +611,8 @@ class CalendarEvent(models.Model):
owned = (
existing_instance and existing_instance.partner_id == user.partner_id
)
values = self._get_values_from_ical_component(component, user)
# Pass for_creation=True only when creating a new event
values = self._get_values_from_ical_component(component, user, for_creation=not existing_instance)
recurrency_vals = self._get_recurrency_values_from_ical_event(component)
if not existing_instance:
# If we're creating an instance and it doesn't follow the recurrence,
@ -647,7 +648,7 @@ class CalendarEvent(models.Model):
return synced_events
@api.model
def _get_existing_instance(self, uid, recurrence_id: datetime) -> CalendarEvent:
def _get_existing_instance(self, uid, recurrence_id: Optional[datetime]) -> CalendarEvent:
"""Find the Odoo calendar.event record matching uid and,
if set, recurrence_id.
"""
@ -686,7 +687,7 @@ class CalendarEvent(models.Model):
return instance or self.env["calendar.event"].search(
[
("caldav_uid", "=", "uid"),
("caldav_uid", "=", uid),
("recurrence_id", "=", False),
]
)
@ -841,14 +842,15 @@ class CalendarEvent(models.Model):
@api.model
def _get_values_from_ical_component(
self, component: icalendar.cal.Component, user: User
self, component: icalendar.cal.Component, user: User, for_creation: bool = False
) -> Dict:
"""Get the dictionary representing calendar.event field values from
an iCalendar event.
:param component: The iCalendar component from which to extract
values.
:param component: The iCalendar component from which to extract values.
:param user: The res.users record the event will belong to.
:param for_creation: Whether these values are for creating a new event (True)
or updating an existing one (False).
:return: The dictionary of values to construct a calendar.event."""
start = component.get("dtstart") and component.decoded("dtstart")
if isinstance(start, datetime):
@ -856,11 +858,11 @@ class CalendarEvent(models.Model):
end = component.get("dtend") and component.decoded("dtend")
if isinstance(end, datetime):
end = end.astimezone(utc).replace(tzinfo=None)
organizer_partner = self._get_organizer_partner(component)
# Get the Odoo user ID associated with the organizer partner
# If no Odoo user is associated, set to False (external)
organizer = organizer_partner.user_ids[0].id if organizer_partner.user_ids else False
# Get attendees regardless of creation/update
attendee_ids = self._get_attendee_partners(component, user.partner_id.email)
# Basic values that apply to both creation and updates
values = {
"name": str(component.get("summary")),
"start": start,
@ -872,10 +874,25 @@ class CalendarEvent(models.Model):
"videocall_location": self._extract_component_text(component, "conference"),
"caldav_uid": str(component.get("uid")),
"partner_ids": [(6, 0, attendee_ids.ids)],
"partner_id": organizer_partner.id if organizer_partner else user.partner_id.id,
"user_id": organizer,
}
# Only set user_id and partner_id during creation
if for_creation:
organizer_partner = self._get_organizer_partner(component)
if organizer_partner:
# Get the Odoo user ID associated with the organizer partner
organizer = organizer_partner.user_ids[0].id if organizer_partner.user_ids else False
values.update({
"partner_id": organizer_partner.id,
"user_id": organizer,
})
else:
# For new events without an organizer, use the current user
values.update({
"partner_id": user.partner_id.id,
"user_id": user.id,
})
return values
@api.model

View file

@ -1,5 +1,5 @@
from collections.abc import Iterable
from odoo.tests import TransactionCase
from odoo.tests import TransactionCase, tagged
from odoo import Command
from unittest.mock import patch, MagicMock, DEFAULT
import icalendar
@ -85,6 +85,7 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None):
yield
@tagged('post_install', '-at_install')
class TestCalendarEvent(TransactionCase, CaldavTestCommon):
@classmethod
def setUpClass(cls):
@ -127,17 +128,29 @@ class TestCalendarEvent(TransactionCase, CaldavTestCommon):
ics_path = _get_ics_path("basic.ics")
with _patch_caldav_with_events_from_ics(ics_path, user):
self.env["calendar.event"].poll_caldav_server()
# Verify the event was created correctly
event = self.env["calendar.event"].search([("user_id", "=", user.id)])
self.assertEqual(event.name, "Test")
orig_start = event.start
orig_stop = event.stop
# Now update the event with the updated ICS data
ics_path = _get_ics_path("basic_updated.ics")
with _patch_caldav_with_events_from_ics(
ics_path,
user,
last_modified=(datetime.now(UTC)),
):
# Clear any caches to ensure fresh data
self.env["calendar.event"].invalidate_model()
self.env["calendar.event"].poll_caldav_server()
# Refresh the event from the database to get updated values
event.invalidate_recordset()
event = self.env["calendar.event"].search([("user_id", "=", user.id)])
# Verify the event was updated correctly
self.assertEqual(event.name, "Test Updated")
# This next one is just lazy avoiding the HTML stripping
self.assertIn("Some note ...", event.description)