Add sync strategy documentation and templates for bidirectional sync between EspoCRM and Advoware

- Introduced SYNC_STRATEGY_ARCHIVE.md detailing the sync process, status values, and flow for updating entities from EspoCRM to Advoware and vice versa.
- Created SYNC_TEMPLATE.md as a guide for implementing new syncs, including field definitions, mapper examples, sync utilities, event handlers, and cron jobs.
- Added README_SYNC.md for the Beteiligte sync event handler, outlining its functionality, event subscriptions, optimizations, error handling, and performance metrics.
This commit is contained in:
2026-02-07 15:54:13 +00:00
parent 8550107b89
commit ae1d96f767
12 changed files with 1162 additions and 1069 deletions

View File

@@ -40,7 +40,7 @@ async def handler(event_data, context):
context.logger.info(f"🔄 Sync-Handler gestartet: {action.upper()} | Entity: {entity_id} | Source: {source}")
# Redis für Queue-Management
# Shared Redis client for distributed locking
redis_client = redis.Redis(
host=Config.REDIS_HOST,
port=int(Config.REDIS_PORT),
@@ -51,7 +51,7 @@ async def handler(event_data, context):
# APIs initialisieren
espocrm = EspoCRMAPI()
advoware = AdvowareAPI(context)
sync_utils = BeteiligteSync(espocrm, context)
sync_utils = BeteiligteSync(espocrm, redis_client, context)
mapper = BeteiligteMapper()
try:
@@ -141,11 +141,13 @@ async def handle_create(entity_id, espo_entity, espocrm, advoware, sync_utils, m
context.logger.info(f"✅ In Advoware erstellt: betNr={new_betnr}")
# Update EspoCRM mit neuer betNr
await sync_utils.release_sync_lock(entity_id, 'clean', error_message=None)
await espocrm.update_entity('CBeteiligte', entity_id, {
'betnr': new_betnr
})
# OPTIMIERT: Kombiniere release_lock + betnr update in 1 API call
await sync_utils.release_sync_lock(
entity_id,
'clean',
error_message=None,
extra_fields={'betnr': new_betnr}
)
context.logger.info(f"✅ CREATE erfolgreich: {entity_id} → betNr {new_betnr}")
@@ -199,15 +201,8 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u
if not espo_entity.get('advowareLastSync'):
context.logger.info(f"📤 Initial Sync → EspoCRM STAMMDATEN zu Advoware")
# WICHTIG: Advoware benötigt vollständiges Objekt für PUT
# Mapper liefert nur STAMMDATEN (keine Kontaktdaten - die kommen später über separate Endpoints)
advo_updates = mapper.map_cbeteiligte_to_advoware(espo_entity)
# Merge mit aktuellen Advoware-Daten
merged_data = {**advo_entity, **advo_updates}
context.logger.info(f"📝 Merge: {len(advo_updates)} Stammdaten-Felder → {len(merged_data)} Gesamt-Felder")
context.logger.debug(f" Gesynct: {', '.join(advo_updates.keys())}")
# OPTIMIERT: Use merge utility (reduces code duplication)
merged_data = sync_utils.merge_for_advoware_put(advo_entity, espo_entity, mapper)
await advoware.api_call(
f'api/v1/advonet/Beteiligte/{betnr}',
@@ -229,15 +224,8 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u
if comparison == 'espocrm_newer':
context.logger.info(f"📤 EspoCRM ist neuer → Update Advoware STAMMDATEN")
# WICHTIG: Advoware benötigt vollständiges Objekt für PUT
# Mapper liefert nur STAMMDATEN (keine Kontaktdaten - die kommen über separate Endpoints)
advo_updates = mapper.map_cbeteiligte_to_advoware(espo_entity)
# Merge mit aktuellen Advoware-Daten
merged_data = {**advo_entity, **advo_updates}
context.logger.info(f"📝 Merge: {len(advo_updates)} Stammdaten-Felder → {len(merged_data)} Gesamt-Felder")
context.logger.debug(f" Gesynct: {', '.join(advo_updates.keys())}")
# OPTIMIERT: Use merge utility
merged_data = sync_utils.merge_for_advoware_put(advo_entity, espo_entity, mapper)
await advoware.api_call(
f'api/v1/advonet/Beteiligte/{betnr}',
@@ -262,11 +250,8 @@ async def handle_update(entity_id, betnr, espo_entity, espocrm, advoware, sync_u
elif comparison == 'conflict':
context.logger.warning(f"⚠️ KONFLIKT erkannt → EspoCRM WINS (STAMMDATEN)")
# Überschreibe Advoware mit EspoCRM (merge mit aktuellen Daten)
advo_updates = mapper.map_cbeteiligte_to_advoware(espo_entity)
merged_data = {**advo_entity, **advo_updates}
context.logger.info(f"📝 Merge: {len(advo_updates)} Stammdaten-Felder → {len(merged_data)} Gesamt-Felder")
# OPTIMIERT: Use merge utility
merged_data = sync_utils.merge_for_advoware_put(advo_entity, espo_entity, mapper)
await advoware.api_call(
f'api/v1/advonet/Beteiligte/{betnr}',