From 1ef3c57e39a3421893add8926781822f56b24a4c Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sun, 4 Jan 2026 01:35:18 +0100
Subject: [PATCH] feature trustee
---
.../doc_trustee_feature_architecture.md | 1815 +++++++++++++++++
.../doc_trustee_feature_ui_demo.html | 1339 ++++++++++++
.../doc_trustee_feature_ui_specification.md | 1749 ++++++++++++++++
3 files changed, 4903 insertions(+)
create mode 100644 ui_nyla/feature-trustee/doc_trustee_feature_architecture.md
create mode 100644 ui_nyla/feature-trustee/doc_trustee_feature_ui_demo.html
create mode 100644 ui_nyla/feature-trustee/doc_trustee_feature_ui_specification.md
diff --git a/ui_nyla/feature-trustee/doc_trustee_feature_architecture.md b/ui_nyla/feature-trustee/doc_trustee_feature_architecture.md
new file mode 100644
index 0000000..09e0f8e
--- /dev/null
+++ b/ui_nyla/feature-trustee/doc_trustee_feature_architecture.md
@@ -0,0 +1,1815 @@
+# Trustee Feature - Architektur und Implementierungsplan
+
+## Executive Summary
+
+Dieses Dokument beschreibt die Architektur und den Implementierungsplan für das **Trustee** Feature, welches das erste Feature in einem neuen Client-Management-System ist. Das Trustee Feature ermöglicht es Treuhandgesellschaften, die Spesenabrechnung und zugehörige Dokumente für ihre Kunden zentral zu verwalten.
+
+## Inhaltsverzeichnis
+
+1. [Übersicht](#overview)
+2. [Architektur-Analyse](#architecture-analysis)
+3. [Architektur-Entscheidungen](#architecture-decisions)
+4. [System-Architektur](#system-architecture)
+5. [Datenmodell](#data-model)
+6. [RBAC-Integration](#rbac-integration)
+7. [API-Design](#api-design)
+8. [Implementierungsplan](#implementation-plan)
+9. [Migrationsstrategie](#migration-strategy)
+10. [Teststrategie](#testing-strategy)
+11. [Implementierungsdetails](#implementation-details)
+
+**Zugehörige Dokumente**:
+- [UI-Spezifikation](./doc_trustee_feature_ui_specification.md) - Frontend-Implementierung (UI-Komponenten, FormGenerator-Pattern, React/TypeScript)
+
+---
+
+## Übersicht
+
+### Zweck
+
+Das Trustee Feature bietet ein zentralisiertes Spesenabrechnungssystem, bei dem:
+- Benutzer ihre Spesen an ihre Treuhandgesellschaft melden können
+- Treuhandgesellschaften Spesendaten zentral für alle ihre Kunden verwalten können
+- Der Zugriff über ein feature-basiertes RBAC-System gesteuert wird
+
+### Schlüsselkonzepte
+
+1. **Feature-basiertes System**: Das System definiert Zugriffe für Features. "Trustee" ist das erste Feature.
+2. **Datenbank-Interface**: Jedes Feature hat ein Datenbank-Interface (wie "chat") mit automatisierten Systemattributen (mandate, created, updated, etc.)
+3. **RBAC-Integration**: Feature-Zugriff wird über RBAC-Ressourcen gesteuert
+4. **Organisations-basierter Zugriff**: Der Zugriff wird auf Organisationsebene innerhalb des Trustee Features gesteuert
+
+---
+
+## Architektur-Analyse
+
+### Verständnis des aktuellen Systems
+
+Basierend auf der Codebase-Analyse:
+
+1. **Interface Pattern**: Custom interfaces follow the pattern of `interfaceDbChatObjects.py`:
+ - Use `DatabaseConnector` for database operations
+ - Implement CRUD operations
+ - Integrate with RBAC through `interfaceRbac.py`
+ - Support standard attributes (mandate, _createdAt, _modifiedAt, _createdBy, _modifiedBy) - automatisch vom DatabaseConnector gesetzt
+
+2. **RBAC System**:
+ - Uses `AccessRuleContext` enum: DATA, UI, RESOURCE
+ - Access levels: ALL, MY, GROUP, NONE
+ - Resources are defined as cascading strings (e.g., "ai.model.anthropic")
+ - UI elements use cascading strings (e.g., "playground.voice.settings")
+ - **Filterung auf DB-Ebene**: `getRecordsetWithRBAC()` filtert Daten automatisch basierend auf:
+ - `ALL`: Keine Filterung (alle Records)
+ - `MY`: Filter nach `_createdBy = userId` (nur eigene Records)
+ - `GROUP`: Filter nach `mandateId = user.mandateId` (nur Records der eigenen Gruppe)
+ - `NONE`: Keine Records sichtbar (`1 = 0` WHERE-Clause)
+ - **View-Permission**: Wird zuerst geprüft - wenn `view=false`, werden keine Records zurückgegeben
+
+3. **Route Pattern**: Routes follow `routeData*.py` pattern:
+ - Use FastAPI routers
+ - Integrate with authentication via `getCurrentUser`
+ - Support pagination
+ - Use interface methods for data access
+
+4. **Data Model Pattern**: Models in `datamodels/datamodel*.py`:
+ - Use Pydantic BaseModel
+ - Register labels with `registerModelLabels`
+ - Include frontend metadata in `json_schema_extra`
+
+5. **DatabaseConnector Pattern**:
+ - **Automatische Tabellenerstellung**: `_ensureTableExists()` erstellt Tabellen automatisch aus Pydantic-Modellen
+ - **Automatische Spaltenerstellung**: Spalten werden aus Modell-Feldern generiert
+ - **Systemattribute**: Automatisch hinzugefügt: `_createdAt`, `_modifiedAt`, `_createdBy`, `_modifiedBy`
+ - **Index-Erstellung**: Automatische Indizes für Foreign Keys (Felder die auf `Id` enden)
+ - **Migration**: Fehlende Spalten werden automatisch hinzugefügt (additive Migration)
+ - **SQL-Typ-Mapping**: Automatische Konvertierung von Python-Typen zu PostgreSQL-Typen
+
+### Architektur-Entscheidungen
+
+1. **Feature-Registrierung**: Das Trustee Feature wird als neues Interface ähnlich wie Chat registriert
+2. **RBAC-Ressourcen**:
+ - `ui.trustee` - UI-Zugriffskontrolle
+ - `resource.trustee` - Feature-Ressourcen-Zugriff
+3. **Datenbank-Isolation**: Jedes Feature hat sein eigenes Datenbank-Interface, teilt aber dieselbe Datenbankinstanz
+4. **Organisation vs Mandate**:
+ - **Mandate**: System-Level-Organisation (bestehendes Konzept)
+ - **Organisation**: Trustee-Feature-spezifische Firma (neues Konzept innerhalb des Trustee Features)
+ - Dies sind separate Konzepte - Organisation ist auf das Trustee Feature beschränkt
+ - **Beziehung**:
+ - Eine `mandate` kann mehrere `organisationId`s haben
+ - Eine `organisationId` gehört zu genau einer `mandate`
+ - `mandate` wird automatisch aus `currentUser.mandateId` gesetzt
+ - `organisationId` Dropdown zeigt alle gelieferten Organisationen (RBAC filtert automatisch)
+ - `organisationId`s können nicht über `mandate`-Grenzen hinweg geteilt werden
+
+---
+
+## System-Architektur
+
+### Komponenten-Übersicht
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Frontend (React/TS) │
+│ - Trustee UI Components │
+│ - Organisation Management │
+│ - Contract Management │
+│ - Expense Reporting (Position + Document) │
+└──────────────────────┬──────────────────────────────────────┘
+ │
+ │ HTTP/REST API
+ │
+┌──────────────────────▼──────────────────────────────────────┐
+│ API Routes Layer │
+│ - routeDataTrusteeOrganisations.py │
+│ - routeDataTrusteeRoles.py │
+│ - routeDataTrusteeAccess.py │
+│ - routeDataTrusteeContracts.py │
+│ - routeDataTrusteeDocuments.py │
+│ - routeDataTrusteePositions.py │
+│ - routeDataTrusteePositionDocuments.py │
+└──────────────────────┬──────────────────────────────────────┘
+ │
+ │ Interface Methods
+ │
+┌──────────────────────▼──────────────────────────────────────┐
+│ Interface Layer (interfaceDbTrusteeObjects) │
+│ - CRUD Operations │
+│ - RBAC Filtering │
+│ - Business Logic │
+└──────────────────────┬──────────────────────────────────────┘
+ │
+ │ Database Connector
+ │
+┌──────────────────────▼──────────────────────────────────────┐
+│ Database Layer (PostgreSQL) │
+│ - trustee.organisation │
+│ - trustee.role │
+│ - trustee.access │
+│ - trustee.contract │
+│ - trustee.document │
+│ - trustee.position │
+│ - trustee.xpositiondocument │
+└─────────────────────────────────────────────────────────────┘
+
+┌─────────────────────────────────────────────────────────────┐
+│ RBAC System │
+│ - resource.trustee (feature access) │
+│ - ui.trustee (UI access) │
+│ - Table-level access (DATA context) │
+│ - Feature-level access (trustee.access table) │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### Interface-Struktur
+
+Das Trustee-Interface folgt dem Muster von `interfaceDbChatObjects.py`:
+
+```python
+# gateway/modules/interfaces/interfaceDbTrusteeObjects.py
+
+class TrusteeInterface:
+ """
+ Interface for Trustee feature database operations.
+ Similar to ChatInterface but for trustee-specific data.
+ """
+
+ def __init__(self, currentUser: User):
+ self.currentUser = currentUser
+ self.db = DatabaseConnector(...) # Trustee database
+ self.rbac = RbacClass(self.db, dbApp=dbApp)
+
+ # Organisation CRUD
+ def createOrganisation(...)
+ def getOrganisation(...)
+ def updateOrganisation(...)
+ def deleteOrganisation(...)
+ def getAllOrganisations(...)
+
+ # Role CRUD
+ def createRole(...)
+ def getAllRoles(...)
+
+ # Access CRUD
+ def createAccess(...)
+ def getUserAccessForOrganisation(...)
+ def checkUserPermission(...)
+
+ # Contract CRUD
+ def createContract(...)
+ def getContract(...)
+ # ... etc
+
+ # Document CRUD
+ def createDocument(...)
+ def getDocument(...)
+ def getDocumentData(...) # Binary data
+
+ # Position CRUD
+ def createPosition(...)
+ def getPosition(...)
+ # ... etc
+
+ # Cross-reference operations
+ def linkPositionDocument(...)
+ def unlinkPositionDocument(...)
+ def getDocumentsForPosition(...)
+ def getPositionsForDocument(...)
+```
+
+---
+
+## Datenmodell
+
+### Tabelle: `trustee.organisation`
+
+Repräsentiert Treuhandgesellschaften (Organisationen) innerhalb des Trustee Features.
+
+```python
+class TrusteeOrganisation(BaseModel):
+ id: str = Field( # Unique string label (PK), not UUID
+ description="Unique organisation identifier (label)",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False, # Änderbar bei Erstellung, danach readonly
+ "frontend_required": True
+ }
+ )
+ label: str = Field(
+ description="Company name",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ enabled: bool = Field(
+ default=True,
+ description="Whether the organisation is enabled",
+ json_schema_extra={
+ "frontend_type": "checkbox",
+ "frontend_readonly": False,
+ "frontend_required": False
+ }
+ )
+ mandate: str = Field( # System attribute
+ description="Mandate ID (system-level organisation)",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt:
+ # _createdAt, _modifiedAt, _createdBy, _modifiedBy
+ # Diese werden nicht im Modell definiert, aber im Backend automatisch verwaltet
+ # Systemattribute-Verwaltung:
+ # - _createdBy: Automatisch aus currentUser.id gesetzt
+ # - _createdAt: Automatisch beim Erstellen gesetzt (float, UTC timestamp in Sekunden)
+ # - _modifiedAt: Automatisch beim Update gesetzt (float, UTC timestamp in Sekunden)
+ # - _modifiedBy: Automatisch aus currentUser.id beim Update gesetzt
+ # - Frontend: Diese Felder werden als readonly angezeigt
+ # - Formatierung: Timestamps als float (UI rendert gemäß Zeitzoneneinstellungen), User-Namen statt User-ID
+ # - Sichtbarkeit: Kann in FormGeneric definiert werden, welche Felder angezeigt werden, aber sie müssen ans UI geliefert werden über die Routes
+
+registerModelLabels(
+ "TrusteeOrganisation",
+ {"en": "Organisation", "fr": "Organisation"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "label": {"en": "Label", "fr": "Libellé"},
+ "enabled": {"en": "Enabled", "fr": "Activé"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all organisations
+- `admin`: Can manage organisations for their group (mandate)
+
+**Indexes**:
+- Primary key on `id`
+- Index on `mandate` for filtering
+
+### Tabelle: `trustee.role`
+
+Definiert Rollen innerhalb des Trustee Features.
+
+```python
+class TrusteeRole(BaseModel):
+ id: str = Field( # Unique string label (PK), not UUID
+ description="Unique role identifier (label)",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ desc: str = Field(
+ description="Role description",
+ json_schema_extra={
+ "frontend_type": "textarea",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ mandate: str = Field( # System attribute
+ description="Mandate ID",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt
+
+registerModelLabels(
+ "TrusteeRole",
+ {"en": "Role", "fr": "Rôle"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "desc": {"en": "Description", "fr": "Description"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+
+**Initiale Rollen**:
+- `userreport`: Kann Benutzerdokumente an das System liefern
+- `admin`: Kann den Zugriff administrieren
+- `operate`: Kann Daten für Operationen verwenden
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all roles
+
+**Indexes**:
+- Primary key on `id`
+- Index on `mandate` for filtering
+
+### Tabelle: `trustee.access`
+
+Definiert Benutzerzugriff auf Organisationen mit spezifischen Rollen. Der Zugriff kann optional auf einen spezifischen Contract beschränkt werden.
+
+```python
+class TrusteeAccess(BaseModel):
+ id: str = Field( # UUID PK
+ default_factory=lambda: str(uuid.uuid4()),
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ organisationId: str = Field(
+ description="Reference to trustee.organisation.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.organisation" # String-Referenz für dynamische Options
+ }
+ )
+ roleId: str = Field(
+ description="Reference to trustee.role.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.role" # String-Referenz für dynamische Options
+ }
+ )
+ userId: str = Field(
+ description="User ID assigned to this role",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "user" # String-Referenz für User-Liste
+ }
+ )
+ contractId: Optional[str] = Field(
+ default=None,
+ description="Optional reference to trustee.contract.id. If None, access is for full organisation. If set, access is limited to this specific contract.",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": False,
+ "frontend_options": "trustee.contract", # String-Referenz, dynamisch gefiltert nach organisationId
+ "frontend_depends_on": "organisationId" # Dropdown wird aktualisiert wenn organisationId geändert wird
+ }
+ )
+ mandate: str = Field( # System attribute
+ description="Mandate ID",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt
+
+registerModelLabels(
+ "TrusteeAccess",
+ {"en": "Access", "fr": "Accès"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "organisationId": {"en": "Organisation", "fr": "Organisation"},
+ "roleId": {"en": "Role", "fr": "Rôle"},
+ "userId": {"en": "User", "fr": "Utilisateur"},
+ "contractId": {"en": "Contract (optional)", "fr": "Contrat (optionnel)"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+
+**Zugriffslogik**:
+- **Ohne `contractId` (None)**: Zugriff gilt für die gesamte Organisation
+- **Mit `contractId`**: Zugriff ist auf diesen spezifischen Contract beschränkt
+- **RBAC-Filterung**: Bei der Datenfilterung wird geprüft:
+ - Hat User Access für Organisation (ohne Contract) → Zugriff auf alle Contracts dieser Organisation
+ - Hat User Access für Organisation + spezifischen Contract → Zugriff nur auf diesen Contract
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all access records
+- `admin`: Can manage access for their group (mandate)
+- Users with `admin` role in trustee.access can manage access for their organisation
+
+**Indexes**:
+- Primary key on `id`
+- Unique constraint on `(organisationId, roleId, userId, contractId)` to prevent duplicates (erlaubt aber mehrere Rollen pro Benutzer-Organisation durch separate Records)
+- Index on `organisationId`
+- Index on `userId`
+- Index on `roleId`
+- Index on `contractId`
+- Index on `mandate`
+
+### Tabelle: `trustee.contract`
+
+Definiert Kundenverträge innerhalb von Organisationen.
+
+```python
+class TrusteeContract(BaseModel):
+ id: str = Field( # UUID PK
+ default_factory=lambda: str(uuid.uuid4()),
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ organisationId: str = Field(
+ description="Reference to trustee.organisation.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False, # Änderbar bei Erstellung, danach readonly (immutable)
+ "frontend_required": True,
+ "frontend_options": "trustee.organisation"
+ }
+ )
+ label: str = Field(
+ description="Label for the customer contract (e.g., 'Muster AG 2026')",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ enabled: bool = Field(
+ default=True,
+ description="Whether the contract is enabled",
+ json_schema_extra={
+ "frontend_type": "checkbox",
+ "frontend_readonly": False,
+ "frontend_required": False
+ }
+ )
+ mandate: str = Field( # System attribute
+ description="Mandate ID",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt
+
+registerModelLabels(
+ "TrusteeContract",
+ {"en": "Contract", "fr": "Contrat"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "organisationId": {"en": "Organisation", "fr": "Organisation"},
+ "label": {"en": "Label", "fr": "Libellé"},
+ "enabled": {"en": "Enabled", "fr": "Activé"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all contracts
+- `admin`: Can manage contracts for their group (mandate)
+- Users with `admin` role in trustee.access: Can CRUD contracts for their organisationId
+ - Wenn `contractId` in trustee.access leer: Zugriff auf alle Contracts der Organisation
+ - Wenn `contractId` gesetzt: Zugriff nur auf diesen spezifischen Contract
+ - New records default to their own organisationId
+ - Dropdown to select from granted organisationIds (und optional Contracts)
+
+**Wichtig**: Verträge sind unveränderlich bezüglich `organisationId` - kann nach der Erstellung nicht mehr geändert werden.
+
+**Implementierung**:
+- **Backend-Validierung**: In `updateContract()` prüfen: Wenn `organisationId` im Update vorhanden und unterschiedlich zum bestehenden Wert → Fehler
+- **Frontend-Readonly**: `organisationId` wird auf readonly gesetzt wenn `id` vorhanden (non-blank) ist
+- **Logik**: ID kann nur gespeichert werden, wenn non-blank. Eine non-blank ID kann nicht mehr geändert werden
+
+**Indexes**:
+- Primary key on `id`
+- Index on `organisationId`
+- Index on `mandate`
+- **Wichtig**: `organisationId` ist immutable nach Erstellung - keine Updates erlaubt
+
+**Validierung**:
+- `id` Format: Alphanumerisch + Bindestrich/Unterstrich
+- Länge: 3-50 Zeichen
+- Validierung: Backend + Frontend
+
+### Tabelle: `trustee.document`
+
+Enthält Dokumentreferenzen und Belege für Buchungen.
+
+```python
+class TrusteeDocument(BaseModel):
+ id: str = Field( # UUID PK
+ default_factory=lambda: str(uuid.uuid4()),
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ organisationId: str = Field(
+ description="Reference to trustee.organisation.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.organisation"
+ }
+ )
+ contractId: str = Field(
+ description="Reference to trustee.contract.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.contract" # Gefiltert nach organisationId
+ }
+ )
+ documentData: bytes = Field( # Binary data
+ description="The file content",
+ json_schema_extra={
+ "frontend_type": "file", # Für File Upload
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ documentName: str = Field(
+ description="File name (e.g., 'Beleg.pdf')",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ documentMimeType: str = Field(
+ description="MIME type of the document",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": [
+ {"value": "application/pdf", "label": {"en": "PDF", "fr": "PDF"}},
+ {"value": "image/jpeg", "label": {"en": "JPEG", "fr": "JPEG"}},
+ {"value": "image/png", "label": {"en": "PNG", "fr": "PNG"}},
+ # ... weitere MIME-Types
+ ]
+ }
+ )
+ mandate: str = Field( # System attribute
+ description="Mandate ID",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt
+
+registerModelLabels(
+ "TrusteeDocument",
+ {"en": "Document", "fr": "Document"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "organisationId": {"en": "Organisation", "fr": "Organisation"},
+ "contractId": {"en": "Contract", "fr": "Contrat"},
+ "documentData": {"en": "Document Data", "fr": "Données du document"},
+ "documentName": {"en": "Document Name", "fr": "Nom du document"},
+ "documentMimeType": {"en": "MIME Type", "fr": "Type MIME"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all documents
+- `admin`: Can manage documents for their group (mandate)
+- Users with `operate` role in trustee.access: Can CRUD documents in their organisationId
+- Users with `userreport` role in trustee.access: Can CRUD own documents (_createdBy = userId, automatisch über Systemattribute gesetzt)
+
+**Speicherung**:
+- Dokument-Binärdaten werden in PostgreSQL BYTEA-Spalte gespeichert
+- Einfach, transaktional, einfaches Backup
+
+**File Upload/Download**:
+- **Nicht direkt integriert**: File Upload/Download erfolgt über das Workflow-System mit einer Action
+- Die Action erstellt automatisch die Datensätze in `trustee.document`
+- Dies ist nicht Teil der direkten Trustee-Feature-Implementierung
+
+**Indexes**:
+- Primary key on `id`
+- Index on `organisationId`
+- Index on `contractId`
+- Index on `mandate`
+- Index on `_created_at` (für userreport Filterung)
+- Index auf `_created_by` (automatisch über Systemattribute, für userreport Filterung)
+
+### Tabelle: `trustee.position`
+
+Enthält Buchungspositionen (Speseneinträge).
+
+```python
+class TrusteePosition(BaseModel):
+ id: str = Field( # UUID PK
+ default_factory=lambda: str(uuid.uuid4()),
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ organisationId: str = Field(
+ description="Reference to trustee.organisation.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.organisation"
+ }
+ )
+ contractId: str = Field(
+ description="Reference to trustee.contract.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.contract" # Gefiltert nach organisationId
+ }
+ )
+ valuta: date = Field(
+ description="Value date",
+ json_schema_extra={
+ "frontend_type": "date",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ transactionDateTime: datetime = Field(
+ description="Transaction timestamp",
+ json_schema_extra={
+ "frontend_type": "timestamp",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ company: str = Field(
+ default="",
+ description="Company name",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False,
+ "frontend_required": False
+ }
+ )
+ desc: str = Field(
+ default="",
+ description="Description",
+ json_schema_extra={
+ "frontend_type": "textarea",
+ "frontend_readonly": False,
+ "frontend_required": False
+ }
+ )
+ tags: str = Field(
+ default="",
+ description="Tags (comma-separated or JSON)",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": False,
+ "frontend_required": False
+ }
+ )
+ bookingCurrency: str = Field(
+ description="Booking currency code",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": [
+ {"value": "CHF", "label": {"en": "CHF", "fr": "CHF"}},
+ {"value": "EUR", "label": {"en": "EUR", "fr": "EUR"}},
+ {"value": "USD", "label": {"en": "USD", "fr": "USD"}},
+ # ... weitere Währungen
+ ]
+ }
+ )
+ bookingAmount: float = Field(
+ description="Booking amount",
+ json_schema_extra={
+ "frontend_type": "number",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ originalCurrency: str = Field(
+ description="Original currency code",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": [
+ {"value": "CHF", "label": {"en": "CHF", "fr": "CHF"}},
+ {"value": "EUR", "label": {"en": "EUR", "fr": "EUR"}},
+ {"value": "USD", "label": {"en": "USD", "fr": "USD"}},
+ ]
+ }
+ )
+ originalAmount: float = Field(
+ description="Original amount (manuelle Eingabe in Phase 1, keine automatische Währungsumrechnung)",
+ json_schema_extra={
+ "frontend_type": "number",
+ "frontend_readonly": False,
+ "frontend_required": True
+ }
+ )
+ vatPercentage: float = Field(
+ default=0.0,
+ description="VAT percentage",
+ json_schema_extra={
+ "frontend_type": "number",
+ "frontend_readonly": False,
+ "frontend_required": False
+ }
+ )
+ vatAmount: float = Field(
+ default=0.0,
+ description="VAT amount (wird automatisch berechnet: bookingAmount * vatPercentage / 100, kann manuell überschrieben werden)",
+ json_schema_extra={
+ "frontend_type": "number",
+ "frontend_readonly": False, # Editierbar für manuelle Überschreibung
+ "frontend_required": False
+ }
+ )
+ # MwSt-Berechnung:
+ # - Automatisch beim Ändern von bookingAmount oder vatPercentage
+ # - Wenn vatAmount manuell geändert wird, wird automatische Berechnung erneut durchgeführt
+ # - Warnung (Toast) erscheint bereits beim Ändern, nicht erst beim Speichern
+ mandate: str = Field( # System attribute
+ description="Mandate ID",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt
+
+registerModelLabels(
+ "TrusteePosition",
+ {"en": "Position", "fr": "Position"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "organisationId": {"en": "Organisation", "fr": "Organisation"},
+ "contractId": {"en": "Contract", "fr": "Contrat"},
+ "valuta": {"en": "Value Date", "fr": "Date de valeur"},
+ "transactionDateTime": {"en": "Transaction Date/Time", "fr": "Date/Heure de transaction"},
+ "company": {"en": "Company", "fr": "Entreprise"},
+ "desc": {"en": "Description", "fr": "Description"},
+ "tags": {"en": "Tags", "fr": "Tags"},
+ "bookingCurrency": {"en": "Booking Currency", "fr": "Devise de comptabilisation"},
+ "bookingAmount": {"en": "Booking Amount", "fr": "Montant de comptabilisation"},
+ "originalCurrency": {"en": "Original Currency", "fr": "Devise d'origine"},
+ "originalAmount": {"en": "Original Amount", "fr": "Montant d'origine"},
+ "vatPercentage": {"en": "VAT Percentage", "fr": "Pourcentage TVA"},
+ "vatAmount": {"en": "VAT Amount", "fr": "Montant TVA"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all positions
+- `admin`: Can manage positions for their group (mandate)
+- Users with `operate` role in trustee.access: Can CRUD positions in their organisationId
+ - Wenn `contractId` in trustee.access leer: Zugriff auf alle Positions der Organisation
+ - Wenn `contractId` gesetzt: Zugriff nur auf Positions dieses Contracts
+- Users with `userreport` role in trustee.access: Can CRUD own positions (_createdBy = userId, automatisch über Systemattribute gesetzt)
+ - Contract-Filterung gilt auch für userreport (nur eigene Positions in erlaubten Contracts)
+
+**Indexes**:
+- Primary key on `id`
+- Index on `organisationId`
+- Index on `contractId`
+- Index on `valuta` (for date-based queries)
+- Index on `transactionDateTime`
+- Index on `mandate`
+- Index auf `_createdBy` (automatisch über Systemattribute, für userreport Filterung)
+
+### Tabelle: `trustee.xpositiondocument`
+
+Verknüpfungstabelle, die Positionen mit Dokumenten verknüpft (viele-zu-viele).
+
+```python
+class TrusteePositionDocument(BaseModel):
+ id: str = Field( # UUID PK
+ default_factory=lambda: str(uuid.uuid4()),
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ organisationId: str = Field(
+ description="Reference to trustee.organisation.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.organisation"
+ }
+ )
+ contractId: str = Field(
+ description="Reference to trustee.contract.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.contract"
+ }
+ )
+ documentId: str = Field(
+ description="Reference to trustee.document.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.document" # Gefiltert nach organisationId/contractId
+ }
+ )
+ positionId: str = Field(
+ description="Reference to trustee.position.id",
+ json_schema_extra={
+ "frontend_type": "select",
+ "frontend_readonly": False,
+ "frontend_required": True,
+ "frontend_options": "trustee.position" # Gefiltert nach organisationId/contractId
+ }
+ )
+ mandate: str = Field( # System attribute
+ description="Mandate ID",
+ json_schema_extra={
+ "frontend_type": "text",
+ "frontend_readonly": True,
+ "frontend_required": False
+ }
+ )
+ # Systemattribute werden automatisch vom DatabaseConnector gesetzt
+
+registerModelLabels(
+ "TrusteePositionDocument",
+ {"en": "Position-Document Link", "fr": "Lien Position-Document"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "organisationId": {"en": "Organisation", "fr": "Organisation"},
+ "contractId": {"en": "Contract", "fr": "Contrat"},
+ "documentId": {"en": "Document", "fr": "Document"},
+ "positionId": {"en": "Position", "fr": "Position"},
+ "mandate": {"en": "Mandate", "fr": "Mandat"},
+ },
+)
+```
+<|tool▁calls▁begin|><|tool▁call▁begin|>
+read_file
+
+**RBAC Rules**:
+- `sysadmin`: Can manage all cross-references
+- `admin`: Can manage cross-references for their group (mandate)
+- Users with `operate` role in trustee.access: Can CRUD cross-references in their organisationId
+ - Wenn `contractId` in trustee.access leer: Zugriff auf alle Cross-References der Organisation
+ - Wenn `contractId` gesetzt: Zugriff nur auf Cross-References dieses Contracts
+- Users with `userreport` role in trustee.access: Can CRUD cross-references for their own positions/documents (createdBy = userId, automatisch über Systemattribute gesetzt)
+ - Contract-Filterung gilt auch für userreport (nur eigene Cross-References in erlaubten Contracts)
+
+**Wichtig**: Verknüpfungen sind optional - Positionen können ohne Dokumente existieren, Dokumente können ohne Positionen existieren.
+
+**Indexes**:
+- Primary key on `id`
+- Unique constraint on `(positionId, documentId)` to prevent duplicates
+- Index on `organisationId`
+- Index on `contractId`
+- Index on `documentId`
+- Index on `positionId`
+- Index on `mandate`
+- Index auf `_created_by` (automatisch über Systemattribute, für userreport Filterung)
+
+---
+
+## RBAC-Integration
+
+### System-Level RBAC
+
+Das Trustee Feature integriert sich auf mehreren Ebenen mit dem bestehenden RBAC-System:
+
+#### 1. Feature Access (`resource.trustee`)
+
+**AccessRule**:
+```python
+AccessRule(
+ roleLabel="admin", # or appropriate system role
+ context=AccessRuleContext.RESOURCE,
+ item="trustee",
+ view=True,
+ # No read/create/update/delete for RESOURCE context
+)
+```
+
+**Zweck**: Steuert, ob ein Benutzer überhaupt auf das Trustee Feature zugreifen kann.
+
+#### 2. UI Access (`ui.trustee`)
+
+**AccessRule**:
+```python
+AccessRule(
+ roleLabel="admin",
+ context=AccessRuleContext.UI,
+ item="trustee",
+ view=True,
+)
+```
+
+**Zweck**: Steuert die UI-Sichtbarkeit für das Trustee Feature.
+
+#### 3. Table-Level Access (DATA context)
+
+Each trustee table needs DATA context access rules:
+
+```python
+# Example for trustee.organisation
+AccessRule(
+ roleLabel="admin",
+ context=AccessRuleContext.DATA,
+ item="trustee.organisation",
+ view=True,
+ read=AccessLevel.GROUP, # Can read group records
+ create=AccessLevel.GROUP,
+ update=AccessLevel.GROUP,
+ delete=AccessLevel.GROUP,
+)
+```
+
+### Feature-Level RBAC (trustee.access Tabelle)
+
+Die `trustee.access` Tabelle erweitert System-RBAC mit Feature-spezifischen Berechtigungen:
+
+1. **Rollen-basierter Zugriff**: Benutzern werden Rollen (`userreport`, `admin`, `operate`) für spezifische Organisationen zugewiesen
+2. **Contract-basierter Zugriff (optional)**:
+ - Wenn `contractId` nicht gesetzt (None): Zugriff gilt für die gesamte Organisation
+ - Wenn `contractId` gesetzt: Zugriff ist auf diesen spezifischen Contract beschränkt
+3. **Berechtigungsprüfung**: Bei der Durchführung von Operationen prüfen:
+ - System RBAC (Feature-Zugriff, Tabellen-Zugriff)
+ - Feature RBAC (trustee.access Tabelle für Organisation, Rolle und optional Contract)
+
+### Permission Checking Logic
+
+```python
+def checkTrusteePermission(
+ user: User,
+ organisationId: str,
+ operation: str, # 'read', 'create', 'update', 'delete'
+ resource: str, # 'contract', 'document', 'position', etc.
+ recordId: Optional[str] = None,
+ contractId: Optional[str] = None # Optional: für Contract-basierte Filterung
+) -> bool:
+ """
+ Check if user has permission for trustee operation.
+
+ Steps:
+ 1. Check system RBAC: resource.trustee access
+ 2. Check system RBAC: table-level access (trustee.{resource})
+ 3. Check feature RBAC: trustee.access table
+ - Get user's roles for organisationId (und optional contractId)
+ - Check if role allows operation on resource
+ - Wenn contractId angegeben: Prüfe ob User Access für diesen Contract hat
+ """
+
+ # Step 1: Feature access
+ if not checkSystemRbac(user, "resource.trustee"):
+ return False
+
+ # Step 2: Table access
+ tableName = f"trustee.{resource}"
+ if not checkSystemRbac(user, tableName, operation):
+ return False
+
+ # Step 3: Feature-level access
+ # Get user's access records for this organisation
+ userAccessRecords = getTrusteeAccessForUser(user.id, organisationId)
+
+ # Filter by contract if contractId is specified
+ if contractId:
+ # User muss Access für diesen spezifischen Contract haben
+ # Oder Access ohne contractId (full organisation access)
+ userAccessRecords = [
+ acc for acc in userAccessRecords
+ if acc.contractId is None or acc.contractId == contractId
+ ]
+ else:
+ # Wenn kein contractId angegeben: User muss Access haben (mit oder ohne contractId)
+ pass
+
+ userRoles = [acc.roleId for acc in userAccessRecords]
+
+ # Check role permissions
+ if "admin" in userRoles:
+ # Admin can do everything for their organisation (or specific contract)
+ return True
+
+ if operation in ["read", "create", "update", "delete"]:
+ if "operate" in userRoles:
+ # Operate can CRUD for organisation (or specific contract)
+ if resource in ["contract", "document", "position", "xpositiondocument"]:
+ return True
+
+ if "userreport" in userRoles:
+ # Userreport can CRUD own records
+ if recordId:
+ record = getRecord(resource, recordId)
+ if record._createdBy == user.id: # _createdBy wird automatisch vom DatabaseConnector gesetzt
+ return True
+ else:
+ # Creating new record - allowed
+ return True
+
+ return False
+```
+
+### Rollen-Berechtigungs-Matrix
+
+| Role | Organisation | Contract | Document | Position | PositionDocument |
+|------|--------------|----------|-----------|----------|------------------|
+| **admin** (system) | CRUD (group) | CRUD (group) | CRUD (group) | CRUD (group) | CRUD (group) |
+| **admin** (trustee.access) | CRUD (org) | CRUD (org) | CRUD (org) | CRUD (org) | CRUD (org) |
+| **operate** | Read (org) | CRUD (org) | CRUD (org) | CRUD (org) | CRUD (org) |
+| **userreport** | Read (org) | Read (org) | CRUD (own) | CRUD (own) | CRUD (own) |
+
+**Legend**:
+- CRUD: Create, Read, Update, Delete
+- (group): Within user's mandate/group
+- (org): Within assigned organisation
+- (own): Only records created by the user
+
+---
+
+## API-Design
+
+### RBAC-Berechtigungsabfrage
+
+Das UI benötigt Berechtigungsinformationen, um die richtigen Komponenten zu rendern. Hierfür stehen folgende RBAC-Routen zur Verfügung:
+
+#### Einzelne Berechtigung abfragen
+```python
+GET /api/rbac/permissions?context=UI&item=trustee
+GET /api/rbac/permissions?context=RESOURCE&item=trustee
+```
+Gibt `UserPermissions` für einen spezifischen Kontext und Item zurück.
+
+#### Alle Berechtigungen abrufen (für UI-Initialisierung)
+```python
+GET /api/rbac/permissions/all # Alle UI- und RESOURCE-Berechtigungen
+GET /api/rbac/permissions/all?context=UI # Nur UI-Berechtigungen
+GET /api/rbac/permissions/all?context=RESOURCE # Nur RESOURCE-Berechtigungen
+```
+Gibt alle Berechtigungen des aktuellen Users zurück:
+```json
+{
+ "ui": {
+ "trustee": {"view": true, "read": null, "create": null, "update": null, "delete": null},
+ "trustee.organisation": {"view": true, ...},
+ "trustee.role": {"view": true, ...},
+ ...
+ },
+ "resource": {
+ "trustee": {"view": true, ...},
+ ...
+ }
+}
+```
+
+**Verwendung**:
+- **UI-Initialisierung**: Einmaliger Call zu `/api/rbac/permissions/all` beim Laden der Anwendung
+- **Spezifische Prüfung**: `/api/rbac/permissions?context=UI&item=trustee.organisation` für dynamische Berechtigungsprüfungen
+
+### Route-Struktur
+
+Alle Trustee-Routen folgen dem Muster `/api/trustee/{resource}`:
+
+#### Organisation Routes (`routeDataTrusteeOrganisations.py`)
+
+```python
+GET /api/trustee/organisations/ # List organisations (filtered by RBAC)
+GET /api/trustee/organisations/{id} # Get organisation
+POST /api/trustee/organisations/ # Create organisation
+PUT /api/trustee/organisations/{id} # Update organisation
+DELETE /api/trustee/organisations/{id} # Delete organisation
+```
+
+#### Role Routes (`routeDataTrusteeRoles.py`)
+
+```python
+GET /api/trustee/roles/ # List roles
+GET /api/trustee/roles/{id} # Get role
+POST /api/trustee/roles/ # Create role (sysadmin only)
+PUT /api/trustee/roles/{id} # Update role (sysadmin only)
+DELETE /api/trustee/roles/{id} # Delete role (sysadmin only)
+```
+
+#### Access Routes (`routeDataTrusteeAccess.py`)
+
+```python
+GET /api/trustee/access/ # List access records (filtered)
+GET /api/trustee/access/{id} # Get access record
+POST /api/trustee/access/ # Create access record
+PUT /api/trustee/access/{id} # Update access record
+DELETE /api/trustee/access/{id} # Delete access record
+GET /api/trustee/access/organisation/{orgId} # Get access for organisation
+GET /api/trustee/access/user/{userId} # Get access for user
+```
+
+#### Contract Routes (`routeDataTrusteeContracts.py`)
+
+```python
+GET /api/trustee/contracts/ # List contracts (filtered)
+GET /api/trustee/contracts/{id} # Get contract
+POST /api/trustee/contracts/ # Create contract
+PUT /api/trustee/contracts/{id} # Update contract
+DELETE /api/trustee/contracts/{id} # Delete contract
+GET /api/trustee/contracts/organisation/{orgId} # Get contracts for organisation
+```
+
+#### Document Routes (`routeDataTrusteeDocuments.py`)
+
+```python
+GET /api/trustee/documents/ # List documents (filtered)
+GET /api/trustee/documents/{id} # Get document metadata
+GET /api/trustee/documents/{id}/data # Get document binary data
+POST /api/trustee/documents/ # Create document (with upload)
+PUT /api/trustee/documents/{id} # Update document metadata
+DELETE /api/trustee/documents/{id} # Delete document
+GET /api/trustee/documents/contract/{contractId} # Get documents for contract
+```
+
+#### Position Routes (`routeDataTrusteePositions.py`)
+
+```python
+GET /api/trustee/positions/ # List positions (filtered)
+GET /api/trustee/positions/{id} # Get position
+POST /api/trustee/positions/ # Create position
+PUT /api/trustee/positions/{id} # Update position
+DELETE /api/trustee/positions/{id} # Delete position
+GET /api/trustee/positions/contract/{contractId} # Get positions for contract
+GET /api/trustee/positions/organisation/{orgId} # Get positions for organisation
+```
+
+#### Position-Document Cross-Reference Routes (`routeDataTrusteePositionDocuments.py`)
+
+```python
+GET /api/trustee/position-documents/ # List cross-references
+GET /api/trustee/position-documents/{id} # Get cross-reference
+POST /api/trustee/position-documents/ # Link position to document
+DELETE /api/trustee/position-documents/{id} # Unlink position from document
+GET /api/trustee/position-documents/position/{positionId} # Get documents for position
+GET /api/trustee/position-documents/document/{documentId} # Get positions for document
+```
+
+### Request/Response Examples
+
+#### Create Organisation
+
+```http
+POST /api/trustee/organisations/
+Content-Type: application/json
+
+{
+ "id": "acme-corp",
+ "label": "ACME Corporation",
+ "enabled": true
+}
+```
+
+Response:
+```json
+{
+ "id": "acme-corp",
+ "label": "ACME Corporation",
+ "enabled": true,
+ "mandate": "mandate-123",
+ "created": 1704067200.0,
+ "updated": 1704067200.0
+}
+```
+
+#### Create Access Record
+
+```http
+POST /api/trustee/access/
+Content-Type: application/json
+
+{
+ "organisationId": "acme-corp",
+ "roleId": "operate",
+ "userId": "user-456"
+}
+```
+
+#### Upload Document
+
+```http
+POST /api/trustee/documents/
+Content-Type: multipart/form-data
+
+organisationId: acme-corp
+contractId: contract-789
+documentName: receipt.pdf
+documentMimeType: application/pdf
+file:
+```
+
+---
+
+## Implementierungsplan
+
+### Phase 1: Grundlagen (Woche 1-2)
+
+#### 1.1 Datenmodelle
+- [ ] Create `datamodelTrustee.py` with all model classes
+- [ ] Register model labels
+- [ ] Add frontend metadata
+- [ ] Define validation rules
+
+#### 1.2 Datenbank-Interface
+- [ ] Create `interfaceDbTrusteeObjects.py`
+- [ ] Implement database connector initialization
+- [ ] Implement table creation/initialization
+- [ ] Implement basic CRUD operations for all tables
+- [ ] Add RBAC integration helpers
+
+#### 1.3 RBAC Setup
+- [ ] Create initial AccessRules for:
+ - `resource.trustee`
+ - `ui.trustee`
+ - All trustee tables (DATA context)
+- [ ] Create bootstrap script to initialize roles
+- [ ] Document RBAC configuration
+
+### Phase 2: Core API (Woche 3-4)
+
+#### 2.1 Route Implementation
+- [ ] `routeDataTrusteeOrganisations.py`
+- [ ] `routeDataTrusteeRoles.py`
+- [ ] `routeDataTrusteeAccess.py`
+- [ ] `routeDataTrusteeContracts.py`
+- [ ] `routeDataTrusteeDocuments.py`
+- [ ] `routeDataTrusteePositions.py`
+- [ ] `routeDataTrusteePositionDocuments.py`
+
+#### 2.2 Berechtigungsprüfung
+- [ ] Implement `checkTrusteePermission()` helper
+- [ ] Integrate permission checks in all routes
+- [ ] Add permission checks in interface methods
+- [ ] Test permission scenarios
+
+#### 2.3 Route Registration
+- [ ] Register routes in main app
+- [ ] Add route documentation
+- [ ] Test API endpoints
+
+### Phase 3: Advanced Features (Week 5-6)
+
+#### 3.1 Dokumentenspeicherung
+- [ ] Decide on storage approach (DB vs file system)
+- [ ] Implement document upload/download
+- [ ] Add file size limits
+- [ ] Add MIME type validation
+
+#### 3.2 Query Optimization
+- [ ] Add database indexes
+- [ ] Optimize RBAC filtering queries
+- [ ] Add pagination support
+- [ ] Add filtering and sorting
+
+#### 3.3 Validierung & Fehlerbehandlung
+- [ ] Add input validation
+- [ ] Add foreign key validation
+- [ ] Improve error messages
+- [ ] Add audit logging
+
+### Phase 4: Frontend-Integration (Woche 7-8)
+
+#### 4.1 UI Components
+- [ ] Organisation management UI
+- [ ] Role management UI (sysadmin only)
+- [ ] Access management UI
+- [ ] Contract management UI
+- [ ] Document upload/management UI
+- [ ] Position entry/management UI
+- [ ] Position-document linking UI
+
+#### 4.2 RBAC UI Integration
+- [ ] Hide UI elements based on `ui.trustee` access
+- [ ] Show/hide features based on roles
+- [ ] Add permission error messages
+
+### Phase 5: Testing & Dokumentation (Woche 9-10)
+
+#### 5.1 Unit Tests
+- [ ] Test data models
+- [ ] Test interface methods
+- [ ] Test permission checking
+- [ ] Test RBAC integration
+
+#### 5.2 Integration Tests
+- [ ] Test API endpoints
+- [ ] Test RBAC scenarios
+- [ ] Test document upload/download
+- [ ] Test cross-reference operations
+
+#### 5.3 Documentation
+- [ ] API documentation
+- [ ] User guide
+- [ ] Admin guide
+- [ ] Developer guide
+
+---
+
+## Migrationsstrategie
+
+### Datenbank-Migration
+
+1. **Tabellen erstellen**: Interface-Initialisierung verwenden, um Tabellen zu erstellen
+2. **Initialdaten**: Bootstrap-Skript zum Erstellen initialer Rollen
+3. **RBAC-Regeln**: Migrationsskript zum Erstellen von AccessRules
+4. **Datenmigration**: Falls Migration von bestehendem System, Migrationsskript erstellen
+
+### Rollout-Plan
+
+1. **Entwicklung**: Implementierung in Entwicklungsumgebung
+2. **Testing**: Deployment in Testumgebung, Integrationstests durchführen
+3. **Staging**: Deployment in Staging, User Acceptance Testing
+4. **Produktion**: Schrittweiser Rollout mit Feature-Flag
+
+### Rückwärtskompatibilität
+
+- Keine Breaking Changes am bestehenden System
+- Trustee Feature ist additiv
+- Bestehendes RBAC-System bleibt unverändert
+
+---
+
+## Testing Strategy
+
+### Unit Tests
+
+```python
+# Test data models
+def test_trustee_organisation_model():
+ org = TrusteeOrganisation(
+ id="test-org",
+ label="Test Organisation",
+ enabled=True
+ )
+ assert org.id == "test-org"
+
+# Test interface methods
+def test_create_organisation():
+ interface = TrusteeInterface(user)
+ org = interface.createOrganisation(...)
+ assert org.id is not None
+
+# Test permission checking
+def test_check_trustee_permission():
+ assert checkTrusteePermission(user, orgId, "read", "contract") == True
+```
+
+### Integrations-Tests
+
+```python
+# Test API endpoints
+def test_create_organisation_api():
+ response = client.post("/api/trustee/organisations/", json={...})
+ assert response.status_code == 201
+
+# Test RBAC filtering
+def test_organisation_list_filtered_by_rbac():
+ # User should only see organisations they have access to
+ response = client.get("/api/trustee/organisations/")
+ assert all(org in allowed_orgs for org in response.json())
+```
+
+### RBAC-Test-Szenarien
+
+1. **Sysadmin**: Kann auf alle Organisationen zugreifen
+2. **Admin (Gruppe)**: Kann auf Organisationen in ihrer Gruppe zugreifen
+3. **Admin (Trustee)**: Kann auf Organisationen zugreifen, denen sie zugewiesen sind
+4. **Operate**: Kann CRUD für Verträge/Dokumente/Positionen in zugewiesenen Organisationen
+5. **Userreport**: Kann nur eigene Datensätze CRUD
+6. **Kein Zugriff**: Kann nicht auf Trustee Feature zugreifen
+
+---
+
+## Implementierungsdetails
+
+### DatabaseConnector - Automatische Tabellenerstellung
+
+Der `DatabaseConnector` erstellt Tabellen automatisch aus Pydantic-Modellen:
+
+#### Prozess
+
+1. **Tabellen-Erstellung**: `_ensureTableExists(model_class)` wird bei jedem Datenbankzugriff aufgerufen
+2. **Spalten-Generierung**: Spalten werden aus Modell-Feldern generiert:
+ - `id`: VARCHAR(255) PRIMARY KEY
+ - Andere Felder: Automatische Typ-Konvertierung (str → TEXT, int → INTEGER, float → DOUBLE PRECISION, bool → BOOLEAN, date → DATE, datetime → TIMESTAMP, bytes → BYTEA)
+ - Systemattribute: Automatisch hinzugefügt (`_createdAt`, `_modifiedAt`, `_createdBy`, `_modifiedBy`)
+3. **Index-Erstellung**: Automatische Indizes für Foreign Keys (Felder die auf `Id` enden)
+4. **Migration**: Fehlende Spalten werden automatisch hinzugefügt (additive Migration, keine Spalten-Löschung)
+
+#### Beispiel Trustee-Modell
+
+```python
+class TrusteeOrganisation(BaseModel):
+ id: str = Field(...) # VARCHAR(255) PRIMARY KEY
+ label: str = Field(...) # TEXT
+ enabled: bool = Field(...) # BOOLEAN
+ mandate: str = Field(...) # TEXT
+ # Systemattribute werden automatisch hinzugefügt:
+ # _createdAt: DOUBLE PRECISION
+ # _modifiedAt: DOUBLE PRECISION
+ # _createdBy: VARCHAR(255)
+ # _modifiedBy: VARCHAR(255)
+```
+
+**Ergebnis**: Tabelle `TrusteeOrganisation` wird automatisch erstellt mit allen Spalten.
+
+### RBAC-Filterung auf DB-Ebene
+
+Das RBAC-System filtert Daten automatisch auf Datenbank-Ebene:
+
+#### Funktion: `getRecordsetWithRBAC()`
+
+```python
+records = getRecordsetWithRBAC(
+ connector=db,
+ modelClass=TrusteeOrganisation,
+ currentUser=user,
+ recordFilter={"enabled": True} # Zusätzliche Filter
+)
+```
+
+#### Filterung basierend auf AccessLevel
+
+1. **View-Permission prüfen**: Wenn `permissions.view = false`, werden keine Records zurückgegeben
+2. **Read-Level Filterung**:
+ - `ALL`: Keine WHERE-Clause (alle Records)
+ - `MY`: `WHERE "_createdBy" = %s` (nur eigene Records)
+ - `GROUP`: `WHERE "mandateId" = %s` (nur Records der eigenen Gruppe)
+ - `NONE`: `WHERE 1 = 0` (keine Records)
+
+#### Trustee-spezifische Filterung
+
+Für Trustee-Feature müssen zusätzliche Filter implementiert werden:
+
+1. **Organisation-Filterung**: Basierend auf `trustee.access` Tabelle
+ - User mit `admin` Rolle: Nur Records mit `organisationId` aus eigenen Zugriffen
+ - User mit `operate` Rolle: Nur Records mit `organisationId` aus eigenen Zugriffen
+ - User mit `userreport` Rolle: Nur eigene Records (`_createdBy = userId`)
+
+2. **Contract-Filterung**: Basierend auf `contractId` in `trustee.access` Tabelle
+ - **Ohne `contractId` (None)**: User hat Zugriff auf alle Contracts der Organisation
+ - **Mit `contractId`**: User hat Zugriff nur auf diesen spezifischen Contract
+ - **Filter-Logik**:
+ - Wenn User Access ohne `contractId` hat → Zugriff auf alle Contracts der Organisation
+ - Wenn User Access mit spezifischem `contractId` hat → Zugriff nur auf diesen Contract
+ - Wenn User beide hat → Zugriff auf alle Contracts (weil Access ohne contractId übergeordnet ist)
+ - **Anwendung**: Bei Queries für `contract`, `document`, `position` wird zusätzlich nach `contractId` gefiltert:
+ - Records mit `contractId` werden nur angezeigt wenn User Access für diesen Contract hat (oder Access ohne contractId)
+ - Records ohne `contractId` werden angezeigt wenn User Access ohne contractId hat
+
+**Implementierung**:
+- **Zwei-Stufen-Filterung**: Zuerst System-RBAC (`getRecordsetWithRBAC()`), dann zusätzliche Trustee-Filterung in der Interface-Schicht
+- Diese Filterung muss in der Trustee-Interface-Schicht implementiert werden, da sie feature-spezifisch ist
+- **Wichtig**: Für das UI ist diese Logik nicht relevant, da RBAC automatisch alle Daten korrekt gefiltert liefert
+
+### FormGenerator - Automatische UI-Generierung
+
+**FormGenerator** bietet folgende automatische Features:
+
+#### Tabellen-Features (automatisch)
+- ✅ Auto-Spalten-Erkennung (wenn keine `columns` übergeben)
+- ✅ Sortierung (Klick auf Header: asc → desc → keine)
+- ✅ Filter (Text, Boolean, Enum, Date)
+- ✅ Pagination (konfigurierbare `pageSize`)
+- ✅ Spalten-Resize (Drag & Drop)
+- ✅ Row Selection (Checkboxen)
+- ✅ Custom Actions (Buttons pro Zeile)
+- ✅ Custom Formatter (pro Spalte)
+- ✅ Loading State
+
+#### Formular-Features (automatisch)
+- ✅ Feld-Typen basierend auf `frontend_type`
+- ✅ Validierung basierend auf `frontend_required`
+- ✅ Readonly-Felder basierend auf `frontend_readonly`
+- ✅ Select-Optionen aus `frontend_options`
+- ✅ Floating Labels
+
+#### Custom Logic (manuell implementieren)
+- 🔧 Custom Validierungen (z.B. MwSt-Berechnung)
+- 🔧 Custom Formatter (z.B. Links zu verknüpften Records)
+- 🔧 Custom Actions (z.B. Download-Button)
+- 🔧 Custom Business Logic
+
+## Implementierungsdetails
+
+### Dokumentenspeicherung
+
+**Entscheidung**: Dokument-Binärdaten werden in PostgreSQL BYTEA-Spalte gespeichert.
+- Einfach, transaktional, einfaches Backup
+- Für Phase 1 ausreichend
+
+### Organisation ID Format
+
+**Entscheidung**: String-Label (z.B. "acme-corp")
+- Menschenlesbar, benutzerfreundlich
+- Validierung: Alphanumerisch, Bindestriche, Unterstriche
+- Längenbegrenzung: 3-50 Zeichen
+- Groß-/Kleinschreibung-unabhängige Eindeutigkeitsprüfung
+
+### Rollen-Management
+
+**Entscheidung**: Rollen sind bearbeitbar
+- Rollen werden in `trustee.role` Tabelle gespeichert
+- Initiale Rollen werden beim Bootstrap erstellt
+- Rollen können bearbeitet werden, aber nicht gelöscht werden, wenn sie in Verwendung sind
+
+### Access-Record Eindeutigkeit
+
+**Entscheidung**: Mehrere Access-Records erlaubt
+- Ein Benutzer kann mehrere Rollen für dieselbe Organisation haben
+- Unique Constraint auf `(organisationId, roleId, userId)` verhindert Duplikate
+- Separate Records für jede Rollenkombination
+
+### Vertrags-Organisations-Beziehung
+
+**Entscheidung**: Verträge sind unveränderlich
+- `contract.organisationId` kann nach der Erstellung NICHT mehr geändert werden
+- Datenintegrität und Audit-Trail werden dadurch gewährleistet
+
+### Position-Dokument-Beziehung
+
+**Entscheidung**: Verknüpfungen sind optional
+- Positionen können ohne Dokumente existieren
+- Dokumente können ohne Positionen existieren
+- Viele-zu-viele-Beziehung über `xpositiondocument` Tabelle
+
+### Währungsumrechnung
+
+**Entscheidung**: Phase 1 - Manuelle Eingabe, keine automatische Umrechnung
+- Beide Währungen werden gespeichert: `bookingCurrency/bookingAmount` und `originalCurrency/originalAmount`
+- Manuelle Eingabe durch Benutzer
+- Wechselkurstabelle und automatische Umrechnung können in Phase 2 hinzugefügt werden
+
+### MwSt-Berechnung
+
+**Entscheidung**: Automatische Berechnung mit manueller Überschreibung
+- `vatAmount = bookingAmount * vatPercentage / 100` wird automatisch berechnet
+- Manuelle Überschreibung erlaubt, falls erforderlich
+- Validierungswarnung, wenn Werte nicht übereinstimmen
+
+---
+
+## Nächste Schritte
+
+1. **Dokument mit Stakeholdern final überprüfen**
+2. **Detaillierte technische Spezifikationen** für jede Komponente erstellen
+3. **Entwicklungsumgebung** und Projektstruktur einrichten
+4. **Phase 1 Implementierung beginnen**
+
+---
+
+## Anhang
+
+### A. Datenbank-Schema SQL
+
+```sql
+-- Organisation table
+CREATE TABLE trustee_organisation (
+ id VARCHAR(255) PRIMARY KEY,
+ label VARCHAR(255) NOT NULL,
+ enabled BOOLEAN DEFAULT TRUE,
+ mandate VARCHAR(255) NOT NULL,
+ _created_at FLOAT NOT NULL, -- Systemattribut
+ _modified_at FLOAT NOT NULL, -- Systemattribut
+ _created_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ _modified_by VARCHAR(255) -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+);
+
+CREATE INDEX idx_trustee_organisation_mandate ON trustee_organisation(mandate);
+
+-- Role table (Rollen sind bearbeitbar)
+CREATE TABLE trustee_role (
+ id VARCHAR(255) PRIMARY KEY, -- String-Label (z.B. "userreport", "admin", "operate")
+ desc TEXT NOT NULL,
+ mandate VARCHAR(255) NOT NULL,
+ _created_at FLOAT NOT NULL, -- Systemattribut
+ _modified_at FLOAT NOT NULL, -- Systemattribut
+ _created_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ _modified_by VARCHAR(255) -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+);
+
+CREATE INDEX idx_trustee_role_mandate ON trustee_role(mandate);
+
+-- Access table (ein Benutzer kann mehrere Rollen für dieselbe Organisation haben)
+CREATE TABLE trustee_access (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ organisation_id VARCHAR(255) NOT NULL REFERENCES trustee_organisation(id),
+ role_id VARCHAR(255) NOT NULL REFERENCES trustee_role(id),
+ user_id VARCHAR(255) NOT NULL,
+ mandate VARCHAR(255) NOT NULL,
+ _created_at FLOAT NOT NULL, -- Systemattribut
+ _modified_at FLOAT NOT NULL, -- Systemattribut
+ _created_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ _modified_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ UNIQUE(organisation_id, role_id, user_id) -- Verhindert Duplikate, erlaubt aber mehrere Rollen pro Benutzer-Organisation
+);
+
+CREATE INDEX idx_trustee_access_organisation ON trustee_access(organisation_id);
+CREATE INDEX idx_trustee_access_user ON trustee_access(user_id);
+CREATE INDEX idx_trustee_access_role ON trustee_access(role_id);
+CREATE INDEX idx_trustee_access_mandate ON trustee_access(mandate);
+
+-- Contract table (Verträge sind unveränderlich: organisation_id kann nicht geändert werden)
+CREATE TABLE trustee_contract (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ organisation_id VARCHAR(255) NOT NULL REFERENCES trustee_organisation(id), -- Immutable nach Erstellung
+ label VARCHAR(255) NOT NULL,
+ enabled BOOLEAN DEFAULT TRUE,
+ mandate VARCHAR(255) NOT NULL,
+ _created_at FLOAT NOT NULL, -- Systemattribut
+ _modified_at FLOAT NOT NULL, -- Systemattribut
+ _created_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ _modified_by VARCHAR(255) -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+);
+
+CREATE INDEX idx_trustee_contract_organisation ON trustee_contract(organisation_id);
+CREATE INDEX idx_trustee_contract_mandate ON trustee_contract(mandate);
+
+-- Document table
+CREATE TABLE trustee_document (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ organisation_id VARCHAR(255) NOT NULL REFERENCES trustee_organisation(id),
+ contract_id UUID NOT NULL REFERENCES trustee_contract(id),
+ document_data BYTEA NOT NULL, -- Binärdaten werden direkt in Datenbank gespeichert
+ document_name VARCHAR(255) NOT NULL,
+ document_mime_type VARCHAR(100) NOT NULL,
+ mandate VARCHAR(255) NOT NULL,
+ created FLOAT NOT NULL,
+ updated FLOAT NOT NULL,
+ created_by VARCHAR(255) NOT NULL
+);
+
+CREATE INDEX idx_trustee_document_organisation ON trustee_document(organisation_id);
+CREATE INDEX idx_trustee_document_contract ON trustee_document(contract_id);
+CREATE INDEX idx_trustee_document_mandate ON trustee_document(mandate);
+CREATE INDEX idx_trustee_document_created_by ON trustee_document(_created_by);
+
+-- Position table
+CREATE TABLE trustee_position (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ organisation_id VARCHAR(255) NOT NULL REFERENCES trustee_organisation(id),
+ contract_id UUID NOT NULL REFERENCES trustee_contract(id),
+ valuta DATE NOT NULL,
+ transaction_date_time TIMESTAMP NOT NULL,
+ company VARCHAR(255),
+ desc TEXT,
+ tags VARCHAR(255),
+ booking_currency VARCHAR(10) NOT NULL,
+ booking_amount FLOAT NOT NULL,
+ original_currency VARCHAR(10) NOT NULL,
+ original_amount FLOAT NOT NULL,
+ vat_percentage FLOAT DEFAULT 0.0,
+ vat_amount FLOAT DEFAULT 0.0,
+ mandate VARCHAR(255) NOT NULL,
+ created FLOAT NOT NULL,
+ updated FLOAT NOT NULL,
+ created_by VARCHAR(255) NOT NULL
+);
+
+CREATE INDEX idx_trustee_position_organisation ON trustee_position(organisation_id);
+CREATE INDEX idx_trustee_position_contract ON trustee_position(contract_id);
+CREATE INDEX idx_trustee_position_valuta ON trustee_position(valuta);
+CREATE INDEX idx_trustee_position_transaction_date_time ON trustee_position(transaction_date_time);
+CREATE INDEX idx_trustee_position_mandate ON trustee_position(mandate);
+CREATE INDEX idx_trustee_position_created_by ON trustee_position(_created_by); -- Für userreport Filterung
+
+-- Position-Document cross-reference table (Tabellenname in Kleinbuchstaben: xpositiondocument)
+CREATE TABLE trustee_xpositiondocument (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ organisation_id VARCHAR(255) NOT NULL REFERENCES trustee_organisation(id),
+ contract_id UUID NOT NULL REFERENCES trustee_contract(id),
+ document_id UUID NOT NULL REFERENCES trustee_document(id),
+ position_id UUID NOT NULL REFERENCES trustee_position(id),
+ mandate VARCHAR(255) NOT NULL,
+ _created_at FLOAT NOT NULL, -- Systemattribut
+ _modified_at FLOAT NOT NULL, -- Systemattribut
+ _created_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ _modified_by VARCHAR(255), -- Wird automatisch über Systemattribute vom DatabaseConnector gesetzt
+ UNIQUE(position_id, document_id) -- Verknüpfungen sind optional: Positionen/Dokumente können unabhängig existieren
+);
+
+CREATE INDEX idx_trustee_xpd_organisation ON trustee_xpositiondocument(organisation_id);
+CREATE INDEX idx_trustee_xpd_contract ON trustee_xpositiondocument(contract_id);
+CREATE INDEX idx_trustee_xpd_document ON trustee_xpositiondocument(document_id);
+CREATE INDEX idx_trustee_xpd_position ON trustee_xpositiondocument(position_id);
+CREATE INDEX idx_trustee_xpd_mandate ON trustee_xpositiondocument(mandate);
+CREATE INDEX idx_trustee_xpd_created_by ON trustee_xpositiondocument(_created_by); -- Für userreport Filterung
+```
+
+### B. Initial Roles Bootstrap
+
+**Implementierung**: Bootstrap-Script erstellt initiale Rollen automatisch beim ersten Start des Trustee-Features (ähnlich wie `initBootstrap()` für andere System-Tabellen).
+
+```python
+# Bootstrap script to create initial roles
+initial_roles = [
+ {
+ "id": "userreport",
+ "desc": "Can deliver user documents to the system"
+ },
+ {
+ "id": "admin",
+ "desc": "Can administrate the access"
+ },
+ {
+ "id": "operate",
+ "desc": "Can use data for operations"
+ }
+]
+```
+
+### C. RBAC Access Rules Bootstrap
+
+```python
+# Bootstrap script to create initial AccessRules
+initial_access_rules = [
+ # Feature access
+ {
+ "roleLabel": "sysadmin",
+ "context": "RESOURCE",
+ "item": "trustee",
+ "view": True
+ },
+ # UI access
+ {
+ "roleLabel": "sysadmin",
+ "context": "UI",
+ "item": "trustee",
+ "view": True
+ },
+ # Table access rules for each table...
+]
+```
+
+---
+
+**Dokumentversion**: 1.0
+**Letzte Aktualisierung**: 2025-01-03
+**Autor**: Architektur-Review
+**Status**: Ausstehende Überprüfung
diff --git a/ui_nyla/feature-trustee/doc_trustee_feature_ui_demo.html b/ui_nyla/feature-trustee/doc_trustee_feature_ui_demo.html
new file mode 100644
index 0000000..e2a65fc
--- /dev/null
+++ b/ui_nyla/feature-trustee/doc_trustee_feature_ui_demo.html
@@ -0,0 +1,1339 @@
+
+
+
+
+
+ Trustee Feature - UI Demo
+
+
+
+
+
+
🏢 Trustee Feature - UI Demo
+
Demonstration der UI-Komponenten basierend auf FormGenerator-Pattern
+
+
+
+ Hinweis: Dies ist eine statische Demo zur Visualisierung der UI-Struktur.
+ Die tatsächliche Implementierung verwendet React/TypeScript mit FormGenerator-Komponente.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Organisationen
+
+
+
+
+ RBAC: sysadmin kann alle verwalten, admin kann für Gruppe verwalten
+ Komponente: FormGenerator mit Logic-Hook
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ID
+
Label
+
Enabled
+
Mandate
+
Aktionen
+
+
+
+
+
acme-corp
+
ACME Corporation
+
Ja
+
mandate-001
+
+
+
+
+
+
+
tech-solutions
+
Tech Solutions AG
+
Ja
+
mandate-001
+
+
+
+
+
+
+
global-trust
+
Global Trust Ltd.
+
Nein
+
mandate-002
+
+
+
+
+
+
+
+
+
+
+
Zeige 1-3 von 3 Einträgen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Rollen
+
+
+
+
+ RBAC: Nur sysadmin kann Rollen verwalten
+ Initiale Rollen: userreport, admin, operate (werden automatisch erstellt)
+
+
+
+
+
+
+
+
+
+
+
ID
+
Beschreibung
+
Aktionen
+
+
+
+
+
userreport
+
Kann Benutzerdokumente an das System liefern
+
+
+
+
+
+
+
admin
+
Kann den Zugriff administrieren
+
+
+
+
+
+
+
operate
+
Kann Daten für Operationen verwenden
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Access
+
+
+
+
+ RBAC: sysadmin kann alle verwalten, admin kann für Gruppe verwalten
+ Besonderheit: Dropdowns zeigen nur erlaubte Optionen (RBAC-gefiltert)
+ Contract-Zugriff: contractId ist optional - wenn leer, Zugriff auf gesamte Organisation; wenn gesetzt, nur auf diesen Contract
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ID
+
Organisation
+
Rolle
+
User ID
+
Contract
+
Aktionen
+
+
+
+
+
a1b2c3d4-...
+
ACME Corporation
+
admin
+
user-123
+
Gesamte Organisation
+
+
+
+
+
+
+
e5f6g7h8-...
+
Tech Solutions AG
+
operate
+
user-456
+
Gesamte Organisation
+
+
+
+
+
+
+
i9j0k1l2-...
+
ACME Corporation
+
userreport
+
user-789
+
Muster AG 2026
+
+
+
+
+
+
+
m3n4o5p6-...
+
ACME Corporation
+
operate
+
user-999
+
Muster AG 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Contracts
+
+
+
+
+ RBAC: sysadmin, admin (für Gruppe), trustee.admin (für zugewiesene Organisationen)
+ Besonderheit: organisationId ist immutable nach Erstellung (readonly wenn id vorhanden)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ID
+
Organisation
+
Label
+
Enabled
+
Aktionen
+
+
+
+
+
c1d2e3f4-...
+
ACME Corporation
+
Muster AG 2026
+
Ja
+
+
+
+
+
+
+
g5h6i7j8-...
+
Tech Solutions AG
+
Vertrag 2025
+
Ja
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Documents
+
+
+
+
+ RBAC: sysadmin, admin (für Gruppe), trustee.operate (für Organisationen), trustee.userreport (eigene Records)
+ Besonderheit: File Upload/Download erfolgt über Workflow-System, nicht direkt integriert
+