diff --git a/README.md b/README.md index d34251f4..6f12165f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,26 @@ KI-basierte Bearbeitung von EspoCRM: Struktur und Funktionsweise +## Inhaltsverzeichnis +1. [Überblick](#überblick) +2. [Relevante Dateipfade und Verzeichnisstruktur](#1-relevante-dateipfade-und-verzeichnisstruktur) +3. [Workflow-Verwaltung](#workflow-verwaltung) +4. [Auslösen von Änderungen und Rebuild-Prozess](#auslösen-von-änderungen-und-rebuild-prozess) + +## Überblick + Unter der Annahme, dass die KI direkten Zugriff auf das Dateisystem des EspoCRM-Servers hat (z. B. via SSH, API-Integration oder lokales Scripting), kann sie EspoCRM modifizieren, indem sie JSON-basierte Metadata-Dateien bearbeitet. EspoCRM ist modular aufgebaut und speichert Konfigurationen für Entitäten, Felder, Beziehungen, Views und Layouts in diesen Dateien. Änderungen erfolgen idealerweise im custom/-Verzeichnis, um Core-Dateien nicht zu überschreiben und Upgrades zu erleichtern. Die KI würde Dateien lesen, parsen (z. B. als JSON), modifizieren und speichern – gefolgt von einem Rebuild-Prozess, um die Änderungen anzuwenden. EspoCRM basiert auf PHP (Backend) und Backbone.js (Frontend), mit einer rekursiven Merging-Mechanik: Custom-Dateien überschreiben oder erweitern Core-Definitionen. Keine integrierte KI-Schnittstelle existiert, aber mit Dateizugriff kann die KI automatisierte Anpassungen vornehmen, z. B. Felder hinzufügen, Views anpassen oder Beziehungen definieren. Nachfolgend detaillierte Infos basierend auf der offiziellen Dokumentation und Community-Beiträgen. + +### Custom Scripts & Tools + +**Workflow-Verwaltung:** +- `custom/scripts/workflow_manager.php` - Zentrale Schnittstelle für Workflow-Verwaltung (Import/Export/List/Delete) +- `custom/scripts/check_and_rebuild.sh` - Validierungs- und Rebuild-Script + +**Workflow-Definitionen:** +- `custom/workflows/*.json` - Versionierte Workflow-Definitionen (Simple & BPM) +- `custom/workflows/README.md` - Workflow-Format-Dokumentation 1. Relevante Dateipfade und Verzeichnisstruktur Alle relevanten Dateien liegen im JSON-Format und werden in einer hierarchischen Struktur organisiert. Die KI sollte immer im custom/Espo/Custom/Resources/metadata/-Ordner arbeiten, da Änderungen hier persistent sind und nicht bei Updates verloren gehen. Core-Dateien (z. B. unter application/Espo/Resources/metadata/) dienen als Referenz, aber sollten nicht modifiziert werden. @@ -148,6 +166,177 @@ JSON Datenbank-Effekte: Neue Felder/Links in entityDefs erzeugen Tabellen/Spalten (bei Rebuild). Frontend-Effekte: clientDefs/Layouts ändern UI sofort nach Rebuild (z. B. neue Panels, Views). Fehlerquellen: Ungültiges JSON oder falsche Typen können zu Fehlern führen (z. B. fehlende required-Felder). + +## Workflow-Verwaltung + +EspoCRM bietet zwei Arten von Workflows für Automatisierung: + +### Simple Workflows (Regel-basiert) +- Trigger-basierte Workflows für einfache Automationen +- **Trigger-Typen:** + - `afterRecordSaved` - Nach Erstellen oder Aktualisieren + - `afterRecordCreated` - Nur nach Erstellen + - `afterRecordUpdated` - Nur nach Aktualisieren + - `manual` - Manuell ausgeführt + - `scheduled` - Zeitgesteuert +- **Bedingungen:** + - Vergleiche: `equals`, `notEquals`, `greaterThan`, `lessThan`, `contains`, `isEmpty` + - Änderungen: `changed`, `notChanged`, `wasEqual` +- **Aktionen:** + - `sendEmail` - E-Mail versenden + - `createEntity` - Record erstellen + - `updateEntity` - Record aktualisieren + - `relateTo` / `unrelateFrom` - Verknüpfungen + - `createNotification` - Benachrichtigung + +### BPM Flowcharts (Komplex) +- Visuelle Workflows mit BPMN 2.0-Standard +- Start-Events: Signal, Conditional, Timer +- Gateways (Exclusive, Inclusive, Parallel), Tasks, End-Events +- Für komplexe, mehrstufige Geschäftsprozesse + +### Workflow-Dateien +Workflow-Definitionen werden im Ordner `custom/workflows/` als JSON abgelegt: +- **`custom/workflows/*.json`** - Workflow-Definitionen (Simple oder BPM) +- **`custom/workflows/README.md`** - Dokumentation zu Formaten und Verwendung + +### Workflow Manager Script +**Zentrale Schnittstelle:** `custom/scripts/workflow_manager.php` + +Dieses Script ermöglicht die Verwaltung aller Workflows (Simple und BPM) über die Kommandozeile: + +#### Verfügbare Aktionen + +**1. Alle Workflows auflisten** +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php list +``` +Zeigt beide Workflow-Typen (BPM Flowcharts und Simple Workflows) mit Status, ID, Name und Entity. + +**2. Workflow-Details anzeigen** +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php read +``` +Gibt alle Details eines Workflows als JSON aus. + +**3. Workflow importieren** +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php import /var/www/html/custom/workflows/workflow.json +``` +Importiert einen Workflow aus einer JSON-Datei. Unterstützt sowohl Simple Workflows als auch BPM Flowcharts. Erstellt automatisch eine neue ID. + +**4. Workflow exportieren** +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php export /var/www/html/custom/workflows/exported.json +``` +Exportiert einen Workflow in eine JSON-Datei für Backup oder Migration. + +**5. Workflow löschen** +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php delete +``` +Löscht einen Workflow (mit Bestätigung). Funktioniert für beide Workflow-Typen. + +### JSON-Formate + +#### Simple Workflow Format +```json +{ + "type": "simple", + "name": "workflow-name", + "entity_type": "EntityName", + "trigger_type": "afterRecordSaved", + "is_active": true, + "description": "Beschreibung der Funktion", + "conditions_all": [ + { + "comparison": "equals", + "fieldToCompare": "fieldName", + "value": "expectedValue", + "subjectType": "value" + } + ], + "conditions_any": [], + "conditions_formula": null, + "actions": [ + { + "type": "sendEmail", + "from": "specifiedEmailAddress", + "fromEmailAddress": "sender@example.com", + "to": "targetEntity", + "emailTemplateId": null, + "doNotStore": false + } + ] +} +``` + +**Wichtige Felder:** +- `comparison` - Vergleichsoperator (siehe Bedingungen oben) +- `fieldToCompare` - Feldname für Bedingung +- `subjectType` - Typ des Vergleichswerts (`value`, `field`, etc.) +- `from` / `to` - E-Mail-Empfänger (`targetEntity`, `specifiedEmailAddress`, `system`) + +#### BPM Flowchart Format +```json +{ + "type": "bpm", + "name": "flowchart-name", + "target_type": "EntityName", + "is_active": true, + "description": "Beschreibung", + "data": { + "list": [ + { + "type": "eventStartSignal", + "id": "start1", + "signalName": "@signalName" + } + ] + }, + "elements_data_hash": {}, + "event_start_all_id_list": [] +} +``` + +### Best Practices + +1. **Versionierung:** Workflows als JSON-Dateien im `custom/workflows/` Verzeichnis versionieren +2. **Naming Convention:** Beschreibende Namen mit Präfix (z.B. `vmh-erstberatung-abschliessen.json`) +3. **Testen:** Nach Import immer über Admin-Interface testen +4. **Backup:** Regelmäßig Export für wichtige Workflows durchführen +5. **Dokumentation:** Description-Feld aussagekräftig füllen + +### Beispiel-Workflows + +**`custom/workflows/vmh-erstberatung-abschliessen.json`** +- Sendet E-Mail bei Status-Wechsel zu "Warte auf Mandatierung" +- Trigger: afterRecordSaved +- Bedingungen: Status = "Warte auf Mandatierung" UND Status hat sich geändert +- Aktion: E-Mail an targetEntity senden + +**Anwendungsbeispiel:** +```bash +# Alle Workflows exportieren (Backup) +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php list | grep ID | \ + awk '{print $3}' | while read id; do + docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php export "${id%,}" \ + "/var/www/html/custom/workflows/backup-${id%,}.json" +done + +# Workflow aus Datei (re-)importieren +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php import \ + /var/www/html/custom/workflows/vmh-erstberatung-abschliessen.json +``` + +### Workflow-Entwicklung mit KI + +Für KI-gestützte Workflow-Erstellung: +1. Workflow-Definition im `custom/workflows/` Verzeichnis als JSON ablegen +2. Mit `import` Befehl in EspoCRM einspielen +3. Im Admin-Interface testen und bei Bedarf anpassen +4. Mit `export` Befehl aktualisierten Workflow sichern +5. JSON-Datei im Repository committen Rebuild auslösen: Manuell: Administration > Clear Cache & Rebuild (löscht Caches und merged Metadata neu). Programmatisch (für KI): Die KI kann den Cache-Ordner löschen (data/cache/) oder ein PHP-Skript ausführen, das den Rebuild triggert (z. B. via EspoCRMs CLI: php command.php Rebuild). Keine direkte API, aber machbar mit Dateizugriff (z. B. exec("php rebuild.php")). diff --git a/custom/CUSTOM_DIRECTORY.md b/custom/CUSTOM_DIRECTORY.md new file mode 100644 index 00000000..a16ba5e8 --- /dev/null +++ b/custom/CUSTOM_DIRECTORY.md @@ -0,0 +1,122 @@ +# EspoCRM Custom Directory - Übersicht + +## Verzeichnisstruktur + +``` +custom/ +├── Espo/ +│ ├── Custom/ # Custom Entitäten, Services, Controller etc. +│ └── Modules/ # Custom Module +├── scripts/ # Custom PHP Scripts für Wartung/Verwaltung +│ ├── check_and_rebuild.sh +│ └── workflow_manager.php +└── workflows/ # Workflow-Definitionen als JSON + ├── README.md + ├── vmh-erstberatung-abschliessen.json + └── vmh-erstberatung-abschliessen-backup.json +``` + +## Zweck der Verzeichnisse + +### custom/Espo/Custom/ +Hauptverzeichnis für EspoCRM-Customizations: +- `Resources/metadata/` - Entity-, Field-, Client-Definitionen +- `Resources/layouts/` - UI-Layout-Definitionen +- `Resources/i18n/` - Übersetzungen +- `Classes/` - Custom PHP-Klassen +- `Controllers/` - Custom Controller +- `Services/` - Custom Services +- `Repositories/` - Custom Repositories + +### custom/Espo/Modules/ +Zusätzliche Module (z.B. von Extensions): +- Jedes Modul hat eigene `Resources/metadata/` Struktur +- Module sind gekapselt und wiederverwendbar + +### custom/scripts/ +**PHP Scripts für Wartung und Verwaltung:** + +#### workflow_manager.php +Zentrale Schnittstelle für Workflow-Verwaltung: +- **list** - Alle Workflows (BPM + Simple) auflisten +- **read** - Workflow-Details anzeigen +- **import** - Workflow aus JSON importieren +- **export** - Workflow nach JSON exportieren +- **delete** - Workflow löschen + +**Verwendung:** +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php [args] +``` + +#### check_and_rebuild.sh +Validierung und Rebuild: +- JSON-Syntax-Prüfung +- EspoCRM Rebuild +- Cache-Bereinigung + +### custom/workflows/ +**Workflow-Definitionen als JSON:** +- Versionskontrolle für Workflows +- Import/Export über workflow_manager.php +- Sowohl Simple Workflows als auch BPM Flowcharts +- Format-Dokumentation in `README.md` + +**Workflow-Typen:** +1. **Simple Workflows** - Regel-basierte Automationen + - Trigger: afterRecordSaved, afterRecordCreated, scheduled, manual + - Bedingungen: Feld-Vergleiche (equals, changed, contains, etc.) + - Aktionen: sendEmail, createEntity, updateEntity, etc. + +2. **BPM Flowcharts** - Komplexe BPMN 2.0-Workflows + - Visuelle Designer im Admin-Interface + - Start-Events, Gateways, Tasks, End-Events + - Für mehrstufige Geschäftsprozesse + +## Best Practices + +### Workflow-Entwicklung +1. Workflow als JSON in `custom/workflows/` erstellen +2. Mit `workflow_manager.php import` einspielen +3. Im Admin-Interface testen +4. Bei Änderungen: Export → Anpassung → Import + +### Versionskontrolle +- Alle JSON-Dateien in Git committen +- Beschreibende Dateinamen (z.B. `vmh--.json`) +- Regelmäßig Backups per `export` erstellen + +### Änderungen anwenden +Nach Metadata-Änderungen immer: +```bash +docker exec espocrm php /var/www/html/command.php rebuild +# oder +./custom/scripts/check_and_rebuild.sh +``` + +## Nützliche Befehle + +```bash +# Alle Workflows auflisten +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php list + +# Workflow importieren +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php import \ + /var/www/html/custom/workflows/my-workflow.json + +# Workflow exportieren (Backup) +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php export \ + /var/www/html/custom/workflows/backup-workflow.json + +# System rebuilden +docker exec espocrm php /var/www/html/command.php rebuild + +# Cache löschen +docker exec espocrm rm -rf /var/www/html/data/cache/* +``` + +## Dokumentation + +- **Hauptdokumentation:** `/README.md` - Vollständige KI-Dokumentation für EspoCRM +- **Workflow-Formate:** `/custom/workflows/README.md` - JSON-Format-Spezifikationen +- **EspoCRM Docs:** https://docs.espocrm.com diff --git a/custom/Espo/Custom/Resources/i18n/de_DE/CVmhErstgespraech.json b/custom/Espo/Custom/Resources/i18n/de_DE/CVmhErstgespraech.json index 94aef0a5..b6448c05 100644 --- a/custom/Espo/Custom/Resources/i18n/de_DE/CVmhErstgespraech.json +++ b/custom/Espo/Custom/Resources/i18n/de_DE/CVmhErstgespraech.json @@ -59,7 +59,6 @@ "beendigungstatbestand": "Beendigungstatbestand", "contact": "Kontakt", "nchsterAnruf": "Nächster Anruf", - "runWorkflow": "Workflow ausführen", "dokumentesvmherstgespraech": "Dokumente" }, "links": { diff --git a/custom/Espo/Custom/Resources/i18n/en_US/CVmhErstgespraech.json b/custom/Espo/Custom/Resources/i18n/en_US/CVmhErstgespraech.json index 07ad992a..3300bac0 100644 --- a/custom/Espo/Custom/Resources/i18n/en_US/CVmhErstgespraech.json +++ b/custom/Espo/Custom/Resources/i18n/en_US/CVmhErstgespraech.json @@ -60,7 +60,6 @@ "contact": "Contact", "nchsterAnruf": "Next Call", "dokumentesvmherstgespraech": "Documents", - "runWorkflow": "Run Workflow", "testArray": "Test Array" }, "links": { diff --git a/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/defaultSidePanel.json b/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/defaultSidePanel.json index 7ea4f2b5..56643cc9 100644 --- a/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/defaultSidePanel.json +++ b/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/defaultSidePanel.json @@ -1,7 +1,4 @@ [ - { - "name": "runWorkflow" - }, { "name": ":assignedUser" }, diff --git a/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/detail.json b/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/detail.json index 3e555308..b1ba6437 100644 --- a/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/detail.json +++ b/custom/Espo/Custom/Resources/layouts/CVmhErstgespraech/detail.json @@ -7,6 +7,9 @@ }, { "name": "nchsterAnruf" + }, + { + "name": "runWorkflow" } ] ], diff --git a/custom/Espo/Custom/Resources/metadata/entityDefs/CVmhErstgespraech.json b/custom/Espo/Custom/Resources/metadata/entityDefs/CVmhErstgespraech.json index 16ec1bd8..b3a1332c 100644 --- a/custom/Espo/Custom/Resources/metadata/entityDefs/CVmhErstgespraech.json +++ b/custom/Espo/Custom/Resources/metadata/entityDefs/CVmhErstgespraech.json @@ -204,7 +204,7 @@ "required": true, "optionsReference": "CVmhMietverhltnis.kndigungsgrundWohnraum", "default": "Mietrückstand", - "style": {}, + "style": [], "maxLength": 100, "isCustom": true }, @@ -416,21 +416,6 @@ "minuteStep": 5, "tooltip": true, "isCustom": true - }, - "runWorkflow": { - "type": "link-button", - "mode": "runEspoWorkflow", - "popupHeight": 800, - "popupWidth": 600, - "style": "primary", - "buttonSize": "btn-md", - "confirmationDialog": true, - "confirmationText": "Soll der Vorgang abgeschlossen und die Ersteinschätzung versandt werden?", - "title": "runWorkflow Title", - "isCustom": true, - "default": null, - "buttonLabel": "Erstberatung versenden", - "readOnly": true } }, "links": { diff --git a/custom/scripts/workflow_manager.php b/custom/scripts/workflow_manager.php index 92a0e09a..84321dd9 100644 --- a/custom/scripts/workflow_manager.php +++ b/custom/scripts/workflow_manager.php @@ -1,8 +1,17 @@ [options] - * Actions: list, read , delete , edit , execute , test + * + * Actions: + * list - List all workflows (BPM and Simple) + * read - Read workflow details + * delete - Delete a workflow + * import - Import workflow from JSON file + * export - Export workflow to JSON file */ $config = require '/var/www/html/data/config-internal.php'; @@ -22,69 +31,196 @@ function connectDB() { } function listWorkflows($pdo) { - $stmt = $pdo->query("SELECT id, name FROM bpmn_flowchart WHERE deleted = 0"); + echo "=== BPM Flowcharts ===\n"; + $stmt = $pdo->query("SELECT id, name, target_type, is_active FROM bpmn_flowchart WHERE deleted = 0"); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); - echo "Verfügbare Workflows:\n"; foreach ($results as $row) { - echo "- ID: {$row['id']}, Name: {$row['name']}\n"; + $active = $row['is_active'] ? '[AKTIV]' : '[INAKTIV]'; + echo " {$active} ID: {$row['id']}, Name: {$row['name']}, Entity: {$row['target_type']}\n"; + } + + echo "\n=== Simple Workflows ===\n"; + $stmt = $pdo->query("SELECT id, name, entity_type, type, is_active FROM workflow WHERE deleted = 0"); + $results = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($results as $row) { + $active = $row['is_active'] ? '[AKTIV]' : '[INAKTIV]'; + $name = $row['name'] ?: '(unnamed)'; + echo " {$active} ID: {$row['id']}, Name: {$name}, Entity: {$row['entity_type']}, Type: {$row['type']}\n"; } } function readWorkflow($pdo, $id) { + // Try BPM first $stmt = $pdo->prepare("SELECT * FROM bpmn_flowchart WHERE id = ? AND deleted = 0"); $stmt->execute([$id]); $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row) { - echo "Workflow Details:\n"; + echo "=== BPM Flowchart ===\n"; echo json_encode($row, JSON_PRETTY_PRINT) . "\n"; - } else { - echo "Workflow nicht gefunden.\n"; + return; } + + // Try Simple Workflow + $stmt = $pdo->prepare("SELECT * FROM workflow WHERE id = ? AND deleted = 0"); + $stmt->execute([$id]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($row) { + echo "=== Simple Workflow ===\n"; + echo json_encode($row, JSON_PRETTY_PRINT) . "\n"; + return; + } + + echo "Workflow nicht gefunden.\n"; } function deleteWorkflow($pdo, $id) { echo "Bist du sicher, dass du den Workflow $id löschen möchtest? (ja/nein): "; $input = trim(fgets(STDIN)); - if (strtolower($input) === 'ja') { - $stmt = $pdo->prepare("UPDATE bpmn_flowchart SET deleted = 1 WHERE id = ?"); - $stmt->execute([$id]); - echo "Workflow gelöscht.\n"; - } else { + if (strtolower($input) !== 'ja') { echo "Abgebrochen.\n"; + return; } + + // Try BPM first + $stmt = $pdo->prepare("UPDATE bpmn_flowchart SET deleted = 1 WHERE id = ?"); + $stmt->execute([$id]); + if ($stmt->rowCount() > 0) { + echo "BPM Flowchart gelöscht.\n"; + return; + } + + // Try Simple Workflow + $stmt = $pdo->prepare("UPDATE workflow SET deleted = 1 WHERE id = ?"); + $stmt->execute([$id]); + if ($stmt->rowCount() > 0) { + echo "Simple Workflow gelöscht.\n"; + return; + } + + echo "Workflow nicht gefunden.\n"; } -function editWorkflow($pdo, $id, $jsonData) { - $data = json_decode($jsonData, true); +function importWorkflow($pdo, $file) { + if (!file_exists($file)) { + die("Datei nicht gefunden: $file\n"); + } + + $json = file_get_contents($file); + $data = json_decode($json, true); + if (json_last_error() !== JSON_ERROR_NONE) { - die("Ungültiges JSON.\n"); + die("Ungültiges JSON in Datei.\n"); + } + + $type = $data['type'] ?? 'unknown'; + $now = date('Y-m-d H:i:s'); + + if ($type === 'simple') { + // Import Simple Workflow + $id = substr(bin2hex(random_bytes(12)), 0, 17); + $stmt = $pdo->prepare(" + INSERT INTO workflow ( + id, name, entity_type, type, is_active, is_internal, + description, conditions_all, conditions_any, conditions_formula, + actions, portal_only, scheduling, scheduling_apply_timezone, + process_order, created_at, modified_at, deleted + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0) + "); + $stmt->execute([ + $id, + $data['name'], + $data['entity_type'], + $data['trigger_type'], + $data['is_active'] ?? 1, + 0, + $data['description'] ?? null, + json_encode($data['conditions_all'] ?? []), + json_encode($data['conditions_any'] ?? []), + $data['conditions_formula'] ?? null, + json_encode($data['actions'] ?? []), + 0, + '0 0 * * *', + 1, + 10, + $now, + $now + ]); + echo "✓ Simple Workflow importiert: $id - {$data['name']}\n"; + } elseif ($type === 'bpm') { + // Import BPM Flowchart + $id = substr(bin2hex(random_bytes(12)), 0, 17); + $stmt = $pdo->prepare(" + INSERT INTO bpmn_flowchart ( + id, name, target_type, is_active, + created_at, modified_at, data, elements_data_hash, + deleted, has_none_start_event, event_start_id_list, event_start_all_id_list + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 0, '[]', ?) + "); + $stmt->execute([ + $id, + $data['name'], + $data['target_type'], + $data['is_active'] ?? 1, + $now, + $now, + json_encode($data['data']), + json_encode($data['elements_data_hash']), + json_encode($data['event_start_all_id_list'] ?? []) + ]); + echo "✓ BPM Flowchart importiert: $id - {$data['name']}\n"; + } else { + die("Unbekannter Workflow-Typ: $type\n"); } - $stmt = $pdo->prepare("UPDATE bpmn_flowchart SET data = ?, modified_at = NOW() WHERE id = ?"); - $stmt->execute([json_encode($data), $id]); - echo "Workflow aktualisiert.\n"; } -function executeWorkflow($pdo, $id, $recordId) { - // Einfache Simulation: Setze Status oder triggere Event - // In Realität würde man EspoCRM-API verwenden - echo "Workflow $id für Record $recordId ausführen...\n"; - // Beispiel: Status setzen - $stmt = $pdo->prepare("UPDATE c_vmh_erstgespraech SET status = 'Warte auf Mandatierung' WHERE id = ?"); - $stmt->execute([$recordId]); - echo "Status gesetzt. Workflow sollte ausgelöst werden.\n"; -} - -function testWorkflow($pdo, $id) { - // Simuliere Bedingungen - $stmt = $pdo->prepare("SELECT data FROM bpmn_flowchart WHERE id = ?"); +function exportWorkflow($pdo, $id, $file) { + // Try BPM first + $stmt = $pdo->prepare("SELECT * FROM bpmn_flowchart WHERE id = ? AND deleted = 0"); $stmt->execute([$id]); $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row) { - $data = json_decode($row['data'], true); - echo "Teste Workflow: " . (isset($data['list'][0]['text']) ? $data['list'][0]['text'] : 'Unbekannt') . "\n"; - // Prüfe Bedingungen (vereinfacht) - echo "Bedingungen: " . json_encode(isset($data['list'][0]['conditionsAll']) ? $data['list'][0]['conditionsAll'] : []) . "\n"; + $export = [ + 'type' => 'bpm', + 'name' => $row['name'], + 'target_type' => $row['target_type'], + 'is_active' => (bool)$row['is_active'], + 'description' => $row['description'], + 'data' => json_decode($row['data'], true), + 'elements_data_hash' => json_decode($row['elements_data_hash'], true), + 'event_start_all_id_list' => json_decode($row['event_start_all_id_list'], true) + ]; + file_put_contents($file, json_encode($export, JSON_PRETTY_PRINT)); + echo "✓ BPM Flowchart exportiert nach: $file\n"; + return; } + + // Try Simple Workflow + $stmt = $pdo->prepare("SELECT * FROM workflow WHERE id = ? AND deleted = 0"); + $stmt->execute([$id]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($row) { + $export = [ + 'type' => 'simple', + 'name' => $row['name'], + 'entity_type' => $row['entity_type'], + 'trigger_type' => $row['type'], + 'is_active' => (bool)$row['is_active'], + 'description' => $row['description'], + 'conditions_all' => json_decode($row['conditions_all'], true), + 'conditions_any' => json_decode($row['conditions_any'], true), + 'conditions_formula' => $row['conditions_formula'], + 'actions' => json_decode($row['actions'], true) + ]; + file_put_contents($file, json_encode($export, JSON_PRETTY_PRINT)); + echo "✓ Simple Workflow exportiert nach: $file\n"; + return; + } + + echo "Workflow nicht gefunden.\n"; } $pdo = connectDB(); @@ -102,19 +238,16 @@ switch ($action) { if (!isset($argv[2])) die("Usage: delete \n"); deleteWorkflow($pdo, $argv[2]); break; - case 'edit': - if (!isset($argv[2]) || !isset($argv[3])) die("Usage: edit \n"); - editWorkflow($pdo, $argv[2], $argv[3]); + case 'import': + if (!isset($argv[2])) die("Usage: import \n"); + importWorkflow($pdo, $argv[2]); break; - case 'execute': - if (!isset($argv[2]) || !isset($argv[3])) die("Usage: execute \n"); - executeWorkflow($pdo, $argv[2], $argv[3]); - break; - case 'test': - if (!isset($argv[2])) die("Usage: test \n"); - testWorkflow($pdo, $argv[2]); + case 'export': + if (!isset($argv[2]) || !isset($argv[3])) die("Usage: export \n"); + exportWorkflow($pdo, $argv[2], $argv[3]); break; default: - echo "Unbekannte Aktion. Verfügbare: list, read, delete, edit, execute, test\n"; + echo "Unbekannte Aktion.\n"; + echo "Verfügbare Aktionen: list, read, delete, import, export\n"; } -?> \ No newline at end of file +?> diff --git a/custom/workflows/README.md b/custom/workflows/README.md new file mode 100644 index 00000000..06ea05ae --- /dev/null +++ b/custom/workflows/README.md @@ -0,0 +1,70 @@ +# Workflow Definitions + +This directory contains workflow definitions in JSON format that can be imported into EspoCRM using the workflow manager script. + +## File Format + +### Simple Workflow +```json +{ + "type": "simple", + "name": "workflow-name", + "entity_type": "EntityName", + "trigger_type": "afterRecordSaved", + "is_active": true, + "description": "Description of what this workflow does", + "conditions_all": [], + "conditions_any": [], + "conditions_formula": null, + "actions": [] +} +``` + +**Trigger Types:** +- `afterRecordSaved` - After record is created or updated +- `afterRecordCreated` - Only after record is created +- `scheduled` - Runs on a schedule + +**Condition Types:** +- `equals`, `notEquals`, `greaterThan`, `lessThan`, `contains`, `notContains`, `isEmpty`, `isNotEmpty`, `isTrue`, `isFalse`, `wasEqual`, `wasNotEqual`, `changed`, `notChanged` + +**Action Types:** +- `sendEmail` - Send email to recipient +- `createEntity` - Create a new record +- `updateEntity` - Update current record +- `relateTo` - Link to another record +- `unrelateFrom` - Unlink from record +- `applyAssignmentRule` - Apply assignment rules +- `createNotification` - Create notification + +### BPM Flowchart +```json +{ + "type": "bpm", + "name": "flowchart-name", + "target_type": "EntityName", + "is_active": true, + "description": "Description", + "data": { + "list": [] + }, + "elements_data_hash": {}, + "event_start_all_id_list": [] +} +``` + +## Usage + +Import a workflow: +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php import /var/www/html/custom/workflows/your-workflow.json +``` + +Export a workflow: +```bash +docker exec espocrm php /var/www/html/custom/scripts/workflow_manager.php export /var/www/html/custom/workflows/exported.json +``` + +## Examples + +- `vmh-erstberatung-abschliessen.json` - Sends email and sets status when consultation is completed diff --git a/custom/workflows/vmh-erstberatung-abschliessen-backup.json b/custom/workflows/vmh-erstberatung-abschliessen-backup.json new file mode 100644 index 00000000..ddf45184 --- /dev/null +++ b/custom/workflows/vmh-erstberatung-abschliessen-backup.json @@ -0,0 +1,43 @@ +{ + "type": "simple", + "name": "vmh-erstberatung-abschlie\u00dfen", + "entity_type": "CVmhErstgespraech", + "trigger_type": "afterRecordSaved", + "is_active": true, + "description": "Versendet Erstberatungs-E-Mail und setzt Status auf \"Warte auf Mandatierung\"", + "conditions_all": [ + { + "comparison": "equals", + "subjectType": "value", + "cid": 0, + "fieldToCompare": "status", + "type": "all", + "value": "Warte auf Mandatierung" + }, + { + "comparison": "changed", + "subjectType": null, + "cid": 1, + "fieldToCompare": "status", + "type": "all" + } + ], + "conditions_any": [], + "conditions_formula": null, + "actions": [ + { + "type": "sendEmail", + "cid": 0, + "id": "423a120400", + "from": "specifiedEmailAddress", + "fromEmailAddress": "anwalt@vermieterhelden.de", + "to": "targetEntity", + "toEmailAddress": null, + "replyTo": null, + "emailTemplateId": null, + "emailTemplateName": null, + "doNotStore": false, + "optOutLink": false + } + ] +} \ No newline at end of file diff --git a/custom/workflows/vmh-erstberatung-abschliessen.json b/custom/workflows/vmh-erstberatung-abschliessen.json new file mode 100644 index 00000000..999f62c1 --- /dev/null +++ b/custom/workflows/vmh-erstberatung-abschliessen.json @@ -0,0 +1,27 @@ +{ + "type": "simple", + "name": "vmh-erstberatung-abschließen", + "entity_type": "CVmhErstgespraech", + "trigger_type": "afterRecordSaved", + "is_active": true, + "description": "Sendet E-Mail und setzt Status wenn Erstberatung abgeschlossen wird", + "conditions_all": [ + { + "type": "equals", + "attribute": "status", + "value": "Warte auf Mandatierung" + } + ], + "conditions_any": [], + "conditions_formula": null, + "actions": [ + { + "type": "sendEmail", + "from": "system", + "to": "contactId", + "emailTemplateId": null, + "subject": "Erstberatung abgeschlossen", + "body": "Ihre Erstberatung wurde erfolgreich abgeschlossen. Wir warten nun auf Ihre Mandatierung." + } + ] +} diff --git a/data/config.php b/data/config.php index 23a830be..53895c02 100644 --- a/data/config.php +++ b/data/config.php @@ -348,8 +348,8 @@ return [ 0 => 'youtube.com', 1 => 'google.com' ], - 'cacheTimestamp' => 1768945598, - 'microtime' => 1768945598.792157, + 'cacheTimestamp' => 1768949725, + 'microtime' => 1768949725.049459, 'siteUrl' => 'https://crm.bitbylaw.com', 'fullTextSearchMinLength' => 4, 'appTimestamp' => 1768843902,