feat: Enhance sync conflict detection and resolution logic in BeteiligteSync class

This commit is contained in:
2026-02-07 20:04:58 +00:00
parent 101f290293
commit 3d3014750f
2 changed files with 41 additions and 33 deletions

View File

@@ -241,28 +241,37 @@ class BeteiligteSync:
# PRIMÄR: rowId-basierte Änderungserkennung (zuverlässiger!)
espo_rowid = espo_entity.get('advowareRowId')
advo_rowid = advo_entity.get('rowId')
last_sync = espo_entity.get('advowareLastSync')
espo_modified = espo_entity.get('modifiedAt')
if espo_rowid and advo_rowid:
if espo_rowid != advo_rowid:
# rowId unterschiedlich → Advoware wurde geändert
if espo_rowid and advo_rowid and last_sync:
# Prüfe ob Advoware geändert wurde (rowId)
advo_changed = (espo_rowid != advo_rowid)
# Prüfe ob EspoCRM auch geändert wurde (seit letztem Sync)
espo_changed = False
if espo_modified:
try:
espo_ts = self.parse_timestamp(espo_modified)
sync_ts = self.parse_timestamp(last_sync)
if espo_ts and sync_ts:
espo_changed = (espo_ts > sync_ts)
except Exception as e:
self._log(f"Timestamp-Parse-Fehler: {e}", level='debug')
# Konfliktlogik: Beide geändert seit letztem Sync?
if advo_changed and espo_changed:
self._log(f"🚨 KONFLIKT: Beide Seiten geändert seit letztem Sync")
return 'conflict'
elif advo_changed:
self._log(f"Advoware rowId geändert: {espo_rowid[:20]}... → {advo_rowid[:20]}...")
return 'advoware_newer'
elif espo_changed:
self._log(f"EspoCRM neuer (modifiedAt > lastSync)")
return 'espocrm_newer'
else:
# rowId gleich → keine Änderung in Advoware
# Prüfe ob EspoCRM geändert wurde (via modifiedAt)
espo_modified = espo_entity.get('modifiedAt')
last_sync = espo_entity.get('advowareLastSync')
if espo_modified and last_sync:
try:
espo_ts = self.parse_timestamp(espo_modified)
sync_ts = self.parse_timestamp(last_sync)
if espo_ts and sync_ts and espo_ts > sync_ts:
self._log(f"EspoCRM neuer (rowId gleich, aber modifiedAt > lastSync)")
return 'espocrm_newer'
except Exception as e:
self._log(f"Timestamp-Parse-Fehler: {e}", level='debug')
# Weder Advoware noch EspoCRM geändert
return 'no_change'
# Keine Änderungen
self._log("Keine Änderungen (rowId identisch)")
@@ -502,10 +511,6 @@ class BeteiligteSync:
update_data.update(extra_fields)
await self.espocrm.update_entity('CBeteiligte', entity_id, update_data)
'advowareLastSync': now,
'syncErrorMessage': f"Konflikt am {now}: {conflict_details}. EspoCRM hat gewonnen.",
'syncRetryCount': 0
})
self._log(f"Konflikt gelöst für {entity_id}: EspoCRM wins")

View File

@@ -30,8 +30,9 @@ class BeteiligteMapper:
"""
logger.debug(f"Mapping EspoCRM → Advoware STAMMDATEN: {espo_entity.get('id')}")
# Bestimme ob Person oder Firma
is_firma = bool(espo_entity.get('firmenname'))
# Bestimme ob Person oder Firma (über firmenname-Feld)
firmenname = espo_entity.get('firmenname')
is_firma = bool(firmenname and firmenname.strip())
rechtsform = espo_entity.get('rechtsform', '')
# Basis-Struktur (nur Stammdaten, keine Kontaktdaten!)
@@ -41,11 +42,11 @@ class BeteiligteMapper:
# NAME: Person vs. Firma
if is_firma:
# Firma: name = firmenname
advo_data['name'] = espo_entity.get('firmenname', '')
# Firma: Lese von firmenname-Feld
advo_data['name'] = firmenname
advo_data['vorname'] = None
else:
# Person: name = lastName, vorname = firstName
# Natürliche Person: Lese von lastName/firstName
advo_data['name'] = espo_entity.get('lastName', '')
advo_data['vorname'] = espo_entity.get('firstName', '')
@@ -100,17 +101,19 @@ class BeteiligteMapper:
'advowareRowId': advo_entity.get('rowId'), # Änderungserkennung
}
# NAME: Person vs. Firma
# NAME: Person vs. Firma (EspoCRM blendet lastName/firstName aus bei Firmen)
if is_person:
# Person
# Natürliche Person → lastName/firstName verwenden
espo_data['firstName'] = vorname
espo_data['lastName'] = advo_entity.get('name', '')
espo_data['name'] = f"{vorname} {advo_entity.get('name', '')}".strip()
espo_data['firmenname'] = None
espo_data['firmenname'] = None # Firma-Feld leer lassen
else:
# Firma
espo_data['firmenname'] = advo_entity.get('name', '')
espo_data['name'] = advo_entity.get('name', '')
# Firma → firmenname verwenden (EspoCRM zeigt dann nur dieses Feld)
firma_name = advo_entity.get('name', '')
espo_data['firmenname'] = firma_name
espo_data['name'] = firma_name
# lastName/firstName nicht setzen (EspoCRM blendet sie aus bei Firmen)
espo_data['firstName'] = None
espo_data['lastName'] = None