# -*- coding: utf-8 -*- import logging import secrets from odoo import api, fields, models _logger = logging.getLogger(__name__) class CalendarEvent(models.Model): _inherit = 'calendar.event' x_fc_source_account_id = fields.Many2one( 'fusion.calendar.account', string='Source Calendar Account', help='The external calendar account that originally synced this event.', ) x_fc_is_external = fields.Boolean( string='External Event', compute='_compute_is_external', store=True, ) x_fc_link_ids = fields.One2many( 'fusion.calendar.event.link', 'x_fc_event_id', string='External Links', ) x_fc_manage_token = fields.Char( string='Manage Token', index=True, copy=False, help='Random token allowing public visitors to manage their booking.', ) x_fc_client_email = fields.Char(string='Client Email') x_fc_client_phone = fields.Char(string='Client Phone') x_fc_address_lat = fields.Float(string='Location Latitude', digits=(10, 7)) x_fc_address_lng = fields.Float(string='Location Longitude', digits=(10, 7)) x_fc_travel_minutes_before = fields.Integer( string='Travel Time Before (min)', help='Estimated travel time to reach this appointment from the previous one.', ) x_fc_is_travel_block = fields.Boolean( string='Travel Block', help='Auto-generated travel time placeholder event.', ) @api.depends('x_fc_source_account_id') def _compute_is_external(self): for event in self: event.x_fc_is_external = bool(event.x_fc_source_account_id) def _skip_fc_sync(self): """Check if Fusion Schedule should skip syncing (native Odoo sync active).""" ctx = self.env.context return ctx.get('no_calendar_sync') or ctx.get('dont_notify') def unlink(self): """On delete, also remove from all linked external calendars.""" if not self._skip_fc_sync(): for event in self: if not event.x_fc_link_ids: continue for link in event.x_fc_link_ids: account = link.x_fc_account_id if not account.x_fc_active or not account.sudo().x_fc_rtoken: continue try: token = account._get_valid_token() if not token: continue if account.x_fc_provider == 'google': account._google_delete_event(link.x_fc_external_id, token) elif account.x_fc_provider == 'microsoft': account._microsoft_delete_event(link.x_fc_external_id, token) except Exception as e: _logger.warning( "Failed to delete external event %s from account %s: %s", link.x_fc_external_id, account.id, e, ) return super().unlink() def write(self, vals): """On update, push changes to all linked external calendars.""" res = super().write(vals) if self._skip_fc_sync(): return res sync_fields = {'name', 'description', 'location', 'start', 'stop', 'allday', 'start_date', 'stop_date', 'privacy', 'show_as', 'active'} if not sync_fields.intersection(vals.keys()): return res for event in self: if not event.x_fc_link_ids: continue for link in event.x_fc_link_ids: account = link.x_fc_account_id if not account.x_fc_active or account.x_fc_sync_status != 'active': continue try: account._sync_push_event(event) except Exception as e: _logger.warning( "Failed to push event update %s to account %s: %s", event.id, account.id, e, ) return res