diff --git a/addons/google_calendar/models/calendar.py b/addons/google_calendar/models/calendar.py index 9cbe2f25921..ed591740852 100644 --- a/addons/google_calendar/models/calendar.py +++ b/addons/google_calendar/models/calendar.py @@ -14,10 +14,13 @@ class Meeting(models.Model): _name = 'calendar.event' _inherit = ['calendar.event', 'google.calendar.sync'] + MEET_ROUTE = 'meet.google.com' + google_id = fields.Char( 'Google Calendar Event Id', compute='_compute_google_id', store=True, readonly=False) guests_readonly = fields.Boolean( 'Guests Event Modification Permission', default=False) + videocall_source = fields.Selection(selection_add=[('google_meet', 'Google Meet')], ondelete={'google_meet': 'set discuss'}) @api.depends('recurrence_id.google_id') def _compute_google_id(self): @@ -32,6 +35,12 @@ class Meeting(models.Model): elif not event.google_id: event.google_id = False + @api.depends('videocall_location') + def _compute_videocall_source(self): + events_with_google_url = self.filtered(lambda event: self.MEET_ROUTE in (event.videocall_location or '')) + events_with_google_url.videocall_source = 'google_meet' + super(Meeting, self - events_with_google_url)._compute_videocall_source() + @api.model def _get_google_synced_fields(self): return {'name', 'description', 'allday', 'start', 'date_end', 'stop', diff --git a/addons/google_calendar/models/res_users.py b/addons/google_calendar/models/res_users.py index c636c3e0bc9..429bbcbb1ef 100644 --- a/addons/google_calendar/models/res_users.py +++ b/addons/google_calendar/models/res_users.py @@ -53,24 +53,10 @@ class User(models.Model): def _sync_google_calendar(self, calendar_service: GoogleCalendarService): self.ensure_one() - if self._get_google_sync_status() != "sync_active": + results = self._sync_request(calendar_service) + if not results or not results.get('events'): return False - # don't attempt to sync when another sync is already in progress, as we wouldn't be - # able to commit the transaction anyway (row is locked) - self.env.cr.execute("""SELECT id FROM res_users WHERE id = %s FOR NO KEY UPDATE SKIP LOCKED""", [self.id]) - if not self.env.cr.rowcount: - _logger.info("skipping calendar sync, locked user %s", self.login) - return False - - full_sync = not bool(self.google_calendar_sync_token) - with google_calendar_token(self) as token: - try: - events, next_sync_token, default_reminders = calendar_service.get_events(self.google_calendar_account_id.calendar_sync_token, token=token) - except InvalidSyncToken: - events, next_sync_token, default_reminders = calendar_service.get_events(token=token) - full_sync = True - self.google_calendar_account_id.calendar_sync_token = next_sync_token - + events, default_reminders, full_sync = results.values() # Google -> Odoo send_updates = not full_sync events.clear_type_ambiguity(self.env) @@ -89,6 +75,50 @@ class User(models.Model): return bool(events | synced_events) or bool(recurrences | synced_recurrences) + def _sync_single_event(self, calendar_service: GoogleCalendarService, odoo_event, event_id): + self.ensure_one() + results = self._sync_request(calendar_service, event_id) + if not results or not results.get('events'): + return False + event, default_reminders, full_sync = results.values() + # Google -> Odoo + send_updates = not full_sync + event.clear_type_ambiguity(self.env) + synced_events = self.env['calendar.event']._sync_google2odoo(event, default_reminders=default_reminders) + # Odoo -> Google + odoo_event.with_context(send_updates=send_updates)._sync_odoo2google(calendar_service) + return bool(odoo_event | synced_events) + + def _sync_request(self, calendar_service, event_id=None): + if self._get_google_sync_status() != "sync_active": + return False + # don't attempt to sync when another sync is already in progress, as we wouldn't be + # able to commit the transaction anyway (row is locked) + self.env.cr.execute("""SELECT id FROM res_users WHERE id = %s FOR NO KEY UPDATE SKIP LOCKED""", [self.id]) + if not self.env.cr.rowcount: + _logger.info("skipping calendar sync, locked user %s", self.login) + return False + + full_sync = not bool(self.google_calendar_sync_token) + with google_calendar_token(self) as token: + try: + if not event_id: + events, next_sync_token, default_reminders = calendar_service.get_events(self.google_calendar_account_id.calendar_sync_token, token=token) + else: + # We force the sync_token parameter to avoid doing a full sync. + # Other events are fetched when the calendar view is displayed. + events, next_sync_token, default_reminders = calendar_service.get_events(sync_token=token, token=token, event_id=event_id) + except InvalidSyncToken: + events, next_sync_token, default_reminders = calendar_service.get_events(token=token) + full_sync = True + if next_sync_token: + self.google_calendar_account_id.calendar_sync_token = next_sync_token + return { + 'events': events, + 'default_reminders': default_reminders, + 'full_sync': full_sync, + } + @api.model def _sync_all_google_calendar(self): """ Cron job """ @@ -103,6 +133,12 @@ class User(models.Model): _logger.exception("[%s] Calendar Synchro - Exception : %s!", user, exception_to_unicode(e)) self.env.cr.rollback() + def is_google_calendar_synced(self): + """ True if Google Calendar settings are filled (Client ID / Secret) and user calendar is synced + meaning we can make API calls, false otherwise.""" + self.ensure_one() + return self.google_calendar_token and self._get_google_sync_status() == 'sync_active' + def stop_google_synchronization(self): self.ensure_one() self.google_synchronization_stopped = True diff --git a/addons/google_calendar/utils/google_calendar.py b/addons/google_calendar/utils/google_calendar.py index a2bf89bd32d..d063de6806a 100644 --- a/addons/google_calendar/utils/google_calendar.py +++ b/addons/google_calendar/utils/google_calendar.py @@ -29,8 +29,10 @@ class GoogleCalendarService(): self.google_service = google_service @requires_auth_token - def get_events(self, sync_token=None, token=None, timeout=TIMEOUT): + def get_events(self, sync_token=None, token=None, event_id=None, timeout=TIMEOUT): url = "/calendar/v3/calendars/primary/events" + if event_id: + url += f"/{event_id}" headers = {'Content-type': 'application/json'} params = {'access_token': token} if sync_token: @@ -51,6 +53,11 @@ class GoogleCalendarService(): raise InvalidSyncToken("Invalid sync token. Full sync required") raise e + if event_id: + next_sync_token = None + default_reminders = () + return GoogleEvent([data]), next_sync_token, default_reminders + events = data.get('items', []) next_page_token = data.get('nextPageToken') while next_page_token: