diff --git a/caldav_sync/__manifest__.py b/caldav_sync/__manifest__.py index fba9cf5..ab5c20a 100644 --- a/caldav_sync/__manifest__.py +++ b/caldav_sync/__manifest__.py @@ -8,7 +8,7 @@ { "name": "CalDAV Synchronization", - "version": "18.0.0.6.3", + "version": "18.0.0.7.0", "license": "LGPL-3", "category": "Productivity", "summary": "Synchronize Odoo Calendar Events with CalDAV Servers", diff --git a/caldav_sync/models/calendar_event.py b/caldav_sync/models/calendar_event.py index 42245dd..4fb38e1 100644 --- a/caldav_sync/models/calendar_event.py +++ b/caldav_sync/models/calendar_event.py @@ -584,11 +584,11 @@ class CalendarEvent(models.Model): @api.model def _sync_event_from_ical( - self, ical_event: icalendar.cal.Event, user: User + self, ical_event: icalendar.cal.Event, user: User ) -> CalendarEvent: """Given an iCalendar event, compare the event with any existing Odoo event that it matches and synchronize the changes. If no event - exists, create one. + exists, create one iff the event is in the future. :param ical_event: The iCalendar event to synchronize with Odoo. :param user: The res.user record for whom to synchronize the event. @@ -609,12 +609,15 @@ class CalendarEvent(models.Model): existing_instance = self._get_existing_instance(uid, recurrence_id) outdated = self._get_outdated(component, existing_instance, synced_events) owned = ( - existing_instance and existing_instance.partner_id == user.partner_id + existing_instance and existing_instance.partner_id == user.partner_id ) # 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 the event is in the past, we just ignore it. + if values.get("stop") < datetime.now(tz=None): + continue # If we're creating an instance and it doesn't follow the recurrence, # just scrap the recurrency vals, they're not useful if not recurrency_vals.get("follow_recurrence"): diff --git a/caldav_sync/tests/test_calendar.py b/caldav_sync/tests/test_calendar.py index 5be48d7..093b91f 100644 --- a/caldav_sync/tests/test_calendar.py +++ b/caldav_sync/tests/test_calendar.py @@ -25,7 +25,7 @@ def _get_ics_path(filename): @contextmanager -def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None): +def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None, futurize=True): with ( patch("caldav.DAVClient") as MockDAVClient, patch("caldav.Calendar") as MockCalendar, @@ -46,8 +46,8 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None): # Get or create the mock calendar for this user mock_calendar = calendar_side_effect(user.caldav_calendar_url) - def event_by_uid_side_effect(self, uid): - for event in self.events(): + def event_by_uid_side_effect(uid): + for event in mock_calendar.events(): if str(event.icalendar_component.get("uid")) == uid: return event return DEFAULT @@ -66,6 +66,18 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None): if subcomponent.name == "VEVENT": subcomponent["last-modified"] = icalendar.vDate(last_modified) subcomponent["dtstamp"] = icalendar.vDate(last_modified) + if futurize: + for event in ical_events: + for subcomponent in event.subcomponents: + if subcomponent.name == "VEVENT": + start = subcomponent.get("dtstart") and subcomponent.decoded("dtstart") + end = subcomponent.get("dtend") and subcomponent.decoded("dtend") + if isinstance(start, datetime) and isinstance(end, datetime): + duration = end - start + else: + duration = timedelta(hours=1) + subcomponent["dtstart"] = icalendar.vDDDTypes(datetime.now()) + subcomponent["dtend"] = icalendar.vDDDTypes(datetime.now() + duration) base_events = [event for event in ical_events if not event.get("recurrence-id")] for base_event in base_events: @@ -123,6 +135,16 @@ class TestCalendarEvent(TransactionCase, CaldavTestCommon): new_events = events_after_sync - current_events self.assertEqual(len(new_events), 1) + def test_basic_past_event_from_server_no_create(self): + user = self.user_1 + ics_path = _get_ics_path("basic.ics") + with _patch_caldav_with_events_from_ics(ics_path, user, futurize=False): + current_events = self.env["calendar.event"].search([]) + self.env["calendar.event"].poll_caldav_server() + events_after_sync = self.env["calendar.event"].search([]) + new_events = events_after_sync - current_events + self.assertEqual(len(new_events), 0) + def test_basic_event_from_server_update(self): user = self.user_1 ics_path = _get_ics_path("basic.ics") @@ -239,6 +261,7 @@ class TestCalendarEvent(TransactionCase, CaldavTestCommon): self.assertIn(user2.partner_id, event.partner_ids) self.assertIn(user3.partner_id, event.partner_ids) + def test_multiple_user_attendees_event_from_server_update(self): """Test event has (as in above test): Organizer: user1 (test1@example.com)