diff --git a/connector-superoffice/README.md b/connector-superoffice/README.md index 767127f2..aa29bb5c 100644 --- a/connector-superoffice/README.md +++ b/connector-superoffice/README.md @@ -87,6 +87,20 @@ Ursprünglich war ein "Server-to-Server" (S2S) Flow mittels RSA-Zertifikaten gep * **Vorteil:** Einfacher einzurichten, Token ist "ewig" gültig (solange er genutzt wird). * **Nachteil:** Muss einmalig manuell via Browser generiert werden (bereits erledigt). +### Lessons Learned (Feb 2026): Zugriff auf Zusatz-Tabellen (Extra Tables) +Der Versuch, auf eine neu erstellte Zusatz-Tabelle (`ExtraTableId 1` / `y_marketing_copy`) zuzugreifen, hat eine tiefgreifende Blockade aufgedeckt, die über einfache Benutzerrechte hinausgeht. + +1. **Direkter API-Zugriff (Table & Archive) ist blockiert:** + * Sowohl der `GET /api/v1/Table/Extra1` als auch der `GET /api/v1/Archive/dynamic?providerName=extra_table_1` Endpunkt scheiterten konsistent mit `403 Forbidden` oder `405 Method Not Allowed`, selbst mit vollen Administratorrechten für den API-Benutzer. + * Die spezifische Fehlermeldung `"Archive provider DotSyntaxProvider has no registered entities..."` deutet auf ein Konfigurations- oder Lizenzproblem im Mandanten hin, das den API-Zugriff auf Zusatz-Tabellen generell verhindert. + +2. **CRMScript als Workaround:** + * Um die Blockade zu umgehen, wurde ein CRMScript-Proxy (`GTM_Proxy`) implementiert, der über `POST /api/v1/Scripts/GTM_Proxy/Execute` erreichbar ist. + * **Erfolg:** Der Endpunkt des Proxys selbst ist erreichbar (kein `403`-Fehler). + * **Blocker:** Der Proxy meldet bei der Ausführung einen `500 Internal Server Error`. Dies bedeutet, dass das Skript selbst bei der internen Ausführung auf einen Fehler stößt – höchstwahrscheinlich, weil es ebenfalls von der zugrundeliegenden Berechtigungsblockade betroffen ist. + +**Schlussfolgerung:** Der einzig gangbare Weg, um mit Zusatz-Tabellen zu interagieren, ist die **Fehlerbehebung und korrekte Konfiguration des `GTM_Proxy` CRMScripts** direkt in SuperOffice. Der direkte API-Weg ist für diesen Mandanten derzeit nicht verfügbar. + ## 5. Nächste Schritte ### Detaillierter Plan: Marketing Automation (SuperOffice-zentrisch) diff --git a/connector-superoffice/explorer_client.py b/connector-superoffice/explorer_client.py deleted file mode 100644 index 45f6e0e2..00000000 --- a/connector-superoffice/explorer_client.py +++ /dev/null @@ -1,76 +0,0 @@ -import requests -import logging -from config import Config -from logging_config import setup_logging - -# Use the centralized logging configuration -logger = setup_logging(__name__) - -class CompanyExplorerClient: - """ - Client for the Company Explorer API (Intelligence Engine). - Handles authentication and data synchronization. - """ - def __init__(self, base_url=None, api_user=None, api_password=None): - # Prefer Config values, allow overrides - self.base_url = base_url or Config.CE_API_URL - self.api_user = api_user or Config.CE_API_USER - self.api_password = api_password or Config.CE_API_PASSWORD - - self.session = requests.Session() - - # Setup Basic Auth - if self.api_user and self.api_password: - self.session.auth = (self.api_user, self.api_password) - - def _get_url(self, path): - """Constructs the full URL.""" - base = self.base_url.rstrip('/') - p = path.lstrip('/') - return f"{base}/{p}" - - def check_health(self): - """Checks if the Company Explorer API is reachable.""" - # Assuming a standard health check endpoint or root endpoint - # Trying root first as it's often a dashboard or redirect - url = self._get_url("/") - try: - resp = self.session.get(url, timeout=5) - # We accept 200 (OK) or 401 (Unauthorized - meaning it's there but needs auth) - # Since we provide auth, 200 is expected. - if resp.status_code in [200, 401, 403]: - logger.info(f"Company Explorer is reachable at {self.base_url} (Status: {resp.status_code})") - return True - else: - logger.warning(f"Company Explorer returned unexpected status: {resp.status_code}") - return False - except Exception as e: - logger.error(f"Company Explorer health check failed: {e}") - return False - - def import_company(self, company_data): - """ - Imports a single company into the Company Explorer. - - Args: - company_data (dict): Dictionary containing company details. - Must include 'name' and 'external_crm_id'. - """ - # Endpoint based on plan: POST /api/companies (assuming standard REST) - # Or bulk endpoint if specified. Let's try single create first. - url = self._get_url("api/companies") - - try: - logger.info(f"Pushing company to Explorer: {company_data.get('name')} (CRM ID: {company_data.get('external_crm_id')})") - resp = self.session.post(url, json=company_data, timeout=10) - - if resp.status_code in [200, 201]: - logger.info("Successfully imported company into Explorer.") - return resp.json() - else: - logger.error(f"Failed to import company. Status: {resp.status_code}, Response: {resp.text}") - return None - - except Exception as e: - logger.error(f"Error importing company: {e}") - return None \ No newline at end of file diff --git a/connector-superoffice/sync_engine.py b/connector-superoffice/sync_engine.py deleted file mode 100644 index c8e26a85..00000000 --- a/connector-superoffice/sync_engine.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -This module contains the core logic for the synchronization process. -- Fetches data from Company Explorer. -- Fetches data from SuperOffice. -- Performs deduplication checks. -- Creates or updates companies and contacts in SuperOffice. -""" - -class SyncEngine: - def __init__(self, superoffice_client, explorer_client): - self.so_client = superoffice_client - self.ex_client = explorer_client - - def run_sync(self): - """ - Executes a full synchronization run. - """ - # 1. Get companies from Explorer - # 2. For each company, check if it exists in SuperOffice - # 3. If not, create it - # 4. If yes, update it (or skip) - pass