From af00495cee82f7666328346e4e516269c8557ecc Mon Sep 17 00:00:00 2001 From: bitbylaw Date: Mon, 9 Feb 2026 09:52:52 +0000 Subject: [PATCH] feat(sync): Optimize matching and updating of communication entries in bidirectional sync --- bitbylaw/services/kommunikation_sync_utils.py | 79 ++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/bitbylaw/services/kommunikation_sync_utils.py b/bitbylaw/services/kommunikation_sync_utils.py index 4916096f..7e3b4a04 100644 --- a/bitbylaw/services/kommunikation_sync_utils.py +++ b/bitbylaw/services/kommunikation_sync_utils.py @@ -594,14 +594,69 @@ class KommunikationSyncManager: try: advo_kommunikationen = advo_bet.get('kommunikation', []) - # Var2: In EspoCRM gelöscht → Empty Slot in Advoware + # OPTIMIERUNG: Matche Var2 (Delete) + Var1 (New) mit gleichem kommKz + # → Direkt UPDATE statt DELETE+RELOAD+CREATE + var2_by_kommkz = {} # kommKz → [komm, ...] + var1_by_kommkz = {} # kommKz → [(value, espo_item), ...] + + # Gruppiere Var2 nach kommKz + for komm in diff['espo_deleted']: + bemerkung = komm.get('bemerkung') or '' + marker = parse_marker(bemerkung) + if marker: + kommkz = marker['kommKz'] + if kommkz not in var2_by_kommkz: + var2_by_kommkz[kommkz] = [] + var2_by_kommkz[kommkz].append(komm) + + # Gruppiere Var1 nach kommKz + for value, espo_item in diff['espo_new']: + espo_type = espo_item.get('type', 'email' if '@' in value else None) + kommkz = detect_kommkz(value, advo_bet, espo_type=espo_type) + if kommkz not in var1_by_kommkz: + var1_by_kommkz[kommkz] = [] + var1_by_kommkz[kommkz].append((value, espo_item)) + + # Matche und führe direkte Updates aus + matched_var2_ids = set() + matched_var1_indices = {} # kommkz → set of matched indices + + for kommkz in var2_by_kommkz.keys(): + if kommkz in var1_by_kommkz: + var2_list = var2_by_kommkz[kommkz] + var1_list = var1_by_kommkz[kommkz] + + # Matche paarweise + for i, (value, espo_item) in enumerate(var1_list): + if i < len(var2_list): + komm = var2_list[i] + komm_id = komm['id'] + + self.logger.info(f"[KOMM] 🔄 Var2+Var1 Match: kommKz={kommkz}, updating slot {komm_id} with '{value[:30]}...'") + + # Direktes UPDATE statt DELETE+CREATE + await self.advoware.update_kommunikation(betnr, komm_id, { + 'tlf': value, + 'online': espo_item['primary'], + 'bemerkung': create_marker(value, kommkz) + }) + + matched_var2_ids.add(komm_id) + if kommkz not in matched_var1_indices: + matched_var1_indices[kommkz] = set() + matched_var1_indices[kommkz].add(i) + + result['created'] += 1 + self.logger.info(f"[KOMM] ✅ Slot updated (optimized merge)") + + # Unmatched Var2: Erstelle Empty Slots for komm in diff['espo_deleted']: komm_id = komm.get('id') - tlf = (komm.get('tlf') or '').strip() - self.logger.info(f"[KOMM] 🗑️ Var2: Deleted in EspoCRM - komm_id={komm_id}, value='{tlf[:30]}...'") - await self._create_empty_slot(betnr, komm) - self.logger.info(f"[KOMM] ✅ Empty slot created for komm_id={komm_id}") - result['deleted'] += 1 + if komm_id not in matched_var2_ids: + synced_value = komm.get('_synced_value', '') + self.logger.info(f"[KOMM] 🗑️ Var2: Deleted in EspoCRM - komm_id={komm_id}, synced_value='{synced_value[:30]}...'") + await self._create_empty_slot(betnr, komm, synced_value=synced_value) + result['deleted'] += 1 # Var5: In EspoCRM geändert (z.B. primary Flag) for value, advo_komm, espo_item in diff['espo_changed']: @@ -630,12 +685,16 @@ class KommunikationSyncManager: result['updated'] += 1 # Var1: Neu in EspoCRM → Create oder reuse Slot in Advoware - for value, espo_item in diff['espo_new']: - self.logger.info(f"[KOMM] ➕ Var1: New in EspoCRM '{value[:30]}...', type={espo_item.get('type')}") - - # Erkenne kommKz mit espo_type + # Überspringe bereits gematchte Einträge (Var2+Var1 merged) + for idx, (value, espo_item) in enumerate(diff['espo_new']): espo_type = espo_item.get('type', 'email' if '@' in value else None) kommkz = detect_kommkz(value, advo_bet, espo_type=espo_type) + + # Skip wenn bereits als Var2+Var1 Match verarbeitet + if kommkz in matched_var1_indices and idx in matched_var1_indices[kommkz]: + continue + + self.logger.info(f"[KOMM] ➕ Var1: New in EspoCRM '{value[:30]}...', type={espo_item.get('type')}") self.logger.info(f"[KOMM] 🔍 kommKz detected: espo_type={espo_type}, kommKz={kommkz}") # Suche leeren Slot