Files
motia/bitbylaw/steps/advoware_cal_sync

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:

{
  "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:

{
  "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

# 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

curl -X POST "http://localhost:3000/advoware/calendar/sync" \
  -H "Content-Type: application/json" \
  -d '{"full_content": true}'

Event-basierter Sync

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

{
  "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

{
  "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)