[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:
@@ -1 +1 @@
|
|||||||
{"task_id": "2ff88f42-8544-8000-8314-c9013414d1d0", "token": "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", "session_start_time": "2026-02-10T07:58:14.713674"}
|
{"task_id": "2ff88f42-8544-8000-8314-c9013414d1d0", "token": "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", "session_start_time": "2026-02-10T11:06:21.850683"}
|
||||||
@@ -13,7 +13,7 @@ Der **Proof of Concept (POC)** ist erfolgreich abgeschlossen.
|
|||||||
- **Personen (Persons):** Erfolgreich angelegt und mit Firmen verknüpft (inkl. E-Mail).
|
- **Personen (Persons):** Erfolgreich angelegt und mit Firmen verknüpft (inkl. E-Mail).
|
||||||
- **Verkauf (Sale):** Erfolgreich angelegt.
|
- **Verkauf (Sale):** Erfolgreich angelegt.
|
||||||
- **Projekt (Project):** Erfolgreich angelegt und Person als Mitglied hinzugefügt.
|
- **Projekt (Project):** Erfolgreich angelegt und Person als Mitglied hinzugefügt.
|
||||||
- **UDF-Felder:** Vorbereitet im Code, erfordert finalen Abgleich der `ProgId` nach Anlage im Admin-Panel.
|
- **UDF-Felder:** Erfolgreich aktualisiert für `Contact` und `Person`.
|
||||||
|
|
||||||
## 2. Einrichtung & Installation
|
## 2. Einrichtung & Installation
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ cd connector-superoffice
|
|||||||
- `create_person(...)`: Erstellt einen Ansprechpartner (verknüpft mit ContactId).
|
- `create_person(...)`: Erstellt einen Ansprechpartner (verknüpft mit ContactId).
|
||||||
- `create_sale(...)`: Erstellt einen Verkauf/Opportunity.
|
- `create_sale(...)`: Erstellt einen Verkauf/Opportunity.
|
||||||
- `create_project(...)`: Erstellt ein Projekt und fügt optional eine Person als Mitglied hinzu.
|
- `create_project(...)`: Erstellt ein Projekt und fügt optional eine Person als Mitglied hinzu.
|
||||||
- `update_udfs(...)`: (Vorbereitet) Aktualisiert KI-Felder.
|
- `update_entity_udfs(...)`: Aktualisiert UDFs für Kontakte und Personen.
|
||||||
|
|
||||||
### Felder entdecken
|
### Felder entdecken
|
||||||
Listet alle verfügbaren Felder (inkl. UDFs) auf, um die technischen Namen (`ProgId`) für das Mapping zu finden.
|
Listet alle verfügbaren Felder (inkl. UDFs) auf, um die technischen Namen (`ProgId`) für das Mapping zu finden.
|
||||||
@@ -130,4 +130,4 @@ Um diesen Plan umzusetzen, werden die folgenden UDFs in SuperOffice benötigt (A
|
|||||||
2. Am Objekt **`Person` (Ansprechpartner):** Ein großes Textfeld (Memo/Long Text). Vorschlag: `AI_Email_Draft`
|
2. Am Objekt **`Person` (Ansprechpartner):** Ein großes Textfeld (Memo/Long Text). Vorschlag: `AI_Email_Draft`
|
||||||
3. Am Objekt **`Person` (Ansprechpartner):** Ein Listenfeld. Vorschlag: `MA_Status` mit den Werten `Init`, `Ready_to_Craft`, `Ready_to_Send`, `Sent_Week1`, `Sent_Week2`, `Replied`, `Paused`.
|
3. Am Objekt **`Person` (Ansprechpartner):** Ein Listenfeld. Vorschlag: `MA_Status` mit den Werten `Init`, `Ready_to_Craft`, `Ready_to_Send`, `Sent_Week1`, `Sent_Week2`, `Replied`, `Paused`.
|
||||||
|
|
||||||
Sobald diese Felder angelegt sind, können die `ProgId`s in den Connector integriert werden.
|
**Wichtiger Blocker:** Das Erstellen von E-Mail-Aktivitäten per API ist aktuell blockiert, da das E-Mail-Modul in der Zielumgebung (SOD) nicht aktiviert oder konfiguriert zu sein scheint. Dies führt zu einem `500 Internal Server Error` bei API-Aufrufen. Die Implementierung dieser Funktionalität im Connector ist daher bis auf Weiteres ausgesetzt.
|
||||||
@@ -8,63 +8,71 @@ from superoffice_client import SuperOfficeClient
|
|||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def discover_fields():
|
def get_list_items_by_prog_id(client, prog_id, entity_name):
|
||||||
|
"""Fetches and prints list items for a specific ProgId."""
|
||||||
|
logger.info(f"--- Fetching list items for {entity_name} ProgId: {prog_id} ---")
|
||||||
|
|
||||||
|
# The endpoint for user-defined lists is typically generic
|
||||||
|
list_url = client._get_url(f"v1/List/UserDefinedField/{prog_id}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
list_resp = client.session.get(list_url, headers=client._get_headers())
|
||||||
|
list_resp.raise_for_status()
|
||||||
|
list_items = list_resp.json()
|
||||||
|
|
||||||
|
if list_items.get("value"):
|
||||||
|
print(" --- List Items Found ---")
|
||||||
|
for item in list_items["value"]:
|
||||||
|
print(f" ID: {item.get('Id'):<5} | Name: {item.get('Name')}")
|
||||||
|
print(" ------------------------")
|
||||||
|
return {item.get('Name'): item.get('Id') for item in list_items["value"]}
|
||||||
|
else:
|
||||||
|
print(" (No list items found or unexpected response structure)")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as list_e:
|
||||||
|
logger.error(f" Failed to fetch list items for {prog_id}: {list_e}")
|
||||||
|
if hasattr(list_e, 'response') and list_e.response is not None:
|
||||||
|
logger.error(f" List fetch details: {list_e.response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_activity_types(client):
|
||||||
|
logger.info("--- Fetching Activity Types ---")
|
||||||
|
# Common endpoint for activity types
|
||||||
|
activity_type_url = client._get_url("v1/ActivityType") # Trying direct ActivityType endpoint
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = client.session.get(activity_type_url, headers=client._get_headers())
|
||||||
|
resp.raise_for_status()
|
||||||
|
activity_types = resp.json()
|
||||||
|
|
||||||
|
if activity_types:
|
||||||
|
print(" --- Activity Types Found ---")
|
||||||
|
for atype in activity_types:
|
||||||
|
print(f" ID: {atype.get('Id'):<5} | Name: {atype.get('Name')}")
|
||||||
|
print(" ------------------------")
|
||||||
|
return {atype.get('Name'): atype.get('Id') for atype in activity_types}
|
||||||
|
else:
|
||||||
|
print(" (No activity types found or unexpected response structure)")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f" Failed to fetch activity types: {e}")
|
||||||
|
if hasattr(e, 'response') and e.response is not None:
|
||||||
|
logger.error(f" Activity type fetch details: {e.response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def main():
|
||||||
load_dotenv(dotenv_path="../.env")
|
load_dotenv(dotenv_path="../.env")
|
||||||
auth = AuthHandler()
|
auth = AuthHandler()
|
||||||
client = SuperOfficeClient(auth)
|
client = SuperOfficeClient(auth)
|
||||||
|
|
||||||
|
# --- We know the ProgIds, so we query them directly ---
|
||||||
|
# ProgId for the "MA Status" list on the Person entity
|
||||||
|
#person_ma_status_prog_id = "SuperOffice:2" # Keep for future reference
|
||||||
|
#get_list_items_by_prog_id(client, person_ma_status_prog_id, "Person MA Status")
|
||||||
|
|
||||||
try:
|
get_activity_types(client)
|
||||||
logger.info("Fetching general Contact metadata...")
|
|
||||||
general_metadata_url = client._get_url("v1/Metadata/Contact")
|
|
||||||
resp = client.session.get(general_metadata_url, headers=client._get_headers())
|
|
||||||
resp.raise_for_status()
|
|
||||||
general_metadata = resp.json()
|
|
||||||
print("\n--- GENERAL CONTACT METADATA (partial) ---")
|
|
||||||
print(f"DisplayName: {general_metadata.get('DisplayName')}")
|
|
||||||
print(f"HasUserDefinedFields: {general_metadata.get('HasUserDefinedFields')}")
|
|
||||||
print("----------------------------------------")
|
|
||||||
|
|
||||||
logger.info("Attempting to fetch UDF metadata...")
|
|
||||||
# Get metadata for Contact (Company)
|
|
||||||
url = client._get_url("v1/Metadata/Contact/UserDefinedFields")
|
|
||||||
resp = client.session.get(url, headers=client._get_headers())
|
|
||||||
resp.raise_for_status()
|
|
||||||
fields = resp.json()
|
|
||||||
|
|
||||||
print("\n--- AVAILABLE UDF FIELDS ---")
|
|
||||||
for field in fields.get("value", []):
|
|
||||||
label = field.get('FieldLabel')
|
|
||||||
prog_id = field.get('ProgId')
|
|
||||||
field_type = field.get('FieldType')
|
|
||||||
print(f"Label: {label} -> Technical Name: {prog_id} (Type: {field_type})")
|
|
||||||
|
|
||||||
# Check if the field is a list type
|
|
||||||
if field_type in ["List", "DropDown", "UserDefinedList"]: # Common types, might need adjustment
|
|
||||||
logger.info(f" -> Fetching list items for {label} (ProgId: {prog_id})...")
|
|
||||||
# Attempt to get the list items. The actual endpoint might vary.
|
|
||||||
# Assuming a pattern like 'v1/List/UserDefinedField/{ProgId}' or 'v1/UserDefinedFieldList/{ProgId}/Items'
|
|
||||||
list_url = client._get_url(f"v1/List/UserDefinedField/{prog_id}")
|
|
||||||
try:
|
|
||||||
list_resp = client.session.get(list_url, headers=client._get_headers())
|
|
||||||
list_resp.raise_for_status()
|
|
||||||
list_items = list_resp.json()
|
|
||||||
|
|
||||||
if list_items.get("value"):
|
|
||||||
print(" --- List Items ---")
|
|
||||||
for item in list_items["value"]:
|
|
||||||
print(f" ID: {item.get('Id')}, Name: {item.get('Name')}")
|
|
||||||
else:
|
|
||||||
print(" (No list items found or unexpected response structure)")
|
|
||||||
|
|
||||||
except Exception as list_e:
|
|
||||||
logger.warning(f" Failed to fetch list items for {label}: {list_e}")
|
|
||||||
if hasattr(list_e, 'response') and list_e.response is not None:
|
|
||||||
logger.warning(f" List fetch details: {list_e.response.text}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to fetch metadata: {e}")
|
|
||||||
if hasattr(e, 'response') and e.response is not None:
|
|
||||||
print(f"Details: {e.response.text}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
discover_fields()
|
main()
|
||||||
40
connector-superoffice/inspect_person.py
Normal file
40
connector-superoffice/inspect_person.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from auth_handler import AuthHandler
|
||||||
|
from superoffice_client import SuperOfficeClient
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def inspect_person(person_id):
|
||||||
|
load_dotenv(dotenv_path="../.env")
|
||||||
|
auth = AuthHandler()
|
||||||
|
client = SuperOfficeClient(auth)
|
||||||
|
|
||||||
|
logger.info(f"Fetching Person with ID {person_id} to inspect structure...")
|
||||||
|
|
||||||
|
url = client._get_url(f"v1/Person/{person_id}")
|
||||||
|
try:
|
||||||
|
resp = client.session.get(url, headers=client._get_headers())
|
||||||
|
resp.raise_for_status()
|
||||||
|
person_data = resp.json()
|
||||||
|
|
||||||
|
print(f"\n--- PERSON STRUCTURE (ID: {person_id}) ---")
|
||||||
|
print(json.dumps(person_data, indent=2))
|
||||||
|
print("\n--- USER DEFINED FIELDS FOR THIS PERSON ---")
|
||||||
|
if person_data.get("UserDefinedFields"):
|
||||||
|
print(json.dumps(person_data["UserDefinedFields"], indent=2))
|
||||||
|
else:
|
||||||
|
print("(No UserDefinedFields found)")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to fetch person data: {e}")
|
||||||
|
if hasattr(e, 'response') and e.response is not None:
|
||||||
|
print(f"Details: {e.response.text}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Use the specific person ID provided by the user
|
||||||
|
target_person_id = 9
|
||||||
|
inspect_person(target_person_id)
|
||||||
@@ -107,6 +107,33 @@ def main():
|
|||||||
logger.info("SUCCESS: Project created and person added!")
|
logger.info("SUCCESS: Project created and person added!")
|
||||||
logger.info(f"Project Name: {project.get('Name')}")
|
logger.info(f"Project Name: {project.get('Name')}")
|
||||||
logger.info(f"Project ID: {project.get('ProjectId')}")
|
logger.info(f"Project ID: {project.get('ProjectId')}")
|
||||||
|
|
||||||
|
# 6. Update Contact UDFs
|
||||||
|
logger.info(f"Step 6: Updating Contact UDFs for Contact ID {target_contact_id}...")
|
||||||
|
contact_udf_data = {
|
||||||
|
"ai_challenge_sentence": "The company faces challenges in automating its logistics processes due to complex infrastructure.",
|
||||||
|
"ai_sentence_timestamp": "2026-02-10T12:00:00Z", # Using a fixed timestamp for demo
|
||||||
|
"ai_sentence_source_hash": "website_v1_hash_abc",
|
||||||
|
"ai_last_outreach_date": "2026-02-10T12:00:00Z" # Using a fixed timestamp for demo
|
||||||
|
}
|
||||||
|
updated_contact = client.update_entity_udfs(target_contact_id, "Contact", contact_udf_data)
|
||||||
|
if updated_contact:
|
||||||
|
logger.info("SUCCESS: Contact UDFs updated!")
|
||||||
|
else:
|
||||||
|
logger.error("Failed to update Contact UDFs.")
|
||||||
|
|
||||||
|
# 7. Update Person UDFs
|
||||||
|
logger.info(f"Step 7: Updating Person UDFs for Person ID {person_id}...")
|
||||||
|
person_udf_data = {
|
||||||
|
"ai_email_draft": "This is a short draft for the personalized email.", # Placeholder, as it's currently a short text field
|
||||||
|
"ma_status": "Ready_to_Send"
|
||||||
|
}
|
||||||
|
updated_person = client.update_entity_udfs(person_id, "Person", person_udf_data)
|
||||||
|
if updated_person:
|
||||||
|
logger.info("SUCCESS: Person UDFs updated!")
|
||||||
|
else:
|
||||||
|
logger.error("Failed to update Person UDFs.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error("Failed to create project.")
|
logger.error("Failed to create project.")
|
||||||
|
|
||||||
|
|||||||
@@ -12,21 +12,30 @@ class SuperOfficeClient:
|
|||||||
self.auth_handler = auth_handler
|
self.auth_handler = auth_handler
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
|
|
||||||
# Mapping for UDF fields (These are typical technical names, but might need adjustment)
|
# Mapping for UDF fields for Contact entity
|
||||||
self.udf_mapping = {
|
self.udf_contact_mapping = {
|
||||||
"robotics_potential": "x_robotics_potential",
|
"ai_challenge_sentence": "SuperOffice:1",
|
||||||
"industry": "x_ai_industry",
|
"ai_sentence_timestamp": "SuperOffice:2",
|
||||||
"summary": "x_ai_summary",
|
"ai_sentence_source_hash": "SuperOffice:3",
|
||||||
"last_update": "x_ai_last_update",
|
"ai_last_outreach_date": "SuperOffice:4"
|
||||||
"status": "x_ai_status"
|
}
|
||||||
|
|
||||||
|
# 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)
|
# Mapping for MA Status list values (Text Label -> SO ID)
|
||||||
self.potential_id_map = {
|
self.ma_status_id_map = {
|
||||||
"High": 1,
|
"Ready_to_Send": 11,
|
||||||
"Medium": 2,
|
"Sent_Week1": 12,
|
||||||
"Low": 3,
|
"Sent_Week2": 13,
|
||||||
"None": 4
|
"Bounced": 14,
|
||||||
|
"Soft_Denied": 15,
|
||||||
|
"Interested": 16,
|
||||||
|
"Out_of_Office": 17,
|
||||||
|
"Unsubscribed": 18
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_headers(self):
|
def _get_headers(self):
|
||||||
@@ -220,3 +229,176 @@ class SuperOfficeClient:
|
|||||||
if hasattr(e, 'response') and e.response is not None:
|
if hasattr(e, 'response') and e.response is not None:
|
||||||
logger.error(f"Response: {e.response.text}")
|
logger.error(f"Response: {e.response.text}")
|
||||||
return None
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user