# Advoware Calendar Sync - Event-Driven Design

Dieser Abschnitt implementiert die bidirektionale Synchronisation zwischen Advoware-Terminen und Google Calendar. Das System wurde zu einem einfachen, event-driven Ansatz refaktoriert, der auf direkten API-Calls basiert, mit Redis für Locking und Deduplikation. Es stellt sicher, dass Termine konsistent gehalten werden, mit Fokus auf Robustheit, Fehlerbehandlung und korrekte Handhabung von mehrtägigen Terminen.

## Übersicht

Das System synchronisiert Termine zwischen:
- **Advoware**: Zentrale Terminverwaltung mit detaillierten Informationen.
- **Google Calendar**: Benutzerfreundliche Kalenderansicht für jeden Mitarbeiter.

## Architektur

### Event-Driven Design
- **Direkte API-Synchronisation**: Kein zentraler Hub; Sync läuft direkt zwischen APIs.
- **Redis Locking**: Per-Employee Locking verhindert Race-Conditions.
- **Event Emission**: Cron → All-Step → Employee-Step für skalierbare Verarbeitung.
- **Fehlerresistenz**: Einzelne Fehler stoppen nicht den gesamten Sync.
- **Logging**: Alle Logs erscheinen im Motia Workbench via context.logger.

### Sync-Phasen
1. **Cron-Step**: Tägliche Auslösung des Syncs.
2. **All-Step**: Fetcht alle Mitarbeiter und emittiert Events pro Employee.
3. **Employee-Step**: Synchronisiert Termine für einen einzelnen Mitarbeiter.

### Datenmapping und Standardisierung
Beide Systeme werden auf gemeinsames Format normalisiert (Berlin TZ):
```python
{
    'start': datetime,  # Berlin TZ
    'end': datetime,
    'text': str,
    'notiz': str,
    'ort': str,
    'dauertermin': int,  # 0/1
    'turnus': int,       # 0/1
    'turnusArt': int,
    'recurrence': str    # RRULE oder None
}
```

#### Advoware → Standard
- Start: `datum` + `uhrzeitVon` (Fallback 09:00), oder `datum` als datetime.
- End: `datumBis` + `uhrzeitBis` (Fallback 10:00), oder `datum` + 1h.
- All-Day: `dauertermin=1` oder Dauer >1 Tag.
- Recurring: `turnus`/`turnusArt` (vereinfacht, keine RRULE).

#### Google → Standard
- Start/End: `dateTime` oder `date` (All-Day).
- All-Day: `dauertermin=1` wenn All-Day oder Dauer >1 Tag.
- Recurring: RRULE aus `recurrence`.

#### Standard → Advoware
- POST/PUT: `datum`/`uhrzeitBis`/`datumBis` aus start/end.
- Defaults: `vorbereitungsDauer='00:00:00'`, `sb`/`anwalt`=employee_kuerzel.

#### Standard → Google
- All-Day: `date` statt `dateTime`, end +1 Tag.
- Recurring: RRULE aus `recurrence`.

## 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.

### Sync-Details

#### Cron-Step (calendar_sync_cron_step.py)
- Läuft täglich und emittiert "calendar_sync_all".

#### All-Step (calendar_sync_all_step.py)
- Fetcht alle Mitarbeiter aus Advoware.
- Filtert Debug-Liste (falls konfiguriert).
- Setzt Redis-Lock pro Employee.
- Emittiert "calendar_sync_employee" pro Employee.

#### Employee-Step (calendar_sync_event_step.py)
- Fetcht Advoware-Termine für den Employee.
- Fetcht Google-Events für den Employee.
- Synchronisiert: Neue erstellen, Updates anwenden, Deletes handhaben.
- Verwendet Locking, um parallele Syncs zu verhindern.

### API-Step (calendar_sync_api_step.py)
- Manueller Trigger für einzelnen Employee oder "ALL".
- Bei "ALL": Emittiert "calendar_sync_all".
- Bei Employee: Setzt Lock und emittiert "calendar_sync_employee".

## API-Schwächen und Fixes

### Advoware API
- **Mehrtägige Termine**: `datumBis` wird korrekt für Enddatum verwendet; '00:00:00' als '23:59:59' interpretiert.
- **Zeitformate**: Robuste Parsing mit Fallbacks.
- **Keine 24h-Limit**: Termine können länger als 24h sein; Google Calendar unterstützt das.

### Google Calendar API
- **Zeitbereiche**: Akzeptiert Events >24h ohne Probleme.
- **Rate Limits**: Backoff-Retry implementiert.

## Step-Konfiguration

### calendar_sync_cron_step.py
- **Type:** cron
- **Flows:** advoware-calendar-sync

### calendar_sync_all_step.py
- **Type:** event
- **Subscribes:** calendar_sync_all
- **Flows:** advoware-calendar-sync

### calendar_sync_event_step.py
- **Type:** event
- **Subscribes:** calendar_sync_employee
- **Flows:** advoware-calendar-sync

### calendar_sync_api_step.py
- **Type:** api
- **Flows:** advoware-calendar-sync

## Setup

### 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
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB_CALENDAR_SYNC=1
REDIS_TIMEOUT_SECONDS=5

# Debug
CALENDAR_SYNC_DEBUG_EMPLOYEES=PB,AI  # Optional, filter employees
```

## Verwendung

### Manueller Sync
```bash
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
  -H "Content-Type: application/json" \
  -d '{"kuerzel": "PB"}'
```

### Automatischer Sync
Cron-Step läuft täglich.

## Fehlerbehandlung und Logging

- **Locking**: Redis NX/EX verhindert parallele Syncs.
- **Logging**: context.logger für Workbench-Sichtbarkeit.
- **API-Fehler**: Retry mit Backoff.
- **Parsing-Fehler**: Robuste Fallbacks.

## Sicherheit

- Service Account für Google.
- HMAC für Advoware.
- Redis für Locking.

## Bekannte Probleme

- Recurring-Events: Begrenzte Unterstützung.
- Performance: Bei vielen Terminen Paginierung prüfen.

## Audit und Management Tool (`audit_calendar_sync.py`)

Das `audit_calendar_sync.py` Tool bietet umfassende Audit-, Management- und Debugging-Funktionen für die Calendar-Synchronisation. Es ermöglicht die Überprüfung der Sync-Integrität, das Aufräumen von Duplikaten und verwaisten Einträgen sowie detaillierte Abfragen einzelner Termine.

### Verwendung

```bash
cd /opt/motia-app/bitbylaw
source python_modules/bin/activate
python steps/advoware_cal_sync/audit_calendar_sync.py <command> [options]
```

### Befehle

#### `audit <employee_kuerzel> <google|advoware> [--delete-orphaned-google]`

Auditiert Sync-Einträge für einen spezifischen Mitarbeiter und prüft deren Existenz in beiden Systemen.

**Parameter:**
- `employee_kuerzel`: Mitarbeiter-Kürzel (z.B. "SB", "UR")
- `google|advoware`: System, das auditiert werden soll
- `--delete-orphaned-google`: Optional, löscht Google-Events die in Google existieren aber nicht in der DB

**Beispiel:**
```bash
# Audit Google Calendar für Mitarbeiter SB
python audit_calendar_sync.py audit SB google

# Audit Advoware für Mitarbeiter UR mit Löschung verwaister Google-Events
python audit_calendar_sync.py audit UR google --delete-orphaned-google
```

**Ausgabe:**
- Anzahl der DB-Einträge
- Anzahl der Events im Zielsystem
- Anzahl existierender/verwaiste Einträge
- Details zu verwaisten Einträgen

#### `delete-calendar <employee_kuerzel>`

Löscht den Google Calendar für einen spezifischen Mitarbeiter (falls vorhanden).

**Beispiel:**
```bash
python audit_calendar_sync.py delete-calendar SB
```

#### `list-all`

Listet alle Google Calendars auf, einschließlich Name, ID, Primary-Status und Access-Role.

**Beispiel:**
```bash
python audit_calendar_sync.py list-all
```

**Ausgabe:**
```
=== All Google Calendars (27) ===
  AW-SB (ID: abc123@group.calendar.google.com, Primary: False, Access: owner)
  AW-UR (ID: def456@group.calendar.google.com, Primary: False, Access: owner)
  ...
```

#### `find-duplicates`

Findet duplizierte Google Calendars nach Namen.

**Beispiel:**
```bash
python audit_calendar_sync.py find-duplicates
```

**Ausgabe:**
```
=== Duplicate Calendars Found (2 unique names with duplicates) ===
Total duplicate calendars: 3

Calendar Name: 'AW-SB' - 2 instances
  ID: abc123@group.calendar.google.com, Primary: False, Access Role: owner
  ID: xyz789@group.calendar.google.com, Primary: False, Access Role: owner
```

#### `delete-duplicates`

Findet und löscht duplizierte Calendars (behält jeweils einen pro Namen).

**Beispiel:**
```bash
python audit_calendar_sync.py delete-duplicates
```

#### `find-orphaned`

Findet AW-* Calendars ohne entsprechende Mitarbeiter in der Datenbank.

**Beispiel:**
```bash
python audit_calendar_sync.py find-orphaned
```

#### `cleanup-orphaned`

Findet und löscht verwaiste AW-* Calendars.

**Beispiel:**
```bash
python audit_calendar_sync.py cleanup-orphaned
```

#### `query-frnr <frnr>`

Zeigt alle Sync-Informationen für eine spezifische Advoware frNr.

**Beispiel:**
```bash
python audit_calendar_sync.py query-frnr 79291
```

**Ausgabe:**
```
=== Sync Information for frNr: 79291 ===
Found 1 sync entry

Sync ID: 6ee9ba95-8aff-4868-9171-c10a8789427c
  Employee: UR
  Advoware frNr: 79291
  Google Event ID: jao7r00j26lt1i0chk454bi9as
  Source System: advoware
  Sync Strategy: source_system_wins
  Sync Status: synced
  Last Sync: 2025-10-24 23:30:17.692668+00:00
  Created: 2025-10-24 07:22:41.729295+00:00
  Updated: 2025-10-24 07:22:41.729295+00:00
```

#### `query-event <event_id>`

Zeigt Sync-Informationen für eine spezifische Google Event ID.

**Beispiel:**
```bash
python audit_calendar_sync.py query-event jao7r00j26lt1i0chk454bi9as
```

#### `verify-sync <frnr>`

Vollständige Sync-Verifikation: Prüft einen Termin in beiden Systemen (Advoware und Google Calendar).

**Beispiel:**
```bash
python audit_calendar_sync.py verify-sync 79291
```

**Ausgabe:**
```
=== Sync Verification for frNr: 79291 ===
Employee: UR
Sync Status: synced
Last Sync: 2025-10-24 23:30:17.692668+00:00

--- Checking Advoware ---
✅ Found in Advoware:
   Subject: Jour fixe iS Neomi - Teilnahme im Einzelfall
   Date: 2024-06-04T17:00:00
   Time: N/A
   End Time: 19:00:00
   End Date: 2026-02-03T00:00:00
   Last Modified: 2025-09-29T11:55:43.624
   frNr: 79291

--- Checking Google Calendar ---
✅ Found in Google Calendar:
   Summary: Advoware (frNr: 79291)
   Start: 2024-06-04T17:00:00+02:00
   End: 2024-06-04T19:00:00+02:00

--- Sync Status Summary ---
✅ Synchronized: Exists in both systems
```

### Technische Details

#### Datenbank-Integration
- Verwendet PostgreSQL-Verbindung aus `config.py`
- Tabelle: `calendar_sync`
- Felder: `sync_id`, `employee_kuerzel`, `advoware_frnr`, `google_event_id`, etc.

#### API-Integration
- **Google Calendar API**: `calendarList().list()` mit Paginierung (maxResults=250)
- **Advoware API**: `GET /api/v1/advonet/Termine` mit `frnr` Filter
- Automatische Token-Verwaltung und Fehlerbehandlung

#### Sicherheit
- Verwendet bestehende Service-Account und API-Credentials
- Keine zusätzlichen Berechtigungen erforderlich

### Häufige Anwendungsfälle

#### 1. Nach der Erstinstallation
```bash
# Alle Calendars auflisten
python audit_calendar_sync.py list-all

# Duplikate finden und entfernen
python audit_calendar_sync.py find-duplicates
python audit_calendar_sync.py delete-duplicates

# Verwaiste Calendars entfernen
python audit_calendar_sync.py find-orphaned
python audit_calendar_sync.py cleanup-orphaned
```

#### 2. Bei Sync-Problemen
```bash
# Sync-Status für einen Mitarbeiter prüfen
python audit_calendar_sync.py audit SB google

# Einzelnen Termin verifizieren
python audit_calendar_sync.py verify-sync 79291

# Sync-Informationen abfragen
python audit_calendar_sync.py query-frnr 79291
```

#### 3. Regelmäßige Wartung
```bash
# Wöchentliche Überprüfung auf Duplikate
python audit_calendar_sync.py find-duplicates

# Monatliche Bereinigung verwaister Einträge
python audit_calendar_sync.py cleanup-orphaned
```

### Fehlerbehandlung

- **API-Fehler**: Automatische Retry-Logik mit Backoff
- **Berechtigungsfehler**: Klare Fehlermeldungen mit Lösungsvorschlägen
- **Netzwerkprobleme**: Timeout-Handling und Wiederholungen
- **Dateninkonsistenzen**: Detaillierte Logging für Debugging

### Performance

- **Paginierung**: Automatische Handhabung großer Resultsets
- **Batch-Verarbeitung**: Effiziente API-Calls mit minimalen Requests
- **Caching**: Wiederverwendung von API-Verbindungen wo möglich

### Logging

Alle Operationen werden über `context.logger` geloggt und sind in der Motia Workbench sichtbar. Zusätzliche Debug-Informationen werden auf der Konsole ausgegeben.

---

## Utility Scripts

Für Wartung und Debugging stehen Helper-Scripts zur Verfügung:

**Dokumentation**: [scripts/calendar_sync/README.md](../../scripts/calendar_sync/README.md)

**Verfügbare Scripts**:
- `delete_employee_locks.py` - Löscht Redis-Locks (bei hängenden Syncs)
- `delete_all_calendars.py` - Löscht alle Google Kalender (Reset)

**Verwendung**:
```bash
# Lock-Cleanup
python3 scripts/calendar_sync/delete_employee_locks.py

# Calendar-Reset (VORSICHT!)
python3 scripts/calendar_sync/delete_all_calendars.py
```

---

## Siehe auch

- [Calendar Sync Architecture](../../docs/ARCHITECTURE.md#2-calendar-sync-system)
- [Calendar Sync Cron Step](calendar_sync_cron_step.md)
- [Google Calendar Setup](../../docs/GOOGLE_SETUP.md)
- [Troubleshooting Guide](../../docs/TROUBLESHOOTING.md)
