11 KiB
Kommunikation Sync Implementation
⚠️ Diese Datei ist veraltet und wird nicht mehr gepflegt.
Aktuelle Dokumentation: ../docs/SYNC_OVERVIEW.md
Quick Reference
Für die vollständige und aktuelle Dokumentation siehe SYNC_OVERVIEW.md.
Implementiert in: services/kommunikation_sync_utils.py
Kern-Features
- Base64-Marker in Advoware
bemerkung:[ESPOCRM:base64_value:kommKz] - Hash-basierte Change Detection: MD5 von allen Kommunikation-rowIds
- 6 Sync-Varianten: Var1-6 für alle Szenarien (neu, gelöscht, geändert)
- Empty Slots: Workaround für DELETE 403
- 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?
# 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
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):
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):
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
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:
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
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
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
-
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
-
Empty Slots Accumulation:
- Gelöschte Einträge werden zu leeren Slots
- Werden wiederverwendet, aber akkumulieren
- TODO: Periodic cleanup job
-
Partial Type Loss:
- Advoware-Kommunikationen ohne Top-Level Match verlieren Feintyp
- Fallback: @ → Email (4), sonst Phone (1)
-
kommKz READ-ONLY:
- Typ kann nach Erstellung nicht geändert werden
- Workaround: DELETE + CREATE (manuell)
-
Marker sichtbar:
[ESPOCRM:...]ist in Advoware UI sichtbar- User kann Text dahinter hinzufügen
Documentation
- Vollständige Analyse: docs/KOMMUNIKATION_SYNC_ANALYSE.md
- API Tests: scripts/test_kommunikation_api.py
- Implementation Tests: scripts/test_kommunikation_sync_implementation.py
Implementation Status
✅ COMPLETE
- Marker-System (Hash + kommKz)
- 4-Stufen Typ-Erkennung
- Empty Slot System
- Bidirektionale Sync-Logik
- Advoware Service Wrapper
- Change Detection
- Test Suite
- Documentation
Next Steps
-
Integration in Webhook System
- Add kommunikation change detection to beteiligte webhooks
- Wire up sync calls
-
Monitoring
- Add metrics for sync operations
- Track empty slot accumulation
-
Maintenance
- Implement periodic cleanup job for old empty slots
- Add notification for type-change scenarios
-
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