Compare commits
15 Commits
8d912c6613
...
56271ca05a
| Author | SHA1 | Date | |
|---|---|---|---|
| 56271ca05a | |||
| 42d396f460 | |||
| cf60d2a91d | |||
| e1a7158931 | |||
| 15ecc7068f | |||
| ac58b51452 | |||
| 185524e432 | |||
| 99045d55d2 | |||
| c9e5846110 | |||
| 37e158c806 | |||
| 8c83e54650 | |||
| e878125489 | |||
| 06326d4d0b | |||
| 96becef2ba | |||
| 5750c4486e |
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -4,6 +4,10 @@
|
||||
"approve": true,
|
||||
"matchCommandLine": true
|
||||
},
|
||||
"./custom/scripts/check_and_rebuild.sh": true
|
||||
"./custom/scripts/check_and_rebuild.sh": true,
|
||||
"/^bash /var/lib/docker/volumes/vmh-espocrm_espocrm/_data/custom/scripts/check_and_rebuild\\.sh$/": {
|
||||
"approve": true,
|
||||
"matchCommandLine": true
|
||||
}
|
||||
}
|
||||
}
|
||||
657
README.md
657
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.
|
||||
@@ -141,6 +159,269 @@ JSON
|
||||
layoutAvailabilityList: Array für Feld-Sichtbarkeit in Layouts (z. B. ["list", "detail"]).
|
||||
layoutIgnoreList: Zu ignorierende Layouts.
|
||||
|
||||
## Internationalisierung (i18n) und Tooltips
|
||||
|
||||
**KRITISCH: Mehrsprachige Tooltip-Verwaltung**
|
||||
|
||||
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
|
||||
<?php
|
||||
namespace Espo\Custom\Classes\FormulaFunctions\IbanGroup;
|
||||
|
||||
use Espo\Core\Formula\Functions\BaseFunction;
|
||||
use Espo\Core\Formula\ArgumentList;
|
||||
|
||||
class ValidateType extends BaseFunction
|
||||
{
|
||||
public function process(ArgumentList $args)
|
||||
{
|
||||
if (count($args) < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$iban = $this->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:
|
||||
@@ -148,6 +429,185 @@ 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.
|
||||
|
||||
**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 <workflow-id>
|
||||
```
|
||||
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 <workflow-id> /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 <workflow-id>
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### 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")).
|
||||
@@ -393,7 +853,202 @@ sudo find custom/ -type f -name "*.json" -exec chmod 664 {} \;
|
||||
sudo find custom/ -type d -exec chmod 775 {} \;
|
||||
```
|
||||
|
||||
## 9. Portal-Freigabe-System
|
||||
## 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
|
||||
docker exec espocrm php /var/www/html/command.php Rebuild
|
||||
|
||||
# Oder Check-Script verwenden
|
||||
./custom/scripts/check_and_rebuild.sh
|
||||
```
|
||||
|
||||
## 10. Portal-Freigabe-System
|
||||
|
||||
Um Entitäten für Portalnutzer (Contact-Entität) freizugeben, wurde ein konsistentes Freigabe-System implementiert:
|
||||
|
||||
|
||||
291
client/custom/src/modules/rvg-calculator.js
Normal file
291
client/custom/src/modules/rvg-calculator.js
Normal file
@@ -0,0 +1,291 @@
|
||||
// RVG-Kalkulation ab 1.6.2025 (strenge Stufen-Regel)
|
||||
// Keine Interpolation innerhalb der Stufen – nur Sprung bei Überschreiten der Obergrenze
|
||||
|
||||
define('custom:modules/rvg-calculator', [], function () {
|
||||
|
||||
const rvgTabellen = {
|
||||
'2025': [ // [bis einschließlich, 1,0-Gebühr]
|
||||
[500, 51.50], [1000, 93.00], [1500, 134.50], [2000, 176.00],
|
||||
[3000, 235.50], [4000, 295.00], [5000, 354.50], [6000, 414.00],
|
||||
[7000, 473.50], [8000, 533.00], [9000, 592.50], [10000, 652.00],
|
||||
[13000, 707.00], [16000, 762.00], [19000, 817.00], [22000, 872.00],
|
||||
[25000, 927.00], [30000, 1013.00], [35000, 1099.00], [40000, 1185.00],
|
||||
[45000, 1271.00], [50000, 1357.00], [65000, 1456.50], [80000, 1556.00],
|
||||
[95000, 1655.50], [110000, 1755.00], [125000, 1854.50], [140000, 1954.00],
|
||||
[155000, 2053.50], [170000, 2153.00], [185000, 2252.50], [200000, 2352.00],
|
||||
[230000, 2492.00], [260000, 2632.00], [290000, 2772.00], [320000, 2912.00],
|
||||
[350000, 3052.00], [380000, 3192.00], [410000, 3332.00], [440000, 3472.00],
|
||||
[470000, 3612.00], [500000, 3752.00], [550000, 3927.00], [600000, 4102.00],
|
||||
[650000, 4277.00], [700000, 4452.00], [750000, 4627.00], [800000, 4802.00],
|
||||
[850000, 4977.00], [900000, 5152.00], [950000, 5327.00], [1000000, 5502.00],
|
||||
[1050000, 5677.00], [1100000, 5852.00], [1150000, 6027.00], [1200000, 6202.00],
|
||||
[1250000, 6377.00], [1300000, 6552.00], [1350000, 6727.00], [1400000, 6902.00],
|
||||
[1450000, 7077.00], [1500000, 7252.00], [1550000, 7427.00], [1600000, 7602.00],
|
||||
[1650000, 7777.00], [1700000, 7952.00], [1750000, 8127.00], [1800000, 8302.00],
|
||||
[1850000, 8477.00], [1900000, 8652.00], [1950000, 8827.00], [2000000, 9002.00]
|
||||
]
|
||||
};
|
||||
|
||||
function getWertgebuehr(streitwert, tabelleName) {
|
||||
tabelleName = tabelleName || '2025';
|
||||
const tabelle = rvgTabellen[tabelleName];
|
||||
if (!tabelle) return 0;
|
||||
|
||||
streitwert = Number(streitwert);
|
||||
if (isNaN(streitwert) || streitwert <= 0) return 0;
|
||||
if (streitwert > 2000000) return 0; // Fehler: zu hoch
|
||||
|
||||
for (let i = 0; i < tabelle.length; i++) {
|
||||
const [bis, gebuehr] = tabelle[i];
|
||||
if (streitwert <= bis) {
|
||||
return gebuehr;
|
||||
}
|
||||
}
|
||||
|
||||
return tabelle[tabelle.length - 1][1];
|
||||
}
|
||||
|
||||
function getZuschlag(anzahl) {
|
||||
const extra = Math.max(0, Math.floor(anzahl) - 1);
|
||||
return Math.min(2.0, extra * 0.3); // +0,3 pro weiterer Person, max +2,0
|
||||
}
|
||||
|
||||
// GKG Gerichtsgebühr berechnen (Gerichtskostengesetz)
|
||||
function getGerichtsgebuehr(streitwert) {
|
||||
if (streitwert <= 0) return 0;
|
||||
|
||||
let gebuehr = 40; // Basisgebühr bis 500€
|
||||
|
||||
if (streitwert <= 500) {
|
||||
return gebuehr;
|
||||
}
|
||||
|
||||
// Bis 2.000€: je angefangene 500€ weitere 21€
|
||||
if (streitwert <= 2000) {
|
||||
const schritte = Math.ceil((streitwert - 500) / 500);
|
||||
gebuehr += schritte * 21;
|
||||
return Math.round(gebuehr * 100) / 100; // Rundung auf Cent
|
||||
}
|
||||
gebuehr += Math.ceil((2000 - 500) / 500) * 21; // = 3 * 21 = 63
|
||||
|
||||
// Bis 10.000€: je angefangene 1.000€ weitere 22,50€
|
||||
if (streitwert <= 10000) {
|
||||
const schritte = Math.ceil((streitwert - 2000) / 1000);
|
||||
gebuehr += schritte * 22.50;
|
||||
return Math.round(gebuehr * 100) / 100;
|
||||
}
|
||||
gebuehr += Math.ceil((10000 - 2000) / 1000) * 22.50; // = 8 * 22.50 = 180
|
||||
|
||||
// Bis 25.000€: je angefangene 3.000€ weitere 30,50€
|
||||
if (streitwert <= 25000) {
|
||||
const schritte = Math.ceil((streitwert - 10000) / 3000);
|
||||
gebuehr += schritte * 30.50;
|
||||
return Math.round(gebuehr * 100) / 100;
|
||||
}
|
||||
gebuehr += Math.ceil((25000 - 10000) / 3000) * 30.50; // = 5 * 30.50 = 152.50
|
||||
|
||||
// Bis 50.000€: je angefangene 5.000€ weitere 40,50€
|
||||
if (streitwert <= 50000) {
|
||||
const schritte = Math.ceil((streitwert - 25000) / 5000);
|
||||
gebuehr += schritte * 40.50;
|
||||
return Math.round(gebuehr * 100) / 100;
|
||||
}
|
||||
gebuehr += Math.ceil((50000 - 25000) / 5000) * 40.50; // = 5 * 40.50 = 202.50
|
||||
|
||||
// Bis 200.000€: je angefangene 15.000€ weitere 140€
|
||||
if (streitwert <= 200000) {
|
||||
const schritte = Math.ceil((streitwert - 50000) / 15000);
|
||||
gebuehr += schritte * 140;
|
||||
return Math.round(gebuehr * 100) / 100;
|
||||
}
|
||||
gebuehr += Math.ceil((200000 - 50000) / 15000) * 140; // = 10 * 140 = 1400
|
||||
|
||||
// Bis 500.000€: je angefangene 30.000€ weitere 210€
|
||||
if (streitwert <= 500000) {
|
||||
const schritte = Math.ceil((streitwert - 200000) / 30000);
|
||||
gebuehr += schritte * 210;
|
||||
return Math.round(gebuehr * 100) / 100;
|
||||
}
|
||||
gebuehr += Math.ceil((500000 - 200000) / 30000) * 210; // = 10 * 210 = 2100
|
||||
|
||||
// Über 500.000€: je angefangene 50.000€ weitere 210€
|
||||
const schritte = Math.ceil((streitwert - 500000) / 50000);
|
||||
gebuehr += schritte * 210;
|
||||
|
||||
return Math.round(gebuehr * 100) / 100; // Rundung auf Cent
|
||||
}
|
||||
|
||||
return {
|
||||
kalkuliereKosten: function(streitwert, anzahlKlaeger, anzahlBeklagte, ustProzent, tabelle) {
|
||||
anzahlKlaeger = anzahlKlaeger || 1;
|
||||
anzahlBeklagte = anzahlBeklagte || 1;
|
||||
if (typeof ustProzent === 'undefined' || ustProzent === null) {
|
||||
ustProzent = 19;
|
||||
}
|
||||
tabelle = tabelle || '2025';
|
||||
|
||||
const ustSatz = ustProzent / 100;
|
||||
const w = getWertgebuehr(streitwert, tabelle);
|
||||
|
||||
if (w === 0) {
|
||||
return {
|
||||
streitwert: streitwert,
|
||||
wertgebuehr: 0,
|
||||
aussergerichtlichNetto: 0,
|
||||
aussergerichtlichBrutto: 0,
|
||||
gerichtskosten1Instanz: 0,
|
||||
anwaltskostenKlaeger1InstanzNetto: 0,
|
||||
anwaltskostenKlaeger1InstanzBrutto: 0,
|
||||
vergleichsgebuehrKlaeger1InstanzNetto: 0,
|
||||
vergleichsgebuehrKlaeger1InstanzBrutto: 0,
|
||||
anwaltskostenBeklagte1InstanzNetto: 0,
|
||||
anwaltskostenBeklagte1InstanzBrutto: 0,
|
||||
vergleichsgebuehrBeklagte1InstanzNetto: 0,
|
||||
vergleichsgebuehrBeklagte1InstanzBrutto: 0,
|
||||
gerichtskosten2Instanz: 0,
|
||||
anwaltskostenKlaeger2InstanzNetto: 0,
|
||||
anwaltskostenKlaeger2InstanzBrutto: 0,
|
||||
vergleichsgebuehrKlaeger2InstanzNetto: 0,
|
||||
vergleichsgebuehrKlaeger2InstanzBrutto: 0,
|
||||
anwaltskostenBeklagte2InstanzNetto: 0,
|
||||
anwaltskostenBeklagte2InstanzBrutto: 0,
|
||||
vergleichsgebuehrBeklagte2InstanzNetto: 0,
|
||||
vergleichsgebuehrBeklagte2InstanzBrutto: 0,
|
||||
zwangsvollstreckungRaeumungNetto: 0,
|
||||
zwangsvollstreckungRaeumungBrutto: 0,
|
||||
kostenRaeumungsantrag: 0,
|
||||
vorzusch1InstanzNetto: 0,
|
||||
vorzusch1InstanzBrutto: 0,
|
||||
vergleich1InstanzGk: 0,
|
||||
vergleich1InstanzAnwK: 0,
|
||||
vergleich1InstanzSumme: 0,
|
||||
saeumnis1InstanzGk: 0,
|
||||
saeumnis1InstanzAnwK: 0,
|
||||
saeumnis1InstanzSumme: 0
|
||||
};
|
||||
}
|
||||
|
||||
const zuschlagK = getZuschlag(anzahlKlaeger);
|
||||
const zuschlagB = getZuschlag(anzahlBeklagte);
|
||||
|
||||
|
||||
// Außergerichtlich (nur Klägerseite)
|
||||
const ausserSatz = 1.3 + zuschlagK;
|
||||
const ausserGeb = w * ausserSatz;
|
||||
const ausserPausch = Math.min(20, 0.2 * ausserGeb);
|
||||
const ausserNetto = ausserGeb + ausserPausch;
|
||||
const ausserBrutto = ausserNetto * (1 + ustSatz);
|
||||
|
||||
// Gerichtskosten 1. Instanz (GKG-Tabelle, 3,0 Gebühren)
|
||||
const gkEinfach = getGerichtsgebuehr(streitwert);
|
||||
const gk1 = gkEinfach * 3.0;
|
||||
|
||||
// Anwaltskosten Kläger 1. Instanz
|
||||
const inst1VerfK = w * (1.3 + zuschlagK);
|
||||
const inst1TermK = w * 1.2;
|
||||
const inst1PauschK = Math.min(20, 0.2 * (inst1VerfK + inst1TermK));
|
||||
const inst1NettoK = inst1VerfK + inst1TermK + inst1PauschK;
|
||||
const inst1BruttoK = inst1NettoK * (1 + ustSatz);
|
||||
|
||||
const vergl1KNetto = w * 1.0;
|
||||
const vergl1KBrutto = vergl1KNetto * (1 + ustSatz);
|
||||
|
||||
// Anwaltskosten Beklagte 1. Instanz
|
||||
const inst1VerfB = w * (1.3 + zuschlagB);
|
||||
const inst1TermB = w * 1.2;
|
||||
const inst1PauschB = Math.min(20, 0.2 * (inst1VerfB + inst1TermB));
|
||||
const inst1NettoB = inst1VerfB + inst1TermB + inst1PauschB;
|
||||
const inst1BruttoB = inst1NettoB * (1 + ustSatz);
|
||||
|
||||
const vergl1BNetto = w * 1.0;
|
||||
const vergl1BBrutto = vergl1BNetto * (1 + ustSatz);
|
||||
|
||||
// Gerichtskosten 2. Instanz (GKG-Tabelle, 4,0 Gebühren)
|
||||
const gk2 = gkEinfach * 4.0;
|
||||
|
||||
// Anwaltskosten Kläger 2. Instanz
|
||||
const inst2VerfK = w * (1.6 + zuschlagK);
|
||||
const inst2TermK = w * 1.2;
|
||||
const inst2PauschK = Math.min(20, 0.2 * (inst2VerfK + inst2TermK));
|
||||
const inst2NettoK = inst2VerfK + inst2TermK + inst2PauschK;
|
||||
const inst2BruttoK = inst2NettoK * (1 + ustSatz);
|
||||
|
||||
const vergl2KNetto = w * 1.0;
|
||||
const vergl2KBrutto = vergl2KNetto * (1 + ustSatz);
|
||||
|
||||
// Anwaltskosten Beklagte 2. Instanz
|
||||
const inst2VerfB = w * (1.6 + zuschlagB);
|
||||
const inst2TermB = w * 1.2;
|
||||
const inst2PauschB = Math.min(20, 0.2 * (inst2VerfB + inst2TermB));
|
||||
const inst2NettoB = inst2VerfB + inst2TermB + inst2PauschB;
|
||||
const inst2BruttoB = inst2NettoB * (1 + ustSatz);
|
||||
|
||||
const vergl2BNetto = w * 1.0;
|
||||
const vergl2BBrutto = vergl2BNetto * (1 + ustSatz);
|
||||
|
||||
// Zwangsvollstreckung Räumung = Räumungsantrag (identisch)
|
||||
// Verfahrensgebühr 0,3 + 0,3 pro weitere Person + Pauschale 20% (max 20€)
|
||||
const zvBasis = w * 0.3;
|
||||
const zvExtraPersonen = ((anzahlKlaeger - 1) + (anzahlBeklagte - 1)) * 0.3 * w;
|
||||
const zvVerfK = zvBasis + zvExtraPersonen;
|
||||
const zvPauschK = Math.min(20, 0.2 * zvVerfK);
|
||||
const zvNetto = zvVerfK + zvPauschK;
|
||||
const zvBrutto = zvNetto * (1 + ustSatz);
|
||||
|
||||
// Kosten Räumungsantrag (identisch mit Zwangsvollstreckung)
|
||||
const raeumGesamt = zvBrutto;
|
||||
|
||||
// Vorauszuschießende Kosten 1. Instanz (inkl. Gerichtskosten!)
|
||||
const vorzusch1Netto = inst1VerfK + inst1TermK + inst1PauschK + gk1;
|
||||
const vorzusch1Brutto = (inst1VerfK + inst1TermK + inst1PauschK) * (1 + ustSatz) + gk1;
|
||||
|
||||
// Vergleichsszenario 1. Instanz (Vergleichsgebühr + nur 1,0 Gerichtsgebühr statt 3,0)
|
||||
const vergl1Gk = gkEinfach * 1.0; // nur 1,0 statt 3,0
|
||||
const vergl1AnwKNetto = inst1VerfK + inst1TermK + vergl1KNetto + inst1PauschK; // Verf + Term + Vergl
|
||||
const vergl1AnwKBrutto = vergl1AnwKNetto * (1 + ustSatz);
|
||||
const vergl1GesamtBrutto = vergl1AnwKBrutto + vergl1Gk;
|
||||
|
||||
// Säumnisszenario 1. Instanz (0,5 Terminsgebühr statt 1,2)
|
||||
const saeum1TermK = w * 0.5; // reduzierte Terminsgebühr
|
||||
const saeum1AnwKNetto = inst1VerfK + saeum1TermK + inst1PauschK;
|
||||
const saeum1AnwKBrutto = saeum1AnwKNetto * (1 + ustSatz);
|
||||
const saeum1GesamtBrutto = saeum1AnwKBrutto + gk1;
|
||||
|
||||
return {
|
||||
streitwert: streitwert,
|
||||
wertgebuehr: w,
|
||||
aussergerichtlichNetto: ausserNetto,
|
||||
aussergerichtlichBrutto: ausserBrutto,
|
||||
gerichtskosten1Instanz: gk1,
|
||||
anwaltskostenKlaeger1InstanzNetto: inst1NettoK,
|
||||
anwaltskostenKlaeger1InstanzBrutto: inst1BruttoK,
|
||||
vergleichsgebuehrKlaeger1InstanzNetto: vergl1KNetto,
|
||||
vergleichsgebuehrKlaeger1InstanzBrutto: vergl1KBrutto,
|
||||
anwaltskostenBeklagte1InstanzNetto: inst1NettoB,
|
||||
anwaltskostenBeklagte1InstanzBrutto: inst1BruttoB,
|
||||
vergleichsgebuehrBeklagte1InstanzNetto: vergl1BNetto,
|
||||
vergleichsgebuehrBeklagte1InstanzBrutto: vergl1BBrutto,
|
||||
gerichtskosten2Instanz: gk2,
|
||||
anwaltskostenKlaeger2InstanzNetto: inst2NettoK,
|
||||
anwaltskostenKlaeger2InstanzBrutto: inst2BruttoK,
|
||||
vergleichsgebuehrKlaeger2InstanzNetto: vergl2KNetto,
|
||||
vergleichsgebuehrKlaeger2InstanzBrutto: vergl2KBrutto,
|
||||
anwaltskostenBeklagte2InstanzNetto: inst2NettoB,
|
||||
anwaltskostenBeklagte2InstanzBrutto: inst2BruttoB,
|
||||
vergleichsgebuehrBeklagte2InstanzNetto: vergl2BNetto,
|
||||
vergleichsgebuehrBeklagte2InstanzBrutto: vergl2BBrutto,
|
||||
zwangsvollstreckungRaeumungNetto: zvNetto,
|
||||
zwangsvollstreckungRaeumungBrutto: zvBrutto,
|
||||
kostenRaeumungsantrag: raeumGesamt,
|
||||
vorzusch1InstanzNetto: vorzusch1Netto,
|
||||
vorzusch1InstanzBrutto: vorzusch1Brutto,
|
||||
vergleich1InstanzGk: vergl1Gk,
|
||||
vergleich1InstanzAnwK: vergl1AnwKBrutto,
|
||||
vergleich1InstanzSumme: vergl1GesamtBrutto,
|
||||
saeumnis1InstanzGk: gk1,
|
||||
saeumnis1InstanzAnwK: saeum1AnwKBrutto,
|
||||
saeumnis1InstanzSumme: saeum1GesamtBrutto
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
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.calculating = false;
|
||||
|
||||
// Listen to changes on relevant fields (combined listener)
|
||||
this.listenTo(this.model, 'change:streitwert change:anzahlVermieter change:anzahlMieter change:anzahlSonstigeVolljhrigeBewohner change:ustSatz', this.calculate.bind(this));
|
||||
|
||||
// Initial calculation
|
||||
this.calculate();
|
||||
},
|
||||
|
||||
calculate: function () {
|
||||
if (this.calculating) return;
|
||||
this.calculating = true;
|
||||
|
||||
var streitwert = parseFloat(this.model.get('streitwert')) || 0;
|
||||
var anzahlKlaeger = parseInt(this.model.get('anzahlVermieter')) || 1;
|
||||
var anzahlMieter = parseInt(this.model.get('anzahlMieter')) || 0;
|
||||
var anzahlSonstige = parseInt(this.model.get('anzahlSonstigeVolljhrigeBewohner')) || 0;
|
||||
var anzahlBeklagte = anzahlMieter + anzahlSonstige;
|
||||
if (anzahlBeklagte < 1) anzahlBeklagte = 1;
|
||||
|
||||
// USt-Satz: "0" oder "19" (String) direkt als Integer
|
||||
var ustSatzString = this.model.get('ustSatz') || '19';
|
||||
var ustSatz = parseInt(ustSatzString);
|
||||
|
||||
var result = RvgCalculator.kalkuliereKosten(streitwert, anzahlKlaeger, anzahlBeklagte, ustSatz);
|
||||
|
||||
// Update all cost fields
|
||||
this.model.set({
|
||||
'aussergerichtlicheGebuehren': result.aussergerichtlichBrutto,
|
||||
'kostenRaeumungsantrag': result.kostenRaeumungsantrag,
|
||||
'gerichtskosten1Instanz': result.gerichtskosten1Instanz,
|
||||
'anwaltskostenKlaeger1Instanz': result.anwaltskostenKlaeger1InstanzBrutto,
|
||||
'vorzusch1Instanz': result.vorzusch1InstanzBrutto,
|
||||
'vergleich1InstanzGk': result.vergleich1InstanzGk,
|
||||
'vergleich1InstanzAnwK': result.vergleich1InstanzAnwK,
|
||||
'vergleich1InstanzSumme': result.vergleich1InstanzSumme,
|
||||
'saeumnis1InstanzGk': result.saeumnis1InstanzGk,
|
||||
'saeumnis1InstanzAnwK': result.saeumnis1InstanzAnwK,
|
||||
'saeumnis1InstanzSumme': result.saeumnis1InstanzSumme
|
||||
});
|
||||
|
||||
this.calculating = false;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
define('custom:views/c-vmh-erstgespraech/fields/streitwert', ['views/fields/currency'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.calculating = false;
|
||||
|
||||
// Listen to changes on dependent fields
|
||||
this.listenTo(this.model, 'change:kaltmiete', this.calculate.bind(this));
|
||||
this.listenTo(this.model, 'change:bKPauschale', this.calculate.bind(this));
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
// Don't calculate on initial render - only on changes
|
||||
},
|
||||
|
||||
calculate: function () {
|
||||
if (this.calculating) return;
|
||||
this.calculating = true;
|
||||
|
||||
var kaltmiete = parseFloat(this.model.get('kaltmiete')) || 0;
|
||||
var bKPauschale = parseFloat(this.model.get('bKPauschale')) || 0;
|
||||
|
||||
var streitwert = (kaltmiete + bKPauschale) * 12;
|
||||
|
||||
// Set value - this will trigger the parent view to update
|
||||
this.model.set('streitwert', streitwert);
|
||||
|
||||
this.calculating = false;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
define('custom:views/c-vmh-erstgespraech/fields/warmmiete', ['views/fields/currency'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
this.calculating = false;
|
||||
|
||||
// Listen to changes on dependent fields
|
||||
this.listenTo(this.model, 'change:kaltmiete', this.calculate.bind(this));
|
||||
this.listenTo(this.model, 'change:bKVorauszahlung', this.calculate.bind(this));
|
||||
this.listenTo(this.model, 'change:bKPauschale', this.calculate.bind(this));
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
Dep.prototype.afterRender.call(this);
|
||||
// Don't calculate on initial render - only on changes
|
||||
},
|
||||
|
||||
calculate: function () {
|
||||
if (this.calculating) return;
|
||||
this.calculating = true;
|
||||
|
||||
var kaltmiete = parseFloat(this.model.get('kaltmiete')) || 0;
|
||||
var bKVorauszahlung = parseFloat(this.model.get('bKVorauszahlung')) || 0;
|
||||
var bKPauschale = parseFloat(this.model.get('bKPauschale')) || 0;
|
||||
|
||||
var warmmiete = kaltmiete + bKVorauszahlung + bKPauschale;
|
||||
|
||||
// Set value - this will trigger the parent view to update
|
||||
this.model.set('warmmiete', warmmiete);
|
||||
|
||||
this.calculating = false;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
49
client/custom/src/views/c-vmh-erstgespraech/record/edit.js
Normal file
49
client/custom/src/views/c-vmh-erstgespraech/record/edit.js
Normal file
@@ -0,0 +1,49 @@
|
||||
define('custom:views/c-vmh-erstgespraech/record/edit', ['views/record/edit'], function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setup: function () {
|
||||
Dep.prototype.setup.call(this);
|
||||
|
||||
// Listen for changes on Kaltmiete, BK-Vorauszahlung, and BK-Pauschale
|
||||
this.listenTo(this.model, 'change:kaltmiete change:bKVorauszahlung change:bKPauschale', function () {
|
||||
this.calculateFields();
|
||||
}, this);
|
||||
|
||||
// Initial calculation after fields are ready
|
||||
this.once('after:render', function () {
|
||||
this.calculateFields();
|
||||
}, this);
|
||||
},
|
||||
|
||||
calculateFields: function () {
|
||||
var kaltmiete = parseFloat(this.model.get('kaltmiete')) || 0;
|
||||
var bKVorauszahlung = parseFloat(this.model.get('bKVorauszahlung')) || 0;
|
||||
var bKPauschale = parseFloat(this.model.get('bKPauschale')) || 0;
|
||||
|
||||
// Berechne Warmmiete
|
||||
var warmmiete = kaltmiete + bKVorauszahlung + bKPauschale;
|
||||
this.model.set('warmmiete', warmmiete, {silent: false});
|
||||
|
||||
// Berechne Streitwert
|
||||
var streitwert = (kaltmiete + bKPauschale) * 12;
|
||||
this.model.set('streitwert', streitwert, {silent: false});
|
||||
|
||||
// Force update der View
|
||||
if (this.hasView('warmmiete')) {
|
||||
var warmmieteView = this.getView('warmmiete');
|
||||
if (warmmieteView && warmmieteView.reRender) {
|
||||
warmmieteView.reRender();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasView('streitwert')) {
|
||||
var streitwertView = this.getView('streitwert');
|
||||
if (streitwertView && streitwertView.reRender) {
|
||||
streitwertView.reRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
121
custom/CUSTOM_DIRECTORY.md
Normal file
121
custom/CUSTOM_DIRECTORY.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
## 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 <action> [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-<entity>-<funktion>.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 \
|
||||
<workflow-id> /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
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Custom\Classes\FormulaFunctions\IbanGroup;
|
||||
|
||||
use Espo\Core\Formula\Functions\BaseFunction;
|
||||
use Espo\Core\Formula\ArgumentList;
|
||||
|
||||
/**
|
||||
* IBAN-Validierung mit Modulo-97-Algorithmus (ISO 13616)
|
||||
* Mathematische Prüfung ohne externe Datenbank
|
||||
*/
|
||||
class ValidateType extends BaseFunction
|
||||
{
|
||||
public function process(ArgumentList $args)
|
||||
{
|
||||
if (count($args) < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$iban = $this->evaluate($args[0]);
|
||||
|
||||
if (!$iban || !is_string($iban)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Leerzeichen entfernen und in Großbuchstaben umwandeln
|
||||
$iban = strtoupper(str_replace(' ', '', trim($iban)));
|
||||
|
||||
// Basis-Format prüfen: Mindestens 15, maximal 34 Zeichen
|
||||
$length = strlen($iban);
|
||||
if ($length < 15 || $length > 34) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format prüfen: 2 Buchstaben (Ländercode) + 2 Ziffern (Prüfziffer) + Rest alphanumerisch
|
||||
if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/', $iban)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ländercode extrahieren und Länge prüfen (optionale erweiterte Prüfung)
|
||||
$countryCode = substr($iban, 0, 2);
|
||||
$expectedLength = $this->getIbanLength($countryCode);
|
||||
if ($expectedLength && $length !== $expectedLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modulo-97-Algorithmus (ISO 13616)
|
||||
// 1. IBAN umstellen: ersten 4 Zeichen ans Ende
|
||||
$rearranged = substr($iban, 4) . substr($iban, 0, 4);
|
||||
|
||||
// 2. Buchstaben in Zahlen umwandeln (A=10, B=11, ..., Z=35)
|
||||
$numericString = '';
|
||||
for ($i = 0; $i < strlen($rearranged); $i++) {
|
||||
$char = $rearranged[$i];
|
||||
if (ctype_alpha($char)) {
|
||||
// A=65 -> 10, B=66 -> 11, ..., Z=90 -> 35
|
||||
$numericString .= (string)(ord($char) - 55);
|
||||
} else {
|
||||
$numericString .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Modulo 97 berechnen (schrittweise wegen großer Zahlen)
|
||||
$remainder = 0;
|
||||
for ($i = 0; $i < strlen($numericString); $i++) {
|
||||
$remainder = ($remainder * 10 + (int)$numericString[$i]) % 97;
|
||||
}
|
||||
|
||||
// 4. Rest muss genau 1 sein für gültige IBAN
|
||||
return $remainder === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die erwartete Länge einer IBAN für einen Ländercode zurück
|
||||
* Quelle: ISO 13616 / SWIFT IBAN Registry
|
||||
*/
|
||||
private function getIbanLength(string $countryCode): ?int
|
||||
{
|
||||
$lengths = [
|
||||
'AD' => 24, 'AE' => 23, 'AL' => 28, 'AT' => 20, 'AZ' => 28,
|
||||
'BA' => 20, 'BE' => 16, 'BG' => 22, 'BH' => 22, 'BR' => 29,
|
||||
'BY' => 28, 'CH' => 21, 'CR' => 22, 'CY' => 28, 'CZ' => 24,
|
||||
'DE' => 22, 'DK' => 18, 'DO' => 28, 'EE' => 20, 'EG' => 29,
|
||||
'ES' => 24, 'FI' => 18, 'FO' => 18, 'FR' => 27, 'GB' => 22,
|
||||
'GE' => 22, 'GI' => 23, 'GL' => 18, 'GR' => 27, 'GT' => 28,
|
||||
'HR' => 21, 'HU' => 28, 'IE' => 22, 'IL' => 23, 'IS' => 26,
|
||||
'IT' => 27, 'JO' => 30, 'KW' => 30, 'KZ' => 20, 'LB' => 28,
|
||||
'LC' => 32, 'LI' => 21, 'LT' => 20, 'LU' => 20, 'LV' => 21,
|
||||
'MC' => 27, 'MD' => 24, 'ME' => 22, 'MK' => 19, 'MR' => 27,
|
||||
'MT' => 31, 'MU' => 30, 'NL' => 18, 'NO' => 15, 'PK' => 24,
|
||||
'PL' => 28, 'PS' => 29, 'PT' => 25, 'QA' => 29, 'RO' => 24,
|
||||
'RS' => 22, 'SA' => 24, 'SE' => 24, 'SI' => 19, 'SK' => 24,
|
||||
'SM' => 27, 'TN' => 24, 'TR' => 26, 'UA' => 29, 'VA' => 22,
|
||||
'VG' => 24, 'XK' => 20,
|
||||
];
|
||||
|
||||
return $lengths[$countryCode] ?? null;
|
||||
}
|
||||
}
|
||||
7
custom/Espo/Custom/Controllers/CBankverbindungen.php
Normal file
7
custom/Espo/Custom/Controllers/CBankverbindungen.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Custom\Controllers;
|
||||
|
||||
class CBankverbindungen extends \Espo\Core\Templates\Controllers\Base
|
||||
{
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Custom\Controllers;
|
||||
|
||||
class CVMHBeteiligte extends \Espo\Core\Templates\Controllers\Person
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Custom\Hooks\CBankverbindungen;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Utils\Language;
|
||||
|
||||
class BankdatenValidation
|
||||
{
|
||||
private $language;
|
||||
|
||||
public function __construct(Language $language)
|
||||
{
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
public function beforeSave(Entity $entity, array $options): void
|
||||
{
|
||||
// IBAN-Normalisierung und Validierung
|
||||
$iban = $entity->get('iban');
|
||||
if ($iban !== null && $iban !== '') {
|
||||
// IBAN normalisieren: Leerzeichen entfernen und Großbuchstaben
|
||||
$ibanClean = strtoupper(str_replace(' ', '', $iban));
|
||||
$entity->set('iban', $ibanClean);
|
||||
|
||||
// Mathematische IBAN-Prüfung mit Modulo-97
|
||||
if (!$this->validateIban($ibanClean)) {
|
||||
$message = $this->language->translateLabel('invalidIbanChecksum', 'messages', 'CBankverbindungen');
|
||||
throw new BadRequest($message);
|
||||
}
|
||||
}
|
||||
|
||||
// BIC-Normalisierung und Validierung
|
||||
$bic = $entity->get('bic');
|
||||
if ($bic !== null && $bic !== '') {
|
||||
// BIC normalisieren: Leerzeichen entfernen und Großbuchstaben
|
||||
$bicClean = strtoupper(str_replace(' ', '', $bic));
|
||||
$entity->set('bic', $bicClean);
|
||||
|
||||
// BIC-Format prüfen: 8 oder 11 Zeichen
|
||||
$bicLength = strlen($bicClean);
|
||||
if ($bicLength !== 8 && $bicLength !== 11) {
|
||||
$message = $this->language->translateLabel('invalidBicLength', 'messages', 'CBankverbindungen');
|
||||
throw new BadRequest($message);
|
||||
}
|
||||
|
||||
// BIC-Format prüfen: Regex
|
||||
if (!preg_match('/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/', $bicClean)) {
|
||||
$message = $this->language->translateLabel('invalidBicFormat', 'messages', 'CBankverbindungen');
|
||||
throw new BadRequest($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function validateIban(string $iban): bool
|
||||
{
|
||||
// IBAN-Länge prüfen (mindestens 15 Zeichen)
|
||||
if (strlen($iban) < 15) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// IBAN umstellen: erste 4 Zeichen ans Ende
|
||||
$rearranged = substr($iban, 4) . substr($iban, 0, 4);
|
||||
|
||||
// Buchstaben in Zahlen umwandeln (A=10, B=11, ..., Z=35)
|
||||
$numeric = '';
|
||||
for ($i = 0; $i < strlen($rearranged); $i++) {
|
||||
$char = $rearranged[$i];
|
||||
if (ctype_alpha($char)) {
|
||||
$numeric .= (string)(ord($char) - ord('A') + 10);
|
||||
} else {
|
||||
$numeric .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
// Modulo-97-Prüfung
|
||||
return $this->bcmod($numeric, '97') === '1';
|
||||
}
|
||||
|
||||
private function bcmod(string $number, string $modulus): string
|
||||
{
|
||||
// Für große Zahlen: Modulo in Schritten berechnen
|
||||
$take = 9;
|
||||
$mod = '';
|
||||
|
||||
do {
|
||||
$a = (int)($mod . substr($number, 0, $take));
|
||||
$number = substr($number, $take);
|
||||
$mod = (string)($a % (int)$modulus);
|
||||
} while (strlen($number) > 0);
|
||||
|
||||
return $mod;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "إنشاء {الكيانTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "العنوان"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "الاجتماعات",
|
||||
"calls": "المكالمات",
|
||||
"tasks": "مهام"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "إنشاء {الكيانTypeTranslated}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Създаване на Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Адрес"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Срещи",
|
||||
"calls": "Разговори",
|
||||
"tasks": "Задачи"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Създаване на VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Vytvořit Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Vytvořit VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Opret Bankverbindung "
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresse"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Møder",
|
||||
"calls": "Opkald",
|
||||
"tasks": "Opgaver"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Opret VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Bankverbindung erstellen",
|
||||
"CBankverbindungen": "Bankverbindungen"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"iban": "IBAN",
|
||||
"bic": "BIC/SWIFT",
|
||||
"kontoinhaber": "Kontoinhaber",
|
||||
"bankname": "Bankname",
|
||||
"istAktiv": "Ist aktiv",
|
||||
"istStandard": "Standard-Konto",
|
||||
"advowareKontoId": "Advoware Konto-ID",
|
||||
"advowareLastSync": "Advoware letzte Synchronisation",
|
||||
"syncStatus": "Sync-Status",
|
||||
"beteiligte": "Beteiligter",
|
||||
"createdAt": "Erstellt am",
|
||||
"modifiedAt": "Geändert am",
|
||||
"createdBy": "Erstellt von",
|
||||
"modifiedBy": "Geändert von",
|
||||
"assignedUser": "Zugewiesen an",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"links": {
|
||||
"beteiligte": "Beteiligter",
|
||||
"createdBy": "Erstellt von",
|
||||
"modifiedBy": "Geändert von",
|
||||
"assignedUser": "Zugewiesen an",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"tooltips": {
|
||||
"name": "Bezeichnung der Bankverbindung (z.B. 'Hauptkonto', 'Geschäftskonto', 'Mietkautionskonto')",
|
||||
"iban": "Internationale Bankkontonummer im Format DE89370400440532013000. Nur Großbuchstaben und Ziffern, 2-stelliger Ländercode gefolgt von Prüfziffer und Kontonummer (max. 34 Zeichen)",
|
||||
"bic": "Bank Identifier Code (SWIFT-Code) im Format COBADEFFXXX. 8 oder 11 Zeichen: 4 Zeichen Bankcode, 2 Zeichen Ländercode, 2 Zeichen Ortscode, optional 3 Zeichen Filialcode",
|
||||
"kontoinhaber": "Vollständiger Name des Kontoinhabers, wie auf dem Bankkonto hinterlegt",
|
||||
"bankname": "Name der kontoführenden Bank (z.B. 'Sparkasse Köln Bonn', 'Deutsche Bank')",
|
||||
"istAktiv": "Deaktivierte Bankverbindungen werden nicht mehr für neue Transaktionen verwendet, bleiben aber im System erhalten",
|
||||
"istStandard": "Das Standard-Konto wird automatisch vorgeschlagen, wenn für diesen Beteiligten eine Zahlung erfasst wird. Nur ein Konto kann als Standard markiert sein",
|
||||
"beteiligte": "Der Beteiligte, dem diese Bankverbindung zugeordnet ist",
|
||||
"advowareKontoId": "Eindeutige Konto-ID aus Advoware für die Synchronisation. Wird automatisch bei der Synchronisation gesetzt",
|
||||
"advowareLastSync": "Zeitpunkt der letzten erfolgreichen Synchronisation mit Advoware. Wird automatisch aktualisiert",
|
||||
"syncStatus": "Status der Advoware-Synchronisation: 'Synchronisiert' = Daten sind aktuell, 'Abweichungen' = Daten weichen ab, 'Fehlgeschlagen' = Sync-Fehler aufgetreten"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"clean": "Synchronisiert",
|
||||
"unclean": "Abweichungen",
|
||||
"failed": "Fehlgeschlagen"
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"invalidIbanChecksum": "Die eingegebene IBAN ist ungueltig. Bitte pruefen Sie die Nummer auf Tippfehler.",
|
||||
"invalidBicLength": "Der BIC/SWIFT-Code muss 8 oder 11 Zeichen lang sein.",
|
||||
"invalidBicFormat": "Der eingegebene BIC/SWIFT-Code hat ein ungueltiges Format. Beispiel: COBADEFFXXX"
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
"calls1": "Anrufe",
|
||||
"contactsBeteiligte": "Freigegebene Nutzer",
|
||||
"dokumentesBeteiligte": "Dokumente",
|
||||
"bankverbindungens": "Bankverbindungen",
|
||||
"betnr": "Advoware Identifikator",
|
||||
"advowareLastSync": "Advoware letzte Synchronisation",
|
||||
"syncStatus": "Sync-Status"
|
||||
@@ -28,7 +29,8 @@
|
||||
"adressens": "Adressen",
|
||||
"calls1": "Anrufe",
|
||||
"contactsBeteiligte": "Freigegebene Nutzer",
|
||||
"dokumentesBeteiligte": "Dokumente"
|
||||
"dokumentesBeteiligte": "Dokumente",
|
||||
"bankverbindungens": "Bankverbindungen"
|
||||
},
|
||||
"labels": {
|
||||
"Create CBeteiligte": "Beteiligte erstellen"
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
{
|
||||
"fields": {
|
||||
"user": "Benutzer",
|
||||
"calls1": "Anrufe"
|
||||
},
|
||||
"links": {
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
"tasks": "Aufgaben",
|
||||
"user": "Benutzer",
|
||||
"calls1": "Anrufe"
|
||||
},
|
||||
"labels": {
|
||||
"Create CCallQueues": "Call-Queue erstellen"
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
"anschrift": "Anschrift",
|
||||
"objekttyp": "Objekttyp",
|
||||
"lage": "Lage",
|
||||
"vmhMietverhltnises2Mietobjekt": "Mietverhältnisse",
|
||||
"vmhMietverhltnises2Mietobjekt": "Mietverhältnisse (veraltet)",
|
||||
"vmhMietverhltnises": "Mietverhältnisse",
|
||||
"contactsMietobjekt": "Freigegebene Nutzer",
|
||||
"dokumentesMietobjekt": "Dokumente"
|
||||
},
|
||||
"links": {
|
||||
"vmhMietverhltnises2Mietobjekt": "Mietverhältnisse",
|
||||
"vmhMietverhltnises2Mietobjekt": "Mietverhältnisse (veraltet)",
|
||||
"vmhMietverhltnises": "Mietverhältnisse",
|
||||
"contactsMietobjekt": "Freigegebene Nutzer",
|
||||
"dokumentesMietobjekt": "Dokumente"
|
||||
},
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresse"
|
||||
},
|
||||
"links": {
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "VMHBeteiligte erstellen"
|
||||
}
|
||||
}
|
||||
@@ -59,8 +59,19 @@
|
||||
"beendigungstatbestand": "Beendigungstatbestand",
|
||||
"contact": "Kontakt",
|
||||
"nchsterAnruf": "Nächster Anruf",
|
||||
"runWorkflow": "Workflow ausführen",
|
||||
"dokumentesvmherstgespraech": "Dokumente"
|
||||
"dokumentesvmherstgespraech": "Dokumente",
|
||||
"ustSatz": "USt-Satz",
|
||||
"aussergerichtlicheGebuehren": "Außergerichtliche Gebühren",
|
||||
"kostenRaeumungsantrag": "Kosten Räumungsantrag",
|
||||
"gerichtskosten1Instanz": "GK-Kosten I. Inst.",
|
||||
"anwaltskostenKlaeger1Instanz": "RA-Kosten Kläger I. Inst.",
|
||||
"vorzusch1Instanz": "Vorauszuschießende Kosten I. Inst.",
|
||||
"vergleich1InstanzGk": "Vergleich I. Inst.: GK-Kosten",
|
||||
"vergleich1InstanzAnwK": "Vergleich I. Inst.: RA-Kosten",
|
||||
"vergleich1InstanzSumme": "Vergleich I. Inst.: Summe",
|
||||
"saeumnis1InstanzGk": "Säumnis I. Inst.: GK-Kosten",
|
||||
"saeumnis1InstanzAnwK": "Säumnis I. Inst.: RA-Kosten",
|
||||
"saeumnis1InstanzSumme": "Säumnis I. Inst.: Summe"
|
||||
},
|
||||
"links": {
|
||||
"calls": "Anrufe",
|
||||
@@ -123,6 +134,10 @@
|
||||
"OPKonto": "Mietkonto / Offene-Posten Liste",
|
||||
"WiderspruchMieter": "Widerspruch des Mieter",
|
||||
"SonstigeUnterlagen": "Sonstige Unterlagen"
|
||||
},
|
||||
"ustSatz": {
|
||||
"0": "0%",
|
||||
"19": "19%"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
"contactsMietverhltnis": "Freigegebene Nutzer",
|
||||
"sonstigebesitzervmhmietverhltnis": "Sonstige Bewohner",
|
||||
"dokumentesvmhMietverhltnisse": "Dokumente",
|
||||
"vmhMietobjekt2Mietverhltnis": "Mietobjekte",
|
||||
"vmhMietobjekt2Mietverhltnis": "Mietobjekte (veraltet)",
|
||||
"vmhMietobjekt": "Mietobjekt"
|
||||
},
|
||||
"links": {
|
||||
@@ -25,7 +25,7 @@
|
||||
"contactsMietverhltnis": "Freigegebene Nutzer",
|
||||
"sonstigebesitzervmhmietverhltnis": "Sonstige Bewohner",
|
||||
"dokumentesvmhMietverhltnisse": "Dokumente",
|
||||
"vmhMietobjekt2Mietverhltnis": "Mietobjekte",
|
||||
"vmhMietobjekt2Mietverhltnis": "Mietobjekte (veraltet)",
|
||||
"vmhMietobjekt": "Mietobjekt"
|
||||
},
|
||||
"labels": {
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
"kuendigungsservice": "Kündigungsservice",
|
||||
"aussergerichtlicheGebuehren13": "Außergerichtliche Gebühren 1,3",
|
||||
"gerichtskosten1Instanz": "Gerichtskosten 1. Instanz",
|
||||
"anwaltskosten1Instanz": "Anwaltskosten 1. Instanz"
|
||||
"anwaltskosten1Instanz": "Anwaltskosten 1. Instanz",
|
||||
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
|
||||
"collaborators": "Mitarbeiter",
|
||||
"vmhVermietersRKL": "Vermieter"
|
||||
},
|
||||
"links": {
|
||||
"calls": "Anrufe",
|
||||
@@ -24,7 +27,10 @@
|
||||
"beklagte": "Beklagte",
|
||||
"vmhMietverhltnises": "Mietverhältnisse",
|
||||
"contactsRumungsklage": "Freigegebene Nutzer",
|
||||
"dokumentesvmhraumungsklage": "Dokumente"
|
||||
"dokumentesvmhraumungsklage": "Dokumente",
|
||||
"freigeschalteteNutzer": "Freigeschaltete Nutzer (veraltet)",
|
||||
"collaborators": "Mitarbeiter",
|
||||
"vmhVermietersRKL": "Vermieter"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVmhRumungsklage": "Räumungsklage erstellen"
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresse"
|
||||
"address": "Adresse",
|
||||
"rechtsform": "Rechtsform",
|
||||
"advowareBetnr": "Advoware Beteiligten-Nr.",
|
||||
"vmhRumungsklagesVermieter": "Räumungsklagen",
|
||||
"contactsVermieter": "Freigegebene Nutzer",
|
||||
"vmhKontaktpersonenVermieter": "Kontaktpersonen"
|
||||
},
|
||||
"links": {
|
||||
"calls": "Anrufe",
|
||||
"tasks": "Aufgaben"
|
||||
"tasks": "Aufgaben",
|
||||
"vmhRumungsklagesVermieter": "Räumungsklagen",
|
||||
"contactsVermieter": "Freigegebene Nutzer",
|
||||
"vmhKontaktpersonenVermieter": "Kontaktpersonen"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVmhVermieter": "Vermieter erstellen"
|
||||
},
|
||||
"tooltips": {
|
||||
"advowareBetnr": "Eindeutige Beteiligten-Nummer aus Advoware"
|
||||
}
|
||||
}
|
||||
41
custom/Espo/Custom/Resources/i18n/de_DE/Call.json
Normal file
41
custom/Espo/Custom/Resources/i18n/de_DE/Call.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"options": {
|
||||
"cWichtigkeit": {
|
||||
"Standard": "Standard",
|
||||
"Erhöht": "Erhöht",
|
||||
"Hoch": "Hoch",
|
||||
"Dringend": "Dringend"
|
||||
},
|
||||
"status": {
|
||||
"Planned": "Geplant",
|
||||
"Held": "Erfolgreich",
|
||||
"Not Held": "Abgesagt",
|
||||
"TempNotReached": "Vorübergehend nicht erreicht",
|
||||
"FinalNotReached": "Dauerhaft nicht erreicht"
|
||||
},
|
||||
"direction": {
|
||||
"Outbound": "Ausgehend",
|
||||
"Inbound": "Eingehend",
|
||||
"Internal": "Intern"
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"cWichtigkeit": "Wichtigkeit",
|
||||
"cBeteiligtes": "Beteiligte",
|
||||
"cCallQueues": "Call-Queue",
|
||||
"user": "Benutzer",
|
||||
"calls1": "Anrufe"
|
||||
},
|
||||
"tooltips": {
|
||||
"cWichtigkeit": "Wichtigkeit des Anrufes aus Sicht der Kanzlei"
|
||||
},
|
||||
"labels": {
|
||||
"Create Call": "Anruf erstellen"
|
||||
},
|
||||
"links": {
|
||||
"cBeteiligtes": "Beteiligte",
|
||||
"cCallQueues": "Call-Queue",
|
||||
"user": "Benutzer",
|
||||
"calls1": "Anrufe"
|
||||
}
|
||||
}
|
||||
58
custom/Espo/Custom/Resources/i18n/de_DE/Contact.json
Normal file
58
custom/Espo/Custom/Resources/i18n/de_DE/Contact.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"fields": {
|
||||
"cRechtsform": "Rechtsform",
|
||||
"cVmhRumungsklages": "Räumungsklagen",
|
||||
"cDISGTyp": "Persönlichkeitstyp (nach DISG)",
|
||||
"cVmhErstgespraechs": "Erstgespräch",
|
||||
"cVmhVermietersFreigabe": "Vermieter (Freigabe)",
|
||||
"cVmhVermietersKontaktpersonen": "Vermieter (Kontaktpersonen)",
|
||||
"cVmhMietverhltnisesContact": "Mietverhältnisse",
|
||||
"cDokumentesvmhcontact": "Dokumente",
|
||||
"cVmhmietobjekt2contact": "Mietobjekte",
|
||||
"cBeteiligteContact": "Beteiligte",
|
||||
"cMietobjekteContactPortal": "Mietobjekte (Portal)",
|
||||
"cAdressenContact": "Adressen",
|
||||
"cVmhRumungsklageContact": "Räumungsklagen (Portal)",
|
||||
"cBankverbindungenContact": "Bankverbindungen"
|
||||
},
|
||||
"links": {
|
||||
"cVmhRumungsklages": "Räumungsklagen",
|
||||
"cVmhErstgespraechs": "Erstgespräch",
|
||||
"cVmhVermietersFreigabe": "Vermieter (Freigabe)",
|
||||
"cVmhVermietersKontaktpersonen": "Vermieter (Kontaktpersonen)",
|
||||
"cVmhMietverhltnisesContact": "Mietverhältnisse",
|
||||
"cDokumentesvmhcontact": "Dokumente",
|
||||
"cVmhmietobjekt2contact": "Mietobjekte",
|
||||
"cBeteiligteContact": "Beteiligte",
|
||||
"cMietobjekteContactPortal": "Mietobjekte (Portal)",
|
||||
"cAdressenContact": "Adressen",
|
||||
"cVmhRumungsklageContact": "Räumungsklagen (Portal)",
|
||||
"cBankverbindungenContact": "Bankverbindungen"
|
||||
},
|
||||
"options": {
|
||||
"cRechtsform": {
|
||||
"": "",
|
||||
"Herr": "Herr",
|
||||
"Frau": "Frau",
|
||||
"GmbH": "GmbH"
|
||||
},
|
||||
"cDISGTyp": {
|
||||
"Dominant": "Dominant",
|
||||
"Initiativ": "Initiativ",
|
||||
"Stetig": "Stetig",
|
||||
"Gewissenhaft": "Gewissenhaft",
|
||||
"Unbekannt": "Unbekannt"
|
||||
},
|
||||
"salutationName": {
|
||||
"": "",
|
||||
"Mr.": "Herr",
|
||||
"Ms.": "Frau"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"cDISGTyp": "Persönlichkeitstyp nach DISG:\n\nDominant = Schnelles Tempo, fordernder Ton, Fokus auf Ergebnisse.\nInitiativ = Hohe Sprechgeschwindigkeit, emotionale Sprache, sucht Bestätigung.\nStetig = Langsames Tempo, zögernde Pausen, risikoscheu.\nGewissenhaft = Präzise Formulierungen, sucht Daten, skeptisch."
|
||||
},
|
||||
"labels": {
|
||||
"Create Contact": "Portalnutzer erstellen"
|
||||
}
|
||||
}
|
||||
8
custom/Espo/Custom/Resources/i18n/de_DE/PhoneNumber.json
Normal file
8
custom/Espo/Custom/Resources/i18n/de_DE/PhoneNumber.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"fields": {
|
||||
"advoware_tlf_id": "Advoware Telefon-ID"
|
||||
},
|
||||
"tooltips": {
|
||||
"advoware_tlf_id": "Eindeutige Telefonnummer-ID aus dem Advoware-System"
|
||||
}
|
||||
}
|
||||
8
custom/Espo/Custom/Resources/i18n/de_DE/User.json
Normal file
8
custom/Espo/Custom/Resources/i18n/de_DE/User.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"fields": {
|
||||
"cCallQueues": "Persönliche Warteschleife"
|
||||
},
|
||||
"links": {
|
||||
"cCallQueues": "Persönliche Warteschleife"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"presetFilters": {
|
||||
"reportFilter6972174b6540731c1": "UserTask"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"iban": "IBAN",
|
||||
"bic": "BIC/SWIFT",
|
||||
"kontoinhaber": "Account Holder",
|
||||
"bankname": "Bank Name",
|
||||
"istAktiv": "Is Active",
|
||||
"istStandard": "Default Account",
|
||||
"advowareKontoId": "Advoware Account ID",
|
||||
"advowareLastSync": "Advoware Last Sync",
|
||||
"syncStatus": "Sync Status",
|
||||
"beteiligte": "Participant"
|
||||
},
|
||||
"links": {
|
||||
"beteiligte": "Participant"
|
||||
},
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Create Bank Account"
|
||||
},
|
||||
"tooltips": {
|
||||
"name": "Name or description of the bank account (e.g. 'Main Account', 'Business Account', 'Deposit Account')",
|
||||
"iban": "International Bank Account Number in format DE89370400440532013000. Only uppercase letters and digits, 2-letter country code followed by check digit and account number (max. 34 characters)",
|
||||
"bic": "Bank Identifier Code (SWIFT code) in format COBADEFFXXX. 8 or 11 characters: 4 characters bank code, 2 characters country code, 2 characters location code, optionally 3 characters branch code",
|
||||
"kontoinhaber": "Full name of the account holder as registered with the bank",
|
||||
"bankname": "Name of the bank (e.g. 'Sparkasse Cologne Bonn', 'Deutsche Bank')",
|
||||
"istAktiv": "Inactive bank accounts are no longer used for new transactions but remain in the system",
|
||||
"istStandard": "The default account is automatically suggested when a payment is recorded for this participant. Only one account can be marked as default",
|
||||
"beteiligte": "The participant to whom this bank account belongs",
|
||||
"advowareKontoId": "Unique account ID from Advoware for synchronization. Automatically set during synchronization",
|
||||
"advowareLastSync": "Time of last successful synchronization with Advoware. Automatically updated",
|
||||
"syncStatus": "Advoware synchronization status: 'Synchronized' = data is current, 'Discrepancies' = data differs, 'Failed' = sync error occurred"
|
||||
},
|
||||
"options": {
|
||||
"syncStatus": {
|
||||
"clean": "Synchronized",
|
||||
"unclean": "Discrepancies",
|
||||
"failed": "Failed"
|
||||
}
|
||||
},
|
||||
"messages": {
|
||||
"invalidIbanChecksum": "The entered IBAN is invalid. Please check for typos.",
|
||||
"invalidBicLength": "The BIC/SWIFT code must be 8 or 11 characters long.",
|
||||
"invalidBicFormat": "The entered BIC/SWIFT code has an invalid format. Example: COBADEFFXXX"
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"anschriftPostalCode": "Postal Code",
|
||||
"anschriftMap": "Map",
|
||||
"objekttyp": "Property Type",
|
||||
"lage": "Location",
|
||||
"vmhMietverhltnises2Mietobjekt": "Tenancies",
|
||||
"contact2mietobjekt": "Contacts",
|
||||
"contactsMietobjekt": "Portal Users",
|
||||
@@ -35,5 +36,8 @@
|
||||
"Gewerbehalle": "Gewerbehalle",
|
||||
"Sonstiges": "Sonstiges"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
"lage": "Location within the property (e.g. ground floor left, 1st floor right)"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Address"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
"calls": "Calls",
|
||||
"tasks": "Tasks"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Create VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,9 @@
|
||||
"contact": "Contact",
|
||||
"nchsterAnruf": "Next Call",
|
||||
"dokumentesvmherstgespraech": "Documents",
|
||||
"runWorkflow": "Run Workflow",
|
||||
"testArray": "Test Array"
|
||||
"testArray": "Test Array",
|
||||
"vorzusch1Instanz": "Vorauszuschießende Kosten I. Inst.",
|
||||
"kostenRaeumungstitel": "Kosten Räumungsantrag"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Meetings",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"Call": "Anruf",
|
||||
"Contact": "Portalnutzer",
|
||||
"CCallQueues": "Call-Queue",
|
||||
"CVMHBeteiligte": "VMHBeteiligte"
|
||||
"CBankverbindungen": "Bankverbindung"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"CVmhMietverhltnis": "Mietverhältnisse",
|
||||
@@ -25,6 +25,6 @@
|
||||
"Call": "Anrufe",
|
||||
"Contact": "Portalnutzer",
|
||||
"CCallQueues": "Call-Queues",
|
||||
"CVMHBeteiligte": "VMHBeteiligtes"
|
||||
"CBankverbindungen": "Bankverbindungen"
|
||||
}
|
||||
}
|
||||
8
custom/Espo/Custom/Resources/i18n/en_US/PhoneNumber.json
Normal file
8
custom/Espo/Custom/Resources/i18n/en_US/PhoneNumber.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"fields": {
|
||||
"advoware_tlf_id": "Advoware Phone ID"
|
||||
},
|
||||
"tooltips": {
|
||||
"advoware_tlf_id": "Unique phone number ID from the Advoware system"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Crear Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Dirección"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Reuniones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Crear VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Crear Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Dirección"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Presentaciones",
|
||||
"calls": "Llamadas",
|
||||
"tasks": "Tareas"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Crear VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "ایجاد Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "آدرس"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "جلسات",
|
||||
"calls": "تماس ها",
|
||||
"tasks": "وظایف"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "ایجاد VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Créer un Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Créer un VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Napravi Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresa"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Sastanci",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Kreiraj VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "{EntityTypeTranslated} létrehozása"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Cím"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "találkozók",
|
||||
"calls": "felhívja",
|
||||
"tasks": "Feladatok"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "{EntityTypeTranslated} létrehozása"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Buat Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Buat VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Crea Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Indirizzo"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Riunioni",
|
||||
"calls": "Chiamate",
|
||||
"tasks": "Compiti"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Crea VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Bankverbindung を作成する"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "住所"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "会議",
|
||||
"calls": "通話",
|
||||
"tasks": "タスク"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "VMHBeteiligte を作成する"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Sukurti Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresas"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Susitikimai",
|
||||
"calls": "Skambučiai",
|
||||
"tasks": "Užduotys"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Sukurti VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Izveidot Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adrese"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Tikšanās",
|
||||
"calls": "Zvani",
|
||||
"tasks": "Uzdevumi"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Izveidot VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Opprett Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresse"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Møter",
|
||||
"calls": "Samtaler",
|
||||
"tasks": "Oppgaver"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Opprett VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Creëer Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adres"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Vergaderingen",
|
||||
"calls": "Gesprekken",
|
||||
"tasks": "Taken"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Creëer VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Utwórz Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adres"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Spotkania",
|
||||
"calls": "Rozmowy",
|
||||
"tasks": "Zadania"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Utwórz VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Criar Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Endereço"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Reuniões",
|
||||
"calls": "Ligações",
|
||||
"tasks": "Tarefas"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Criar VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Criar Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Endereço"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Reuniões",
|
||||
"calls": "Chamadas",
|
||||
"tasks": "Tarefas"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Criar VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Creare Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Creare VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Создать Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Адрес"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Встречи",
|
||||
"calls": "Звонки",
|
||||
"tasks": "Задачи"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Создать VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Vytvoriť Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresa"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Stretnutia",
|
||||
"calls": "Hovory",
|
||||
"tasks": "Úlohy"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Vytvoriť VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Ustvari Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Naslov"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Srečanja",
|
||||
"calls": "Klici",
|
||||
"tasks": "Naloge"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Ustvari VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Napravi Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adresa"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Ročišta",
|
||||
"calls": "Pozivi",
|
||||
"tasks": "Zadaci"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Napravi VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Skapa Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adress"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Möten",
|
||||
"calls": "Samtal",
|
||||
"tasks": "Uppgifter"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Skapa VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "สร้าง Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "ที่อยู่"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "การประชุม",
|
||||
"calls": "โทร",
|
||||
"tasks": "งาน"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "สร้าง VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Bankverbindung oluştur"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Adres "
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Toplantılar",
|
||||
"calls": "(Seslenme)",
|
||||
"tasks": "Görevler"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "VMHBeteiligte oluştur"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Створити Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Адреса"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Зустрічі",
|
||||
"calls": "Дзвінки",
|
||||
"tasks": "Завдання"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "Створити VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "Tạo Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "Địa chỉ"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "Cuộc hẹn",
|
||||
"calls": "Cuộc gọi",
|
||||
"tasks": "Nhiệm vụ"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "创建 Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "地址"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "会晤",
|
||||
"calls": "通话",
|
||||
"tasks": "任务"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "创建 VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"labels": {
|
||||
"Create CBankverbindungen": "建立Bankverbindung"
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"fields": {
|
||||
"address": "地址"
|
||||
},
|
||||
"links": {
|
||||
"meetings": "會議",
|
||||
"calls": "通話",
|
||||
"tasks": "任務"
|
||||
},
|
||||
"labels": {
|
||||
"Create CVMHBeteiligte": "建立VMHBeteiligte"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
[
|
||||
{
|
||||
"label": "Übersicht",
|
||||
"rows": [
|
||||
[
|
||||
{"name": "name"},
|
||||
{"name": "beteiligte"}
|
||||
],
|
||||
[
|
||||
{"name": "istAktiv"},
|
||||
{"name": "istStandard"}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Bankdaten",
|
||||
"rows": [
|
||||
[
|
||||
{"name": "iban"},
|
||||
{"name": "bic"}
|
||||
],
|
||||
[
|
||||
{"name": "kontoinhaber"},
|
||||
{"name": "bankname"}
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Advoware Sync",
|
||||
"tabBreak": true,
|
||||
"tabLabel": "Erweitert",
|
||||
"rows": [
|
||||
[
|
||||
{"name": "advowareKontoId"},
|
||||
{"name": "syncStatus"},
|
||||
{"name": "advowareLastSync"}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -5,28 +5,28 @@
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
false
|
||||
{
|
||||
"name": "istAktiv"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "emailAddress"
|
||||
"name": "iban"
|
||||
},
|
||||
{
|
||||
"name": "phoneNumber"
|
||||
"name": "bic"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "address"
|
||||
"name": "bankname"
|
||||
},
|
||||
false
|
||||
],
|
||||
[
|
||||
{
|
||||
"name": "description",
|
||||
"fullWidth": true
|
||||
"name": "istStandard"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"style": "default",
|
||||
"label": ""
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"link": true,
|
||||
"width": 20
|
||||
},
|
||||
{
|
||||
"name": "beteiligte",
|
||||
"width": 20
|
||||
},
|
||||
{
|
||||
"name": "iban",
|
||||
"width": 20
|
||||
},
|
||||
{
|
||||
"name": "bankname",
|
||||
"width": 15
|
||||
},
|
||||
{
|
||||
"name": "istAktiv",
|
||||
"width": 10
|
||||
},
|
||||
{
|
||||
"name": "istStandard",
|
||||
"width": 10
|
||||
},
|
||||
{
|
||||
"name": "syncStatus",
|
||||
"width": 10
|
||||
}
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user