[2ff88f42] 1. Umfassende Entitäten-Erstellung: Wir haben erfolgreich Methoden implementiert, um die Kern-SuperOffice-Entitäten per API zu erstellen:

1. Umfassende Entitäten-Erstellung: Wir haben erfolgreich Methoden implementiert, um die Kern-SuperOffice-Entitäten per API zu erstellen:
       * Firmen (`Contact`)
       * Personen (`Person`)
       * Verkäufe (`Sale`) (entspricht D365 Opportunity)
       * Projekte (`Project`) (entspricht D365 Campaign), inklusive der Verknüpfung von Personen als Projektmitglieder.
   2. Robuste UDF-Aktualisierung: Wir haben eine generische und fehlertolerante Methode (update_entity_udfs) implementiert, die benutzerdefinierte Felder (UDFs) für sowohl Contact- als
      auch Person-Entitäten aktualisieren kann. Diese Methode ruft zuerst das bestehende Objekt ab, um die Konsistenz zu gewährleisten.
   3. UDF-ID-Discovery: Durch eine iterative Inspektionsmethode haben wir erfolgreich alle internen SuperOffice-IDs für die Listenwerte deines MA Status-Feldes (Ready_to_Send, Sent_Week1,
      Sent_Week2, Bounced, Soft_Denied, Interested, Out_of_Office, Unsubscribed) ermittelt und im Connector hinterlegt.
   4. Vollständiger End-to-End Test-Workflow: Unser main.py-Skript demonstriert nun einen kompletten Ablauf, der alle diese Schritte von der Erstellung bis zur UDF-Aktualisierung umfasst.
   5. Architekturplan für Marketing Automation: Wir haben einen detaillierten "Butler-Service"-Architekturplan für die Marketing-Automatisierung entworfen, der den Connector für die
      Textgenerierung und SuperOffice für den Versand und das Status-Management nutzt.
   6. Identifikation des E-Mail-Blockers: Wir haben festgestellt, dass das Erstellen von E-Mail-Aktivitäten per API in deiner aktuellen SuperOffice-Entwicklungsumgebung aufgrund fehlender
      Lizenzierung/Konfiguration des E-Mail-Moduls blockiert ist (500 Internal Server Error).
This commit is contained in:
2026-02-10 11:06:32 +00:00
parent 23559cff08
commit 312ce8aa1d
6 changed files with 328 additions and 71 deletions

View File

@@ -12,21 +12,30 @@ class SuperOfficeClient:
self.auth_handler = auth_handler
self.session = requests.Session()
# Mapping for UDF fields (These are typical technical names, but might need adjustment)
self.udf_mapping = {
"robotics_potential": "x_robotics_potential",
"industry": "x_ai_industry",
"summary": "x_ai_summary",
"last_update": "x_ai_last_update",
"status": "x_ai_status"
# Mapping for UDF fields for Contact entity
self.udf_contact_mapping = {
"ai_challenge_sentence": "SuperOffice:1",
"ai_sentence_timestamp": "SuperOffice:2",
"ai_sentence_source_hash": "SuperOffice:3",
"ai_last_outreach_date": "SuperOffice:4"
}
# Mapping for UDF fields for Person entity
self.udf_person_mapping = {
"ai_email_draft": "SuperOffice:1", # NOTE: This is currently a Date field in SO and needs to be changed to Text (Long/Memo)
"ma_status": "SuperOffice:2"
}
# Mapping for list values (Explorer -> SO ID)
self.potential_id_map = {
"High": 1,
"Medium": 2,
"Low": 3,
"None": 4
# Mapping for MA Status list values (Text Label -> SO ID)
self.ma_status_id_map = {
"Ready_to_Send": 11,
"Sent_Week1": 12,
"Sent_Week2": 13,
"Bounced": 14,
"Soft_Denied": 15,
"Interested": 16,
"Out_of_Office": 17,
"Unsubscribed": 18
}
def _get_headers(self):
@@ -220,3 +229,176 @@ class SuperOfficeClient:
if hasattr(e, 'response') and e.response is not None:
logger.error(f"Response: {e.response.text}")
return None
def update_entity_udfs(self, entity_id, entity_type, udf_data: dict):
"""Updates user-defined fields for a given entity (Contact or Person)."""
if entity_type not in ["Contact", "Person"]:
logger.error(f"Invalid entity_type: {entity_type}. Must be 'Contact' or 'Person'.")
return None
# 1. Retrieve the existing entity to ensure all required fields are present in the PUT payload
get_url = self._get_url(f"v1/{entity_type}/{entity_id}")
try:
get_resp = self.session.get(get_url, headers=self._get_headers())
get_resp.raise_for_status()
existing_entity = get_resp.json()
logger.info(f"Successfully retrieved existing {entity_type} ID {entity_id}.")
except Exception as e:
logger.error(f"Error retrieving existing {entity_type} ID {entity_id}: {e}")
if hasattr(e, 'response') and e.response is not None:
logger.error(f"Response: {e.response.text}")
return None
# Use the existing entity data as the base for the PUT payload
payload = existing_entity
if "UserDefinedFields" not in payload:
payload["UserDefinedFields"] = {}
# Select the correct mapping based on entity type
udf_mapping = self.udf_contact_mapping if entity_type == "Contact" else self.udf_person_mapping
for key, value in udf_data.items():
prog_id = udf_mapping.get(key)
if prog_id:
if key == "ma_status" and entity_type == "Person":
# For MA Status, we need to send the internal ID directly as an integer
internal_id = self.ma_status_id_map.get(value)
if internal_id:
payload["UserDefinedFields"][prog_id] = internal_id
else:
logger.warning(f"Unknown MA Status value '{value}'. Skipping update for {key}.")
else:
# For other UDFs, send the value directly
payload["UserDefinedFields"][prog_id] = value
else:
logger.warning(f"Unknown UDF key for {entity_type}: {key}. Skipping.")
if not payload["UserDefinedFields"]:
logger.info(f"No valid UDF data to update for {entity_type} ID {entity_id}.")
return None
# 2. Send the updated entity (including all original fields + modified UDFs) via PUT
put_url = self._get_url(f"v1/{entity_type}/{entity_id}")
try:
logger.info(f"Attempting to update UDFs for {entity_type} ID {entity_id} with: {payload['UserDefinedFields']}")
resp = self.session.put(put_url, headers=self._get_headers(), json=payload)
resp.raise_for_status()
updated_entity = resp.json()
logger.info(f"Successfully updated UDFs for {entity_type} ID {entity_id}.")
return updated_entity
except Exception as e:
logger.error(f"Error updating UDFs for {entity_type} ID {entity_id}: {e}")
if hasattr(e, 'response') and e.response is not None:
logger.error(f"Response: {e.response.text}")
return None
# NOTE: The create_email_activity method is currently blocked due to SuperOffice environment limitations.
# Attempting to create an Email Activity via API results in a 500 Internal Server Error,
# likely because the email module is not licensed or configured in the SOD environment.
# This method is temporarily commented out.
#
# def create_email_activity(self, person_id, contact_id, subject, body):
# """Creates an Email Activity linked to a person and contact."""
# url = self._get_url("v1/Activity")
#
# payload = {
# "Type": { # Assuming ID 2 for "Email" ActivityType
# "Id": 2
# },
# "Title": subject,
# "Details": body,
# "Person": {
# "PersonId": person_id
# },
# "Contact": {
# "ContactId": contact_id
# }
# }
#
# try:
# logger.info(f"Attempting to create Email Activity with subject '{subject}' for Person ID {person_id} and Contact ID {contact_id}")
# resp = self.session.post(url, headers=self._get_headers(), json=payload)
# resp.raise_for_status()
# created_activity = resp.json()
# logger.info(f"Successfully created Email Activity: '{created_activity.get('Title')}' (ID: {created_activity.get('ActivityId')})")
# return created_activity
# except Exception as e:
# logger.error(f"Error creating Email Activity: {e}")
# if hasattr(e, 'response') and e.response is not None:
# logger.error(f"Response: {e.response.text}")
# return None