#!/usr/bin/env python3 """ Helper-Script zum Vergleichen der Beteiligten-Strukturen zwischen Advoware und EspoCRM. Usage: python scripts/compare_beteiligte.py [advoware_id] Examples: # Vergleiche EspoCRM Beteiligten (automatische Suche in Advoware) python scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc # Vergleiche mit spezifischer Advoware ID python scripts/compare_beteiligte.py 64a3f2b8c9e1234567890abc 12345 """ import sys import asyncio import json import os from pathlib import Path # Add bitbylaw directory to path for imports bitbylaw_dir = Path(__file__).parent.parent sys.path.insert(0, str(bitbylaw_dir)) from services.espocrm import EspoCRMAPI from services.advoware import AdvowareAPI from config import Config class SimpleContext: """Simple context for logging""" class Logger: def info(self, msg): print(f"[INFO] {msg}") def error(self, msg): print(f"[ERROR] {msg}") def debug(self, msg): print(f"[DEBUG] {msg}") def warning(self, msg): print(f"[WARNING] {msg}") def __init__(self): self.logger = self.Logger() async def fetch_from_espocrm(entity_id: str): """Fetch Beteiligter from EspoCRM""" print("\n" + "="*80) print("ESPOCRM - Fetching Beteiligter") print("="*80) context = SimpleContext() espo = EspoCRMAPI(context=context) try: # Try different entity types that might contain Beteiligte entity_types = ['CBeteiligte', 'Beteiligte', 'Contact', 'Account', 'Lead', 'CVmhErstgespraech', 'CVmhBeteiligte'] for entity_type in entity_types: try: print(f"\nTrying entity type: {entity_type}") result = await espo.get_entity(entity_type, entity_id) print(f"\n✓ Success! Found in {entity_type}") print(f"\nEntity Structure:") print("-" * 80) print(json.dumps(result, indent=2, ensure_ascii=False)) return result except Exception as e: print(f" ✗ Not found in {entity_type}: {e}") continue print("\n✗ Entity not found in any known entity type") return None except Exception as e: print(f"\n✗ Error fetching from EspoCRM: {e}") return None async def fetch_from_advoware(advoware_id: str = None, search_name: str = None): """Fetch Beteiligter from Advoware""" print("\n" + "="*80) print("ADVOWARE - Fetching Beteiligter") print("="*80) context = SimpleContext() advo = AdvowareAPI(context=context) try: # Try to fetch by ID if provided if advoware_id: print(f"\nFetching by ID: {advoware_id}") # Try correct Advoware endpoint endpoints = [ f'/api/v1/advonet/Beteiligte/{advoware_id}', ] for endpoint in endpoints: try: print(f" Trying endpoint: {endpoint}") result = await advo.api_call(endpoint, method='GET') if result: # Advoware gibt oft Listen zurück, nehme erstes Element if isinstance(result, list) and len(result) > 0: result = result[0] print(f"\n✓ Success! Found at {endpoint}") print(f"\nEntity Structure:") print("-" * 80) print(json.dumps(result, indent=2, ensure_ascii=False)) return result except Exception as e: print(f" ✗ Not found at {endpoint}: {e}") continue # Try to search by name if EspoCRM data available if search_name: print(f"\nSearching by name: {search_name}") search_endpoints = [ '/api/v1/advonet/Beteiligte', ] for endpoint in search_endpoints: try: print(f" Trying endpoint: {endpoint}") result = await advo.api_call( endpoint, method='GET', params={'search': search_name, 'limit': 5} ) if result and (isinstance(result, list) and len(result) > 0 or isinstance(result, dict) and result.get('data')): print(f"\n✓ Found {len(result) if isinstance(result, list) else len(result.get('data', []))} results") print(f"\nSearch Results:") print("-" * 80) print(json.dumps(result, indent=2, ensure_ascii=False)) return result except Exception as e: print(f" ✗ Search failed at {endpoint}: {e}") continue print("\n✗ Entity not found in Advoware") return None except Exception as e: print(f"\n✗ Error fetching from Advoware: {e}") import traceback traceback.print_exc() return None async def compare_structures(espo_data: dict, advo_data: dict): """Compare field structures between EspoCRM and Advoware""" print("\n" + "="*80) print("STRUCTURE COMPARISON") print("="*80) if not espo_data or not advo_data: print("\n⚠ Cannot compare - missing data from one or both systems") return # Extract fields espo_fields = set(espo_data.keys()) if isinstance(espo_data, dict) else set() # Handle Advoware data structure (might be nested) if isinstance(advo_data, dict): if 'data' in advo_data: advo_data = advo_data['data'] if isinstance(advo_data, list) and len(advo_data) > 0: advo_data = advo_data[0] advo_fields = set(advo_data.keys()) if isinstance(advo_data, dict) else set() print(f"\nEspoCRM Fields ({len(espo_fields)}):") print("-" * 40) for field in sorted(espo_fields): value = espo_data.get(field) value_type = type(value).__name__ print(f" {field:<30} ({value_type})") print(f"\nAdvoware Fields ({len(advo_fields)}):") print("-" * 40) for field in sorted(advo_fields): value = advo_data.get(field) value_type = type(value).__name__ print(f" {field:<30} ({value_type})") # Find common fields (potential mappings) common = espo_fields & advo_fields espo_only = espo_fields - advo_fields advo_only = advo_fields - espo_fields print(f"\nCommon Fields ({len(common)}):") print("-" * 40) for field in sorted(common): espo_val = espo_data.get(field) advo_val = advo_data.get(field) match = "✓" if espo_val == advo_val else "✗" print(f" {match} {field}") if espo_val != advo_val: print(f" EspoCRM: {espo_val}") print(f" Advoware: {advo_val}") print(f"\nEspoCRM Only ({len(espo_only)}):") print("-" * 40) for field in sorted(espo_only): print(f" {field}") print(f"\nAdvoware Only ({len(advo_only)}):") print("-" * 40) for field in sorted(advo_only): print(f" {field}") # Suggest potential mappings based on field names print(f"\nPotential Field Mappings:") print("-" * 40) mapping_suggestions = [] # Common name patterns name_patterns = [ ('name', 'name'), ('firstName', 'first_name'), ('lastName', 'last_name'), ('email', 'email'), ('emailAddress', 'email'), ('phone', 'phone'), ('phoneNumber', 'phone_number'), ('address', 'address'), ('street', 'street'), ('city', 'city'), ('postalCode', 'postal_code'), ('zipCode', 'postal_code'), ('country', 'country'), ] for espo_field, advo_field in name_patterns: if espo_field in espo_fields and advo_field in advo_fields: mapping_suggestions.append((espo_field, advo_field)) print(f" {espo_field:<30} → {advo_field}") return { 'espo_fields': list(espo_fields), 'advo_fields': list(advo_fields), 'common': list(common), 'espo_only': list(espo_only), 'advo_only': list(advo_only), 'suggested_mappings': mapping_suggestions } async def main(): """Main function""" if len(sys.argv) < 2: print(__doc__) sys.exit(1) espocrm_id = sys.argv[1] advoware_id = sys.argv[2] if len(sys.argv) > 2 else None print("\n" + "="*80) print("BETEILIGTE STRUCTURE COMPARISON TOOL") print("="*80) print(f"\nEspoCRM Entity ID: {espocrm_id}") if advoware_id: print(f"Advoware ID: {advoware_id}") # Check environment variables print("\nEnvironment Check:") print("-" * 40) print(f"ESPOCRM_API_BASE_URL: {Config.ESPOCRM_API_BASE_URL}") print(f"ESPOCRM_API_KEY: {'✓ Set' if Config.ESPOCRM_API_KEY else '✗ Missing'}") print(f"ADVOWARE_API_BASE_URL: {Config.ADVOWARE_API_BASE_URL}") print(f"ADVOWARE_API_KEY: {'✓ Set' if Config.ADVOWARE_API_KEY else '✗ Missing'}") # Fetch from EspoCRM espo_data = await fetch_from_espocrm(espocrm_id) # Extract name for Advoware search search_name = None if espo_data: search_name = ( espo_data.get('name') or f"{espo_data.get('firstName', '')} {espo_data.get('lastName', '')}".strip() or None ) # Fetch from Advoware advo_data = await fetch_from_advoware(advoware_id, search_name) # Compare structures if espo_data or advo_data: comparison = await compare_structures(espo_data, advo_data) # Save comparison to file output_file = Path(__file__).parent / 'beteiligte_comparison_result.json' with open(output_file, 'w', encoding='utf-8') as f: json.dump({ 'espocrm_data': espo_data, 'advoware_data': advo_data, 'comparison': comparison }, f, indent=2, ensure_ascii=False) print(f"\n\n{'='*80}") print(f"Comparison saved to: {output_file}") print(f"{'='*80}\n") else: print("\n⚠ No data available for comparison") if __name__ == '__main__': asyncio.run(main())