# Advoware Calendar Sync Dieser Abschnitt implementiert die bidirektionale Synchronisation zwischen Advoware-Terminen und Google Calendar. Für jeden Mitarbeiter in Advoware wird automatisch ein entsprechender Google Calendar erstellt und gepflegt. ## Übersicht Das System synchronisiert Termine zwischen: - **Advoware**: Zentrale Terminverwaltung mit detaillierten Informationen - **Google Calendar**: Benutzerfreundliche Kalenderansicht für jeden Mitarbeiter ## Funktionalität ### Automatische Kalender-Erstellung - Für jeden Advoware-Mitarbeiter wird ein Google Calendar mit dem Namen `AW-{Kuerzel}` erstellt - Beispiel: Mitarbeiter mit Kürzel "SB" → Calendar "AW-SB" - Kalender wird mit dem Haupt-Google-Account (`lehmannundpartner@gmail.com`) als Owner geteilt ### Bidirektionale Synchronisation mit Token-Validierung #### Advoware → Google Calendar - Alle Termine eines Mitarbeiters werden aus Advoware abgerufen (Zeitraum: aktuelles Jahr + 2 Jahre) - Neue Termine werden in den entsprechenden Google Calendar eingetragen - Die Advoware-Termin-ID (`frNr`) wird als Metadaten gespeichert - Bestehende Termine werden aktualisiert, wenn Änderungen erkannt werden #### Google Calendar → Advoware - Termine aus Google Calendar ohne `frNr` werden als neue Termine in Advoware erstellt - Die generierte `frNr` wird zurück in den Google Calendar geschrieben - Token-basierte Validierung verhindert unbefugte Änderungen - Sync-Info wird in der Description/Notiz gespeichert ### Token-Sicherheit - MD5-Hash mit Salt (aus Umgebungsvariable `CALENDAR_SYNC_SALT`) für Änderungsvalidierung - Sync-Info Format: `## no change below this line ##\nfrNr: {frNr}\nsync-token: {token}` - Token wird bei jeder Änderung neu berechnet und validiert ### Löschungen - Google-initiale Termine, die in Google gelöscht werden, werden auch in Advoware gelöscht - Tracking von gelöschten `frNr` um Re-Sync zu verhindern ## API-Endpunkte ### Advoware API - `GET /api/v1/advonet/Mitarbeiter?aktiv=true` - Liste aktiver Mitarbeiter - `GET /api/v1/advonet/Termine?frnr={frnr}` - Einzelner Termin - `GET /api/v1/advonet/Termine?kuerzel={kuerzel}&from={date}&to={date}` - Termine eines Mitarbeiters - `POST /api/v1/advonet/Termine` - Neuen Termin erstellen - `PUT /api/v1/advonet/Termine` - Termin aktualisieren - `DELETE /api/v1/advonet/Termine?frnr={frnr}` - Termin löschen **API-Schwächen und Seltsamkeiten:** - Parameter müssen als Strings übergeben werden (z.B. `aktiv='true'`, nicht `True`) - Response kann manchmal HTML statt JSON zurückgeben, auch bei 200 Status - Content-Type Header ist nicht immer korrekt gesetzt - Token-Authentifizierung mit HMAC-Signature erforderlich - Keine Paginierung für große Resultate - Zeitformate: `datum` als `YYYY-MM-DDTHH:MM:SS`, aber manchmal ohne T ### Google Calendar API - Kalender-Management (erstellen, auflisten, ACL setzen) - Event-Management (erstellen, aktualisieren, löschen) - Service Account Authentifizierung mit Backoff bei Rate Limits ## Step-Konfiguration ### calendar_sync_event_step.py - **Type:** event - **Subscribes:** calendar.sync.triggered - **Flows:** advoware **Event Data:** ```json { "full_content": true // oder false für nur "blocked" } ``` ### calendar_sync_api_step.py - **Type:** api - **Path:** `/advoware/calendar/sync` - **Method:** POST - **Flows:** advoware **Request Body:** ```json { "full_content": true // oder false für nur "blocked" } ``` ### calendar_sync_cron_step.py - **Type:** cron - **Schedule:** Täglich um 2:00 Uhr - **Flows:** advoware ## Setup ### Google API Credentials 1. Google Cloud Console Projekt erstellen 2. Google Calendar API aktivieren 3. Service Account erstellen (kein OAuth) 4. `service-account.json` Datei im Projektverzeichnis bereitstellen ### Advoware API Credentials OAuth-ähnliche Authentifizierung mit HMAC-Signature. ### Umgebungsvariablen ```env # Google Calendar GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=service-account.json # Advoware API ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/ ADVOWARE_PRODUCT_ID=64 ADVOWARE_APP_ID=your_app_id ADVOWARE_API_KEY=your_api_key ADVOWARE_KANZLEI=your_kanzlei ADVOWARE_DATABASE=your_database ADVOWARE_USER=your_user ADVOWARE_ROLE=2 ADVOWARE_PASSWORD=your_password ADVOWARE_TOKEN_LIFETIME_MINUTES=55 ADVOWARE_API_TIMEOUT_SECONDS=30 # Redis für Token Caching REDIS_HOST=localhost REDIS_PORT=6379 REDIS_DB_ADVOWARE_CACHE=1 REDIS_TIMEOUT_SECONDS=5 # Calendar Sync CALENDAR_SYNC_SALT=your_secret_salt ``` ## Verwendung ### Manueller Sync ```bash curl -X POST "http://localhost:3000/advoware/calendar/sync" \ -H "Content-Type: application/json" \ -d '{"full_content": true}' ``` ### Event-basierter Sync ```bash curl -X POST "http://localhost:3000/events" \ -H "Content-Type: application/json" \ -d '{"type": "calendar.sync.triggered", "data": {"full_content": true}}' ``` ### Automatischer Sync Der Cron-Step führt täglich um 2:00 Uhr einen vollständigen Sync aus. ## Datenmapping ### Advoware → Google Calendar ```javascript { "summary": "Advoware blocked", // Immer "blocked" für Privacy "description": `Advoware Termin ID: ${appointment.frNr}`, "location": "", "start": { "dateTime": start_datetime, // Berechnet aus datum/uhrzeitVon "timeZone": "Europe/Berlin" }, "end": { "dateTime": end_datetime, // Berechnet aus datumBis/uhrzeitBis "timeZone": "Europe/Berlin" }, "extendedProperties": { "private": { "advoware_frnr": appointment.frNr.toString() } } } ``` ### Google Calendar → Advoware ```javascript { "frNr": int(frnr), "text": event.summary, "notiz": updated_description, // Mit sync-info "ort": event.location, "datum": start.split('+')[0] if '+' in start else start, "uhrzeitBis": end.split('T')[1].split('+')[0] if 'T' in end else '09:00:00', "datumBis": end.split('+')[0] if '+' in end else end, "sb": employee_kuerzel, "anwalt": employee_kuerzel, "vorbereitungsDauer": "00:00:00" } ``` ## Fehlerbehandlung - **Google API Fehler:** Exponentieller Backoff bei 403/429, automatische Wiederholung - **Advoware API Fehler:** Token-Refresh bei 401, detaillierte Logging - **JSON Parse Fehler:** Response-Text logging für Debugging - **Netzwerkfehler:** Timeout-Handling, konfigurierbare Timeouts - **Dateninkonsistenzen:** Token-Validierung, Änderungsvergleich vor Update ## Monitoring ### Logs - Erfolgreiche Synchronisationen mit Anzahl - Fehlerhafte API-Calls mit Details - Kalender-Erstellungen und ACL-Setups - Performance-Metriken (Sync-Dauer) - Debug-Logs für Änderungsvergleiche ### Metriken - Anzahl synchronisierter Termine - Verarbeitete Mitarbeiter - Fehlerquoten pro API - Gelöschte Termine - Token-Validierungsfehler ## Sicherheit - Service Account für Google API (kein User-Consent) - HMAC-SHA512 für Advoware Token-Generierung - Token-Caching in Redis mit TTL - MD5-Token für Sync-Validierung mit Salt - Scoped Permissions (nur Calendar-Zugriff) - Audit-Logs für alle Änderungen ## Bekannte Probleme und Workarounds ### Advoware API - **Parameter-Typen:** Alle Query-Parameter müssen Strings sein (z.B. `'true'` statt `True`) - **Response-Formate:** Manchmal HTML statt JSON, auch bei 200 Status - **Content-Type:** Nicht immer korrekt gesetzt, führt zu Parse-Fehlern - **Zeitformate:** Inkonsistente Formate, manuelle Parsing erforderlich - **Rate Limits:** Keine dokumentierten Limits, aber praktische Limits vorhanden ### Google Calendar API - **Rate Limits:** 403/429 mit Backoff-Handling - **ACL-Sharing:** Owner-Rolle für Hauptaccount erforderlich - **Event-Updates:** Vollständige Event-Daten bei Updates senden ### Sync-Logik - **Token-Mismatch:** Verhindert unbefugte Änderungen - **Deleted Tracking:** Verhindert Re-Sync gelöschter Termine - **Änderungsvergleich:** Nur tatsächliche Änderungen syncen ## Erweiterungen ### Geplante Features - Inkrementelle Syncs (nur geänderte Termine seit letztem Sync) - Konfliktlösungsstrategien (Advoware gewinnt, Google gewinnt, Manuell) - Batch-Verarbeitung für bessere Performance - Webhook-Integration für Echtzeit-Syncs - Mehrere Google-Accounts unterstützen - Outlook/Apple Calendar Integration ### Code-Verbesserungen - Unit-Tests für API-Calls - Mock-Server für Testing - Config-Validation beim Startup - Health-Checks für beide APIs - Metrics-Export (Prometheus)