docs(so-api): [31188f42] Diagnosis of email sending blockade & cleanup
This commit is contained in:
80
SUPEROFFICE_MEETING_PREP.md
Normal file
80
SUPEROFFICE_MEETING_PREP.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Vorbereitung für SuperOffice Meeting: API E-Mail-Versand (Shipment)
|
||||||
|
**Datum:** 05.03.2026
|
||||||
|
**Teilnehmer:** Christian Godelmann (RoboPlanet), Frau Grilnberger / Herr Eberhard (SuperOffice)
|
||||||
|
**Thema:** Fehlende Berechtigung für automatisierten E-Mail-Versand via API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Ausgangslage & Ziel
|
||||||
|
Wir haben die **GTM-Engine** (KI-basierte Anreicherung) erfolgreich an SuperOffice (Tenant `Cust26720`) angebunden.
|
||||||
|
* ✅ **Lese-Zugriff:** Funktioniert (Webhooks, Person/Contact lesen).
|
||||||
|
* ✅ **Schreib-Zugriff (Daten):** Funktioniert (UDFs schreiben, Sales erstellen).
|
||||||
|
* ❌ **E-Mail-Versand (Shipment):** Schlägt fehl (500 Internal Server Error).
|
||||||
|
|
||||||
|
**Ziel:** Der API-User (Client ID `0fd8...`) soll automatisierte Erstkontakt-E-Mails ("Shipments") im Namen des zugewiesenen Vertriebsmitarbeiters versenden können.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Diagnose-Ergebnisse (Live-Test vom 05.03.2026)
|
||||||
|
|
||||||
|
Wir haben eine Tiefenanalyse mit Admin-Rechten durchgeführt. Hier sind die harten Fakten:
|
||||||
|
|
||||||
|
### A. Identitätsproblem (Ursache)
|
||||||
|
Der API-User hat keine verknüpfte "Person" im System.
|
||||||
|
* **Request:** `GET /api/v1/Associate/Me`
|
||||||
|
* **Response:** `500 Internal Server Error`
|
||||||
|
* **Bedeutung:** Das System weiß nicht, "wer" der API-User ist. Ohne Identität können keine personalisierten Aktionen (wie E-Mail-Versand) ausgeführt werden.
|
||||||
|
|
||||||
|
### B. E-Mail-Versand (Blockade)
|
||||||
|
Trotz aktiver Marketing-Lizenz (ShipmentTypes sind abrufbar) scheitert der Versand.
|
||||||
|
* **Test:** Erstellung eines `Shipment` Objekts (Type: Email).
|
||||||
|
* **Response:** `500 Internal Server Error`
|
||||||
|
* **Log-Auszug:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Error": true,
|
||||||
|
"ErrorType": "NullReferenceException",
|
||||||
|
"Message": "Object reference not set to an instance of an object.",
|
||||||
|
"Source": "SoDataBase"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*(Hinweis: Der Fehler deutet darauf hin, dass interne E-Mail-Einstellungen (SMTP/Exchange) für den user `null` sind.)*
|
||||||
|
|
||||||
|
### C. Schreibrechte (Erfolgreich)
|
||||||
|
Zum Vergleich haben wir andere Objekte erstellt, um generelle API-Probleme auszuschließen.
|
||||||
|
* **Sale (Verkauf):** ✅ Erfolgreich erstellt (ID 342539).
|
||||||
|
* **Appointment (Termin):** ✅ Erfolgreich erstellt (ID 993350).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Unsere Bitte an SuperOffice (Lösungsvorschlag)
|
||||||
|
|
||||||
|
Um das Problem zu lösen, benötigen wir folgende Anpassungen für den API-User (oder einen dedizierten System-User):
|
||||||
|
|
||||||
|
1. **Personalisierung:** Verknüpfung des API-Users mit einer **Personalkarte** im SuperOffice (damit `Associate/Me` funktioniert).
|
||||||
|
2. **Rollen:** Zuweisung der Rolle **"Mailing Administrator"** (oder vergleichbar), um Shipments zu erstellen.
|
||||||
|
3. **E-Mail-Konfiguration:** Hinterlegung der **E-Mail-Einstellungen** (idealerweise "Send As" Recht für die Account-Manager, damit die Mails im Namen von Herrn Godelmann/Herrn X rausgehen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Aktueller Workaround (Plan B)
|
||||||
|
|
||||||
|
Bis zur Lösung nutzen wir folgenden Workaround:
|
||||||
|
1. Die KI generiert den E-Mail-Text.
|
||||||
|
2. Anstatt die Mail zu senden, erstellen wir einen **Termin** (`Appointment`) in der Vergangenheit.
|
||||||
|
3. Der E-Mail-Text wird in die **Beschreibung** des Termins geschrieben.
|
||||||
|
4. Der Vertriebler muss den Text manuell kopieren und versenden.
|
||||||
|
|
||||||
|
*Dies ist funktionstüchtig (getestet), aber keine Dauerlösung.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Technische Details (für Support)
|
||||||
|
|
||||||
|
* **Tenant:** `Cust26720` (Online3)
|
||||||
|
* **Client ID:** `0fd8...` (Name: "Gemini Connector Production")
|
||||||
|
* **Authentication:** System User Flow (Refresh Token)
|
||||||
|
* **Endpoint:** `/api/v1/Shipment`
|
||||||
|
|
||||||
|
---
|
||||||
|
*Ende des Protokolls*
|
||||||
40
cleanup_test_data.py
Normal file
40
cleanup_test_data.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Ensure we use the correct config and client
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'connector-superoffice')))
|
||||||
|
|
||||||
|
from superoffice_client import SuperOfficeClient
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
print("🧹 Cleaning up Test Data...")
|
||||||
|
client = SuperOfficeClient()
|
||||||
|
|
||||||
|
if not client.access_token:
|
||||||
|
print("❌ Auth failed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Objects to delete (Reverse order of dependency)
|
||||||
|
to_delete = [
|
||||||
|
("Sale", 342539),
|
||||||
|
("Appointment", 993350),
|
||||||
|
("Appointment", 993347),
|
||||||
|
("Person", 193092),
|
||||||
|
("Contact", 171185) # Attempting to delete the company too
|
||||||
|
]
|
||||||
|
|
||||||
|
for entity_type, entity_id in to_delete:
|
||||||
|
print(f"🗑️ Deleting {entity_type} {entity_id}...")
|
||||||
|
try:
|
||||||
|
# SuperOffice DELETE usually returns 204 No Content
|
||||||
|
# Our client returns None on success if response body is empty, or the JSON if not.
|
||||||
|
# We need to catch exceptions if it fails.
|
||||||
|
resp = client._delete(f"{entity_type}/{entity_id}")
|
||||||
|
print(f"✅ Deleted {entity_type} {entity_id}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Failed to delete {entity_type} {entity_id}: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cleanup()
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Explicitly load .env from the project root.
|
||||||
|
# CRITICAL: override=True ensures we read from the .env file even if
|
||||||
|
# stale env vars are present in the shell process.
|
||||||
|
dotenv_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '.env'))
|
||||||
|
if os.path.exists(dotenv_path):
|
||||||
|
load_dotenv(dotenv_path=dotenv_path, override=True)
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
@@ -99,16 +99,18 @@ def main():
|
|||||||
|
|
||||||
print("\n--- 1. Finding a Target Contact (Company) ---")
|
print("\n--- 1. Finding a Target Contact (Company) ---")
|
||||||
# Search for "Test" to avoid hitting the Wackler parent company (ID 3)
|
# Search for "Test" to avoid hitting the Wackler parent company (ID 3)
|
||||||
contacts = client._get("Contact?$top=1&$filter=name contains 'Test'&$select=ContactId,Name")
|
# contacts = client._get("Contact?$top=1&$filter=name contains 'Test'&$select=ContactId,Name")
|
||||||
|
|
||||||
# Fallback if no test company found, but warn user
|
# Fallback if no test company found, but warn user
|
||||||
if not contacts or 'value' not in contacts or len(contacts['value']) == 0:
|
# if not contacts or 'value' not in contacts or len(contacts['value']) == 0:
|
||||||
print("⚠️ No company with 'Test' found. Please create a test company first.")
|
# print("⚠️ No company with 'Test' found. Please create a test company first.")
|
||||||
return
|
# return
|
||||||
|
|
||||||
target_contact = contacts['value'][0]
|
# target_contact = contacts['value'][0]
|
||||||
contact_id = target_contact.get('contactId') or target_contact.get('ContactId')
|
# contact_id = target_contact.get('contactId') or target_contact.get('ContactId')
|
||||||
contact_name = target_contact.get('name') or target_contact.get('Name')
|
# contact_name = target_contact.get('name') or target_contact.get('Name')
|
||||||
|
contact_id = 171185
|
||||||
|
contact_name = "Bremer Abenteuerland"
|
||||||
|
|
||||||
# SAFEGUARD: Do not post to Wackler Service Group (ID 3)
|
# SAFEGUARD: Do not post to Wackler Service Group (ID 3)
|
||||||
if int(contact_id) == 3:
|
if int(contact_id) == 3:
|
||||||
@@ -156,18 +158,16 @@ def main():
|
|||||||
print(f"Payload Preview: {json.dumps(sale_payload, indent=2)}")
|
print(f"Payload Preview: {json.dumps(sale_payload, indent=2)}")
|
||||||
|
|
||||||
# Uncomment to actually run creation
|
# Uncomment to actually run creation
|
||||||
# new_sale = client._post("Sale", sale_payload)
|
new_sale = client._post("Sale", sale_payload)
|
||||||
|
|
||||||
# print("\n--- ✅ SUCCESS: Sale Created! ---")
|
print("\n--- ✅ SUCCESS: Sale Created! ---")
|
||||||
# sale_id = new_sale.get('SaleId')
|
sale_id = new_sale.get('SaleId')
|
||||||
# sale_number = new_sale.get('SaleNumber')
|
sale_number = new_sale.get('SaleNumber')
|
||||||
# print(f"Sale ID: {sale_id}")
|
print(f"Sale ID: {sale_id}")
|
||||||
# print(f"Sale Number: {sale_number}")
|
print(f"Sale Number: {sale_number}")
|
||||||
|
|
||||||
# sale_link = f"https://{auth.env}.superoffice.com/{auth.cust_id}/default.aspx?sale?sale_id={sale_id}"
|
sale_link = f"https://{auth.env}.superoffice.com/{auth.cust_id}/default.aspx?sale?sale_id={sale_id}"
|
||||||
# print(f"Direct Link: {sale_link}")
|
print(f"Direct Link: {sale_link}")
|
||||||
|
|
||||||
print("\nNOTE: Creation is commented out to prevent accidental data creation. Review payload above.")
|
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
logger.error(f"❌ API Error: {e}")
|
logger.error(f"❌ API Error: {e}")
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from config import settings
|
|||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger("simulation-e2e")
|
logger = logging.getLogger("simulation-e2e")
|
||||||
|
|
||||||
def simulate_sendout(contact_id: int, person_id: int):
|
def simulate_sendout(contact_id: int):
|
||||||
print(f"🚀 Starting E2E Sendout Simulation for Contact {contact_id}, Person {person_id}...")
|
print(f"🚀 Starting Appointment Creation Test for Contact {contact_id}...")
|
||||||
|
|
||||||
# 1. Initialize SuperOffice Client
|
# 1. Initialize SuperOffice Client
|
||||||
so_client = SuperOfficeClient()
|
so_client = SuperOfficeClient()
|
||||||
@@ -18,73 +18,27 @@ def simulate_sendout(contact_id: int, person_id: int):
|
|||||||
print("❌ Auth failed. Check .env")
|
print("❌ Auth failed. Check .env")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 2. Get Data from Company Explorer
|
# 2. Create Appointment (The "Sendout Proof")
|
||||||
# We simulate what the worker would do
|
|
||||||
print(f"📡 Requesting provisioning from Company Explorer...")
|
|
||||||
ce_url = f"{settings.COMPANY_EXPLORER_URL}/api/provision/superoffice-contact"
|
|
||||||
ce_req = {
|
|
||||||
"so_contact_id": contact_id,
|
|
||||||
"so_person_id": person_id,
|
|
||||||
"crm_name": "RoboPlanet GmbH",
|
|
||||||
"crm_website": "www.roboplanet.de",
|
|
||||||
"job_title": "Geschäftsführer" # Explicit job title for persona mapping
|
|
||||||
}
|
|
||||||
ce_auth = (os.getenv("API_USER", "admin"), os.getenv("API_PASSWORD", "gemini"))
|
|
||||||
|
|
||||||
try:
|
|
||||||
resp = requests.post(ce_url, json=ce_req, auth=ce_auth)
|
|
||||||
resp.raise_for_status()
|
|
||||||
provisioning_data = resp.json()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ CE API failed: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"✅ Received Data: {json.dumps(provisioning_data, indent=2)}")
|
|
||||||
|
|
||||||
if provisioning_data.get("status") == "processing":
|
|
||||||
print("⏳ CE is still processing. Please wait 1-2 minutes and try again.")
|
|
||||||
return
|
|
||||||
|
|
||||||
texts = provisioning_data.get("texts", {})
|
|
||||||
if not texts.get("subject"):
|
|
||||||
print("⚠️ No marketing texts found for this combination (Vertical x Persona).")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 3. Write Texts to SuperOffice UDFs
|
|
||||||
print("✍️ Writing marketing texts to SuperOffice UDFs...")
|
|
||||||
udf_payload = {
|
|
||||||
settings.UDF_SUBJECT: texts["subject"],
|
|
||||||
settings.UDF_INTRO: texts["intro"],
|
|
||||||
settings.UDF_SOCIAL_PROOF: texts["social_proof"]
|
|
||||||
}
|
|
||||||
|
|
||||||
success = so_client.update_entity_udfs(person_id, "Person", udf_payload)
|
|
||||||
if success:
|
|
||||||
print("✅ UDFs updated successfully.")
|
|
||||||
else:
|
|
||||||
print("❌ Failed to update UDFs.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 4. Create Appointment (The "Sendout Proof")
|
|
||||||
print("📅 Creating Appointment as sendout proof...")
|
print("📅 Creating Appointment as sendout proof...")
|
||||||
app_subject = f"[SIMULATION] Mail Sent: {texts['subject']}"
|
app_subject = f"[DIAGNOSTIC TEST] Can we create an activity?"
|
||||||
app_desc = f"Content Simulation:\n\n{texts['intro']}\n\n{texts['social_proof']}"
|
app_desc = f"This is a test to see if the API user can create appointments."
|
||||||
|
|
||||||
appointment = so_client.create_appointment(
|
appointment = so_client.create_appointment(
|
||||||
subject=app_subject,
|
subject=app_subject,
|
||||||
description=app_desc,
|
description=app_desc,
|
||||||
contact_id=contact_id,
|
contact_id=contact_id,
|
||||||
person_id=person_id
|
person_id=None # Explicitly test without a person
|
||||||
)
|
)
|
||||||
|
|
||||||
if appointment:
|
if appointment:
|
||||||
print(f"✅ Simulation Complete! Appointment ID: {appointment.get('AppointmentId')}")
|
# The key might be 'appointmentId' (lowercase 'a')
|
||||||
|
appt_id = appointment.get('appointmentId') or appointment.get('AppointmentId')
|
||||||
|
print(f"✅ SUCCESS! Appointment Created with ID: {appt_id}")
|
||||||
print(f"🔗 Check SuperOffice for Contact {contact_id} and look at the activities.")
|
print(f"🔗 Check SuperOffice for Contact {contact_id} and look at the activities.")
|
||||||
else:
|
else:
|
||||||
print("❌ Failed to create appointment.")
|
print("❌ Failed to create appointment.")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Using the IDs we know exist from previous tests/status
|
# Using the IDs we know exist from previous tests/status
|
||||||
TEST_CONTACT_ID = 2
|
TEST_CONTACT_ID = 171185
|
||||||
TEST_PERSON_ID = 2 # Usually same or linked
|
simulate_sendout(TEST_CONTACT_ID)
|
||||||
simulate_sendout(TEST_CONTACT_ID, TEST_PERSON_ID)
|
|
||||||
@@ -93,6 +93,8 @@ class SuperOfficeClient:
|
|||||||
resp = requests.put(url, headers=self.headers, json=payload)
|
resp = requests.put(url, headers=self.headers, json=payload)
|
||||||
elif method == "PATCH":
|
elif method == "PATCH":
|
||||||
resp = requests.patch(url, headers=self.headers, json=payload)
|
resp = requests.patch(url, headers=self.headers, json=payload)
|
||||||
|
elif method == "DELETE":
|
||||||
|
resp = requests.delete(url, headers=self.headers)
|
||||||
|
|
||||||
# 401 Handling
|
# 401 Handling
|
||||||
if resp.status_code == 401 and retry:
|
if resp.status_code == 401 and retry:
|
||||||
@@ -108,6 +110,9 @@ class SuperOfficeClient:
|
|||||||
logger.error("❌ Token Refresh failed during retry.")
|
logger.error("❌ Token Refresh failed during retry.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if resp.status_code == 204:
|
||||||
|
return True
|
||||||
|
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return resp.json()
|
return resp.json()
|
||||||
|
|
||||||
@@ -130,6 +135,9 @@ class SuperOfficeClient:
|
|||||||
def _post(self, endpoint, payload):
|
def _post(self, endpoint, payload):
|
||||||
return self._request_with_retry("POST", endpoint, payload)
|
return self._request_with_retry("POST", endpoint, payload)
|
||||||
|
|
||||||
|
def _delete(self, endpoint):
|
||||||
|
return self._request_with_retry("DELETE", endpoint)
|
||||||
|
|
||||||
# --- Convenience Wrappers ---
|
# --- Convenience Wrappers ---
|
||||||
|
|
||||||
def get_person(self, person_id, select: list = None):
|
def get_person(self, person_id, select: list = None):
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ def create_test_company():
|
|||||||
return
|
return
|
||||||
# Check if company already exists
|
# Check if company already exists
|
||||||
existing = client.search(f"Contact?$select=contactId,name&$filter=name eq '{company_name}'")
|
existing = client.search(f"Contact?$select=contactId,name&$filter=name eq '{company_name}'")
|
||||||
|
print(f"DEBUG: Raw search response: {existing}")
|
||||||
if existing:
|
if existing:
|
||||||
contact_id = existing[0]['ContactId']
|
contact_id = existing[0]['contactId']
|
||||||
print(f"⚠️ Company '{company_name}' already exists with ContactId: {contact_id}.")
|
print(f"⚠️ Company '{company_name}' already exists with ContactId: {contact_id}.")
|
||||||
print("Skipping creation.")
|
print("Skipping creation.")
|
||||||
return contact_id
|
return contact_id
|
||||||
@@ -42,8 +43,8 @@ def create_test_company():
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_company = client._post("Contact", payload)
|
new_company = client._post("Contact", payload)
|
||||||
if new_company and "ContactId" in new_company:
|
if new_company and "contactId" in new_company:
|
||||||
contact_id = new_company["ContactId"]
|
contact_id = new_company["contactId"]
|
||||||
print(f"✅ SUCCESS! Created company '{company_name}' with ContactId: {contact_id}")
|
print(f"✅ SUCCESS! Created company '{company_name}' with ContactId: {contact_id}")
|
||||||
return contact_id
|
return contact_id
|
||||||
else:
|
else:
|
||||||
|
|||||||
44
connector-superoffice/tools/create_person_test.py
Normal file
44
connector-superoffice/tools/create_person_test.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
from superoffice_client import SuperOfficeClient
|
||||||
|
|
||||||
|
def create_test_person(contact_id: int):
|
||||||
|
"""
|
||||||
|
Creates a new person for a given contact ID.
|
||||||
|
"""
|
||||||
|
print(f"🚀 Attempting to create a person for Contact ID: {contact_id}")
|
||||||
|
try:
|
||||||
|
client = SuperOfficeClient()
|
||||||
|
if not client.access_token:
|
||||||
|
print("❌ Authentication failed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"Contact": {"ContactId": contact_id},
|
||||||
|
"Firstname": "Test",
|
||||||
|
"Lastname": "Person",
|
||||||
|
"Emails": [
|
||||||
|
{
|
||||||
|
"Value": "floke.com@gmail.com",
|
||||||
|
"Description": "Work Email"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
new_person = client._post("Person", payload)
|
||||||
|
if new_person and "PersonId" in new_person:
|
||||||
|
person_id = new_person["PersonId"]
|
||||||
|
print(f"✅ SUCCESS! Created person with PersonId: {person_id}")
|
||||||
|
return person_id
|
||||||
|
else:
|
||||||
|
print(f"❌ Failed to create person. Response: {new_person}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
TEST_CONTACT_ID = 171185
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
TEST_CONTACT_ID = int(sys.argv[1])
|
||||||
|
create_test_person(TEST_CONTACT_ID)
|
||||||
72
final_mailing_test.py
Normal file
72
final_mailing_test.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Ensure we use the correct config and client from the connector-superoffice subdir
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'connector-superoffice')))
|
||||||
|
|
||||||
|
from superoffice_client import SuperOfficeClient
|
||||||
|
|
||||||
|
def run_final_test():
|
||||||
|
print("🚀 Starting Final Mailing Test for floke.com@gmail.com...")
|
||||||
|
|
||||||
|
# 1. Initialize Client
|
||||||
|
client = SuperOfficeClient()
|
||||||
|
if not client.access_token:
|
||||||
|
print("❌ Auth failed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. Use Target Contact (Bremer Abenteuerland)
|
||||||
|
contact_id = 171185
|
||||||
|
print(f"✅ Using Contact ID: {contact_id}")
|
||||||
|
|
||||||
|
# 3. Use Created Person
|
||||||
|
person_id = 193092
|
||||||
|
print(f"✅ Using Person ID: {person_id} (floke.com@gmail.com)")
|
||||||
|
|
||||||
|
# 4. Attempt Shipment (Mailing)
|
||||||
|
print("📤 Attempting to create Shipment (the direct email send)...")
|
||||||
|
shipment_payload = {
|
||||||
|
"Name": "Gemini Diagnostics: Test Shipment",
|
||||||
|
"Subject": "Hallo aus der Gemini GTM Engine",
|
||||||
|
"Body": "Dies ist ein Testversuch für den direkten E-Mail-Versand via SuperOffice API.",
|
||||||
|
"DocumentTemplateId": 157, # Outgoing Email (ID 157 is confirmed from previous runs as typical)
|
||||||
|
"ShipmentType": "Email",
|
||||||
|
"AssociateId": 528, # API User RCGO
|
||||||
|
"ContactId": contact_id,
|
||||||
|
"PersonId": person_id,
|
||||||
|
"Status": "Ready"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
shipment_resp = client._post("Shipment", shipment_payload)
|
||||||
|
if shipment_resp:
|
||||||
|
print("✅ UNEXPECTED SUCCESS: Shipment created!")
|
||||||
|
print(json.dumps(shipment_resp, indent=2))
|
||||||
|
else:
|
||||||
|
print("❌ Shipment creation returned empty response.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ EXPECTED FAILURE: Shipment creation failed as predicted.")
|
||||||
|
print(f"Error details: {e}")
|
||||||
|
|
||||||
|
# 5. Fallback: Create Appointment as "Proof of Work"
|
||||||
|
print("\n📅 Running Workaround: Creating Appointment instead...")
|
||||||
|
appt_resp = client.create_appointment(
|
||||||
|
subject="KI: E-Mail Testversuch an floke.com@gmail.com",
|
||||||
|
description="Hier würde der E-Mail-Text stehen, der aufgrund technischer Blockaden (Mailing-Modul/Identität) nicht direkt versendet werden konnte.",
|
||||||
|
contact_id=contact_id,
|
||||||
|
person_id=person_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if appt_resp:
|
||||||
|
appt_id = appt_resp.get("appointmentId") or appt_resp.get("AppointmentId")
|
||||||
|
print(f"✅ Workaround Successful: Appointment ID: {appt_id}")
|
||||||
|
print(f"🔗 Link: https://online3.superoffice.com/Cust26720/default.aspx?appointment_id={appt_id}")
|
||||||
|
else:
|
||||||
|
print("❌ Workaround (Appointment) failed too.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_final_test()
|
||||||
Reference in New Issue
Block a user