feat: Implement entity comparison logic for improved sync detection between EspoCRM and Advoware

This commit is contained in:
2026-02-07 19:41:20 +00:00
parent 9076688f58
commit 101f290293
3 changed files with 121 additions and 16 deletions

View File

@@ -217,6 +217,65 @@ class BeteiligteSync:
return None
def compare_entities(
self,
espo_entity: Dict[str, Any],
advo_entity: Dict[str, Any]
) -> TimestampResult:
"""
Vergleicht Änderungen zwischen EspoCRM und Advoware
PRIMÄR: rowId-Vergleich (Advoware rowId ändert sich bei jedem Update - SEHR zuverlässig!)
FALLBACK: Timestamp-Vergleich (wenn rowId nicht verfügbar)
Args:
espo_entity: EspoCRM CBeteiligte
advo_entity: Advoware Beteiligte
Returns:
"espocrm_newer": EspoCRM wurde geändert
"advoware_newer": Advoware wurde geändert
"conflict": Beide wurden geändert
"no_change": Keine Änderungen
"""
# PRIMÄR: rowId-basierte Änderungserkennung (zuverlässiger!)
espo_rowid = espo_entity.get('advowareRowId')
advo_rowid = advo_entity.get('rowId')
if espo_rowid and advo_rowid:
if espo_rowid != advo_rowid:
# rowId unterschiedlich → Advoware wurde geändert
self._log(f"Advoware rowId geändert: {espo_rowid[:20]}... → {advo_rowid[:20]}...")
return 'advoware_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')
# Keine Änderungen
self._log("Keine Änderungen (rowId identisch)")
return 'no_change'
# FALLBACK: Timestamp-Vergleich (wenn rowId nicht verfügbar)
self._log("rowId nicht verfügbar, fallback auf Timestamp-Vergleich", level='debug')
return self.compare_timestamps(
espo_entity.get('modifiedAt'),
advo_entity.get('geaendertAm'),
espo_entity.get('advowareLastSync')
)
def compare_timestamps(
self,
espo_modified_at: Any,
@@ -224,7 +283,7 @@ class BeteiligteSync:
last_sync_ts: Any
) -> TimestampResult:
"""
Vergleicht Timestamps und bestimmt Sync-Richtung
Vergleicht Timestamps und bestimmt Sync-Richtung (FALLBACK wenn rowId nicht verfügbar)
Args:
espo_modified_at: EspoCRM modifiedAt
@@ -412,7 +471,8 @@ class BeteiligteSync:
entity_id: str,
espo_entity: Dict[str, Any],
advo_entity: Dict[str, Any],
conflict_details: str
conflict_details: str,
extra_fields: Optional[Dict[str, Any]] = None
) -> None:
"""
Löst Konflikt auf: EspoCRM wins (überschreibt Advoware)
@@ -422,13 +482,26 @@ class BeteiligteSync:
espo_entity: EspoCRM Entity-Daten
advo_entity: Advoware Entity-Daten
conflict_details: Details zum Konflikt
extra_fields: Zusätzliche Felder (z.B. advowareRowId)
"""
try:
now = datetime.now(pytz.UTC).isoformat()
# EspoCRM datetime format
now_utc = datetime.now(pytz.UTC)
espo_datetime = now_utc.strftime('%Y-%m-%d %H:%M:%S')
# Markiere als gelöst mit Konflikt-Info
await self.espocrm.update_entity('CBeteiligte', entity_id, {
update_data = {
'syncStatus': 'clean', # Gelöst!
'advowareLastSync': espo_datetime,
'syncErrorMessage': f'Konflikt: {conflict_details}',
'syncRetryCount': 0
}
# Merge extra fields (z.B. advowareRowId)
if extra_fields:
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

View File

@@ -64,6 +64,11 @@ class BeteiligteMapper:
hr_nummer = espo_entity.get('handelsregisterNummer')
if hr_nummer:
advo_data['handelsRegisterNummer'] = hr_nummer
# Registergericht
registergericht = espo_entity.get('registergericht')
if registergericht:
advo_data['registergericht'] = registergericht
# TODO: Weitere Stammdaten-Felder hier ergänzen (Steuernummer, etc.)
@@ -92,6 +97,7 @@ class BeteiligteMapper:
espo_data = {
'rechtsform': advo_entity.get('rechtsform', ''),
'betnr': advo_entity.get('betNr'), # Link zu Advoware
'advowareRowId': advo_entity.get('rowId'), # Änderungserkennung
}
# NAME: Person vs. Firma
@@ -123,6 +129,11 @@ class BeteiligteMapper:
hr_nummer = advo_entity.get('handelsRegisterNummer')
if hr_nummer:
espo_data['handelsregisterNummer'] = hr_nummer
# Registergericht
registergericht = advo_entity.get('registergericht')
if registergericht:
espo_data['registergericht'] = registergericht
# TODO: Weitere Stammdaten-Felder hier ergänzen
# HINWEIS: Kontaktdaten (Telefon, Email, Fax) werden über separate Endpoints gesynct
@@ -153,7 +164,8 @@ class BeteiligteMapper:
'name', 'firstName', 'lastName', 'firmenname',
'emailAddress', 'phoneNumber',
'dateOfBirth', 'rechtsform',
'handelsregisterNummer'
'handelsregisterNummer', 'handelsregisterArt', 'registergericht',
'betnr', 'advowareRowId'
]
for field in compare_fields: