# 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
