Add tests for Kommunikation Sync implementation and verification scripts
- Implemented comprehensive tests for the Kommunikation Sync functionality, covering base64 encoding, marker parsing, creation, type detection, and integration scenarios. - Added a verification script to check for unique IDs in Advoware communications, ensuring stability and integrity of the IDs. - Created utility scripts for code validation, notification testing, and PUT response detail analysis to enhance development and testing processes. - Updated README with details on new tools and their usage.
This commit is contained in:
41
bitbylaw/scripts/analysis/README.md
Normal file
41
bitbylaw/scripts/analysis/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Analysis Scripts
|
||||
|
||||
Scripts für Analyse und Debugging von Sync-Problemen.
|
||||
|
||||
## Scripts
|
||||
|
||||
### analyze_beteiligte_endpoint.py
|
||||
Analysiert Beteiligte-Endpoint in Advoware.
|
||||
|
||||
**Features:**
|
||||
- Field-Analyse (funktionierende vs. ignorierte Felder)
|
||||
- Response-Structure Analyse
|
||||
- Edge-Case Testing
|
||||
|
||||
### analyze_sync_issues_104860.py
|
||||
Spezifische Analyse für Entity 104860 Sync-Probleme.
|
||||
|
||||
**Analysiert:**
|
||||
- Sync-Status Historie
|
||||
- Timestamp-Vergleiche
|
||||
- Konflikt-Erkennung
|
||||
- Hash-Berechnung
|
||||
|
||||
### compare_entities_104860.py
|
||||
Detaillierter Vergleich von Entity 104860 zwischen Systemen.
|
||||
|
||||
**Features:**
|
||||
- Field-by-Field Diff
|
||||
- Kommunikation-Arrays Vergleich
|
||||
- Marker-Analyse
|
||||
|
||||
## Verwendung
|
||||
|
||||
```bash
|
||||
cd /opt/motia-app/bitbylaw
|
||||
python scripts/analysis/analyze_sync_issues_104860.py
|
||||
```
|
||||
|
||||
## Zweck
|
||||
|
||||
Diese Scripts wurden erstellt, um spezifische Sync-Probleme zu debuggen und die API-Charakteristiken zu verstehen.
|
||||
152
bitbylaw/scripts/analysis/analyze_beteiligte_endpoint.py
Normal file
152
bitbylaw/scripts/analysis/analyze_beteiligte_endpoint.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
Detaillierte Analyse: Was liefert /api/v1/advonet/Beteiligte/{id}?
|
||||
|
||||
Prüfe:
|
||||
1. Kommunikation-Array: Alle Felder
|
||||
2. kommKz und kommArt Werte
|
||||
3. Adressen-Array (falls enthalten)
|
||||
4. Vollständige Struktur
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from services.advoware import AdvowareAPI
|
||||
|
||||
TEST_BETNR = 104860
|
||||
|
||||
|
||||
class SimpleContext:
|
||||
class Logger:
|
||||
def info(self, msg): print(f"[INFO] {msg}")
|
||||
def error(self, msg): print(f"[ERROR] {msg}")
|
||||
def warning(self, msg): print(f"[WARN] {msg}")
|
||||
def debug(self, msg): pass
|
||||
|
||||
def __init__(self):
|
||||
self.logger = self.Logger()
|
||||
|
||||
|
||||
def print_section(title):
|
||||
print("\n" + "="*70)
|
||||
print(title)
|
||||
print("="*70)
|
||||
|
||||
|
||||
async def main():
|
||||
print("\n" + "="*70)
|
||||
print("DETAILLIERTE ANALYSE: Beteiligte Endpoint")
|
||||
print("="*70)
|
||||
|
||||
context = SimpleContext()
|
||||
advo = AdvowareAPI(context)
|
||||
|
||||
# Hole kompletten Beteiligte
|
||||
print(f"\n📋 GET /api/v1/advonet/Beteiligte/{TEST_BETNR}")
|
||||
result = await advo.api_call(f'api/v1/advonet/Beteiligte/{TEST_BETNR}')
|
||||
|
||||
print(f"\nResponse Type: {type(result)}")
|
||||
if isinstance(result, list):
|
||||
print(f"Response Length: {len(result)}")
|
||||
beteiligte = result[0]
|
||||
else:
|
||||
beteiligte = result
|
||||
|
||||
# Zeige Top-Level Struktur
|
||||
print_section("TOP-LEVEL FELDER")
|
||||
print(f"\nVerfügbare Keys:")
|
||||
for key in sorted(beteiligte.keys()):
|
||||
value = beteiligte[key]
|
||||
if isinstance(value, list):
|
||||
print(f" • {key:30s}: [{len(value)} items]")
|
||||
elif isinstance(value, dict):
|
||||
print(f" • {key:30s}: {{dict}}")
|
||||
else:
|
||||
value_str = str(value)[:50]
|
||||
print(f" • {key:30s}: {value_str}")
|
||||
|
||||
# Kommunikationen
|
||||
print_section("KOMMUNIKATION ARRAY")
|
||||
|
||||
kommunikationen = beteiligte.get('kommunikation', [])
|
||||
print(f"\n✅ {len(kommunikationen)} Kommunikationen gefunden")
|
||||
|
||||
if kommunikationen:
|
||||
print(f"\n📋 Erste Kommunikation - ALLE Felder:")
|
||||
first = kommunikationen[0]
|
||||
print(json.dumps(first, indent=2, ensure_ascii=False))
|
||||
|
||||
print(f"\n📊 Übersicht aller Kommunikationen:")
|
||||
print(f"\n{'ID':>8s} | {'kommKz':>6s} | {'kommArt':>7s} | {'online':>6s} | {'Wert':40s} | {'Bemerkung'}")
|
||||
print("-" * 120)
|
||||
|
||||
for k in kommunikationen:
|
||||
komm_id = k.get('id', 'N/A')
|
||||
kommkz = k.get('kommKz', 'N/A')
|
||||
kommart = k.get('kommArt', 'N/A')
|
||||
online = k.get('online', False)
|
||||
wert = (k.get('tlf') or '')[:40]
|
||||
bemerkung = (k.get('bemerkung') or '')[:20]
|
||||
|
||||
# Highlighting
|
||||
kommkz_str = f"✅ {kommkz}" if kommkz not in [0, 'N/A'] else f"❌ {kommkz}"
|
||||
kommart_str = f"✅ {kommart}" if kommart not in [0, 'N/A'] else f"❌ {kommart}"
|
||||
|
||||
print(f"{komm_id:8} | {kommkz_str:>6s} | {kommart_str:>7s} | {str(online):>6s} | {wert:40s} | {bemerkung}")
|
||||
|
||||
# Adressen
|
||||
print_section("ADRESSEN ARRAY")
|
||||
|
||||
adressen = beteiligte.get('adressen', [])
|
||||
print(f"\n✅ {len(adressen)} Adressen gefunden")
|
||||
|
||||
if adressen:
|
||||
print(f"\n📋 Erste Adresse - Struktur:")
|
||||
first_addr = adressen[0]
|
||||
print(json.dumps(first_addr, indent=2, ensure_ascii=False))
|
||||
|
||||
# Bankverbindungen
|
||||
print_section("BANKVERBINDUNGEN")
|
||||
|
||||
bankverb = beteiligte.get('bankkverbindungen', []) # Typo im API?
|
||||
if not bankverb:
|
||||
bankverb = beteiligte.get('bankverbindungen', [])
|
||||
|
||||
print(f"\n✅ {len(bankverb)} Bankverbindungen gefunden")
|
||||
|
||||
if bankverb:
|
||||
print(f"\n📋 Erste Bankverbindung - Keys:")
|
||||
print(list(bankverb[0].keys()))
|
||||
|
||||
# Analyse
|
||||
print_section("ZUSAMMENFASSUNG")
|
||||
|
||||
print(f"\n📊 Verfügbare Daten:")
|
||||
print(f" • Kommunikationen: {len(kommunikationen)}")
|
||||
print(f" • Adressen: {len(adressen)}")
|
||||
print(f" • Bankverbindungen: {len(bankverb)}")
|
||||
|
||||
print(f"\n🔍 kommKz/kommArt Status:")
|
||||
if kommunikationen:
|
||||
kommkz_values = [k.get('kommKz', 0) for k in kommunikationen]
|
||||
kommart_values = [k.get('kommArt', 0) for k in kommunikationen]
|
||||
|
||||
kommkz_non_zero = [v for v in kommkz_values if v != 0]
|
||||
kommart_non_zero = [v for v in kommart_values if v != 0]
|
||||
|
||||
print(f" • kommKz unique values: {set(kommkz_values)}")
|
||||
print(f" • kommKz non-zero count: {len(kommkz_non_zero)} / {len(kommunikationen)}")
|
||||
|
||||
print(f" • kommArt unique values: {set(kommart_values)}")
|
||||
print(f" • kommArt non-zero count: {len(kommart_non_zero)} / {len(kommunikationen)}")
|
||||
|
||||
if kommkz_non_zero:
|
||||
print(f"\n ✅✅✅ JACKPOT! kommKz HAT WERTE im Beteiligte-Endpoint!")
|
||||
print(f" → Wir können den Typ korrekt erkennen!")
|
||||
elif kommart_non_zero:
|
||||
print(f"\n ✅ kommArt hat Werte (Email/Phone unterscheidbar)")
|
||||
else:
|
||||
print(f"\n ❌ Beide sind 0 - müssen Typ aus Wert ableiten")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
209
bitbylaw/scripts/analysis/analyze_sync_issues_104860.py
Normal file
209
bitbylaw/scripts/analysis/analyze_sync_issues_104860.py
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Detaillierte Analyse der Sync-Probleme für Entity 104860
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
import base64
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from services.advoware import AdvowareAPI
|
||||
from services.espocrm import EspoCRMAPI
|
||||
from services.kommunikation_mapper import parse_marker, should_sync_to_espocrm
|
||||
|
||||
|
||||
class SimpleContext:
|
||||
class Logger:
|
||||
def info(self, msg): print(f"ℹ️ {msg}")
|
||||
def debug(self, msg): pass # Suppress debug
|
||||
def warn(self, msg): print(f"⚠️ {msg}")
|
||||
def warning(self, msg): print(f"⚠️ {msg}")
|
||||
def error(self, msg): print(f"❌ {msg}")
|
||||
|
||||
def __init__(self):
|
||||
self.logger = self.Logger()
|
||||
|
||||
|
||||
async def analyze():
|
||||
context = SimpleContext()
|
||||
betnr = 104860
|
||||
espo_id = "68e3e7eab49f09adb"
|
||||
|
||||
# Initialize APIs
|
||||
advoware_api = AdvowareAPI(context)
|
||||
espocrm = EspoCRMAPI(context)
|
||||
|
||||
# Fetch data
|
||||
advo_result = await advoware_api.api_call(f'api/v1/advonet/Beteiligte/{betnr}', method='GET')
|
||||
advo_entity = advo_result[0] if isinstance(advo_result, list) else advo_result
|
||||
|
||||
espo_entity = await espocrm.get_entity('CBeteiligte', espo_id)
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("DETAILLIERTE SYNC-PROBLEM ANALYSE")
|
||||
print("="*80 + "\n")
|
||||
|
||||
# ========== PROBLEM 1: NAME MISMATCH ==========
|
||||
print("🔴 PROBLEM 1: STAMMDATEN NICHT SYNCHRON")
|
||||
print("-" * 80)
|
||||
print(f"EspoCRM Name: '{espo_entity.get('name')}'")
|
||||
print(f"Advoware Name: '{advo_entity.get('name')}'")
|
||||
print(f"")
|
||||
print(f"ANALYSE:")
|
||||
print(f"- syncStatus: {espo_entity.get('syncStatus')}")
|
||||
print(f"- advowareLastSync: {espo_entity.get('advowareLastSync')}")
|
||||
print(f"- modifiedAt (EspoCRM): {espo_entity.get('modifiedAt')}")
|
||||
print(f"- geaendertAm (Advoware): {advo_entity.get('geaendertAm')}")
|
||||
print(f"")
|
||||
print(f"💡 URSACHE:")
|
||||
print(f" - Sync sagt 'clean' aber Daten sind NICHT identisch!")
|
||||
print(f" - Dies ist Problem #13: Keine Validierung von Sync-Ergebnissen")
|
||||
print(f" - Sync glaubt es war erfolgreich, aber Mapping oder API-Call fehlte")
|
||||
print()
|
||||
|
||||
# ========== PROBLEM 2: KOMMUNIKATION COUNTS ==========
|
||||
print("🟡 PROBLEM 2: KOMMUNIKATION ANZAHL-MISMATCH")
|
||||
print("-" * 80)
|
||||
|
||||
advo_kommunikationen = advo_entity.get('kommunikation', [])
|
||||
espo_emails = espo_entity.get('emailAddressData', [])
|
||||
espo_phones = espo_entity.get('phoneNumberData', [])
|
||||
|
||||
# Analysiere Advoware Kommunikationen
|
||||
advo_with_value = []
|
||||
advo_empty_slots = []
|
||||
advo_non_sync = []
|
||||
|
||||
for komm in advo_kommunikationen:
|
||||
tlf = (komm.get('tlf') or '').strip()
|
||||
bemerkung = komm.get('bemerkung', '')
|
||||
marker = parse_marker(bemerkung)
|
||||
|
||||
if not should_sync_to_espocrm(komm):
|
||||
advo_non_sync.append(komm)
|
||||
elif not tlf or (marker and marker.get('is_slot')):
|
||||
advo_empty_slots.append(komm)
|
||||
else:
|
||||
advo_with_value.append(komm)
|
||||
|
||||
print(f"Advoware Kommunikationen: {len(advo_kommunikationen)} total")
|
||||
print(f" - Mit Wert (sollten in EspoCRM sein): {len(advo_with_value)}")
|
||||
print(f" - Empty Slots: {len(advo_empty_slots)}")
|
||||
print(f" - Nicht-sync-relevant: {len(advo_non_sync)}")
|
||||
print()
|
||||
print(f"EspoCRM Kommunikationen: {len(espo_emails) + len(espo_phones)} total")
|
||||
print(f" - Emails: {len(espo_emails)}")
|
||||
print(f" - Phones: {len(espo_phones)}")
|
||||
print()
|
||||
|
||||
# Detaillierte Analyse der Empty Slots
|
||||
print("📋 Empty Slots in Advoware:")
|
||||
for i, slot in enumerate(advo_empty_slots, 1):
|
||||
marker = parse_marker(slot.get('bemerkung', ''))
|
||||
kommkz = marker.get('kommKz') if marker else 'N/A'
|
||||
rowid = slot.get('rowId', 'N/A')[:20]
|
||||
print(f" {i}. kommKz={kommkz} | rowId={rowid}... | bemerkung={slot.get('bemerkung', '')[:40]}")
|
||||
print()
|
||||
|
||||
print("💡 URSACHE:")
|
||||
print(f" - {len(advo_empty_slots)} Empty Slots werden NICHT aufgeräumt")
|
||||
print(f" - Dies ist Problem #2: Empty Slot Accumulation")
|
||||
print(f" - Nur {len(advo_with_value)} Einträge mit Wert, aber Hash beinhaltet ALLE {len(advo_kommunikationen)}")
|
||||
print()
|
||||
|
||||
# ========== PROBLEM 3: MARKER ANALYSIS ==========
|
||||
print("🟡 PROBLEM 3: MARKER VALIDIERUNG")
|
||||
print("-" * 80)
|
||||
|
||||
marker_issues = []
|
||||
|
||||
for komm in advo_with_value:
|
||||
tlf = (komm.get('tlf') or '').strip()
|
||||
bemerkung = komm.get('bemerkung', '')
|
||||
marker = parse_marker(bemerkung)
|
||||
|
||||
if marker:
|
||||
synced_value = marker.get('synced_value', '')
|
||||
if synced_value != tlf:
|
||||
marker_issues.append({
|
||||
'tlf': tlf,
|
||||
'synced_value': synced_value,
|
||||
'marker': bemerkung[:50]
|
||||
})
|
||||
|
||||
if marker_issues:
|
||||
print(f"❌ {len(marker_issues)} Marker stimmen NICHT mit aktuellem Wert überein:")
|
||||
for issue in marker_issues:
|
||||
print(f" - Aktuell: '{issue['tlf']}'")
|
||||
print(f" Marker: '{issue['synced_value']}'")
|
||||
print(f" Marker-String: {issue['marker']}...")
|
||||
print()
|
||||
print("💡 URSACHE:")
|
||||
print(" - Dies deutet auf Problem #6: Marker-Update fehlgeschlagen")
|
||||
print(" - Oder Var6 wurde erkannt aber Marker nicht aktualisiert")
|
||||
else:
|
||||
print("✅ Alle Marker stimmen mit aktuellen Werten überein")
|
||||
print()
|
||||
|
||||
# ========== PROBLEM 4: HASH COVERAGE ==========
|
||||
print("🟡 PROBLEM 4: HASH-BERECHNUNG")
|
||||
print("-" * 80)
|
||||
|
||||
import hashlib
|
||||
|
||||
# Aktueller Code (FALSCH - beinhaltet ALLE)
|
||||
all_rowids = sorted([k.get('rowId', '') for k in advo_kommunikationen if k.get('rowId')])
|
||||
wrong_hash = hashlib.md5(''.join(all_rowids).encode()).hexdigest()[:16]
|
||||
|
||||
# Korrekt (nur sync-relevante)
|
||||
sync_relevant_komm = [k for k in advo_kommunikationen if should_sync_to_espocrm(k) and (k.get('tlf') or '').strip()]
|
||||
sync_rowids = sorted([k.get('rowId', '') for k in sync_relevant_komm if k.get('rowId')])
|
||||
correct_hash = hashlib.md5(''.join(sync_rowids).encode()).hexdigest()[:16]
|
||||
|
||||
stored_hash = espo_entity.get('kommunikationHash')
|
||||
|
||||
print(f"Hash-Vergleich:")
|
||||
print(f" - Gespeichert: {stored_hash}")
|
||||
print(f" - Aktuell (ALL): {wrong_hash} {'✅' if wrong_hash == stored_hash else '❌'}")
|
||||
print(f" - Korrekt (nur sync-relevant): {correct_hash} {'✅' if correct_hash == stored_hash else '❌'}")
|
||||
print()
|
||||
print(f"Rowids einbezogen:")
|
||||
print(f" - ALL: {len(all_rowids)} Kommunikationen")
|
||||
print(f" - Sync-relevant: {len(sync_rowids)} Kommunikationen")
|
||||
print()
|
||||
print("💡 URSACHE:")
|
||||
print(" - Dies ist Problem #3: Hash beinhaltet ALLE statt nur sync-relevante")
|
||||
print(" - Empty Slots ändern Hash obwohl sie nicht in EspoCRM sind")
|
||||
print()
|
||||
|
||||
# ========== ZUSAMMENFASSUNG ==========
|
||||
print("="*80)
|
||||
print("ZUSAMMENFASSUNG DER PROBLEME")
|
||||
print("="*80)
|
||||
print()
|
||||
print("✅ BESTÄTIGT - Die folgenden Probleme existieren:")
|
||||
print()
|
||||
print("1. ❌ Problem #13: Keine Validierung von Sync-Ergebnissen")
|
||||
print(" → Stammdaten sind NICHT synchron obwohl syncStatus='clean'")
|
||||
print()
|
||||
print("2. ❌ Problem #2: Empty Slot Accumulation")
|
||||
print(f" → {len(advo_empty_slots)} Empty Slots sammeln sich an")
|
||||
print()
|
||||
print("3. ❌ Problem #3: Hash-Berechnung inkorrekt")
|
||||
print(f" → Hash beinhaltet {len(all_rowids)} statt {len(sync_rowids)} Kommunikationen")
|
||||
print()
|
||||
|
||||
if marker_issues:
|
||||
print("4. ❌ Problem #6: Marker-Update Failures")
|
||||
print(f" → {len(marker_issues)} Marker stimmen nicht mit aktuellem Wert überein")
|
||||
print()
|
||||
|
||||
print("="*80)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(analyze())
|
||||
233
bitbylaw/scripts/analysis/compare_entities_104860.py
Normal file
233
bitbylaw/scripts/analysis/compare_entities_104860.py
Normal file
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Vergleicht Advoware Entity (betNr 104860) mit EspoCRM Entity (68e3e7eab49f09adb)
|
||||
um zu prüfen ob sie synchron sind.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from services.advoware import AdvowareAPI
|
||||
from services.advoware_service import AdvowareService
|
||||
from services.espocrm import EspoCRMAPI
|
||||
from services.espocrm_mapper import BeteiligteMapper
|
||||
from services.beteiligte_sync_utils import BeteiligteSync
|
||||
import hashlib
|
||||
|
||||
|
||||
class SimpleContext:
|
||||
"""Minimal context for logging"""
|
||||
class Logger:
|
||||
def info(self, msg): print(f"ℹ️ {msg}")
|
||||
def debug(self, msg): print(f"🔍 {msg}")
|
||||
def warn(self, msg): print(f"⚠️ {msg}")
|
||||
def warning(self, msg): print(f"⚠️ {msg}")
|
||||
def error(self, msg): print(f"❌ {msg}")
|
||||
|
||||
def __init__(self):
|
||||
self.logger = self.Logger()
|
||||
|
||||
|
||||
def calculate_komm_hash(kommunikationen):
|
||||
"""Berechnet Hash wie im Code"""
|
||||
komm_rowids = sorted([k.get('rowId', '') for k in kommunikationen if k.get('rowId')])
|
||||
return hashlib.md5(''.join(komm_rowids).encode()).hexdigest()[:16]
|
||||
|
||||
|
||||
async def compare_entities():
|
||||
context = SimpleContext()
|
||||
|
||||
# IDs
|
||||
betnr = 104860
|
||||
espo_id = "68e3e7eab49f09adb"
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"ENTITY COMPARISON")
|
||||
print(f"{'='*80}")
|
||||
print(f"Advoware betNr: {betnr}")
|
||||
print(f"EspoCRM ID: {espo_id}")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
# Initialize APIs
|
||||
advoware_api = AdvowareAPI(context)
|
||||
advoware_service = AdvowareService(context)
|
||||
espocrm = EspoCRMAPI(context)
|
||||
mapper = BeteiligteMapper()
|
||||
sync_utils = BeteiligteSync(espocrm, None, context)
|
||||
|
||||
# ========== FETCH ADVOWARE ==========
|
||||
print("\n📥 Fetching Advoware Entity...")
|
||||
try:
|
||||
advo_result = await advoware_api.api_call(
|
||||
f'api/v1/advonet/Beteiligte/{betnr}',
|
||||
method='GET'
|
||||
)
|
||||
|
||||
if isinstance(advo_result, list):
|
||||
advo_entity = advo_result[0] if advo_result else None
|
||||
else:
|
||||
advo_entity = advo_result
|
||||
|
||||
if not advo_entity:
|
||||
print("❌ Advoware Entity nicht gefunden!")
|
||||
return
|
||||
|
||||
print(f"✅ Advoware Entity geladen")
|
||||
print(f" - Name: {advo_entity.get('name')}")
|
||||
print(f" - rowId: {advo_entity.get('rowId', 'N/A')[:40]}...")
|
||||
print(f" - geaendertAm: {advo_entity.get('geaendertAm')}")
|
||||
print(f" - Kommunikationen: {len(advo_entity.get('kommunikation', []))}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Laden von Advoware: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return
|
||||
|
||||
# ========== FETCH ESPOCRM ==========
|
||||
print("\n📥 Fetching EspoCRM Entity...")
|
||||
try:
|
||||
espo_entity = await espocrm.get_entity('CBeteiligte', espo_id)
|
||||
|
||||
if not espo_entity:
|
||||
print("❌ EspoCRM Entity nicht gefunden!")
|
||||
return
|
||||
|
||||
print(f"✅ EspoCRM Entity geladen")
|
||||
print(f" - Name: {espo_entity.get('name')}")
|
||||
print(f" - betnr: {espo_entity.get('betnr')}")
|
||||
print(f" - modifiedAt: {espo_entity.get('modifiedAt')}")
|
||||
print(f" - syncStatus: {espo_entity.get('syncStatus')}")
|
||||
print(f" - advowareLastSync: {espo_entity.get('advowareLastSync')}")
|
||||
print(f" - advowareRowId: {espo_entity.get('advowareRowId', 'N/A')[:40]}...")
|
||||
print(f" - kommunikationHash: {espo_entity.get('kommunikationHash')}")
|
||||
print(f" - emailAddressData: {len(espo_entity.get('emailAddressData', []))}")
|
||||
print(f" - phoneNumberData: {len(espo_entity.get('phoneNumberData', []))}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Laden von EspoCRM: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return
|
||||
|
||||
# ========== COMPARISON ==========
|
||||
print(f"\n{'='*80}")
|
||||
print("STAMMDATEN VERGLEICH")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
# Timestamp comparison
|
||||
comparison = sync_utils.compare_entities(espo_entity, advo_entity)
|
||||
print(f"🔍 Timestamp-Vergleich: {comparison}")
|
||||
|
||||
# Field-by-field comparison
|
||||
print("\n📊 Feld-für-Feld Vergleich (Stammdaten):\n")
|
||||
|
||||
# Map Advoware → EspoCRM für Vergleich
|
||||
advo_mapped = mapper.map_advoware_to_cbeteiligte(advo_entity)
|
||||
|
||||
fields_to_compare = [
|
||||
'name', 'rechtsform', 'geburtsdatum', 'anrede',
|
||||
'handelsregister', 'geschlecht', 'titel'
|
||||
]
|
||||
|
||||
differences = []
|
||||
for field in fields_to_compare:
|
||||
espo_val = espo_entity.get(field)
|
||||
advo_val = advo_mapped.get(field)
|
||||
|
||||
match = "✅" if espo_val == advo_val else "❌"
|
||||
print(f"{match} {field:20} | EspoCRM: {str(espo_val)[:40]:40} | Advoware: {str(advo_val)[:40]:40}")
|
||||
|
||||
if espo_val != advo_val:
|
||||
differences.append({
|
||||
'field': field,
|
||||
'espocrm': espo_val,
|
||||
'advoware': advo_val
|
||||
})
|
||||
|
||||
# ========== KOMMUNIKATION COMPARISON ==========
|
||||
print(f"\n{'='*80}")
|
||||
print("KOMMUNIKATION VERGLEICH")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
advo_kommunikationen = advo_entity.get('kommunikation', [])
|
||||
espo_emails = espo_entity.get('emailAddressData', [])
|
||||
espo_phones = espo_entity.get('phoneNumberData', [])
|
||||
|
||||
# Hash Vergleich
|
||||
current_hash = calculate_komm_hash(advo_kommunikationen)
|
||||
stored_hash = espo_entity.get('kommunikationHash')
|
||||
|
||||
print(f"📊 Kommunikations-Hash:")
|
||||
print(f" - Gespeichert in EspoCRM: {stored_hash}")
|
||||
print(f" - Aktuell in Advoware: {current_hash}")
|
||||
print(f" - Match: {'✅ JA' if current_hash == stored_hash else '❌ NEIN'}")
|
||||
|
||||
# Advoware Kommunikationen im Detail
|
||||
print(f"\n📞 Advoware Kommunikationen ({len(advo_kommunikationen)}):")
|
||||
for i, komm in enumerate(advo_kommunikationen, 1):
|
||||
tlf = (komm.get('tlf') or '').strip()
|
||||
kommkz = komm.get('kommKz', 0)
|
||||
bemerkung = komm.get('bemerkung', '')[:50]
|
||||
online = komm.get('online', False)
|
||||
rowid = komm.get('rowId', 'N/A')[:20]
|
||||
|
||||
print(f" {i}. {tlf:30} | kommKz={kommkz:2} | online={online} | rowId={rowid}...")
|
||||
if bemerkung:
|
||||
print(f" Bemerkung: {bemerkung}...")
|
||||
|
||||
# EspoCRM Emails
|
||||
print(f"\n📧 EspoCRM Emails ({len(espo_emails)}):")
|
||||
for i, email in enumerate(espo_emails, 1):
|
||||
addr = email.get('emailAddress', '')
|
||||
primary = email.get('primary', False)
|
||||
print(f" {i}. {addr:40} | primary={primary}")
|
||||
|
||||
# EspoCRM Phones
|
||||
print(f"\n📱 EspoCRM Phones ({len(espo_phones)}):")
|
||||
for i, phone in enumerate(espo_phones, 1):
|
||||
num = phone.get('phoneNumber', '')
|
||||
typ = phone.get('type', 'N/A')
|
||||
primary = phone.get('primary', False)
|
||||
print(f" {i}. {num:30} | type={typ:10} | primary={primary}")
|
||||
|
||||
# ========== SUMMARY ==========
|
||||
print(f"\n{'='*80}")
|
||||
print("ZUSAMMENFASSUNG")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
if differences:
|
||||
print(f"❌ STAMMDATEN NICHT SYNCHRON! {len(differences)} Unterschiede gefunden:")
|
||||
for diff in differences:
|
||||
print(f" - {diff['field']}: EspoCRM='{diff['espocrm']}' ≠ Advoware='{diff['advoware']}'")
|
||||
else:
|
||||
print("✅ Stammdaten sind synchron")
|
||||
|
||||
print()
|
||||
|
||||
if current_hash != stored_hash:
|
||||
print(f"❌ KOMMUNIKATION NICHT SYNCHRON! Hash stimmt nicht überein")
|
||||
else:
|
||||
print("✅ Kommunikation-Hash stimmt überein (aber könnte trotzdem Unterschiede geben)")
|
||||
|
||||
print()
|
||||
|
||||
# Total count check
|
||||
total_espo_komm = len(espo_emails) + len(espo_phones)
|
||||
total_advo_komm = len([k for k in advo_kommunikationen if (k.get('tlf') or '').strip()])
|
||||
|
||||
if total_espo_komm != total_advo_komm:
|
||||
print(f"⚠️ Anzahl-Unterschied: EspoCRM={total_espo_komm} ≠ Advoware={total_advo_komm}")
|
||||
else:
|
||||
print(f"✅ Anzahl stimmt überein: {total_espo_komm} Kommunikationen")
|
||||
|
||||
print(f"\n{'='*80}\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(compare_entities())
|
||||
Reference in New Issue
Block a user