feat(sync): Fix Var6 revert logic for direction='to_advoware' and enhance conflict handling

This commit is contained in:
2026-02-08 22:07:55 +00:00
parent 79e097be6f
commit 8de2654d74
3 changed files with 713 additions and 7 deletions

View File

@@ -350,6 +350,116 @@ async def _create_empty_slot(self, betnr: int, advo_komm: Dict) -> None:
---
## 🔴 **Zusätzlicher Bug #2: Var6 nicht revertiert bei direction='to_advoware'** - FIXED ✅
### Problem
Bei `direction='to_advoware'` (EspoCRM wins) und Var6 (Advoware changed):
- ❌ Advoware→EspoCRM wurde geskippt (korrekt)
- ❌ ABER: Advoware-Wert wurde **NICHT** auf EspoCRM-Wert zurückgesetzt
- ❌ Resultat: Advoware behält User-Änderung obwohl EspoCRM gewinnen soll!
**Konkretes Beispiel (Entity 104860 Trace)**:
```
[KOMM] ✏️ Var6: Changed in Advoware - synced='+49111111...', current='+491111112...'
[KOMM] ===== CONFLICT STATUS: espo_wins=False =====
[KOMM] Skipping Advoware→EspoCRM (direction=to_advoware)
[KOMM] ✅ Bidirectional Sync complete: 0 total changes ← FALSCH!
```
→ Die Nummer `+491111112` blieb in Advoware, aber EspoCRM hat `+49111111`!
### Fix
#### 1. Var6-Revert bei direction='to_advoware'
```python
# kommunikation_sync_utils.py:
else:
self.logger.info(f"[KOMM] Skipping Advoware→EspoCRM (direction={direction})")
# FIX: Bei direction='to_advoware' müssen Var6-Änderungen zurückgesetzt werden!
if direction == 'to_advoware' and len(diff['advo_changed']) > 0:
self.logger.info(f"[KOMM] 🔄 Reverting {len(diff['advo_changed'])} Var6 entries to EspoCRM values...")
for komm, old_value, new_value in diff['advo_changed']:
# Revert: new_value (Advoware) → old_value (EspoCRM synced value)
await self._revert_advoware_change(betnr, komm, old_value, new_value, advo_bet)
result['espocrm_to_advoware']['updated'] += 1
# Bei direction='to_advoware' müssen auch Var4-Einträge zu Empty Slots gemacht werden!
if direction == 'to_advoware' and len(diff['advo_new']) > 0:
self.logger.info(f"[KOMM] 🔄 Converting {len(diff['advo_new'])} Var4 entries to Empty Slots...")
for komm in diff['advo_new']:
await self._create_empty_slot(betnr, komm)
result['espocrm_to_advoware']['deleted'] += 1
```
#### 2. Neue Methode: _revert_advoware_change()
```python
async def _revert_advoware_change(
self,
betnr: int,
advo_komm: Dict,
espo_synced_value: str,
advo_current_value: str,
advo_bet: Dict
) -> None:
"""
Revertiert Var6-Änderung in Advoware zurück auf EspoCRM-Wert
Verwendet bei direction='to_advoware' (EspoCRM wins):
- User hat in Advoware geändert
- Aber EspoCRM soll gewinnen
- → Setze Advoware zurück auf EspoCRM-Wert
"""
komm_id = advo_komm['id']
marker = parse_marker(advo_komm.get('bemerkung', ''))
kommkz = marker['kommKz']
user_text = marker.get('user_text', '')
# Revert: Setze tlf zurück auf EspoCRM-Wert
new_marker = create_marker(espo_synced_value, kommkz, user_text)
await self.advoware.update_kommunikation(betnr, komm_id, {
'tlf': espo_synced_value,
'bemerkung': new_marker,
'online': advo_komm.get('online', False)
})
self.logger.info(f"[KOMM] ✅ Reverted Var6: '{advo_current_value[:30]}...''{espo_synced_value[:30]}...'")
```
### Impact
- ✅ Bei `direction='to_advoware'` werden Var6-Änderungen jetzt auf EspoCRM-Wert zurückgesetzt
- ✅ Marker wird mit EspoCRM-Wert aktualisiert
- ✅ User-Bemerkung bleibt erhalten
- ✅ Beide Systeme sind nach Konflikt identisch
### Beispiel Trace (nach Fix)
```
[KOMM] ✏️ Var6: Changed in Advoware - synced='+49111111...', current='+491111112...'
[KOMM] ⚠️ CONFLICT: EspoCRM wins - skipping Advoware→EspoCRM sync
[KOMM] 🔄 Reverting 1 Var6 entries to EspoCRM values (EspoCRM wins)...
[KOMM] ✅ Reverted Var6: '+491111112' → '+49111111'
[KOMM] ✅ Bidirectional Sync complete: 1 total changes ← KORREKT!
```
**WICHTIG**: Gleicher Fix auch bei `espo_wins=True` (direction='both'):
```python
elif direction in ['both', 'to_espocrm'] and espo_wins:
# FIX: Var6-Änderungen revertieren
if len(diff['advo_changed']) > 0:
for komm, old_value, new_value in diff['advo_changed']:
await self._revert_advoware_change(betnr, komm, old_value, new_value, advo_bet)
# FIX: Var4-Einträge zu Empty Slots
if len(diff['advo_new']) > 0:
for komm in diff['advo_new']:
await self._create_empty_slot(betnr, komm)
```
---
## Zusammenfassung
### Geänderte Dateien
@@ -364,9 +474,11 @@ async def _create_empty_slot(self, betnr: int, advo_komm: Dict) -> None:
3.`services/kommunikation_sync_utils.py`
- `sync_bidirectional()` - Hash nur für sync-relevante (Problem #3)
- `sync_bidirectional()` - Var4→Empty Slots bei Konflikt (Zusätzlicher Bug)
- `sync_bidirectional()` - Var4→Empty Slots bei Konflikt (Zusätzlicher Bug #1)
- `sync_bidirectional()` - Var6-Revert bei direction='to_advoware' (Zusätzlicher Bug #2)
- `_compute_diff()` - Hash nur für sync-relevante (Problem #3)
- `_create_empty_slot()` - Unterstützt jetzt Var4 ohne Marker (Zusätzlicher Bug)
- `_create_empty_slot()` - Unterstützt jetzt Var4 ohne Marker (Zusätzlicher Bug #1)
- `_revert_advoware_change()` - NEU: Revertiert Var6 auf EspoCRM-Wert (Zusätzlicher Bug #2)
4.`steps/vmh/beteiligte_sync_event_step.py`
- `handler()` - Retry-Backoff Check (Problem #12)