def _sync_microsoft_calendar(self, calendar_service: MicrosoftCalendarService): self.ensure_one() full_sync = not bool(self.microsoft_calendar_sync_token) with microsoft_calendar_token(self) as token: try: events, next_sync_token, default_reminders = calendar_service.get_events(self.microsoft_calendar_sync_token, token=token) except InvalidSyncToken: events, next_sync_token, default_reminders = calendar_service.get_events(token=token) full_sync = True self.microsoft_calendar_sync_token = next_sync_token # Microsoft -> Odoo recurrences = events.filter(lambda e: e.is_recurrent()) synced_events, synced_recurrences = self.env['calendar.event']._sync_microsoft2odoo(events, default_reminders=default_reminders) if events else (self.env['calendar.event'], self.env['calendar.recurrence']) # Odoo -> Microsoft recurrences = self.env['calendar.recurrence']._get_microsoft_records_to_sync(full_sync=full_sync) recurrences -= synced_recurrences recurrences._sync_odoo2microsoft(calendar_service) synced_events |= recurrences.calendar_event_ids events = self.env['calendar.event']._get_microsoft_records_to_sync(full_sync=full_sync) (events - synced_events)._sync_odoo2microsoft(calendar_service) return bool(events | synced_events) or bool(recurrences | synced_recurrences)
def reset_account(self): microsoft = MicrosoftCalendarService(self.env['microsoft.service']) events = self.env['calendar.event'].search([ ('user_id', '=', self.user_id.id), ('microsoft_id', '!=', False) ]) if self.delete_policy in ('delete_microsoft', 'delete_both'): with microsoft_calendar_token(self.user_id) as token: for event in events: microsoft.delete(event.microsoft_id, token=token) if self.delete_policy in ('delete_odoo', 'delete_both'): events.microsoft_id = False events.unlink() if self.sync_policy == 'all': events.write({ 'microsoft_id': False, 'need_sync_m': True, }) self.user_id._set_microsoft_auth_tokens(False, False, 0) self.user_id.write({ 'microsoft_calendar_sync_token': False, })
def _microsoft_delete(self, microsoft_service: MicrosoftCalendarService, microsoft_id, timeout=TIMEOUT): with microsoft_calendar_token(self.env.user.sudo()) as token: if token: microsoft_service.delete(microsoft_id, token=token, timeout=timeout)
def _microsoft_attendee_answer(self, microsoft_service: MicrosoftCalendarService, microsoft_id, answer, params, timeout=TIMEOUT): if not answer: return with microsoft_calendar_token(self.env.user.sudo()) as token: if token: self._ensure_attendees_have_email() microsoft_service.answer(microsoft_id, answer, params, token=token, timeout=timeout) self.write({ 'need_sync_m': False, })
def _microsoft_patch(self, microsoft_service: MicrosoftCalendarService, microsoft_id, values, timeout=TIMEOUT): with microsoft_calendar_token(self.env.user.sudo()) as token: if token: microsoft_service.patch(microsoft_id, values, token=token, timeout=timeout) self.need_sync_m = False
def write(self, vals): microsoft_service = MicrosoftCalendarService( self.env['microsoft.service']) if 'microsoft_id' in vals: self._from_microsoft_ids.clear_cache(self) synced_fields = self._get_microsoft_synced_fields() if 'need_sync_m' not in vals and vals.keys() & synced_fields: fields_to_sync = [x for x in vals.keys() if x in synced_fields] if fields_to_sync: vals['need_sync_m'] = True else: fields_to_sync = [x for x in vals.keys() if x in synced_fields] result = super().write(vals) for record in self.filtered('need_sync_m'): if record.microsoft_id and fields_to_sync: values = record._microsoft_values(fields_to_sync) if not values: continue record._microsoft_patch(microsoft_service, record.microsoft_id, values, timeout=3) return result
def write(self, vals): microsoft_service = MicrosoftCalendarService( self.env['microsoft.service']) if 'microsoft_id' in vals: self._from_microsoft_ids.clear_cache(self) synced_fields = self._get_microsoft_synced_fields() if 'need_sync_m' not in vals and vals.keys() & synced_fields: fields_to_sync = [x for x in vals.keys() if x in synced_fields] if fields_to_sync: vals['need_sync_m'] = True else: fields_to_sync = [x for x in vals.keys() if x in synced_fields] result = super().write(vals) need_delete = 'active' in vals.keys() and not vals.get('active') for record in self.filtered('need_sync_m'): if need_delete and record.microsoft_id: # We need to delete the event. Cancel is not sufficant. Errors may occurs record._microsoft_delete(microsoft_service, record.microsoft_id, timeout=3) elif record.microsoft_id and fields_to_sync: values = record._microsoft_values(fields_to_sync) if not values: continue record._microsoft_patch(microsoft_service, record.microsoft_id, values, timeout=3) return result
def unlink(self): synced = self.filtered('microsoft_id') microsoft_service = MicrosoftCalendarService( self.env['microsoft.service']) for ev in synced: ev._microsoft_delete(microsoft_service, ev.microsoft_id) return super().unlink()
def _apply_recurrence(self, specific_values_creation=None, no_send_edit=False, generic_values_creation=None): events = self.filtered('need_sync_m').calendar_event_ids detached_events = super()._apply_recurrence(specific_values_creation, no_send_edit, generic_values_creation) microsoft_service = MicrosoftCalendarService( self.env['microsoft.service']) # If a synced event becomes a recurrence, the event needs to be deleted from # Microsoft since it's now the recurrence which is synced. # Those events are kept in the database and their microsoft_id is updated # according to the recurrence microsoft_id, therefore we need to keep an inactive copy # of those events with the original microsoft_id. The next sync will then correctly # delete those events from Microsoft. vals = [] for event in events.filtered('microsoft_id'): if event.active and event.microsoft_id and not event.recurrence_id.microsoft_id: vals += [{ 'name': event.name, 'microsoft_id': event.microsoft_id, 'start': event.start, 'stop': event.stop, 'active': False, 'need_sync_m': True, }] event._microsoft_delete(microsoft_service, event.microsoft_id) event.microsoft_id = False self.env['calendar.event'].create(vals) self.calendar_event_ids.need_sync_m = False return detached_events
def sync_data(self, model, **kw): """ This route/function is called when we want to synchronize Odoo calendar with Microsoft Calendar. Function return a dictionary with the status : need_config_from_admin, need_auth, need_refresh, sync_stopped, success if not calendar_event The dictionary may contains an url, to allow Odoo Client to redirect user on this URL for authorization for example """ if model == 'calendar.event': MicrosoftCal = MicrosoftCalendarService( request.env['microsoft.service']) # Checking that admin have already configured Microsoft API for microsoft synchronization ! client_id = request.env['ir.config_parameter'].sudo().get_param( 'microsoft_calendar_client_id') if not client_id or client_id == '': action_id = '' if MicrosoftCal._can_authorize_microsoft(request.env.user): action_id = request.env.ref( 'base_setup.action_general_configuration').id return { "status": "need_config_from_admin", "url": '', "action": action_id } # Checking that user have already accepted Odoo to access his calendar ! if not MicrosoftCal.is_authorized(request.env.user): url = MicrosoftCal._microsoft_authentication_url( from_url=kw.get('fromurl')) return {"status": "need_auth", "url": url} # If App authorized, and user access accepted, We launch the synchronization need_refresh = request.env.user.sudo()._sync_microsoft_calendar( MicrosoftCal) # If synchronization has been stopped if not need_refresh and request.env.user.microsoft_synchronization_stopped: return {"status": "sync_stopped", "url": ''} return { "status": "need_refresh" if need_refresh else "no_new_event_from_microsoft", "url": '' } return {"status": "success"}
def _microsoft_insert(self, microsoft_service: MicrosoftCalendarService, values, timeout=TIMEOUT): if not values: return with microsoft_calendar_token(self.env.user.sudo()) as token: if token: microsoft_id = microsoft_service.insert(values, token=token, timeout=timeout) self.write({ 'microsoft_id': microsoft_id, 'need_sync_m': False, })
def _sync_all_microsoft_calendar(self): """ Cron job """ users = self.env['res.users'].search([('microsoft_calendar_rtoken', '!=', False)]) microsoft = MicrosoftCalendarService(self.env['microsoft.service']) for user in users: _logger.info("Calendar Synchro - Starting synchronization for %s", user) try: user.with_user(user).sudo()._sync_microsoft_calendar(microsoft) except Exception as e: _logger.exception("[%s] Calendar Synchro - Exception : %s !", user, exception_to_unicode(e))
def _microsoft_sync_event(self, answer): microsoft_service = MicrosoftCalendarService( self.env['microsoft.service']) params = {"comment": "", "sendResponse": True} # Microsoft prevent user to answer the meeting when they are the organizer for event in self.event_id.filtered( lambda e: e.microsoft_id and e.user_id != self.env.user): event._microsoft_attendee_answer(microsoft_service, event.microsoft_id, answer, params)
def create(self, vals_list): if any(vals.get('microsoft_id') for vals in vals_list): self._from_microsoft_ids.clear_cache(self) records = super().create(vals_list) microsoft_service = MicrosoftCalendarService(self.env['microsoft.service']) records_to_sync = records.filtered(lambda r: r.need_sync_m and r.active) for record in records_to_sync: record._microsoft_insert(microsoft_service, record._microsoft_values(self._get_microsoft_synced_fields()), timeout=3) return records
def write(self, vals): """ Tell to resync the link microsoft/odoo of the event when the status of an attendee is modified """ res = super().write(vals) if vals.get('state'): # When the state is changed, the corresponding event must be sync with microsoft microsoft_service = MicrosoftCalendarService( self.env['microsoft.service']) self.event_id.filtered('microsoft_id')._sync_odoo2microsoft( microsoft_service) return res
def setUp(self): super().setUp() self.microsoft_service = MicrosoftCalendarService( self.env['microsoft.service'])