KI-basierte Bearbeitung von EspoCRM: Struktur und Funktionsweise ## 🚀 Schnellstart fĂŒr KI **NEU:** Automatisches KI-Einstiegsscript fĂŒr vollstĂ€ndigen Projekt-Überblick! ```bash # VollstĂ€ndige Projekt-Analyse fĂŒr KI ./custom/scripts/ki-overview.sh # Nur SchnellĂŒbersicht ./custom/scripts/ki-overview.sh --stats # In Datei speichern ./custom/scripts/ki-overview.sh --file /tmp/overview.txt ``` Das Script analysiert automatisch alle EntitĂ€ten, Beziehungen, Custom Code, Workflows und Frontend-Anpassungen. Siehe [KI_OVERVIEW_README.md](custom/scripts/KI_OVERVIEW_README.md) fĂŒr Details. ## Inhaltsverzeichnis 1. [Überblick](#ĂŒberblick) 2. [Custom Directory Struktur](#custom-directory-struktur) 3. [Rebuild-Prozess](#rebuild-prozess) 4. [HĂ€ufige Fehler vermeiden](#hĂ€ufige-fehler-vermeiden) 5. [Dateiformate und JSON-Strukturen](#dateiformate-und-json-strukturen) 6. [Workflow-Verwaltung](#workflow-verwaltung) 7. [Internationalisierung (i18n) und Tooltips](#internationalisierung-i18n-und-tooltips) 8. [Formula-Scripts und Custom PHP-Erweiterungen](#formula-scripts-und-custom-php-erweiterungen) 9. [Panel-Labels und Übersetzungen](#panel-labels-und-ĂŒbersetzungen) 10. [Custom JavaScript & CSS Integration](#custom-javascript--css-integration) 11. [Reports und Report-Panels](#reports-und-report-panels) 12. [Portal-Freigabe-System](#portal-freigabe-system) 13. [Troubleshooting](#troubleshooting) ## Überblick EspoCRM ist ein modular aufgebautes CRM-System, das auf PHP (Backend) und Backbone.js (Frontend) basiert. Konfigurationen fĂŒr EntitĂ€ten, Felder, Beziehungen, Views und Layouts werden in JSON-basierten Metadata-Dateien gespeichert. Die Anpassung erfolgt ĂŒber das **custom/**-Verzeichnis, um Core-Dateien nicht zu ĂŒberschreiben und Upgrades zu erleichtern. EspoCRM verwendet eine rekursive Merging-Mechanik: Custom-Dateien ĂŒberschreiben oder erweitern Core-Definitionen. **Anpassungsprozess:** 1. JSON-Dateien im `custom/`-Verzeichnis erstellen/bearbeiten 2. Rebuild-Script ausfĂŒhren (validiert, merged, aktualisiert DB) 3. Änderungen sind sofort wirksam Keine integrierte KI-Schnittstelle existiert, aber mit Dateizugriff können automatisierte Anpassungen vorgenommen werden: Felder hinzufĂŒgen, Views anpassen, Beziehungen definieren, Workflows erstellen. ## Custom Directory Struktur **VollstĂ€ndige Übersicht:** Siehe `/custom/CUSTOM_DIRECTORY.md` fĂŒr detaillierte Dokumentation aller Custom-Verzeichnisse. **Wichtigste Bereiche:** - `custom/Espo/Custom/Resources/metadata/` - Backend-Definitionen (entityDefs, clientDefs, etc.) - `custom/Espo/Custom/Classes/` - Custom PHP-Klassen (Formula-Funktionen, Services) - `client/custom/src/` - Frontend JavaScript (Views, Module) - `client/custom/css/` - Custom Stylesheets - `custom/scripts/` - Wartungs-Scripts (Rebuild, Workflow-Manager) - `custom/workflows/` - Versionierte Workflow-Definitionen ## Rebuild-Prozess **WICHTIG:** Nach jeder Änderung an Custom-Dateien muss ein Rebuild durchgefĂŒhrt werden! ### Validate & Rebuild Script (Empfohlen) **Zentrales Tool:** `custom/scripts/validate_and_rebuild.py` Dieses Script sollte **IMMER** verwendet werden (niemals manueller Rebuild). Es fĂŒhrt automatisch aus: ✅ **Validierungen:** - JSON-Syntax-PrĂŒfung aller `.json` Dateien im `custom/` Verzeichnis - **Relationship-Konsistenz-PrĂŒfung** (bidirektionale Links, foreign-Definitionen) - **Formula-Script Platzierung** (korrekt in `/formula/` statt `/entityDefs/`) - **i18n-VollstĂ€ndigkeit** (fehlende Übersetzungen fĂŒr Links) - Layout-Struktur-PrĂŒfung - Dateirechte-PrĂŒfung (`www-data:www-data` Owner) ✅ **Automatische Korrekturen:** - Setzt fehlerhafte Dateirechte auf `www-data:www-data` - Korrigiert Verzeichnis-Permissions (775) und Datei-Permissions (664) ✅ **Rebuild:** - **Nur wenn keine kritischen Fehler gefunden werden!** - Merged alle Custom-Metadata mit Core-Definitionen - Aktualisiert Datenbank-Schema (neue Felder, Tabellen, Indizes) - Leert Cache-Verzeichnis - Regeneriert Frontend-Assets **Verwendung:** ```bash # Im EspoCRM-Root-Verzeichnis ausfĂŒhren python3 custom/scripts/validate_and_rebuild.py # Nur Validierung ohne Rebuild python3 custom/scripts/validate_and_rebuild.py --dry-run ``` **Ausgabe:** - ✓ GrĂŒn: Alles in Ordnung, Rebuild erfolgreich - ⚠ Gelb: Warnungen (z.B. fehlende i18n), Rebuild wird trotzdem ausgefĂŒhrt - ✗ Rot: Kritische Fehler (z.B. ungĂŒltiges JSON, fehlende Relationships), Rebuild wird NICHT ausgefĂŒhrt **Bei Fehlern:** - JSON-Syntax-Fehler werden mit Datei und Zeilennummer angezeigt - Relationship-Fehler zeigen fehlende Links zwischen Entities - Formula-Platzierungsfehler werden erkannt und gemeldet - i18n-Probleme werden als Warnungen angezeigt (kein Abbruch) - Dateirechte-Probleme werden automatisch korrigiert **Detaillierte Dokumentation:** Siehe `custom/scripts/VALIDATOR_README.md` ### Wann Rebuild erforderlich? **Backend-Änderungen:** - ✅ entityDefs, clientDefs, layouts, scopes bearbeitet - ✅ Formula-Scripts erstellt/geĂ€ndert - ✅ i18n-Dateien aktualisiert - ✅ Custom PHP-Klassen hinzugefĂŒgt - ✅ CSS in app/client.json registriert **Frontend-Änderungen:** - ✅ JavaScript Views erstellt/geĂ€ndert - ✅ CSS-Dateien hinzugefĂŒgt - ⚠ ZusĂ€tzlich Browser Hard Refresh (Ctrl+Shift+R) erforderlich! **Workflows:** - ❌ Kein Rebuild nötig (Import ĂŒber workflow_manager.php) ### Was der Rebuild bewirkt 1. **Metadata-Merging:** Kombiniert Custom-Definitionen mit Core-Definitionen 2. **Datenbank-Schema-Update:** Erstellt neue Tabellen/Spalten/Indizes basierend auf entityDefs 3. **Cache-Bereinigung:** Löscht gecachte Metadata, Views, Templates 4. **Frontend-Build:** Regeneriert JavaScript/CSS-Bundles 5. **ORM-Update:** Aktualisiert Entity-Klassen und Repositories ### Manuelle Dateirechte-Korrektur Falls `check_and_rebuild.sh` Rechte-Probleme nicht beheben kann: ```bash sudo chown -R www-data:www-data custom/ client/custom/ data/ sudo find custom/ -type f -name "*.json" -exec chmod 664 {} \; sudo find custom/ -type d -exec chmod 775 {} \; sudo find client/custom/ -type f -exec chmod 664 {} \; sudo find client/custom/ -type d -exec chmod 775 {} \; ``` ## HĂ€ufige Fehler vermeiden Basierend auf der Analyse von Git-Commits und praktischen Erfahrungen treten bestimmte Fehler besonders hĂ€ufig auf. Diese Sektion hilft, die **5 hĂ€ufigsten Fehler** zu vermeiden. ### ⚠ Top 5 Fehlerquellen #### 1. Formula-Scripts falsch platziert **Problem:** Formula-Scripts werden in `entityDefs/{Entity}.json` statt in separater `formula/{Entity}.json` abgelegt. **Symptome:** - beforeSaveApiScript wird nicht ausgefĂŒhrt - Validierungen greifen nicht - Keine Fehler in Logs, Script wird einfach ignoriert **❌ FALSCH:** ```json // custom/Espo/Custom/Resources/metadata/entityDefs/CBankverbindungen.json { "fields": { "iban": {"type": "varchar"} }, "formula": { "beforeSaveApiScript": "..." // ← Funktioniert NICHT! } } ``` **✅ RICHTIG:** ```json // custom/Espo/Custom/Resources/metadata/formula/CBankverbindungen.json { "beforeSaveApiScript": "if (iban != null && iban != '') { ... }" } ``` **ZusĂ€tzliche Fallstricke:** - ❌ `string\isEmpty()` verwenden (existiert nicht!) - ✅ Stattdessen: `field != null && field != ''` **Nach Korrektur:** `./custom/scripts/check_and_rebuild.sh` ausfĂŒhren --- #### 2. Layout-Strukturfehler **Problem:** Falsche Platzhalter und Strukturen in Layouts fĂŒhren zu UI-Problemen. **⚠ KRITISCH fĂŒr EspoCRM 7.x:** In EspoCRM 7.x ist `false` als Platzhalter **NICHT MEHR ERLAUBT**! **Symptome:** - Layout wird nicht geladen, Fehlermeldung in UI - Felder werden nicht korrekt angeordnet - Leere Bereiche in Detail-Views - bottomPanelsDetail funktioniert nicht **❌ FALSCH (EspoCRM 7.x):** ```json // layouts/Entity/detail.json { "rows": [ [ {"name": "field1"}, {"name": "field2"}, false, // ← DEPRECATED! Funktioniert in 7.x NICHT! false // ← Verursacht Layout-Fehler! ] ] } ``` **✅ RICHTIG (EspoCRM 7.x):** ```json { "rows": [ [ {"name": "field1"}, {"name": "field2"}, {}, // ← Leeres Objekt verwenden! {} // ← Funktioniert in 7.x korrekt ] ] } ``` **❌ FALSCH - bottomPanelsDetail.json als Array:** ```json // layouts/Entity/bottomPanelsDetail.json [ {"name": "contacts"}, {"name": "documents"} ] ``` **✅ RICHTIG - bottomPanelsDetail.json als Objekt:** ```json { "contacts": { "index": 0, "sticked": true, "style": "warning" }, "_tabBreak_0": { "index": 1, "columnBreak": true }, "documents": { "index": 2 }, "activities": { "disabled": true }, "history": { "disabled": true } } ``` **HĂ€ufige Layout-Fehler:** - ❌ **`false` als Platzhalter** (seit EspoCRM 7.x nicht mehr unterstĂŒtzt!) - ❌ Width-Attribute in Detail-Layouts (nur fĂŒr List-Layouts!) - ❌ bottomPanelsDetail.json als Array statt Objekt - ❌ Index nicht angepasst nach Entfernung von Panels **Best Practice:** - **Immer `{}` statt `false`** fĂŒr leere Zellen in detail.json verwenden - Width nur in `list.json` und `listSmall.json` verwenden - bottomPanelsDetail.json **MUSS** Objekt-Format haben - Rows immer mit 4 Spalten fĂŒllen (nutze `{}` zum AuffĂŒllen) - Nach Panel-Entfernung Indices neu nummerieren - `_tabBreak_{index}` verwenden um Panels auf verschiedene Tabs zu verteilen --- #### 3. UnvollstĂ€ndige Relationship-Definitionen **Problem:** Bei hasMany-Relationships wird nur eine Seite definiert, die Gegenseite fehlt. **Symptome:** - HTTP 404 "Link does not exist"-Fehler in Logs - Relationship-Panel zeigt keine Daten - VerknĂŒpfungen können nicht erstellt werden **❌ FALSCH - Nur eine Seite definiert:** ```json // entityDefs/CMietobjekt.json { "links": { "kontakte": { "type": "hasMany", "entity": "Contact", "foreign": "mietobjekte" // ← Existiert nicht in Contact! } } } // entityDefs/Contact.json - FEHLT! ``` **✅ RICHTIG - Beide Seiten definiert:** ```json // entityDefs/CMietobjekt.json { "links": { "contactsMietobjekt": { "type": "hasMany", "relationName": "cMietobjektContactPortal", "foreign": "cMietobjektContactPortal", "entity": "Contact" } } } // entityDefs/Contact.json { "links": { "cMietobjektContactPortal": { "type": "hasMany", "relationName": "cMietobjektContactPortal", // ← IDENTISCH! "foreign": "contactsMietobjekt", // ← Zeigt auf Gegenseite "entity": "CMietobjekt" } } } ``` **Kritische Punkte:** - ✅ `relationName` muss auf **beiden Seiten identisch** sein - ✅ `foreign` zeigt auf den Link-Namen der **Gegenseite** - ✅ Beide entityDefs-Dateien mĂŒssen erstellt werden **Siehe auch:** Abschnitt "Troubleshooting → Link does not exist" --- #### 4. i18n/Localization-Fehler **Problem:** Labels nicht vollstĂ€ndig oder nur in einer Sprache definiert. **Symptome:** - Relationship-Panels zeigen technische Namen statt Labels - Tooltips zeigen nur Feldnamen - In manchen Sprachen fehlen Beschriftungen **❌ FALSCH - UnvollstĂ€ndig:** ```json // i18n/de_DE/Entity.json { "fields": { "mietobjekte": "Mietobjekte" }, "links": {} // ← Label fehlt! } // i18n/en_US/Entity.json - FEHLT KOMPLETT! ``` **✅ RICHTIG - VollstĂ€ndig in beiden Sprachen:** ```json // i18n/de_DE/Entity.json { "fields": { "mietobjekte": "Mietobjekte" }, "links": { "mietobjekte": "Mietobjekte" // ← BEIDE Sektionen! }, "tooltips": { "mietobjekte": "VerknĂŒpfte Mietobjekte" } } // i18n/en_US/Entity.json ← IMMER erstellen! { "fields": { "mietobjekte": "Rental Properties" }, "links": { "mietobjekte": "Rental Properties" }, "tooltips": { "mietobjekte": "Linked rental properties" } } ``` **Kritische Regeln:** - ✅ Labels IMMER in `fields` UND `links` definieren - ✅ en_US ist Fallback-Sprache → MUSS vollstĂ€ndig sein - ✅ Tooltips in ALLEN Sprachen konsistent definieren - ✅ Bei neuen Relationships beide Sprachen aktualisieren **Warum en_US kritisch ist:** EspoCRM nutzt en_US als Fallback. Fehlt eine Definition dort, ĂŒberschreibt sie möglicherweise die deutschen Labels! **Siehe auch:** Abschnitt "Internationalisierung (i18n) und Tooltips" --- #### 5. Dateirechte-Probleme **Problem:** Custom-Dateien gehören `root` statt `www-data`, EspoCRM kann nicht darauf zugreifen. **Symptome:** - "Permission denied"-Fehler in Logs - Layouts können nicht ĂŒber Admin-UI bearbeitet werden - HTTP 500-Fehler beim Speichern **Ursache:** Dateien werden als Root-User erstellt (z.B. via sudo, Git-Checkout) und EspoCRM lĂ€uft als `www-data`. **✅ Lösung:** Das `check_and_rebuild.sh` Script prĂŒft und korrigiert automatisch: ```bash ./custom/scripts/check_and_rebuild.sh ``` **Manuelle Korrektur (falls nötig):** ```bash sudo chown -R www-data:www-data custom/ client/custom/ data/ sudo find custom/ -type f -name "*.json" -exec chmod 664 {} \; sudo find custom/ -type d -exec chmod 775 {} \; ``` **PrĂ€vention:** - Änderungen direkt im Docker-Container vornehmen - Nach Git-Pull immer `check_and_rebuild.sh` ausfĂŒhren - VSCode mit Remote-Development nutzt automatisch korrekte User --- ### đŸ›Ąïž Fehler-PrĂ€vention: Checkliste **Bei neuer Entity-Erstellung:** - [ ] entityDefs in **beiden** Entities (bei Relationships) - [ ] `relationName` identisch auf beiden Seiten - [ ] `foreign` zeigt auf korrekten Link-Namen - [ ] i18n in **de_DE UND en_US** vollstĂ€ndig - [ ] Labels in **fields UND links** - [ ] Formula in `formula/`, **NICHT** in `entityDefs/` - [ ] Nach Erstellung: `./custom/scripts/check_and_rebuild.sh` **Bei HinzufĂŒgen neuer Felder:** - [ ] Felder in entityDefs definieren mit korrektem Typ und Optionen - [ ] Felder zu relevanten Layouts hinzufĂŒgen (detail.json, list.json, etc.) - [ ] Felder sinnvoll gruppieren (eigenes Panel oder bestehendes Panel) - [ ] Labels in i18n/**de_DE** vollstĂ€ndig (fields UND links) - [ ] Labels in i18n/**en_US** vollstĂ€ndig (Fallback-Sprache!) - [ ] Tooltips in i18n hinzufĂŒgen falls erforderlich - [ ] Nach Änderung: `./custom/scripts/check_and_rebuild.sh` - [ ] Browser Hard Refresh (Ctrl+Shift+R) durchfĂŒhren **Bei Layout-Änderungen:** - [ ] Keine `false` oder leere Objekte in Rows - [ ] Width nur in List-Layouts verwenden - [ ] Indices nach Panel-Entfernung neu nummerieren - [ ] Nach Änderung: `./custom/scripts/check_and_rebuild.sh` **Bei Formula-Scripts:** - [ ] Separate Datei `formula/{Entity}.json` erstellen - [ ] `string\isEmpty()` NICHT verwenden → `!= null && != ''` - [ ] Null-Checks vor String-Operationen - [ ] Nach Erstellung: `./custom/scripts/check_and_rebuild.sh` **Allgemein:** - [ ] Immer `./custom/scripts/check_and_rebuild.sh` nach Änderungen - [ ] Browser Hard Refresh (Ctrl+Shift+R) bei Frontend-Änderungen - [ ] Logs prĂŒfen: `tail -n 100 data/logs/espo-$(date +%Y-%m-%d).log` --- ### 📊 Warum diese Fehler so hĂ€ufig sind **Root Cause:** 1. **EspoCRM Admin-UI** generiert teilweise unvollstĂ€ndige JSON-Definitionen 2. **Bidirektionale Relationships** sind komplex und nicht intuitiv 3. **en_US als Fallback** ist nicht offensichtlich dokumentiert 4. **Formula-Platzierung** wird im Admin-UI falsch vorgeschlagen 5. **Layout-Generierung** erzeugt manchmal ungĂŒltige Array-Strukturen **Lösung:** - Diese Dokumentation **VOR** der Entwicklung lesen - `check_and_rebuild.sh` Script konsequent nutzen - Bei Unsicherheit: Troubleshooting-Abschnitt konsultieren **Siehe auch:** - [Troubleshooting](#troubleshooting) - AusfĂŒhrliche Fehlerdiagnose - [Custom Directory Struktur](/custom/CUSTOM_DIRECTORY.md) - VollstĂ€ndige DateiĂŒbersicht ## Dateiformate und JSON-Strukturen Alle Metadata-Dateien sind im JSON-Format. Die Strukturen sind hierarchisch: Objekte fĂŒr Felder/Links, Arrays fĂŒr Optionen/Listen. **Detaillierte Verzeichnisstruktur:** Siehe `/custom/CUSTOM_DIRECTORY.md` ### entityDefs/{EntityType}.json **Format-Beispiel:** ```json { "fields": { "name": { "type": "varchar", "required": true, "len": 255 }, "status": { "type": "enum", "options": ["Active", "Inactive"], "default": "Active" }, "employeeCount": { "type": "int" } }, "links": { "account": { "type": "belongsTo", "entity": "Account", "foreign": "projects" }, "teams": { "type": "hasMany", "entity": "Team", "relationName": "EntityTeam" } }, "collection": { "sortBy": "createdAt", "asc": false, "boolFilters": ["onlyMy"] }, "indexes": { "name": { "columns": ["name"] } } } ``` **Wichtige Eigenschaften:** - `fields` - Feldtypen (varchar, enum, link, etc.), Validierungen, Optionen - `links` - Beziehungen zwischen EntitĂ€ten (belongsTo, hasMany, hasOne) - `collection` - Listen-View-Einstellungen (Sortierung, Filter) - `indexes` - Datenbank-Performance-Optimierung **KRITISCH - Bidirektionale Relationships:** Bei hasMany-Relationships mĂŒssen **BEIDE Seiten** definiert werden: **Beispiel:** Contact ↔ MietverhĂ€ltnis ```json // custom/Espo/Custom/Resources/metadata/entityDefs/CVmhMietverhltnis.json { "links": { "contactsMietverhltnis": { "type": "hasMany", "relationName": "cVmhMietverhltnisContact", "foreign": "cVmhMietverhltnisContact", "entity": "Contact" } } } // custom/Espo/Custom/Resources/metadata/entityDefs/Contact.json { "links": { "cVmhMietverhltnisContact": { "type": "hasMany", "relationName": "cVmhMietverhltnisContact", "foreign": "contactsMietverhltnis", "entity": "CVmhMietverhltnis" } } } ``` **Wichtig:** - `relationName` muss auf **beiden Seiten identisch** sein - `foreign` zeigt auf den Link-Namen der **Gegenseite** - Fehlt eine Seite → **"404 Link does not exist"-Fehler** **VollstĂ€ndiger Prozess beim HinzufĂŒgen neuer Felder:** Neue Felder in einer Entity erfordern Änderungen in **drei Bereichen**: 1. **entityDefs/{Entity}.json** - Feld-Definition 2. **layouts/{Entity}/*.json** - Sichtbarkeit in UI 3. **i18n/{Sprache}/{Entity}.json** - Beschriftungen **Schritt-fĂŒr-Schritt-Anleitung:** **1. Feld in entityDefs definieren:** ```json // custom/Espo/Custom/Resources/metadata/entityDefs/CVmhMietverhltnis.json { "fields": { "kaltmiete": { "type": "currency", "required": true, "onlyDefaultCurrency": true, "min": 1, "decimal": true, "tooltip": true, "isCustom": true } } } ``` **2. Feld zu Layouts hinzufĂŒgen:** ```json // custom/Espo/Custom/Resources/layouts/CVmhMietverhltnis/detail.json [ { "rows": [ [ {"name": "kaltmiete"}, {"name": "warmmiete"} ] ], "customLabel": "Miethöhe", "noteText": "Erfassen Sie die Miethöhe in ihren einzelnen Bestandteilen", "noteStyle": "info" } ] ``` **3. Labels in ALLEN Sprachen definieren:** ```json // custom/Espo/Custom/Resources/i18n/de_DE/CVmhMietverhltnis.json { "fields": { "kaltmiete": "Kaltmiete" }, "tooltips": { "kaltmiete": "Monatliche Kaltmiete ohne Betriebskosten" } } // custom/Espo/Custom/Resources/i18n/en_US/CVmhMietverhltnis.json { "fields": { "kaltmiete": "Base Rent" } } ``` **Wichtig:** - ✅ **IMMER** de_DE UND en_US pflegen (en_US ist Fallback!) - ✅ Tooltips nur wenn `"tooltip": true` im entityDef gesetzt - ✅ Bei Link-Feldern Labels in `fields` UND `links` definieren - ✅ Nach Änderungen: `./custom/scripts/check_and_rebuild.sh` **Typische Layout-Typen:** - `detail.json` - Detail-Ansicht (Panels mit Rows) - `list.json` - Listen-Ansicht (Spalten mit Width) - `listSmall.json` - Kompakte Listen-Ansicht - `detailSmall.json` - Seitenleisten-Ansicht - `filters.json` - Filter-Felder --- ### ⚠ WICHTIG: Zwei verschiedene Layout-Verzeichnisse! **EspoCRM verwendet zwei verschiedene Layout-Verzeichnisse mit unterschiedlichen Zwecken:** #### 1. **Frontend-Layouts** (PRIMÄR - meistens benötigt) **Pfad:** `custom/Espo/Custom/Resources/layouts/{Entity}/{LayoutType}.json` ✅ **Verwenden fĂŒr:** - Liste-Ansichten: `list.json`, `listSmall.json` - Detail-Ansichten: `detail.json`, `detailSmall.json` - Bottom-Panels: `bottomPanelsDetail.json` - Relationship-Panels (werden ĂŒber `listSmall.json` definiert) - Alle UI-bezogenen Layouts **Beispiel:** ``` custom/Espo/Custom/Resources/layouts/ ├── CBeteiligte/ │ ├── list.json │ ├── listSmall.json ← Wird in Relationship-Panels verwendet! │ ├── detail.json │ └── bottomPanelsDetail.json ``` #### 2. **Metadata-Layouts** (SEKUNDÄR - selten benötigt) **Pfad:** `custom/Espo/Custom/Resources/metadata/layouts/{Entity}/{LayoutType}.json` ⚠ **Nur verwenden fĂŒr:** - Backend-spezifische Layout-Definitionen - Erweiterte Konfigurationen - In den meisten FĂ€llen NICHT benötigt! **Regel:** > **Relationship-Panels in Bottom-Views nutzen IMMER `listSmall.json` aus dem `layouts/` Verzeichnis (NICHT `metadata/layouts/`)!** **HĂ€ufiger Fehler:** ```bash # ❌ FALSCH - wird ignoriert! custom/Espo/Custom/Resources/metadata/layouts/CBeteiligte/listSmall.json # ✅ RICHTIG - wird verwendet! custom/Espo/Custom/Resources/layouts/CBeteiligte/listSmall.json ``` **Best Practice:** - Layouts IMMER in `custom/Espo/Custom/Resources/layouts/` erstellen - Das `metadata/layouts/` Verzeichnis nur verwenden, wenn explizit dokumentiert - Nach Änderungen: `./custom/scripts/check_and_rebuild.sh` - Browser Hard Refresh (Ctrl+Shift+R) durchfĂŒhren --- ### clientDefs/{EntityType}.json **Format-Beispiel:** ```json { "controller": "controllers/record", "collection": "collection", "model": "model", "views": { "list": "views/record/list", "detail": "views/record/detail", "edit": "views/record/edit" }, "recordViews": { "list": "views/record/list", "kanban": "custom:views/record/kanban" }, "viewSetupHandlers": { "record/detail": ["custom:handlers/my-detail-handler"] } } ``` **Wichtige Eigenschaften:** - `views`/`recordViews` - Pfade zu JavaScript Views (Custom mit PrĂ€fix `custom:`) - `viewSetupHandlers` - Dynamische View-Anpassungen - `filterList` - Report-Filter-Integration (mit `__APPEND__`) - `sidePanels` - Report-Panels fĂŒr Side-Panel ### layouts/{EntityType}/{LayoutType}.json **Format-Beispiel fĂŒr Detail-View:** ```json [ { "label": "Overview", "rows": [ [ {"name": "name"}, {"name": "assignedUser"} ], [ {"name": "description"} ] ] }, { "label": "Details", "rows": [ [{"name": "createdAt"}] ] } ] ``` **Struktur:** - Arrays von Panels (Objekte mit `label` und `rows`) - `rows` sind Arrays von Zellen (Objekte mit `name` fĂŒr Felder) - UnterstĂŒtzt Parameter: `width`, `notSortable`, `customLabel` **Spezielle Features:** - `__APPEND__` - Als erstes Array-Element einfĂŒgen, um bestehende Werte zu erweitern - `layoutAvailabilityList` - Array fĂŒr Feld-Sichtbarkeit in Layouts - `layoutIgnoreList` - Zu ignorierende Layouts ## Workflow-Verwaltung EspoCRM bietet zwei Workflow-Typen fĂŒr Prozessautomatisierung: ### 1. 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`, `wasNotEqual` **Aktionen:** - `sendEmail` - E-Mail versenden - `createEntity` - Record erstellen - `updateEntity` - Record aktualisieren - `relateTo` / `unrelateFrom` - VerknĂŒpfungen - `createNotification` - Benachrichtigung ### 2. BPM Flowcharts (Komplex) Visuelle Workflows mit BPMN 2.0-Standard fĂŒr komplexe, mehrstufige GeschĂ€ftsprozesse. **Komponenten:** - Start-Events: Signal, Conditional, Timer - Gateways: Exclusive, Inclusive, Parallel - Tasks, End-Events **Verwendung:** Über visuellen Designer im Admin-Interface ### Workflow-Dateien **Speicherort:** `custom/workflows/*.json` Workflow-Definitionen werden als JSON versioniert und ĂŒber Git verwaltet. **Format-Dokumentation:** Siehe `custom/workflows/README.md` ### Workflow Manager Script **Tool:** `custom/scripts/workflow_manager.php` Kommandozeilen-Tool fĂŒr Workflow-Verwaltung (Simple und BPM). #### 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 JSON-Datei. UnterstĂŒtzt beide Workflow-Typen. Erstellt automatisch 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 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", "category": "Kategorie-Name", "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:** - `category` - Workflow-Kategorie fĂŒr bessere Organisation - `comparison` - Vergleichsoperator - `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": [] } ``` ### Workflow-Entwicklung Best Practices 1. **Versionierung:** Workflows als JSON im `custom/workflows/` Verzeichnis versionieren 2. **Naming Convention:** Beschreibende Namen mit PrĂ€fix (z.B. `vmh-erstberatung-abschliessen.json`) 3. **Entwicklungsprozess:** - Workflow-JSON in `custom/workflows/` erstellen - Mit `workflow_manager.php import` einspielen - Im Admin-Interface testen und bei Bedarf anpassen - Mit `export` aktualisierten Workflow sichern - JSON-Datei im Repository committen 4. **Backup:** RegelmĂ€ĂŸig Export wichtiger Workflows durchfĂŒhren 5. **Dokumentation:** Description-Feld aussagekrĂ€ftig fĂŒllen ### Beispiel-Workflow **Szenario:** E-Mail bei Status-Wechsel zu "Warte auf Mandatierung" ```json { "type": "simple", "name": "vmh-erstberatung-abschliessen", "entity_type": "CVmhErstgespraech", "trigger_type": "afterRecordSaved", "is_active": true, "conditions_all": [ { "comparison": "equals", "fieldToCompare": "status", "value": "Warte auf Mandatierung" }, { "comparison": "changed", "fieldToCompare": "status" } ], "actions": [ { "type": "sendEmail", "to": "targetEntity", "emailTemplateId": "template-id-here" } ] } ``` ## Internationalisierung (i18n) und Tooltips EspoCRM verwendet ein hierarchisches Mehrsprachen-System mit **en_US als Basis-Fallback**: 1. **SprachprioritĂ€t**: en_US → aktuelle Sprache (z.B. de_DE) 2. **Problem**: Tooltips in en_US ĂŒberschreiben Tooltips in anderen Sprachen 3. **Lösung**: Tooltips MÜSSEN in ALLEN Sprachen definiert werden ### Beispiel fĂŒr korrektes Tooltip-Setup: **entityDefs/{Entity}.json:** ```json { "fields": { "iban": { "type": "varchar", "tooltip": true // Aktiviert Tooltip-Anzeige } } } ``` **i18n/de_DE/{Entity}.json:** ```json { "fields": { "iban": "IBAN" }, "tooltips": { "iban": "Internationale Bankkontonummer im Format DE89..." } } ``` **i18n/en_US/{Entity}.json:** ```json { "fields": { "iban": "IBAN" }, "tooltips": { "iban": "International Bank Account Number in format DE89..." } } ``` ### HĂ€ufige Fehler: ❌ **FALSCH** - UnvollstĂ€ndige en_US-Datei: ```json { "fields": [], "tooltips": { "iban": "iban2" // Überschreibt deutschen Tooltip! } } ``` ✅ **RICHTIG** - VollstĂ€ndige Definitionen in beiden Sprachen: - Alle Felder in `fields` definieren - Alle Tooltips in `tooltips` definieren - Konsistente Struktur ĂŒber alle Sprachen ### Debugging von Tooltip-Problemen: 1. **Symptom**: Tooltip zeigt nur Feldnamen (z.B. "iban" statt vollstĂ€ndiger Beschreibung) 2. **Ursache**: Fehlerhafte oder fehlende Definition in `i18n/en_US/{Entity}.json` 3. **PrĂŒfung**: - Existiert `i18n/en_US/{Entity}.json`? - EnthĂ€lt es fehlerhafte Tooltip-Definitionen? - Sind alle Tooltips konsistent ĂŒber alle Sprachen? 4. **Lösung**: VervollstĂ€ndige en_US-Datei mit korrekten englischen Übersetzungen ### Best Practices: - Erstelle **immer** sowohl de_DE als auch en_US Übersetzungen - Verwende beschreibende Tooltips mit Beispielen und Format-Hinweisen - Teste Tooltips nach jedem Rebuild in beiden Sprachen - Bei neuen Feldern: erst i18n-Dateien vollstĂ€ndig ausfĂŒllen, dann Rebuild ## Formula-Scripts und Custom PHP-Erweiterungen EspoCRM bietet mĂ€chtige Erweiterungsmöglichkeiten durch Formula-Scripts und Custom PHP-Funktionen. Diese ermöglichen Validierungen, Berechnungen und Business-Logik direkt beim Speichern von DatensĂ€tzen. ### Formula-Scripts: Grundlagen **WICHTIG: Dateistruktur** ❌ **FALSCH** - Formula in entityDefs: ```json // custom/Espo/Custom/Resources/metadata/entityDefs/Entity.json { "fields": {...}, "formula": { "beforeSaveApiScript": "..." // FUNKTIONIERT NICHT! } } ``` ✅ **RICHTIG** - Separate Formula-Datei: ```json // custom/Espo/Custom/Resources/metadata/formula/Entity.json { "beforeSaveApiScript": "if (field != null) { ... }" } ``` ### VerfĂŒgbare Formula-Script-Typen 1. **`beforeSaveApiScript`** - Wird vor dem Speichern ausgefĂŒhrt (UI + API, ab v7.5+) 2. **`beforeSaveCustomScript`** - Nur bei internen Saves (ohne API) 3. **`afterSaveScript`** - Nach dem Speichern **Verwendung**: Validierungen, Berechnungen, Daten-Transformation vor dem Speichern ### VerfĂŒgbare Formula-Funktionen **String-Funktionen:** - `string\concatenate(str1, str2)` - Strings verbinden - `string\replace(text, search, replace)` - Ersetzen - `string\substring(text, start, length)` - Teilstring - `string\length(text)` - LĂ€nge - `string\test(text, pattern)` - Regex-Test - ⚠ **NICHT verfĂŒgbar**: `string\isEmpty()` → Verwende `field != null && field != ''` **Logik:** - `if (condition) { ... }` - `&&`, `||`, `!` (AND, OR, NOT) - `==`, `!=`, `>`, `<`, `>=`, `<=` **Fehlerbehandlung:** - `recordService\throwBadRequest('Fehlermeldung')` - Speichern abbrechen mit Fehlermeldung ### Custom Formula-Funktionen erstellen Beispiel: IBAN-Validierung mit Modulo-97-Algorithmus **1. PHP-Klasse erstellen:** Pfad: `custom/Espo/Custom/Classes/FormulaFunctions/IbanGroup/ValidateType.php` ```php evaluate($args[0]); if (!$iban || !is_string($iban)) { return false; } // IBAN-Validierungs-Logik hier // ... (siehe ValidateType.php fĂŒr vollstĂ€ndige Implementierung) return $remainder === 1; // Modulo-97-Check } } ``` **2. Funktion registrieren:** Pfad: `custom/Espo/Custom/Resources/metadata/app/formula.json` ```json { "functionList": [ "__APPEND__", { "name": "iban\\validate", "insertText": "iban\\validate(IBAN)" } ], "functionClassNameMap": { "iban\\validate": "Espo\\Custom\\Classes\\FormulaFunctions\\IbanGroup\\ValidateType" } } ``` **3. Funktion verwenden:** Pfad: `custom/Espo/Custom/Resources/metadata/formula/CBankverbindungen.json` ```json { "beforeSaveApiScript": "if (iban != null && iban != '') {\n $ibanClean = string\\replace(iban, ' ', '');\n if (!iban\\validate($ibanClean)) {\n recordService\\throwBadRequest('UngĂŒltige IBAN!');\n }\n}" } ``` ### Wichtige Hinweise zur Formula-Entwicklung **Namespace-Struktur:** - Verzeichnis: `custom/Espo/Custom/Classes/FormulaFunctions/{GroupName}/` - Namespace: `Espo\Custom\Classes\FormulaFunctions\{GroupName}` - Klassenname: `{FunctionName}Type` (z.B. `ValidateType`) - Muss `BaseFunction` erweitern **Funktionsnamen:** - Format: `group\functionName` (z.B. `iban\validate`, `string\replace`) - Backslash `\` wird verwendet (nicht `::` oder `/`) **HĂ€ufige Fehler:** | Fehler | Symptom | Lösung | |--------|---------|--------| | Formula in entityDefs statt formula/ | Script wird nicht ausgefĂŒhrt | Separate `formula/{Entity}.json` erstellen | | `string\isEmpty()` verwendet | Error: "Unknown function" | Verwende `field != null && field != ''` | | Falsche Namespace-Struktur | Funktion nicht gefunden | PrĂŒfe Namespace, Klassenname, Pfad | | Funktion nicht registriert | "Unknown function" | Eintrag in `app/formula.json` erstellen | | Keine `__APPEND__` in functionList | Überschreibt Core-Funktionen | Immer `"__APPEND__"` als erstes Element | ### Debugging von Formula-Scripts **Logs prĂŒfen:** ```bash tail -n 100 /var/www/html/data/logs/espo-*.log | grep -i "formula\|error" ``` **HĂ€ufige Fehlermeldungen:** - `Unknown function: xxx` → Funktion existiert nicht oder nicht registriert - `Error 500` → Syntax-Fehler im Formula-Script oder PHP-Fehler - `validationFailure` → `throwBadRequest()` wurde aufgerufen **Test-Workflow:** 1. Formula-Script schreiben 2. Rebuild ausfĂŒhren: `bash custom/scripts/check_and_rebuild.sh` 3. Cache leeren (falls nötig): `rm -rf data/cache/*` 4. Testdaten speichern und Logs prĂŒfen ### Best Practices ✅ **Empfohlen:** - Separate Formula-Dateien pro Entity in `metadata/formula/` - Wiederverwendbare Logik in Custom PHP-Funktionen auslagern - AussagekrĂ€ftige Fehlermeldungen mit `throwBadRequest()` - Null-Checks vor Operationen: `field != null && field != ''` - Code kommentieren fĂŒr spĂ€tere Wartung ❌ **Vermeiden:** - Formula-Scripts in `entityDefs` ablegen - Nicht-existente String-Funktionen wie `isEmpty()` - Komplexe Logik direkt in Formula (besser: PHP-Funktion) - Fehlende Registrierung in `app/formula.json` ### Beispiel-AnwendungsfĂ€lle 1. **Validierung**: IBAN-Check, Email-Format, Telefonnummern 2. **Berechnung**: Gesamtpreise, Datumsberechnungen, Provisionen 3. **Daten-Transformation**: Großbuchstaben, Formatierungen, Normalisierungen 4. **Business Rules**: Status-ÜberprĂŒfungen, Pflichtfeld-Logik, AbhĂ€ngigkeiten 3. Auslösen von Änderungen und Rebuild-Prozess Was Änderungen auslösen: Datei-Änderungen: Werden bei Merging berĂŒcksichtigt – rekursiv, also ĂŒberschreiben Customs Core. 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. **UnterstĂŒtzte Funktionen:** - ✓ Kategorisierung von Workflows - ✓ Import/Export mit Kategorie-Namen - ✓ Übersichtliche Darstellung nach Kategorien - ✓ UnterstĂŒtzung fĂŒr beide Workflow-Typen (Simple & BPM) #### 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", "category": "Kategorie-Name", "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:** - `category` - **NEU:** Name der Workflow-Kategorie (optional, fĂŒr bessere Organisation) - `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 ``` 6. Bearbeitung von EntitĂ€ten und Layouts Um EspoCRM anzupassen, bearbeite JSON-Dateien im custom/-Verzeichnis. Änderungen bleiben bei Updates erhalten, da sie Core-Dateien nicht ĂŒberschreiben. EntitĂ€ten bearbeiten: Pfad: custom/Espo/Custom/Resources/metadata/entityDefs/{EntityType}.json (z. B. CVmhErstgespraech.json). Struktur: JSON-Objekt mit "fields" (Felder definieren), "links" (Beziehungen), "collection" (Sortierung/Filter), "indexes" (Performance). Beispiel: Feld hinzufĂŒgen – FĂŒge in "fields" ein neues Objekt ein, z. B. {"type": "varchar", "required": true}. Beispiel: Feld entfernen – Lösche den entsprechenden SchlĂŒssel aus "fields". Hinweis: Änderungen wirken sich auf die Datenbank aus (z. B. neue Spalten bei Rebuild). Layouts bearbeiten: Pfad: custom/Espo/Custom/Resources/layouts/{EntityType}/{LayoutType}.json (z. B. detail.json fĂŒr Detail-View). Struktur: Array von Panels, jedes mit "label" und "rows" (Arrays von Zellen mit {"name": "feldname"}). Beispiel: Feld hinzufĂŒgen – FĂŒge {"name": "neuesFeld"} in eine "rows"-Zeile ein. Beispiel: Feld entfernen – Lösche die entsprechende Zelle aus "rows". LayoutTypes: detail, list, edit, etc. – Passe Views an, um UI zu optimieren. Rebuild durchfĂŒhren: Nach Änderungen muss ein Rebuild durchgefĂŒhrt werden. Verwende: `./custom/scripts/check_and_rebuild.sh` (validiert JSON, prĂŒft Rechte, fĂŒhrt Rebuild durch) Siehe Abschnitt "Rebuild-Prozess" fĂŒr Details. ## Panel-Labels und Übersetzungen Um Relationship-Panels und Links korrekt zu beschriften, mĂŒssen Labels in den i18n-Sprachdateien definiert werden. **VollstĂ€ndige i18n-Dokumentation:** Siehe Abschnitt "Internationalisierung (i18n) und Tooltips" **KurzĂŒbersicht:** - Labels in **allen Sprachen** definieren (de_DE UND en_US) - Labels in **zwei Sektionen**: `fields` UND `links` - Nach Änderungen: `./custom/scripts/check_and_rebuild.sh` **Beispiel:** ```json // custom/Espo/Custom/Resources/i18n/de_DE/CBeteiligte.json { "fields": { "vmhvermieterbeteiligte": "Vermieter", "vmhmieterbeteiligte": "Mieter" }, "links": { "vmhvermieterbeteiligte": "Vermieter", "vmhmieterbeteiligte": "Mieter" } } ``` **Tooltips:** - Aktivierung: `"tooltip": true` in entityDef setzen - Definition in `tooltips`-Sektion der i18n-Dateien - **VollstĂ€ndige Dokumentation:** Siehe Abschnitt "Internationalisierung (i18n) und Tooltips" ## Custom JavaScript & CSS Integration ### 404-Fehler "Link does not exist" - **Symptom**: HTTP 404-Fehler in Logs: "Link does not exist" beim Versuch, eine Relationship anzuzeigen oder zu verknĂŒpfen. - **Ursache**: Bei hasMany-Relationships fehlt die Definition auf einer Seite der Beziehung. EspoCRM benötigt bidirektionale Link-Definitionen. - **Lösung**: - PrĂŒfe beide entityDefs-Dateien (z.B. `CBeteiligte.json` UND `Contact.json`). - Stelle sicher, dass beide Seiten den Link mit derselben `relationName` definieren. - Das `foreign`-Attribut muss jeweils auf den Link-Namen der Gegenseite zeigen. - Beispiel: ```json // In CBeteiligte.json: "contactsBeteiligte": { "type": "hasMany", "relationName": "cBeteiligteContact", "foreign": "cBeteiligteContact", "entity": "Contact" } // In Contact.json: "cBeteiligteContact": { "type": "hasMany", "relationName": "cBeteiligteContact", "foreign": "contactsBeteiligte", "entity": "CBeteiligte" } ``` - Nach Korrektur: `./custom/scripts/check_and_rebuild.sh` ausfĂŒhren. ### 500-Fehler bei Layout-Änderungen - **Symptom**: HTTP 500-Fehler beim Versuch, Layouts in der EspoCRM-UI zu bearbeiten (z.B. "Permission denied for custom/Espo/Custom/Resources/layouts/..."). - **Ursache**: Das `custom/`-Verzeichnis gehört `root:root`, aber der EspoCRM-Container lĂ€uft als `www-data`-User, der keine Schreibrechte hat. - **Lösung**: - FĂŒhre auf dem Host aus: `chown -R www-data:www-data /var/lib/docker/volumes/vmh-espocrm_espocrm/_data/custom` - Dies gibt `www-data` Schreibrechte fĂŒr Custom-Dateien. - **PrĂ€vention**: Stelle sicher, dass neue Custom-Dateien mit korrekten Berechtigungen erstellt werden (z.B. via Docker-Container als `www-data`). ### Allgemeine Tipps - **WICHTIG**: Nach jeder Änderung an Custom-Dateien das Check & Rebuild Script ausfĂŒhren: ```bash ./custom/scripts/check_and_rebuild.sh ``` Das Script prĂŒft automatisch auf hĂ€ufige Fehler (JSON-Syntax, Dateirechte) und fĂŒhrt bei Fehlerfreiheit den Rebuild durch. Siehe Abschnitt "Rebuild-Prozess" fĂŒr Details. - Logs prĂŒfen: `tail -n 100 /var/lib/docker/volumes/vmh-espocrm_espocrm/_data/data/logs/espo-YYYY-MM-DD.log` - Bei Relationship-Problemen: Logs nach "404" und "Link does not exist" durchsuchen: `tail -n 500 /var/lib/docker/volumes/vmh-espocrm_espocrm/_data/data/logs/espo-$(date +%Y-%m-%d).log | grep -A 3 "404\|Link does not exist"` - Bei DB-Problemen: Custom-Scripts wie `workflow_manager.php` verwenden. ## 9. Reports und Report-Panels EspoCRM bietet ĂŒber das Advanced Pack zwei Arten von Report-Integrationen: **Report-Filter** und **Report-Panels**. Diese ermöglichen die dynamische Anzeige von gefilterten Listen in Entity-Views. ### Report-Filter Report-Filter ermöglichen es, vordefinierte Filter auf List-Views anzuwenden, die in Datenbanktabellen gespeichert sind. #### Struktur und Dateien: 1. **entityDefs/{EntityType}.json** - Filter-Definition ```json { "collection": { "filters": { "reportFilterXXXXXXXXXX": { "isReportFilter": true, "id": "reportFilterIdHere" } } } } ``` 2. **selectDefs/{EntityType}.json** - Filter-Klasse ```json { "primaryFilterClassNameMap": { "reportFilterXXXXXXXXXX": "Espo\\Modules\\Advanced\\Classes\\Select\\Common\\PrimaryFilters\\ReportFilter" } } ``` 3. **clientDefs/{EntityType}.json** - Frontend-Integration ```json { "filterList": [ "__APPEND__", { "isReportFilter": true, "name": "reportFilterXXXXXXXXXX", "accessDataList": [ { "teamIdList": ["team-id-here"] } ] } ] } ``` 4. **i18n/{Language}/{EntityType}.json** - Übersetzungen ```json { "presetFilters": { "reportFilterXXXXXXXXXX": "Filter-Name" } } ``` ### Report-Panels Report-Panels zeigen Listen von EntitĂ€ten in Side-Panels der Detail-View an. Sie können Team-basierte Zugriffskontrolle haben. #### Struktur: **clientDefs/{EntityType}.json** - Panel-Definition ```json { "sidePanels": { "detail": [ "__APPEND__", { "isReportPanel": true, "name": "reportPanelXXXXXXXXXX", "label": "Panel-Titel", "view": "advanced:views/report-panel/record/panels/report-panel-side", "reportPanelId": "reportPanelIdHere", "reportType": "List", "reportEntityType": "EntityType", "displayType": "List", "displayTotal": false, "displayOnlyTotal": false, "useSiMultiplier": true, "accessDataList": [ { "scope": "EntityType" }, { "teamIdList": ["team-id-here"] } ] } ] } } ``` ### Wichtige Eigenschaften: - **`isReportFilter`/`isReportPanel`**: Markiert den Eintrag als Report-Element - **`accessDataList`**: Array von Zugriffsbedingungen (Team-IDs, Scopes) - **`reportType`**: `"List"` fĂŒr Listen-Reports - **`displayType`**: Anzeige-Typ (`"List"`, `"Chart"`, etc.) - **`view`**: Spezielle Report-Panel-View aus dem Advanced Pack - **`__APPEND__`**: Erweitert bestehende Arrays statt sie zu ĂŒberschreiben ### Best Practices: 1. **Naming Convention**: - Filter: `reportFilter{uniqueId}` (z.B. `reportFilter6972174b6540731c1`) - Panels: `reportPanel{uniqueId}` (z.B. `reportPanel697216784307d43ad`) 2. **Team-basierte Zugriffskontrolle**: - Definiere `teamIdList` in `accessDataList` fĂŒr eingeschrĂ€nkten Zugriff - Mehrere Teams können kombiniert werden 3. **Mehrsprachigkeit**: - Labels in allen Sprachen definieren (de_DE, en_US) - Fehlerhafte Labels können zu UI-Problemen fĂŒhren 4. **Datei-AbhĂ€ngigkeiten**: - Report-Filter benötigen 4 Dateien: entityDefs, selectDefs, clientDefs, i18n - Report-Panels benötigen 1 Datei: clientDefs - Fehlende Dateien fĂŒhren zu nicht-funktionalen Filtern 5. **Placeholder-Dateien**: - `logicDefs/{EntityType}.json` kann als leeres Objekt `{}` angelegt werden - Ermöglicht zukĂŒnftige Erweiterungen ohne Struktur-Änderungen ### Beispiel-Implementation: **Szenario**: UserTask-Filter fĂŒr Team "vermieterhelden" ```bash # entityDefs/BpmnUserTask.json { "collection": { "filters": { "reportFilter6972174b6540731c1": { "isReportFilter": true, "id": "6972174b6540731c1" } } } } # selectDefs/BpmnUserTask.json { "primaryFilterClassNameMap": { "reportFilter6972174b6540731c1": "Espo\\Modules\\Advanced\\Classes\\Select\\Common\\PrimaryFilters\\ReportFilter" } } # clientDefs/BpmnUserTask.json { "filterList": [ "__APPEND__", { "isReportFilter": true, "name": "reportFilter6972174b6540731c1", "accessDataList": [ { "teamIdList": ["68da9bdd622c9958a"] } ] } ] } # i18n/en_US/BpmnUserTask.json { "presetFilters": { "reportFilter6972174b6540731c1": "UserTask" } } ``` ### Troubleshooting: - **Filter erscheint nicht**: PrĂŒfe ob alle 4 Dateien existieren und Rebuild durchgefĂŒhrt wurde - **Zugriffsfehler**: ÜberprĂŒfe `teamIdList` und User-Team-Zuordnung - **Leere Liste**: Report-Definition in DB prĂŒfen (Tabelle: `report`) - **Falsches Label**: i18n-Dateien in allen Sprachen prĂŒfen ### Nach Änderungen: ```bash # Rebuild durchfĂŒhren (validiert JSON, prĂŒft Rechte) ./custom/scripts/check_and_rebuild.sh ``` ## Portal-Freigabe-System Um EntitĂ€ten fĂŒr Portalnutzer (Contact-EntitĂ€t) freizugeben, wurde ein konsistentes Freigabe-System implementiert: ### Implementierte Portal-Relationships: - **CVmhMietverhltnis** → `contactsMietverhltnis` (relationName: `cVmhMietverhltnisContact`) - **CBeteiligte** → `contactsBeteiligte` (relationName: `cBeteiligteContact`) - **CMietobjekt** → `contactsMietobjekt` (relationName: `cMietobjektContactPortal`) - **CAdressen** → `contactsAdressen` (relationName: `cAdressenContact`) - **CVmhRumungsklage** → `contactsRumungsklage` (relationName: `cVmhRumungsklageContact`) ### Pattern fĂŒr neue Portal-Relationships: 1. **entityDefs der HauptentitĂ€t** (z.B. `CBeteiligte.json`): ```json "contactsBeteiligte": { "type": "hasMany", "relationName": "cBeteiligteContact", "foreign": "cBeteiligteContact", "entity": "Contact", "audited": false, "isCustom": true } ``` 2. **entityDefs von Contact** (`Contact.json`): ```json "cBeteiligteContact": { "type": "hasMany", "relationName": "cBeteiligteContact", "foreign": "contactsBeteiligte", "entity": "CBeteiligte", "audited": false, "isCustom": true } ``` 3. **clientDefs der HauptentitĂ€t** (`CBeteiligte.json`): ```json "relationshipPanels": { "contactsBeteiligte": { "layout": null, "selectPrimaryFilterName": "portalUsers" } } ``` 4. **bottomPanelsDetail Layout** (Tab-Ansicht): ```json { "_tabBreak_0": { "index": 0, "tabBreak": true, "tabLabel": "Freigabe fĂŒr" }, "contactsBeteiligte": { "dynamicLogicVisible": null, "style": "warning", "dynamicLogicStyled": null, "sticked": true, "index": 1 } } ``` ### Wichtige Hinweise: - `selectPrimaryFilterName: "portalUsers"` filtert automatisch auf Portal-User - Tab "Freigabe fĂŒr" sollte immer der erste Tab im Bottom-Panel sein (index: 0) - Style "warning" hebt das Panel visuell hervor - Nach Änderungen: `./custom/scripts/check_and_rebuild.sh` - Beide Seiten der Relationship mĂŒssen in entityDefs definiert sein ## Troubleshooting ### "Link does not exist" (404-Fehler) **Symptom:** HTTP 404-Fehler in Logs beim Versuch, eine Relationship anzuzeigen oder zu verknĂŒpfen. **Ursache:** Bei hasMany-Relationships fehlt die Definition auf einer Seite der Beziehung. **Lösung:** 1. PrĂŒfe beide entityDefs-Dateien (z.B. `CBeteiligte.json` UND `Contact.json`) 2. Stelle sicher, dass beide Seiten den Link mit derselben `relationName` definieren 3. Das `foreign`-Attribut muss jeweils auf den Link-Namen der Gegenseite zeigen **Beispiel:** ```json // In CBeteiligte.json: "contactsBeteiligte": { "type": "hasMany", "relationName": "cBeteiligteContact", "foreign": "cBeteiligteContact", "entity": "Contact" } // In Contact.json: "cBeteiligteContact": { "type": "hasMany", "relationName": "cBeteiligteContact", "foreign": "contactsBeteiligte", "entity": "CBeteiligte" } ``` Nach Korrektur: `./custom/scripts/check_and_rebuild.sh` ### Tooltip zeigt nur Feldnamen **Symptom:** Tooltip zeigt nur "iban" statt vollstĂ€ndiger Beschreibung. **Ursache:** Fehlerhafte oder fehlende `en_US` i18n-Datei (en_US ist Fallback-Sprache). **Lösung:** 1. Existiert `custom/Espo/Custom/Resources/i18n/en_US/{Entity}.json`? 2. EnthĂ€lt es fehlerhafte Tooltip-Definitionen? 3. Sind alle Tooltips konsistent ĂŒber alle Sprachen? 4. VervollstĂ€ndige en_US-Datei mit korrekten englischen Übersetzungen **Korrekt:** ```json // de_DE/{Entity}.json { "fields": {"iban": "IBAN"}, "tooltips": {"iban": "Internationale Bankkontonummer im Format DE89..."} } // en_US/{Entity}.json { "fields": {"iban": "IBAN"}, "tooltips": {"iban": "International Bank Account Number in format DE89..."} } ``` Nach Korrektur: `./custom/scripts/check_and_rebuild.sh` ### Formula-Script wird nicht ausgefĂŒhrt **Symptom:** beforeSaveApiScript triggert nicht, keine Validierung. **Ursache:** Script in `entityDefs` statt `formula/` abgelegt. **Lösung:** - Erstelle separate Datei: `custom/Espo/Custom/Resources/metadata/formula/{Entity}.json` - NICHT in `entityDefs/{Entity}.json` einfĂŒgen! ```json // custom/Espo/Custom/Resources/metadata/formula/Entity.json { "beforeSaveApiScript": "if (field != null && field != '') { ... }" } ``` Nach Korrektur: `./custom/scripts/check_and_rebuild.sh` ### CSS-Änderungen nicht sichtbar **Symptom:** CSS-Änderungen werden nicht im Browser angezeigt. **Ursache:** Browser-Cache oder fehlende Registrierung. **Lösung:** 1. CSS in `custom/Espo/Custom/Resources/metadata/app/client.json` registrieren: ```json { "cssList": ["__APPEND__", "client/custom/css/my-styles.css"] } ``` 2. `./custom/scripts/check_and_rebuild.sh` ausfĂŒhren 3. Browser Hard Refresh (Ctrl+Shift+R / Cmd+Shift+R) ### "Permission denied" bei Layout-Bearbeitung **Symptom:** HTTP 500-Fehler beim Versuch, Layouts ĂŒber die UI zu bearbeiten. **Ursache:** Falsche Dateirechte - `custom/`-Verzeichnis gehört `root` statt `www-data`. **Lösung:** Das `check_and_rebuild.sh` Script prĂŒft und korrigiert Dateirechte automatisch. Falls manuelle Korrektur nötig: ```bash sudo chown -R www-data:www-data custom/ client/custom/ sudo find custom/ -type f -name "*.json" -exec chmod 664 {} \; sudo find custom/ -type d -exec chmod 775 {} \; ``` ### JSON-Syntax-Fehler **Symptom:** Rebuild schlĂ€gt fehl, Script zeigt "Invalid JSON" Fehler. **Ursache:** UngĂŒltiges JSON (fehlende Kommas, AnfĂŒhrungszeichen, etc.). **Lösung:** 1. `./custom/scripts/check_and_rebuild.sh` zeigt Datei und Zeilennummer an 2. JSON-Validator verwenden (z.B. jsonlint.com) 3. HĂ€ufige Fehler: - Komma nach letztem Array/Object-Element - Einfache statt doppelte AnfĂŒhrungszeichen - Fehlende schließende Klammern ### "Unknown function" in Formula **Symptom:** Error "Unknown function: xxx" beim Speichern. **Ursache:** Funktion existiert nicht oder ist nicht registriert. **Lösung:** 1. FĂŒr Custom-Funktionen: Registrierung in `app/formula.json` prĂŒfen 2. `string\isEmpty()` existiert nicht → verwende `field != null && field != ''` 3. Nach Registrierung: `./custom/scripts/check_and_rebuild.sh` ### Logs prĂŒfen **Wichtige Log-Dateien:** ```bash # Aktuelles Log (heutiges Datum) tail -n 100 data/logs/espo-$(date +%Y-%m-%d).log # Nach bestimmten Fehlern suchen tail -n 500 data/logs/espo-*.log | grep -i "404\|500\|error\|exception" # Formula-Script-Fehler tail -n 200 data/logs/espo-*.log | grep -i "formula\|script" # Relationship-Fehler tail -n 500 data/logs/espo-*.log | grep -i "link does not exist" ``` ### Allgemeine Troubleshooting-Schritte 1. **Nach jeder Änderung:** ```bash ./custom/scripts/check_and_rebuild.sh ``` 2. **Bei Frontend-Problemen:** - Browser Hard Refresh (Ctrl+Shift+R) - Browser-Cache komplett leeren - Inkognito-Modus testen 3. **Bei Backend-Problemen:** - Logs prĂŒfen (siehe oben) - Dateirechte prĂŒfen (`www-data:www-data`) - JSON-Syntax validieren 4. **Bei Relationship-Problemen:** - Beide Seiten der Relationship prĂŒfen - `relationName` identisch? - `foreign` zeigt auf korrekten Link-Namen? - Nach Rebuild: Cache manuell leeren falls nötig **VollstĂ€ndige Dokumentation zur Custom Directory Struktur:** Siehe `/custom/CUSTOM_DIRECTORY.md` **JavaScript-Module:** `client/custom/src/modules/` - AMD-Module fĂŒr wiederverwendbare Logik **Custom Views:** `client/custom/src/views/` - Backbone.js Views fĂŒr Entity-spezifisches UI **CSS-Stylesheets:** `client/custom/css/` - Custom CSS (Registrierung in `app/client.json` erforderlich) ### JavaScript-Module einbinden EspoCRM verwendet AMD/RequireJS fĂŒr JavaScript-Module. Custom JavaScript-Dateien werden in `client/custom/src/` abgelegt. **Beispiel: RVG-GebĂŒhrenrechner fĂŒr CVmhErstgespraech** **1. Modul erstellen** (`client/custom/src/modules/rvg-calculator.js`): ```javascript define('custom:modules/rvg-calculator', [], function () { return { kalkuliereKosten: function(streitwert, anzahlKlaeger, anzahlBeklagte, ustProzent) { // Berechnungslogik return { /* Ergebnisobjekt */ }; } }; }); ``` **2. Custom Field View erstellen** (`client/custom/src/views/{entity}/fields/{fieldname}.js`): ```javascript define('custom:views/c-vmh-erstgespraech/fields/rvg-calculated', [ 'views/fields/currency', 'custom:modules/rvg-calculator' ], function (Dep, RvgCalculator) { return Dep.extend({ setup: function () { Dep.prototype.setup.call(this); this.listenTo(this.model, 'change:streitwert change:anzahlVermieter', this.calculate); this.listenTo(this.model, 'sync', this.calculate); // Initial load }, calculate: function () { var result = RvgCalculator.kalkuliereKosten(/*...*/); this.model.set('kostenRaeumungsantrag', result.kostenRaeumungsantrag); } }); }); ``` **3. In entityDefs registrieren**: ```json { "fields": { "vergleich1InstanzGk": { "type": "currency", "readOnly": true, "view": "custom:views/c-vmh-erstgespraech/fields/rvg-calculated" } } } ``` **Wichtige Patterns:** - `listenTo(model, 'sync', callback)` - FĂŒr initiale Berechnung beim Laden - `listenTo(model, 'change:field1 change:field2', callback)` - FĂŒr ReaktivitĂ€t - `calculating` Flag verhindert Rekursion bei `model.set()` - Browser-Cache: Hard Refresh (Ctrl+Shift+R) nach JS-Änderungen erforderlich ### CSS-Manipulation & Feld-Hervorhebung EspoCRM erlaubt Custom CSS ĂŒber Metadata-Registrierung. **1. CSS-Datei erstellen** (`client/custom/css/erstgespraech-highlight.css`): ```css /* Feld-Selektor ĂŒber data-name Attribut */ .detail .cell[data-name="vorzusch1Instanz"] { background-color: #d4edda; padding: 10px; border-bottom: 4px solid #28a745; border-radius: 4px; } .detail .cell[data-name="vorzusch1Instanz"] .numeric-text { font-weight: bold; color: #155724; font-size: 1.1em; } ``` **2. CSS in Metadata registrieren** (`custom/Espo/Custom/Resources/metadata/app/client.json`): ```json { "cssList": [ "__APPEND__", "client/custom/css/erstgespraech-highlight.css" ] } ``` **Nach CSS-Änderungen:** 1. `./custom/scripts/check_and_rebuild.sh` ausfĂŒhren 2. Browser Hard Refresh (Ctrl+Shift+R) **CSS-Targeting-Strategien:** - **Feld-spezifisch:** `.cell[data-name="fieldName"]` - **Entity-spezifisch:** `body[data-controller="CVmhErstgespraech"]` - **View-spezifisch:** `.detail` (Detail-View), `.edit` (Edit-View), `.list` (List-View) - **Label vs. Value:** - `.label-text` - Feldlabel - `.numeric-text` / `.text-default` - Feldwert - `.field[data-name="..."]` - Field-Container **HTML-Struktur (Referenz):** ```html
3.067,63 €
``` **Best Practices:** - CSS-Dateien in `client/custom/css/` ablegen - `__APPEND__` verwenden um Core-CSS zu erweitern, nicht zu ĂŒberschreiben - Spezifische Selektoren verwenden um Kollisionen zu vermeiden - Nach CSS-Änderungen: `./custom/scripts/check_and_rebuild.sh` + Browser Hard Refresh ### RVG-GebĂŒhrenrechner (CVmhErstgespraech) **Implementierung:** Automatische Berechnung von Anwalts- und Gerichtskosten nach RVG 2025 / GKG **Komponenten:** 1. **Calculator-Modul** (`client/custom/src/modules/rvg-calculator.js`): - `getWertgebuehr()`: RVG 2025 Tabelle (65 Stufen, €500-€2M) - `getGerichtsgebuehr()`: GKG progressive Berechnung - `getZuschlag()`: §7 RVG Personenzuschlag (+0.3 pro Person, max +2.0) - `kalkuliereKosten()`: Hauptfunktion fĂŒr alle Szenarien 2. **Custom Field Views**: - `rvg-calculated.js`: Trigger fĂŒr alle Berechnungen - `beruecksichtigte-personen.js`: Live-Text-Anzeige "X Vermieter, Y Mieter, Z Dritte" - `warmmiete.js`: Kaltmiete + BK-Vorauszahlung + BK-Pauschale - `streitwert.js`: (Kaltmiete + BK-Pauschale) × 12 3. **Berechnete Felder** (readOnly currency fields): - Außergerichtliche GebĂŒhren: 1.3 + Zuschlag + Pauschale 20% (max 20€) - Kosten RĂ€umungsantrag: 0.3 + 0.3/Person + Pauschale - 1. Instanz: 3.0 GK + RA-Kosten (1.3 Verf + 1.2 Term + Pauschale) - SĂ€umnisszenario: 3.0 GK + reduzierte RA (0.5 Term statt 1.2) - Vergleichsszenario: 1.0 GK + RA (1.3 Verf + 1.2 Term + 1.0 Vergl) 4. **USt-Satz Handling**: - Enum Field: "0" / "19" (String, nicht Integer!) - Konvertierung: `parseInt(ustSatz)` → dann `/100` im Calculator - **Wichtig:** Expliziter Null-Check nötig, `0` ist falsy in `|| 19` 5. **ReaktivitĂ€t**: - Listener auf: streitwert, anzahlVermieter, anzahlMieter, anzahlSonstigeVolljhrigeBewohner, ustSatz - Initial berechnen mit `'sync'` Event - `calculating` Flag verhindert Rekursion **Layout-Panels:** - **GebĂŒhrenberechnung** (primary, info-note): Standard 1. Instanz Kosten - **SĂ€umnisszenario I. Inst.** (primary, success-note): Beklagte erscheint nicht - **Vergleichsszenario I. Inst.** (primary, success-note): Einigung vor Urteil **Hervorhebung:** "Vorauszuschießende Kosten I. Inst." wird via CSS hervorgehoben (grĂŒner Hintergrund, fetter Wert)