diff --git a/custom/docs/ESPOCRM_BEST_PRACTICES.md b/custom/docs/ESPOCRM_BEST_PRACTICES.md index 4e95347d..8df6e8d2 100644 --- a/custom/docs/ESPOCRM_BEST_PRACTICES.md +++ b/custom/docs/ESPOCRM_BEST_PRACTICES.md @@ -1,11 +1,29 @@ # EspoCRM Best Practices & Entwicklungsrichtlinien -**Version:** 2.1 -**Datum:** 9. März 2026 +**Version:** 2.2 +**Datum:** 10. März 2026 **Zielgruppe:** AI Code Agents & Entwickler --- +## 🔄 Letzte Änderungen (v2.2 - 10. März 2026) + +**Kritische Erkenntnisse:** +- ✅ **Service-Klassen sind PFLICHT**: Neue Section über erforderliche Service-Klassen +- ✅ **InjectableFactory-Fehler**: Detaillierter Troubleshooting-Guide hinzugefügt +- ✅ **validate_and_rebuild.py v2.0**: Erweiterte Log-Prüfung und CRUD-Tests +- ✅ **Real-World-Beispiel**: CAICollection/CAdvowareAkten Fehlerfall dokumentiert + +**Tools:** +- 🆕 Minimierter/Verbose Output-Modus +- 🆕 Log-Prüfung nach jedem API-Request +- 🆕 CRUD-Tests für alle Entities +- 🆕 KI-freundliches JSON-Feedback + +--- + +--- + ## 📋 Inhaltsverzeichnis 1. [Projekt-Übersicht](#projekt-übersicht) @@ -273,6 +291,90 @@ client/custom/ # Frontend-Code - `stream: true` - Stream/Activity Feed - `calendar: true` - Für Entities mit Datum-Feldern +### Service-Klassen (KRITISCH!) + +**⚠️ PFLICHT:** Jede Custom Entity MUSS eine Service-Klasse haben! + +**Problem:** Ohne Service-Klasse gibt EspoCRM beim Zugriff folgenden Fehler: +``` +CRITICAL: InjectableFactory: Class 'Espo\Custom\Services\{EntityName}' does not exist. +``` + +**Lösung:** Erstelle für jede Entity eine Service-Klasse: + +**Datei:** `custom/Espo/Custom/Services/{EntityName}.php` + +```php +getAcl()->checkEntityEdit($this->entityType)) { + throw new Forbidden(); + } + + // Load Entity + $entity = $this->getEntityManager()->getEntity($this->entityType, $id); + if (!$entity) { + throw new NotFound(); + } + + // Business Logic hier + $entity->set('status', 'Processed'); + $this->getEntityManager()->saveEntity($entity); + + return [ + 'success' => true, + 'id' => $entity->getId() + ]; + } +} +``` + +**Best Practice:** +1. ✅ Erstelle Service-Klasse SOFORT bei Entity-Erstellung +2. ✅ Auch wenn initial leer, erstelle sie trotzdem +3. ✅ Nutze Service für Business Logic statt Hooks +4. ✅ Verwende Type Hints für bessere IDE-Unterstützung +5. ✅ Dokumentiere Custom-Methoden mit DocBlocks + +--- + ### i18n (Internationalisierung) **KRITISCH:** Immer BEIDE Sprachen pflegen! @@ -1282,35 +1384,113 @@ php custom/scripts/workflow_manager.php list ## Testing & Validierung -### Validierungs-Tool +### Validierungs-Tool v2.0 (Erweitert) **Haupt-Tool:** `custom/scripts/validate_and_rebuild.py` +#### Verwendung + ```bash -# Vollständige Validierung + Rebuild +# Standard: Minimaler Output, vollständige Tests python3 custom/scripts/validate_and_rebuild.py +# Verbose: Detaillierte Ausgabe für Debugging +python3 custom/scripts/validate_and_rebuild.py -v + # Nur Validierung (kein Rebuild) python3 custom/scripts/validate_and_rebuild.py --dry-run -# Mit E2E Tests überspringen -python3 custom/scripts/validate_and_rebuild.py --skip-e2e +# Ohne Entity CRUD-Tests +python3 custom/scripts/validate_and_rebuild.py --skip-tests + +# Mit Custom-Credentials +python3 custom/scripts/validate_and_rebuild.py --username admin --password secret + +# Mit Custom Base-URL +python3 custom/scripts/validate_and_rebuild.py --base-url http://my-espo.local ``` -**Das Tool prüft:** +#### Was das Tool prüft + +**Phase 1: Statische Validierung** 1. ✅ JSON-Syntax aller Custom-Dateien 2. ✅ Relationship-Konsistenz (bidirektionale Links) -3. ✅ Formula-Script Platzierung -4. ✅ i18n-Vollständigkeit (de_DE + en_US) -5. ✅ Layout-Struktur (bottomPanelsDetail, detail.json) -6. ✅ Dateirechte (www-data:www-data) -7. ✅ CSS-Validierung (csslint) -8. ✅ JavaScript-Validierung (jshint) -9. ✅ PHP-Syntax (php -l) -10. ✅ EspoCRM Rebuild -11. ✅ E2E-Tests (CRUD-Operationen) +3. ✅ Erforderliche Dateien (scopes, i18n) +4. ✅ Dateirechte (www-data:www-data) + Auto-Fix +5. ✅ PHP-Syntax (php -l) -**Bei Fehlern:** Automatische Fehlerlog-Analyse der letzten 50 Log-Zeilen! +**Phase 2: Rebuild mit Log-Prüfung** +6. ✅ Cache-Clearing +7. ✅ EspoCRM Rebuild +8. ✅ **Log-Prüfung direkt nach Rebuild** (mit präzisen Zeitstempeln) + +**Phase 3: Live Entity-Tests** (wenn nicht übersprungen) +9. ✅ **CREATE** - Eintrag erstellen + Log-Check +10. ✅ **READ** - Eintrag lesen + Log-Check +11. ✅ **UPDATE** - Eintrag aktualisieren + Log-Check +12. ✅ **LIST** - Liste abrufen + Log-Check +13. ✅ **DELETE** - Eintrag löschen + Log-Check +14. ✅ Automatisches Cleanup aller Test-Records + +#### Neue Features (v2.0) + +**1. Log-Integration:** +- Nach **jedem** API-Request werden Logs geprüft +- Präzise Zeitstempel (nur Logs seit Request-Start) +- Test bricht ab bei Fehler in Logs +- Filtert bekannte unwichtige Meldungen + +**2. Intelligenter Output:** +- **Standard-Modus:** Nur Ergebnisse (✓/✗) +- **Verbose-Modus (`-v`):** Detaillierte Informationen, Timing, Debugging + +**3. CRUD-Tests für alle Entities:** +- Testet jede Custom-Entity einzeln +- Validiert kompletten Lifecycle +- Prüft API-Responses +- Detektiert Service-Klassen-Fehler + +**4. KI-freundliches Feedback:** +```json +{ + "status": "success", + "summary": { + "errors": 0, + "warnings": 1, + "entities_checked": 28 + }, + "errors": [], + "warnings": ["..."], + "recommendations": [ + "Fehlende Service-Klassen - erstelle für jede Entity eine Service-Klasse", + "Unvollständige Übersetzungen - füge fehlende i18n-Dateien hinzu" + ] +} +``` + +**5. Fehler-Abbruch bei Log-Errors:** +- Script bricht sofort ab wenn Rebuild Fehler in Logs produziert +- Zeigt relevante Fehlermeldungen an +- Keine weiteren Tests bei kritischen Fehlern + +#### Typischer Workflow + +```bash +# 1. Nach Code-Änderungen: Quick Check +python3 custom/scripts/validate_and_rebuild.py --dry-run + +# 2. Vor Deployment: Vollständiger Test +python3 custom/scripts/validate_and_rebuild.py -v + +# 3. Rebuild ohne Tests (schneller) +python3 custom/scripts/validate_and_rebuild.py --skip-tests +``` + +**Bei Fehlern:** Das Tool zeigt automatisch: +- Fehlerlog-Analyse +- Betroffene Dateien +- Konkrete Fehlermeldungen +- Empfohlene Fixes ### End-to-End Tests @@ -1542,6 +1722,102 @@ docker exec espocrm php -l custom/Espo/Custom/Controllers/MyController.php - [ ] Action-Methode korrekt benannt? (postAction..., getAction...) - [ ] ACL-Rechte? +### ⚠️ KRITISCH: InjectableFactory Error (Service-Klasse fehlt) + +**Fehlermeldung in Logs:** +``` +CRITICAL: (0) InjectableFactory: Class 'Espo\Custom\Services\{EntityName}' does not exist. +:: GET /{EntityName} :: /var/www/html/application/Espo/Core/InjectableFactory.php(164) +``` + +**Symptome:** +- Entity in UI sichtbar, aber nicht aufrufbar +- API-Requests schlagen fehl (leer oder 500 Error) +- Fehler tritt bei JEDEM Zugriff auf die Entity auf +- Rebuild erfolgreich, aber Funktionalität fehlt + +**Ursache:** +Für die Custom Entity `{EntityName}` wurde keine Service-Klasse erstellt. EspoCRM sucht nach `custom/Espo/Custom/Services/{EntityName}.php` und findet sie nicht. + +**Lösung:** + +**Schritt 1:** Erstelle Service-Datei + +```bash +# Erstelle Datei +touch custom/Espo/Custom/Services/{EntityName}.php +``` + +**Schritt 2:** Füge minimale Service-Klasse ein + +**Datei:** `custom/Espo/Custom/Services/{EntityName}.php` + +```php + custom/Espo/Custom/Services/CAICollection.php << 'EOF' + custom/Espo/Custom/Services/CAdvowareAkten.php << 'EOF' + bool: - """Eigene Validierung.""" - print_header("X. CUSTOM CHECK") - - # Prüflogik hier - if error_found: - self.errors.append("Fehlerbeschreibung") - return False - - print_success("Check erfolgreich") - return True -``` - -Dann in `validate_all()` hinzufügen: - -```python -if not self.validate_custom_check(): - all_valid = False -``` - -## Anforderungen - -- Python 3.6+ -- Keine zusätzlichen Packages erforderlich (nur Standard-Library) -- Optionale sudo-Rechte für Dateirechte-Korrektur - -## Integration in Workflow - -Das Script kann in automatisierte Workflows integriert werden: - +**Lösung:** ```bash -# In CI/CD Pipeline -python3 custom/scripts/validate_and_rebuild.py || exit 1 +# Service-Klasse erstellen +cat > custom/Espo/Custom/Services/{Entity}.php << 'EOF' +