feat: Add EspoCRM and Advoware integration for Beteiligte comparison
- Implemented `compare_beteiligte.py` script for comparing Beteiligte structures between EspoCRM and Advoware. - Created `beteiligte_comparison_result.json` to store comparison results. - Developed `EspoCRMAPI` service for handling API interactions with EspoCRM. - Added comprehensive documentation for the EspoCRM API service. - Included error handling and logging for API operations. - Enhanced entity management with CRUD operations and search capabilities.
This commit is contained in:
323
bitbylaw/scripts/compare_beteiligte.py
Executable file
323
bitbylaw/scripts/compare_beteiligte.py
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Helper-Script zum Vergleichen der Beteiligten-Strukturen zwischen Advoware und EspoCRM.
|
||||
|
||||
Usage:
|
||||
python scripts/compare_beteiligte.py <entity_id_espocrm> [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())
|
||||
Reference in New Issue
Block a user