# Kommunikation Sync Implementation > **⚠️ Diese Datei ist veraltet und wird nicht mehr gepflegt.** > **Aktuelle Dokumentation**: [../docs/SYNC_OVERVIEW.md](../docs/SYNC_OVERVIEW.md) ## Quick Reference Für die vollständige und aktuelle Dokumentation siehe [SYNC_OVERVIEW.md](../docs/SYNC_OVERVIEW.md#kommunikation-sync). **Implementiert in**: `services/kommunikation_sync_utils.py` ### Kern-Features 1. **Base64-Marker** in Advoware `bemerkung`: `[ESPOCRM:base64_value:kommKz]` 2. **Hash-basierte Change Detection**: MD5 von allen Kommunikation-rowIds 3. **6 Sync-Varianten**: Var1-6 für alle Szenarien (neu, gelöscht, geändert) 4. **Empty Slots**: Workaround für DELETE 403 5. **Konflikt-Handling**: EspoCRM wins, direction='to_advoware' --- # Legacy Documentation (Reference Only) ## Architektur-Übersicht ``` ┌─────────────────────────────────────────────────────────────────┐ │ Advoware ↔ EspoCRM Sync │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ADVOWARE ESPOCRM │ │ ───────────────── ────────────────── │ │ Beteiligte CBeteiligte │ │ └─ kommunikation[] ├─ emailAddressData[] │ │ ├─ id (unique int) │ └─ emailAddress │ │ ├─ rowId (string) │ lower, primary │ │ ├─ tlf (value) │ │ │ ├─ bemerkung (marker!) └─ phoneNumberData[] │ │ ├─ kommKz (1-12) └─ phoneNumber │ │ └─ online (bool) type, primary │ │ │ │ MATCHING: Hash in bemerkung-Marker │ │ [ESPOCRM:hash:kommKz] User text │ └─────────────────────────────────────────────────────────────────┘ ``` ## Core Features ### 1. Base64-basiertes Matching ✅ IMPLEMENTIERT - **Problem**: EspoCRM Arrays haben keine IDs - **Lösung**: Base64-kodierter Wert in Advoware bemerkung - **Format**: `[ESPOCRM:bWF4QGV4YW1wbGUuY29t:4] Geschäftlich` - **Vorteil**: Bidirektional! Marker enthält den tatsächlichen Wert (dekodierbar) **Warum Base64 statt Hash?** ```python # Hash-Problem (alt): Nicht rückrechenbar old_hash = hash("old@example.com") # abc12345 # Bei Wert-Änderung in Advoware: Kein Match möglich! ❌ # Base64-Lösung (neu): Bidirektional encoded = base64("old@example.com") # b2xkQGV4YW1wbGUuY29t decoded = decode(encoded) # "old@example.com" ✅ # Kann dekodieren → Match in EspoCRM finden! ``` ### 2. 4-Stufen Typ-Erkennung ```python 1. Aus Marker: [ESPOCRM:hash:3] → kommKz=3 (Mobil) 2. Aus Top-Level: beteiligte.mobil → kommKz=3 3. Aus Pattern: '@' in value → kommKz=4 (Email) 4. Default: Fallback → kommKz=1 oder 4 ``` ### 3. Empty Slot System - **Problem**: DELETE ist 403 Forbidden in Advoware - **Lösung**: Leere Slots mit `[ESPOCRM-SLOT:kommKz]` - **Wiederverwendung**: Neue Einträge reuse leere Slots ### 4. Asymmetrischer Sync **Problem**: Hash-basiertes Matching funktioniert NICHT bidirektional - Wenn Wert in Advoware ändert: Hash ändert sich → Kein Match in EspoCRM möglich **Lösung**: Verschiedene Strategien je Richtung | Richtung | Methode | Grund | |----------|---------|-------| | **Advoware → EspoCRM** | FULL SYNC (kompletter Overwrite) | Kein stabiles Matching möglich | | **EspoCRM → Advoware** | INCREMENTAL SYNC (Hash-basiert) | EspoCRM-Wert bekannt → Hash berechenbar | **Ablauf Advoware → EspoCRM (FULL SYNC)**: ```python 1. Sammle ALLE Kommunikationen (ohne Empty Slots) 2. Setze/Update Marker für Rück-Sync 3. Ersetze KOMPLETTE emailAddressData[] und phoneNumberData[] ``` **Ablauf EspoCRM → Advoware (INCREMENTAL)**: ```python 1. Baue Hash-Maps von beiden Seiten 2. Vergleiche: Deleted, Changed, New 3. Apply Changes (Empty Slots, Updates, Creates) ``` ## Module Structure ``` services/ ├── kommunikation_mapper.py # Datentyp-Mapping & Marker-Logik ├── advoware_service.py # Advoware API-Wrapper └── kommunikation_sync_utils.py # Sync-Manager (bidirectional) ``` ## Usage Example ```python from services.advoware_service import AdvowareService from services.espocrm import EspoCrmService from services.kommunikation_sync_utils import KommunikationSyncManager # Initialize advo = AdvowareService() espo = EspoCrmService() sync_manager = KommunikationSyncManager(advo, espo) # Bidirectional Sync result = sync_manager.sync_bidirectional( beteiligte_id='espocrm-bet-id', betnr=12345, direction='both' # 'both', 'to_espocrm', 'to_advoware' ) print(result) # { # 'advoware_to_espocrm': { # 'emails_synced': 3, # 'phones_synced': 2, # 'errors': [] # }, # 'espocrm_to_advoware': { # 'created': 1, # 'updated': 2, # 'deleted': 0, # 'errors': [] # } # } ``` ## Field Mapping ### kommKz Enum (Advoware) | kommKz | Name | EspoCRM Target | EspoCRM Type | |--------|------|----------------|--------------| | 1 | TelGesch | phoneNumberData | Office | | 2 | FaxGesch | phoneNumberData | Fax | | 3 | Mobil | phoneNumberData | Mobile | | 4 | MailGesch | emailAddressData | - | | 5 | Internet | *(skipped)* | - | | 6 | TelPrivat | phoneNumberData | Home | | 7 | FaxPrivat | phoneNumberData | Fax | | 8 | MailPrivat | emailAddressData | - | | 9 | AutoTelefon | phoneNumberData | Mobile | | 10 | Sonstige | phoneNumberData | Other | | 11 | EPost | emailAddressData | - | | 12 | Bea | emailAddressData | - | **Note**: Internet (kommKz=5) wird nicht synchronisiert (unklar ob Email/Phone). ## Sync Scenarios ### Scenario 1: Delete in EspoCRM ``` EspoCRM: max@example.com gelöscht Advoware: [ESPOCRM:abc:4] max@example.com → UPDATE zu Empty Slot: tlf: '' bemerkung: [ESPOCRM-SLOT:4] online: False ``` ### Scenario 2: Change in EspoCRM ``` EspoCRM: max@old.com → max@new.com Advoware: [ESPOCRM:oldhash:4] max@old.com → UPDATE with new hash: tlf: 'max@new.com' bemerkung: [ESPOCRM:newhash:4] Geschäftlich online: True ``` ### Scenario 3: New in EspoCRM ``` EspoCRM: Neue Email new@example.com → Suche Empty Slot (kommKz=4) IF found: REUSE (UPDATE) ELSE: CREATE new ``` ### Scenario 4: New in Advoware ``` Advoware: Neue Kommunikation (kein Marker) → Typ-Erkennung via Top-Level/Pattern → Sync zu EspoCRM → Marker in Advoware setzen ``` ## API Limitations ### Advoware API v1 - ✅ **POST**: /api/v1/advonet/Beteiligte/{betnr}/Kommunikationen - Required: tlf, kommKz - Optional: bemerkung, online - ✅ **PUT**: /api/v1/advonet/Beteiligte/{betnr}/Kommunikationen/{id} - Writable: tlf, bemerkung, online - **READ-ONLY**: kommKz (cannot change type!) - ❌ **DELETE**: 403 Forbidden - Use Empty Slots instead - ⚠️ **BUG**: kommKz always returns 0 in GET - Use Top-Level fields + Pattern detection ### EspoCRM - ✅ **emailAddressData**: Array ohne IDs - ✅ **phoneNumberData**: Array ohne IDs - ❌ **Kein CKommunikation Entity**: Arrays nur in CBeteiligte ## Testing Run all tests: ```bash cd /opt/motia-app/bitbylaw python3 scripts/test_kommunikation_sync_implementation.py ``` **Test Coverage**: - ✅ Hash-Berechnung und Konsistenz - ✅ Marker-Parsing (Standard + Slot) - ✅ Marker-Erstellung - ✅ 4-Stufen Typ-Erkennung (alle Tiers) - ✅ Typ-Klassifizierung (Email vs Phone) - ✅ Integration Szenario - ✅ Top-Level Feld Priorität ## Change Detection ### Advoware Webhook ```python from services.kommunikation_sync_utils import detect_kommunikation_changes if detect_kommunikation_changes(old_bet, new_bet): # rowId changed → Sync needed sync_manager.sync_bidirectional(bet_id, betnr, direction='to_espocrm') ``` ### EspoCRM Webhook ```python from services.kommunikation_sync_utils import detect_espocrm_kommunikation_changes if detect_espocrm_kommunikation_changes(old_data, new_data): # Array changed → Sync needed sync_manager.sync_bidirectional(bet_id, betnr, direction='to_advoware') ``` ## Known Limitations 1. **FULL SYNC von Advoware → EspoCRM**: - Arrays werden komplett überschrieben (kein Merge) - Grund: Hash-basiertes Matching funktioniert nicht bei Wert-Änderungen in Advoware - Risiko minimal: EspoCRM-Arrays haben keine Relationen 2. **Empty Slots Accumulation**: - Gelöschte Einträge werden zu leeren Slots - Werden wiederverwendet, aber akkumulieren - TODO: Periodic cleanup job 3. **Partial Type Loss**: - Advoware-Kommunikationen ohne Top-Level Match verlieren Feintyp - Fallback: @ → Email (4), sonst Phone (1) 4. **kommKz READ-ONLY**: - Typ kann nach Erstellung nicht geändert werden - Workaround: DELETE + CREATE (manuell) 5. **Marker sichtbar**: - `[ESPOCRM:...]` ist in Advoware UI sichtbar - User kann Text dahinter hinzufügen ## Documentation - **Vollständige Analyse**: [docs/KOMMUNIKATION_SYNC_ANALYSE.md](../docs/KOMMUNIKATION_SYNC_ANALYSE.md) - **API Tests**: [scripts/test_kommunikation_api.py](test_kommunikation_api.py) - **Implementation Tests**: [scripts/test_kommunikation_sync_implementation.py](test_kommunikation_sync_implementation.py) ## Implementation Status ✅ **COMPLETE** - [x] Marker-System (Hash + kommKz) - [x] 4-Stufen Typ-Erkennung - [x] Empty Slot System - [x] Bidirektionale Sync-Logik - [x] Advoware Service Wrapper - [x] Change Detection - [x] Test Suite - [x] Documentation ## Next Steps 1. **Integration in Webhook System** - Add kommunikation change detection to beteiligte webhooks - Wire up sync calls 2. **Monitoring** - Add metrics for sync operations - Track empty slot accumulation 3. **Maintenance** - Implement periodic cleanup job for old empty slots - Add notification for type-change scenarios 4. **Testing** - End-to-end tests with real Advoware/EspoCRM data - Load testing for large kommunikation arrays --- **Last Updated**: 2024-01-26 **Status**: ✅ Implementation Complete - Ready for Integration