- 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.
226 lines
7.4 KiB
Python
226 lines
7.4 KiB
Python
"""
|
||
TEST: Können wir eigene IDs in emailAddressData setzen?
|
||
|
||
Wenn EspoCRM IDs beim UPDATE akzeptiert und speichert,
|
||
dann können wir:
|
||
- Advoware-ID als 'id' in emailAddressData speichern
|
||
- Stabiles Matching haben
|
||
- Bidirektionalen Sync machen
|
||
|
||
Vorsichtiger Test mit Backup!
|
||
"""
|
||
|
||
import asyncio
|
||
import json
|
||
from services.espocrm import EspoCRMAPI
|
||
|
||
TEST_BETEILIGTE_ID = '68e4af00172be7924'
|
||
|
||
|
||
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 test_id_persistence():
|
||
"""
|
||
Teste ob EspoCRM IDs in emailAddressData speichert
|
||
|
||
Ablauf:
|
||
1. Hole aktuelle Daten (Backup)
|
||
2. Füge 'id' Feld zu EINEM Email hinzu
|
||
3. UPDATE
|
||
4. GET wieder
|
||
5. Prüfe ob 'id' noch da ist
|
||
6. Restore original falls nötig
|
||
"""
|
||
print_section("TEST: ID Persistence in emailAddressData")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
# 1. Backup
|
||
print("\n1️⃣ Backup: Hole aktuelle Daten")
|
||
entity_backup = await espo.get_entity('CBeteiligte', TEST_BETEILIGTE_ID)
|
||
emails_backup = entity_backup.get('emailAddressData', [])
|
||
|
||
print(f" Backup: {len(emails_backup)} Emails gesichert")
|
||
for email in emails_backup:
|
||
print(f" • {email['emailAddress']}")
|
||
|
||
# 2. Modifiziere NUR das erste Email (primary)
|
||
print("\n2️⃣ Modifikation: Füge 'id' zu primary Email hinzu")
|
||
|
||
emails_modified = []
|
||
for i, email in enumerate(emails_backup):
|
||
email_copy = email.copy()
|
||
if email_copy.get('primary'): # Nur primary modifizieren
|
||
# Nutze einen recognizable Test-Wert
|
||
test_id = f"advoware-{i+1}-test-123"
|
||
email_copy['id'] = test_id
|
||
print(f" ✏️ {email['emailAddress']:40s} → id={test_id}")
|
||
else:
|
||
print(f" ⏭️ {email['emailAddress']:40s} (unverändert)")
|
||
emails_modified.append(email_copy)
|
||
|
||
# 3. UPDATE
|
||
print("\n3️⃣ UPDATE: Sende modifizierte Daten")
|
||
try:
|
||
await espo.update_entity('CBeteiligte', TEST_BETEILIGTE_ID, {
|
||
'emailAddressData': emails_modified
|
||
})
|
||
print(" ✅ UPDATE erfolgreich")
|
||
except Exception as e:
|
||
print(f" ❌ UPDATE fehlgeschlagen: {e}")
|
||
return
|
||
|
||
# 4. GET wieder
|
||
print("\n4️⃣ GET: Hole Daten wieder ab")
|
||
await asyncio.sleep(0.5) # Kurze Pause
|
||
entity_after = await espo.get_entity('CBeteiligte', TEST_BETEILIGTE_ID)
|
||
emails_after = entity_after.get('emailAddressData', [])
|
||
|
||
print(f" Nach UPDATE: {len(emails_after)} Emails")
|
||
|
||
# 5. Vergleiche
|
||
print("\n5️⃣ VERGLEICH: Ist 'id' noch da?")
|
||
id_found = False
|
||
for email in emails_after:
|
||
email_addr = email['emailAddress']
|
||
has_id = 'id' in email
|
||
|
||
if has_id:
|
||
print(f" ✅ {email_addr:40s} → id={email['id']}")
|
||
id_found = True
|
||
else:
|
||
print(f" ❌ {email_addr:40s} → KEIN id Feld")
|
||
|
||
# 6. Ergebnis
|
||
print(f"\n6️⃣ ERGEBNIS:")
|
||
if id_found:
|
||
print(" 🎉 SUCCESS! EspoCRM speichert und liefert 'id' Feld zurück!")
|
||
print(" → Wir können Advoware-IDs in emailAddressData speichern")
|
||
print(" → Stabiles bidirektionales Matching möglich")
|
||
else:
|
||
print(" ❌ FAILED: EspoCRM ignoriert/entfernt 'id' Feld")
|
||
print(" → Wert-basiertes Matching notwendig")
|
||
print(" → Hybrid-Strategie (primary-Flag) ist beste Option")
|
||
|
||
# 7. Restore (optional - nur wenn User will)
|
||
print(f"\n7️⃣ CLEANUP:")
|
||
print(" Original-Daten (ohne id):")
|
||
for email in emails_backup:
|
||
print(f" • {email['emailAddress']}")
|
||
|
||
if id_found:
|
||
restore = input("\n 🔄 Restore zu Original (ohne id)? [y/N]: ").strip().lower()
|
||
if restore == 'y':
|
||
await espo.update_entity('CBeteiligte', TEST_BETEILIGTE_ID, {
|
||
'emailAddressData': emails_backup
|
||
})
|
||
print(" ✅ Restored")
|
||
else:
|
||
print(" ⏭️ Nicht restored (id bleibt)")
|
||
|
||
return id_found
|
||
|
||
|
||
async def test_custom_field_approach():
|
||
"""
|
||
Alternative: Nutze ein custom field in CBeteiligte für ID-Mapping
|
||
|
||
Idee: Speichere JSON-Mapping in einem Textfeld
|
||
"""
|
||
print_section("ALTERNATIVE: Custom Field für ID-Mapping")
|
||
|
||
print("\n💡 Idee: Nutze custom field 'kommunikationMapping'")
|
||
print(" Struktur:")
|
||
print(" {")
|
||
print(' "emails": [')
|
||
print(' {"emailAddress": "max@example.com", "advowareId": 123, "advowareRowId": "ABC"}')
|
||
print(' ],')
|
||
print(' "phones": [')
|
||
print(' {"phoneNumber": "+49...", "advowareId": 456, "advowareRowId": "DEF"}')
|
||
print(' ]')
|
||
print(" }")
|
||
|
||
print("\n✅ Vorteile:")
|
||
print(" • Stabiles Matching via advowareId")
|
||
print(" • Change Detection via advowareRowId")
|
||
print(" • Bidirektionaler Sync möglich")
|
||
|
||
print("\n❌ Nachteile:")
|
||
print(" • Erfordert custom field in EspoCRM")
|
||
print(" • Daten-Duplikation (in Data + Mapping)")
|
||
print(" • Fragil wenn emailAddress/phoneNumber ändert")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
# Prüfe ob custom field existiert
|
||
print("\n🔍 Prüfe ob 'kommunikationMapping' Feld existiert:")
|
||
try:
|
||
entity = await espo.get_entity('CBeteiligte', TEST_BETEILIGTE_ID)
|
||
if 'kommunikationMapping' in entity:
|
||
print(f" ✅ Feld existiert: {entity['kommunikationMapping']}")
|
||
else:
|
||
print(f" ❌ Feld existiert nicht")
|
||
print(f" → Müsste in EspoCRM angelegt werden")
|
||
except Exception as e:
|
||
print(f" ❌ Error: {e}")
|
||
|
||
|
||
async def main():
|
||
print("\n" + "="*70)
|
||
print("TEST: KÖNNEN WIR EIGENE IDs IN emailAddressData SETZEN?")
|
||
print("="*70)
|
||
print("\nZiel: Herausfinden ob EspoCRM 'id' Felder akzeptiert und speichert\n")
|
||
|
||
try:
|
||
# Haupttest
|
||
id_works = await test_id_persistence()
|
||
|
||
# Alternative
|
||
await test_custom_field_approach()
|
||
|
||
print_section("FINAL RECOMMENDATION")
|
||
|
||
if id_works:
|
||
print("\n🎯 EMPFEHLUNG: Nutze 'id' Feld in emailAddressData")
|
||
print("\n📋 Implementation:")
|
||
print(" 1. Bei Advoware → EspoCRM: Füge 'id' mit Advoware-ID hinzu")
|
||
print(" 2. Matching via 'id' Feld")
|
||
print(" 3. Change Detection via Advoware rowId")
|
||
print(" 4. Bidirektionaler Sync möglich")
|
||
else:
|
||
print("\n🎯 EMPFEHLUNG A: Hybrid-Strategie (primary-Flag)")
|
||
print(" • Einfach zu implementieren")
|
||
print(" • Nutzt Standard-EspoCRM")
|
||
print(" • Eingeschränkt bidirektional")
|
||
|
||
print("\n🎯 EMPFEHLUNG B: Custom Field 'kommunikationMapping'")
|
||
print(" • Vollständig bidirektional")
|
||
print(" • Erfordert EspoCRM-Anpassung")
|
||
print(" • Komplexere Implementation")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ Fehler: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|