- Add AdressenMapper for transforming addresses between EspoCRM and Advoware formats. - Create AdressenSync class to handle address creation, update, and deletion synchronization. - Introduce NotificationManager for managing manual intervention notifications in case of sync issues. - Implement detailed logging for address sync operations and error handling. - Ensure READ-ONLY field changes are detected and notified for manual resolution.
469 lines
17 KiB
Python
469 lines
17 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Test: gueltigBis nachträglich setzen und entfernen (Soft-Delete)
|
||
==================================================================
|
||
|
||
Ziele:
|
||
1. Teste ob gueltigBis via PUT gesetzt werden kann (Deaktivierung)
|
||
2. Teste ob gueltigBis via PUT entfernt werden kann (Reaktivierung)
|
||
3. Teste ob gueltigBis auf null/None gesetzt werden kann
|
||
"""
|
||
|
||
import asyncio
|
||
import sys
|
||
import os
|
||
from datetime import datetime
|
||
|
||
# Add parent directory to path
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||
|
||
from services.advoware import AdvowareAPI
|
||
|
||
# Test-Konfiguration
|
||
TEST_BETNR = 104860
|
||
|
||
# ANSI Color codes
|
||
BOLD = '\033[1m'
|
||
RED = '\033[91m'
|
||
GREEN = '\033[92m'
|
||
YELLOW = '\033[93m'
|
||
BLUE = '\033[94m'
|
||
RESET = '\033[0m'
|
||
|
||
def print_header(text):
|
||
print(f"\n{BOLD}{'='*80}{RESET}")
|
||
print(f"{BOLD}{text}{RESET}")
|
||
print(f"{BOLD}{'='*80}{RESET}\n")
|
||
|
||
def print_success(text):
|
||
print(f"{GREEN}✓ {text}{RESET}")
|
||
|
||
def print_error(text):
|
||
print(f"{RED}✗ {text}{RESET}")
|
||
|
||
def print_warning(text):
|
||
print(f"{YELLOW}⚠ {text}{RESET}")
|
||
|
||
def print_info(text):
|
||
print(f"{BLUE}ℹ {text}{RESET}")
|
||
|
||
|
||
class SimpleLogger:
|
||
def info(self, msg): pass
|
||
def error(self, msg): print_error(msg)
|
||
def debug(self, msg): pass
|
||
def warning(self, msg): pass
|
||
|
||
class SimpleContext:
|
||
def __init__(self):
|
||
self.logger = SimpleLogger()
|
||
def log_info(self, msg): pass
|
||
def log_error(self, msg): print_error(msg)
|
||
def log_debug(self, msg): pass
|
||
|
||
|
||
async def test_1_create_active_address():
|
||
"""Test 1: Erstelle aktive Adresse (ohne gueltigBis)"""
|
||
print_header("TEST 1: Erstelle aktive Adresse (OHNE gueltigBis)")
|
||
|
||
context = SimpleContext()
|
||
advo = AdvowareAPI(context=context)
|
||
|
||
address_data = {
|
||
"strasse": "Soft-Delete Test Straße",
|
||
"plz": "66666",
|
||
"ort": "Teststadt",
|
||
"land": "DE",
|
||
"bemerkung": "TEST-SOFTDELETE: Für gueltigBis Modifikation",
|
||
"gueltigVon": "2026-01-01T00:00:00"
|
||
# KEIN gueltigBis → unbegrenzt gültig
|
||
}
|
||
|
||
print_info("Erstelle Adresse OHNE gueltigBis (unbegrenzt aktiv)...")
|
||
|
||
try:
|
||
result = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='POST',
|
||
json_data=address_data
|
||
)
|
||
|
||
if result and len(result) > 0:
|
||
addr = result[0]
|
||
print_success(f"✓ Adresse erstellt")
|
||
print_info(f" rowId: {addr.get('rowId')}")
|
||
print_info(f" gueltigVon: {addr.get('gueltigVon')}")
|
||
print_info(f" gueltigBis: {addr.get('gueltigBis')} (sollte None sein)")
|
||
|
||
# Hole echten Index via GET
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
for a in all_addresses:
|
||
if (a.get('bemerkung') or '').startswith('TEST-SOFTDELETE'):
|
||
print_info(f" reihenfolgeIndex: {a.get('reihenfolgeIndex')}")
|
||
return a.get('reihenfolgeIndex')
|
||
|
||
return None
|
||
else:
|
||
print_error("POST fehlgeschlagen")
|
||
return None
|
||
|
||
except Exception as e:
|
||
print_error(f"Fehler: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
|
||
async def test_2_deactivate_via_gueltigbis(index):
|
||
"""Test 2: Deaktiviere Adresse durch Setzen von gueltigBis"""
|
||
print_header("TEST 2: Deaktivierung - gueltigBis nachträglich setzen")
|
||
|
||
context = SimpleContext()
|
||
advo = AdvowareAPI(context=context)
|
||
|
||
try:
|
||
# Hole aktuelle Adresse
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
test_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
if not test_addr:
|
||
print_error(f"Adresse mit Index {index} nicht gefunden")
|
||
return False
|
||
|
||
print_info("Status VORHER:")
|
||
print(f" gueltigVon: {test_addr.get('gueltigVon')}")
|
||
print(f" gueltigBis: {test_addr.get('gueltigBis')}")
|
||
|
||
# Setze gueltigBis auf gestern (= deaktiviert)
|
||
print_info("\nSetze gueltigBis auf 2024-12-31 (Vergangenheit = deaktiviert)...")
|
||
|
||
update_data = {
|
||
"strasse": test_addr.get('strasse'),
|
||
"plz": test_addr.get('plz'),
|
||
"ort": test_addr.get('ort'),
|
||
"land": test_addr.get('land'),
|
||
"gueltigVon": test_addr.get('gueltigVon'),
|
||
"gueltigBis": "2024-12-31T23:59:59" # ← Vergangenheit
|
||
}
|
||
|
||
result = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen/{index}',
|
||
method='PUT',
|
||
json_data=update_data
|
||
)
|
||
|
||
print_success("✓ PUT erfolgreich")
|
||
|
||
# Prüfe Ergebnis
|
||
print_info("\nHole Adresse erneut und prüfe gueltigBis...")
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
updated_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
if updated_addr:
|
||
print_info("Status NACHHER:")
|
||
print(f" gueltigVon: {updated_addr.get('gueltigVon')}")
|
||
print(f" gueltigBis: {updated_addr.get('gueltigBis')}")
|
||
|
||
if updated_addr.get('gueltigBis') == "2024-12-31T00:00:00":
|
||
print_success("\n✓✓✓ PERFEKT: gueltigBis wurde nachträglich gesetzt!")
|
||
print_success("✓ Adresse kann via PUT deaktiviert werden!")
|
||
return True
|
||
else:
|
||
print_error(f"\n❌ gueltigBis nicht korrekt: {updated_addr.get('gueltigBis')}")
|
||
return False
|
||
else:
|
||
print_error("Adresse nach PUT nicht gefunden")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print_error(f"Fehler: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
|
||
async def test_3_reactivate_set_far_future(index):
|
||
"""Test 3: Reaktivierung durch Setzen auf weit in Zukunft"""
|
||
print_header("TEST 3: Reaktivierung - gueltigBis auf fernes Datum setzen")
|
||
|
||
context = SimpleContext()
|
||
advo = AdvowareAPI(context=context)
|
||
|
||
try:
|
||
# Hole aktuelle Adresse
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
test_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
if not test_addr:
|
||
print_error(f"Adresse mit Index {index} nicht gefunden")
|
||
return False
|
||
|
||
print_info("Status VORHER (deaktiviert):")
|
||
print(f" gueltigBis: {test_addr.get('gueltigBis')}")
|
||
|
||
# Setze gueltigBis auf weit in Zukunft
|
||
print_info("\nSetze gueltigBis auf 2099-12-31 (weit in Zukunft = aktiv)...")
|
||
|
||
update_data = {
|
||
"strasse": test_addr.get('strasse'),
|
||
"plz": test_addr.get('plz'),
|
||
"ort": test_addr.get('ort'),
|
||
"land": test_addr.get('land'),
|
||
"gueltigVon": test_addr.get('gueltigVon'),
|
||
"gueltigBis": "2099-12-31T23:59:59" # ← Weit in Zukunft
|
||
}
|
||
|
||
await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen/{index}',
|
||
method='PUT',
|
||
json_data=update_data
|
||
)
|
||
|
||
print_success("✓ PUT erfolgreich")
|
||
|
||
# Prüfe Ergebnis
|
||
print_info("\nHole Adresse erneut...")
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
updated_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
if updated_addr:
|
||
print_info("Status NACHHER (reaktiviert):")
|
||
print(f" gueltigBis: {updated_addr.get('gueltigBis')}")
|
||
|
||
if updated_addr.get('gueltigBis') == "2099-12-31T00:00:00":
|
||
print_success("\n✓✓✓ PERFEKT: gueltigBis wurde auf Zukunft gesetzt!")
|
||
print_success("✓ Adresse ist jetzt wieder aktiv!")
|
||
return True
|
||
else:
|
||
print_error(f"\n❌ gueltigBis nicht korrekt: {updated_addr.get('gueltigBis')}")
|
||
return False
|
||
else:
|
||
print_error("Adresse nach PUT nicht gefunden")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print_error(f"Fehler: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
|
||
async def test_4_remove_gueltigbis_completely(index):
|
||
"""Test 4: Entferne gueltigBis komplett (null/None)"""
|
||
print_header("TEST 4: gueltigBis komplett entfernen (null/None)")
|
||
|
||
context = SimpleContext()
|
||
advo = AdvowareAPI(context=context)
|
||
|
||
try:
|
||
# Hole aktuelle Adresse
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
test_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
if not test_addr:
|
||
print_error(f"Adresse mit Index {index} nicht gefunden")
|
||
return None
|
||
|
||
print_info("Status VORHER:")
|
||
print(f" gueltigBis: {test_addr.get('gueltigBis')}")
|
||
|
||
# Versuche 1: gueltigBis weglassen
|
||
print_info("\n=== Versuch 1: gueltigBis komplett weglassen ===")
|
||
|
||
update_data = {
|
||
"strasse": test_addr.get('strasse'),
|
||
"plz": test_addr.get('plz'),
|
||
"ort": test_addr.get('ort'),
|
||
"land": test_addr.get('land'),
|
||
"gueltigVon": test_addr.get('gueltigVon')
|
||
# gueltigBis absichtlich weggelassen
|
||
}
|
||
|
||
await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen/{index}',
|
||
method='PUT',
|
||
json_data=update_data
|
||
)
|
||
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
updated_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
result_1 = updated_addr.get('gueltigBis') if updated_addr else "ERROR"
|
||
print_info(f"Ergebnis: gueltigBis = {result_1}")
|
||
|
||
if result_1 is None:
|
||
print_success("✓ Weglassen entfernt gueltigBis!")
|
||
return "omit"
|
||
|
||
# Versuche 2: gueltigBis = None/null
|
||
print_info("\n=== Versuch 2: gueltigBis explizit auf None setzen ===")
|
||
|
||
update_data['gueltigBis'] = None
|
||
|
||
await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen/{index}',
|
||
method='PUT',
|
||
json_data=update_data
|
||
)
|
||
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
updated_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
result_2 = updated_addr.get('gueltigBis') if updated_addr else "ERROR"
|
||
print_info(f"Ergebnis: gueltigBis = {result_2}")
|
||
|
||
if result_2 is None:
|
||
print_success("✓ None entfernt gueltigBis!")
|
||
return "none"
|
||
|
||
# Versuche 3: gueltigBis = ""
|
||
print_info("\n=== Versuch 3: gueltigBis auf leeren String setzen ===")
|
||
|
||
update_data['gueltigBis'] = ""
|
||
|
||
try:
|
||
await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen/{index}',
|
||
method='PUT',
|
||
json_data=update_data
|
||
)
|
||
|
||
all_addresses = await advo.api_call(
|
||
f'/api/v1/advonet/Beteiligte/{TEST_BETNR}/Adressen',
|
||
method='GET'
|
||
)
|
||
|
||
updated_addr = next((a for a in all_addresses if a.get('reihenfolgeIndex') == index), None)
|
||
result_3 = updated_addr.get('gueltigBis') if updated_addr else "ERROR"
|
||
print_info(f"Ergebnis: gueltigBis = {result_3}")
|
||
|
||
if result_3 is None:
|
||
print_success("✓ Leerer String entfernt gueltigBis!")
|
||
return "empty"
|
||
except Exception as e:
|
||
print_warning(f"⚠ Leerer String wird abgelehnt: {e}")
|
||
|
||
print_warning("\n⚠ gueltigBis kann nicht komplett entfernt werden")
|
||
print_info("💡 Lösung: Setze auf weit in Zukunft (2099-12-31) für 'unbegrenzt aktiv'")
|
||
return "not_possible"
|
||
|
||
except Exception as e:
|
||
print_error(f"Fehler: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return None
|
||
|
||
|
||
async def main():
|
||
print(f"\n{BOLD}╔══════════════════════════════════════════════════════════════╗{RESET}")
|
||
print(f"{BOLD}║ gueltigBis nachträglich ändern (Soft-Delete Tests) ║{RESET}")
|
||
print(f"{BOLD}╚══════════════════════════════════════════════════════════════╝{RESET}\n")
|
||
|
||
print(f"Test-Konfiguration:")
|
||
print(f" BetNr: {TEST_BETNR}")
|
||
print(f" Datum: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
# Test 1: Erstelle aktive Adresse
|
||
index = await test_1_create_active_address()
|
||
if not index:
|
||
print_error("\nTest abgebrochen: Konnte Adresse nicht erstellen")
|
||
return
|
||
|
||
# Test 2: Deaktiviere via gueltigBis
|
||
can_deactivate = await test_2_deactivate_via_gueltigbis(index)
|
||
|
||
# Test 3: Reaktiviere via gueltigBis auf Zukunft
|
||
can_reactivate = await test_3_reactivate_set_far_future(index)
|
||
|
||
# Test 4: Versuche gueltigBis zu entfernen
|
||
remove_method = await test_4_remove_gueltigbis_completely(index)
|
||
|
||
# Finale Zusammenfassung
|
||
print(f"\n{BOLD}╔══════════════════════════════════════════════════════════════╗{RESET}")
|
||
print(f"{BOLD}║ FINALE ERKENNTNISSE ║{RESET}")
|
||
print(f"{BOLD}╚══════════════════════════════════════════════════════════════╝{RESET}\n")
|
||
|
||
print(f"{BOLD}Soft-Delete Funktionalität:{RESET}\n")
|
||
|
||
if can_deactivate:
|
||
print_success("✓✓✓ DEAKTIVIERUNG funktioniert:")
|
||
print_success(" • gueltigBis kann via PUT auf Vergangenheit gesetzt werden")
|
||
print_success(" • Beispiel: gueltigBis = '2024-12-31T23:59:59'")
|
||
print_success(" • Adresse bleibt in GET sichtbar (Client-Filter nötig)")
|
||
else:
|
||
print_error("✗ DEAKTIVIERUNG funktioniert NICHT")
|
||
|
||
print()
|
||
|
||
if can_reactivate:
|
||
print_success("✓✓✓ REAKTIVIERUNG funktioniert:")
|
||
print_success(" • gueltigBis kann via PUT auf Zukunft gesetzt werden")
|
||
print_success(" • Beispiel: gueltigBis = '2099-12-31T23:59:59'")
|
||
print_success(" • Adresse ist damit wieder aktiv")
|
||
else:
|
||
print_error("✗ REAKTIVIERUNG funktioniert NICHT")
|
||
|
||
print()
|
||
|
||
if remove_method:
|
||
if remove_method in ["omit", "none", "empty"]:
|
||
print_success(f"✓ gueltigBis entfernen funktioniert (Methode: {remove_method})")
|
||
if remove_method == "omit":
|
||
print_success(" • Weglassen des Feldes entfernt gueltigBis")
|
||
elif remove_method == "none":
|
||
print_success(" • Setzen auf None/null entfernt gueltigBis")
|
||
elif remove_method == "empty":
|
||
print_success(" • Setzen auf '' entfernt gueltigBis")
|
||
else:
|
||
print_warning("⚠ gueltigBis kann NICHT komplett entfernt werden")
|
||
print_info(" • Lösung: Setze auf 2099-12-31 für 'unbegrenzt aktiv'")
|
||
|
||
print(f"\n{BOLD}Empfohlener Workflow:{RESET}\n")
|
||
print_info("1. AKTIV (Standard):")
|
||
print_info(" → gueltigBis = '2099-12-31T23:59:59' oder None")
|
||
print_info(" → In EspoCRM: isActive = True")
|
||
print()
|
||
print_info("2. DEAKTIVIEREN (Soft-Delete):")
|
||
print_info(" → PUT mit gueltigBis = '2024-01-01T00:00:00' (Vergangenheit)")
|
||
print_info(" → In EspoCRM: isActive = False")
|
||
print()
|
||
print_info("3. REAKTIVIEREN:")
|
||
print_info(" → PUT mit gueltigBis = '2099-12-31T23:59:59' (Zukunft)")
|
||
print_info(" → In EspoCRM: isActive = True")
|
||
print()
|
||
print_info("4. SYNC LOGIC:")
|
||
print_info(" → GET /Adressen → filter wo gueltigBis > heute")
|
||
print_info(" → Sync nur aktive Adressen nach EspoCRM")
|
||
print_info(" → Update isActive basierend auf gueltigBis")
|
||
|
||
print(f"\n{YELLOW}⚠️ ACHTUNG: Test-Adresse 'TEST-SOFTDELETE' sollte bereinigt werden.{RESET}\n")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|