opencrm/README.md

1230 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OpenCRM
Web-basiertes CRM-System für Kundenverwaltung mit Verträgen (Energie, Telekommunikation, KFZ-Versicherung).
**Version: 1.1.0** ([Changelog](#changelog))
## Features
- **Kundenverwaltung**: Privat- und Geschäftskunden mit Stammdaten
- **Adressen**: Mehrere Liefer-/Melde- und Rechnungsadressen pro Kunde
- **Bankkarten**: Mit Ablaufdatum, Aktiv-Status und Dokument-Upload (PDF)
- **Ausweise**: Personalausweis, Reisepass, etc. mit Ablaufdatum und Dokument-Upload (PDF)
- **Zähler**: Strom-/Gaszähler mit Zählerstandhistorie
- **Rechnungen**: Rechnungsverwaltung für Energieverträge mit Dokumenten-Upload
- **Vertrags-Cockpit**: Dashboard zur Überwachung offener Aufgaben (fehlende Dokumente, Rechnungen)
- **Auto-Vertragsstatus**: Lieferbestätigung-Upload setzt `DRAFT``ACTIVE` (mit Vertragsbeginn),
Kündigungsbestätigung-Upload setzt `ACTIVE``CANCELLED` (mit Datum),
nightly-Cron setzt `ACTIVE`-Verträge mit abgelaufenem `endDate` auf `EXPIRED`
- **Verträge**:
- Energie (Strom, Gas)
- Telekommunikation (DSL, Glasfaser, Mobilfunk, TV)
- KFZ-Versicherung
- **Folgeverträge**: Automatische Datenübernahme bei Anbieterwechsel
- **Vertriebsplattformen**: Verwaltbar über WebUI
- **Email-Provisionierung**: Automatische E-Mail-Weiterleitung bei Plesk/cPanel/DirectAdmin
- **Berechtigungssystem**: Admin, Mitarbeiter, Nur-Lesen, Kundenportal
- **Verschlüsselte Zugangsdaten**: Portal-Passwörter AES-256-GCM verschlüsselt
- **DSGVO-Compliance**: Audit-Logging mit Hash-Chain-Integritätsprüfung,
Einwilligungsverwaltung, Datenexport, Löschanfragen
- **Sicherheits-Monitoring**: Realtime-Logging von Login-Fehlversuchen, IDOR-Abwehr,
SSRF-Blocks, JWT-Manipulation; Threshold-Detection (Brute-Force, IDOR-Probing) mit
Sofort-E-Mail-Alerts und stündlichem Digest siehe Einstellungen → Monitoring
- **Production-Hardening**: 10 dokumentierte Hardening-Runden inkl. CORS, Helmet,
IDOR-Schutz, Rate-Limiting, SSRF/DNS-Rebinding-Block, Per-File-Ownership-Check, mehr
in [docs/SECURITY-HARDENING.md](docs/SECURITY-HARDENING.md)
- **Developer-Tools**: Datenbank-Browser und interaktives ER-Diagramm
## Tech Stack
- **Frontend**: React 18, TypeScript, Tailwind CSS, React Query
- **Backend**: Node.js, Express 4.x, TypeScript
- **Datenbank**: MariaDB
- **ORM**: Prisma
- **Auth**: JWT mit Rollen-basierter Zugriffskontrolle
> **Hinweis zu Express 5:** Das Projekt verwendet bewusst Express 4.x (nicht 5.x). Express 5 ist seit Jahren in der Beta-Phase und noch nicht offiziell stable. Bei der Installation darauf achten, dass `@types/express` zur Express-Version passt:
> - Express 4.x → `@types/express@^4.17.x`
> - Express 5.x → `@types/express@^5.x` (erst bei offiziellem Release empfohlen)
## Quick-Start mit Docker (empfohlen)
Komplettes Setup mit MariaDB + OpenCRM + Adminer (DB-UI) in 3 Befehlen:
```bash
git clone <repository-url>
cd opencrm
cp .env.example .env # Werte anpassen, Secrets rotieren!
docker compose up -d
```
Browser:
- **CRM**: http://localhost:3010 (Login: `admin@admin.com` / `admin`)
- **Datenbank-UI** (Adminer): http://localhost:8081 (Server: `db`, User: `root`, DB: `opencrm`)
Alle persistenten Daten liegen in `./data/`:
| Pfad | Inhalt |
|------|--------|
| `./data/db/` | MariaDB-Datafiles |
| `./data/uploads/` | User-Uploads (PDFs, Bilder) |
| `./data/factory-defaults/` | Stammdaten-Kataloge |
| `./data/backups/` | DB-Backups (`npm run db:backup`) |
Ports + Pfade konfigurierst du in `./.env` (Default-Werte siehe `.env.example`).
> **Erste Inbetriebnahme:** In der `.env` einmalig `RUN_SEED=true` setzen,
> `docker compose up -d` ausführen, dann wieder auf `false`. Danach existiert
> der initiale Admin-User `admin@admin.com` / `admin`.
## Voraussetzungen
- Docker & Docker Compose v2
- Für Backend-Entwicklung außerhalb von Docker: Node.js 20+ und npm
## Installation für Entwicklung (ohne Container)
### 1. Repository klonen
```bash
git clone <repository-url>
cd opencrm
cp .env.example .env # Konfiguration anpassen
```
### 2. MariaDB-Container starten
```bash
docker compose up -d db
```
Das startet nur die Datenbank (mit Daten in `./data/db/`).
Konfiguration kommt aus `./.env`:
- **Port:** wie in `DB_PORT` (Standard: 3306, intern auf 127.0.0.1)
- **Datenbank/User/Passwort:** wie in `DB_*`-Variablen
Warte ca. 10 Sekunden bis die Datenbank bereit ist.
### 3. Backend einrichten
```bash
cd backend
# Dependencies installieren
npm install
# .env-Datei erstellen (falls noch nicht vorhanden)
cp .env.example .env
```
Die `.env`-Datei sollte folgende Werte enthalten:
```env
# Database
DATABASE_URL="mysql://root:rootpassword@localhost:3306/opencrm"
# JWT
JWT_SECRET="change-this-to-a-very-long-random-secret-in-production"
JWT_EXPIRES_IN="7d"
# Encryption (for portal credentials) - generate with: openssl rand -hex 32
ENCRYPTION_KEY="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
# Server
PORT=3001
NODE_ENV=development
```
### 4. Datenbank initialisieren
```bash
# Prisma Client generieren und Migrationen ausführen
npx prisma migrate dev
# Seed-Daten einspielen (Admin-User, Rollen, Berechtigungen)
npm run db:seed
```
### 5. Frontend einrichten
```bash
cd ../frontend
# Dependencies installieren
npm install
```
## Anwendung starten
### Backend starten (Terminal 1)
```bash
cd backend
npm run dev
```
Das Backend läuft auf `http://localhost:3001`
### Frontend starten (Terminal 2)
```bash
cd frontend
npm run dev
```
Das Frontend läuft auf `http://localhost:5173`
## Erster Login
Nach dem Seed sind folgende Zugangsdaten verfügbar:
- **E-Mail:** admin@admin.com
- **Passwort:** admin
> **Wichtig:** Vor dem ersten Production-Deployment das Default-Passwort sofort
> ändern und Secrets rotieren siehe [Production-Deployment](#production-deployment).
## Production-Deployment
Vor dem öffentlichen Schalten der Instanz muss in der Production-`.env`:
```env
NODE_ENV=production
# Pflicht-Rotation per `openssl rand` neu generieren!
JWT_SECRET=$(openssl rand -hex 64) # min. 32 Zeichen
ENCRYPTION_KEY=$(openssl rand -hex 32) # genau 64 Hex-Zeichen
# Backend nur lokal lauschen lassen, public-Verkehr läuft über Reverse-Proxy
LISTEN_ADDR=127.0.0.1
# Bei separatem Frontend-Host: erlaubte Origins
CORS_ORIGINS=https://crm.deine-domain.de
```
Plus:
- **Reverse-Proxy** (Nginx/Plesk) so konfigurieren, dass `X-Forwarded-For` hart auf
die echte Client-IP gesetzt wird (nicht nur angefügt) sonst Rate-Limit-Bypass möglich.
- **Default-Admin-Passwort ändern** (admin@admin.com / admin).
- **Manuelle Test-Checkliste** aus [docs/TESTING.md](docs/TESTING.md) einmal komplett
durchklicken.
- **Monitoring konfigurieren**: Einstellungen → Sicherheits-Monitoring → Alert-E-Mail
hinterlegen, Test-Alert senden, Digest aktivieren.
- Vollständige Hardening-Story + restliche Trade-offs:
[docs/SECURITY-HARDENING.md](docs/SECURITY-HARDENING.md)
## Developer-Tools aktivieren
Die Developer-Tools (Datenbankstruktur, ER-Diagramm) sind standardmäßig für Admins verfügbar. Falls der Menüpunkt nicht erscheint:
1. Einmalig im Browser-Console ausführen:
```javascript
fetch('/api/developer/setup', { method: 'POST' })
```
2. Ausloggen und neu einloggen
Alternativ können Developer-Rechte pro Benutzer vergeben werden:
- Benutzer bearbeiten > "Entwicklerzugriff" aktivieren
## Email-Provisionierung
Das System unterstützt die automatische Erstellung von E-Mail-Weiterleitungen auf Hosting-Servern für Stressfrei-Wechseln Adressen.
### Unterstützte Provider
- **Plesk** (implementiert)
- **cPanel** (vorbereitet)
- **DirectAdmin** (vorbereitet)
### Konfiguration
1. **Einstellungen****Email-Provisionierung** öffnen
2. Neuen Provider hinzufügen:
- **Name**: Bezeichnung (z.B. "Plesk Hauptserver")
- **Typ**: Plesk/cPanel/DirectAdmin
- **API-URL**: Server-URL (z.B. `https://server.de:8443`)
- **API-Key** _(empfohlen bei Plesk)_: Key aus Plesk (siehe unten), alternativ Benutzername/Passwort
- **Benutzername/Passwort**: Nur wenn kein API-Key vorhanden
- **Domain**: E-Mail-Domain (z.B. `stressfrei-wechseln.de`)
- **Standard-Weiterleitung**: Zusätzliche Weiterleitungsadresse (optional)
3. Provider als "Standard" und "Aktiv" markieren
4. Verbindung testen
### Plesk: API-Key anlegen
Der API-Key ist die empfohlene Authentifizierungsmethode (sicherer als Passwort, kann pro
Anwendung vergeben und widerrufen werden).
**Variante 1: Über die Plesk-Oberfläche (einfachster Weg)**
1. In Plesk als Admin einloggen
2. Oben rechts auf den **eigenen Namen****"Mein Profil"** (oder direkt URL `/admin/my-profile/`)
3. Tab **"API-Token"** oder **"API-Schlüssel"** öffnen
4. **"API-Schlüssel erstellen"** (bzw. "Add API Key")
5. Beschreibung vergeben (z.B. "OpenCRM")
6. Den angezeigten Schlüssel **sofort kopieren** er wird nur einmal angezeigt!
7. Im CRM bei "API-Key" einfügen
> **Hinweis:** Bei manchen Plesk-Versionen ist die Option unter
> **Tools & Einstellungen** → **API-Schlüssel** oder **Werkzeuge & Einstellungen** →
> **API-Tokens** zu finden. Wenn der Menüpunkt fehlt, muss ggf. die **REST API**
> Extension installiert werden (siehe Variante 2).
**Variante 2: Über die Kommandozeile (SSH als root)**
Falls der API-Key-Button in Plesk nicht vorhanden ist, lässt er sich auch per SSH erstellen:
```bash
# API-Key generieren (läuft nicht ab)
# WICHTIG: -ip-address weglassen, wenn der Key von beliebigen IPs genutzt werden soll!
plesk bin secret_key --create -description "OpenCRM"
# Alternativ mit IP-Einschränkung (nur Zugriffe von dieser IP sind erlaubt):
plesk bin secret_key --create -ip-address <IP-DES-CRM-SERVERS> -description "OpenCRM"
```
> **Achtung:** `-ip-address 0.0.0.0` funktioniert **nicht** wie bei anderen Tools!
> Plesk prüft exakt gegen die eingetragene IP. Für "alle IPs erlauben" muss der
> `-ip-address`-Parameter komplett weggelassen werden.
Der Befehl gibt den Key direkt zurück. Diesen kopieren und im CRM eintragen.
**Alle API-Keys anzeigen:**
```bash
plesk bin secret_key --list
```
**API-Key löschen:**
```bash
plesk bin secret_key --delete <KEY>
```
### Plesk: REST API aktivieren (falls nicht vorhanden)
Bei älteren Plesk-Versionen oder Custom-Installationen kann es sein, dass die
REST API fehlt. Dann:
1. **Tools & Einstellungen****Updates****Erweiterungen hinzufügen/entfernen**
2. Nach **"REST API"** suchen und installieren
3. Plesk-Neustart (meist nicht nötig, aber zur Sicherheit)
### Plesk: Firewall-Hinweis
Der CRM-Server muss den **Plesk-Port 8443** (Standard) erreichen können. Bei Plesk-Firewall:
1. **Tools & Einstellungen****Firewall**
2. **"Plesk-Dienst Panel"** (Port 8443) für die IP des CRM-Servers erlauben
Bei reiner Linux-Firewall (ufw/firewalld):
```bash
# Beispiel ufw
ufw allow from <CRM-SERVER-IP> to any port 8443
```
### Verwendung
Beim Anlegen einer Stressfrei-Wechseln Adresse im Kundenbereich erscheint die Checkbox **"Beim E-Mail-Provider anlegen"**, wenn:
- Ein aktiver Standard-Provider konfiguriert ist
- Der Kunde eine E-Mail-Adresse hat
Bei aktivierter Checkbox wird automatisch:
1. Geprüft, ob die E-Mail-Adresse bereits existiert
2. Falls nicht: E-Mail-Adresse beim Provider angelegt mit Weiterleitung an:
- Kunden-E-Mail-Adresse
- Standard-Weiterleitungsadresse (falls konfiguriert)
## Befehle
### Backend
```bash
npm run dev # Entwicklungsserver starten
npm run build # Produktions-Build erstellen
npm run db:studio # Prisma Studio (Datenbank-GUI)
npm run db:migrate # Neue Migration erstellen
npm run db:seed # Seed erneut ausführen
```
### Frontend
```bash
npm run dev # Entwicklungsserver starten
npm run build # Produktions-Build erstellen
npm run preview # Build-Vorschau
```
### Docker (Entwicklung)
```bash
docker-compose up -d # Container starten
docker-compose down # Container stoppen
docker-compose down -v # Container stoppen + Daten löschen
docker-compose logs -f # Logs anzeigen
```
### Docker (Produktion)
Im `docker/` Verzeichnis liegt ein komplettes Produktions-Setup:
```bash
cd docker
# Image bauen
docker-compose build
# Container starten
docker-compose up -d
# Logs anzeigen
docker-compose logs -f app
```
**Komponenten:**
- **MariaDB 10.11**: Datenbank
- **App**: Backend + Frontend in einem Container
- **Caddy**: Reverse-Proxy mit automatischem SSL
**Umgebungsvariablen (`docker/.env`):**
```env
MYSQL_ROOT_PASSWORD=sicheres-root-passwort
MYSQL_DATABASE=opencrm
MYSQL_USER=opencrm
MYSQL_PASSWORD=sicheres-passwort
JWT_SECRET=sehr-langer-zufaelliger-string
ENCRYPTION_KEY=64-zeichen-hex-string
DOMAIN=crm.example.com
RUN_SEED=true # Nur beim ersten Start
```
## Projektstruktur
```
opencrm/
├── backend/
│ ├── src/
│ │ ├── controllers/ # Request-Handler
│ │ │ ├── auditLog.controller.ts # Audit-Log API
│ │ │ └── gdpr.controller.ts # DSGVO API
│ │ ├── middleware/ # Auth, Validation
│ │ │ ├── audit.ts # Automatisches API-Logging
│ │ │ └── auditContext.ts # Before/After Context
│ │ ├── routes/ # API-Endpunkte
│ │ │ ├── auditLog.routes.ts # Audit-Log Routes
│ │ │ └── gdpr.routes.ts # DSGVO Routes
│ │ ├── services/ # Business-Logik
│ │ │ ├── audit.service.ts # Hash-Kette, Logging
│ │ │ ├── consent.service.ts # Einwilligungen
│ │ │ └── gdpr.service.ts # Export, Löschung
│ │ ├── lib/
│ │ │ └── prisma.ts # Prisma mit Audit-Middleware
│ │ ├── types/ # TypeScript-Typen
│ │ └── index.ts # Server-Einstiegspunkt
│ ├── prisma/
│ │ ├── schema.prisma # Datenbank-Schema
│ │ └── seed.ts # Seed-Daten
│ ├── uploads/ # Hochgeladene Dokumente
│ │ ├── bank-cards/ # Bankkarten-Dokumente
│ │ ├── documents/ # Ausweis-Scans
│ │ ├── invoices/ # Rechnungsdokumente (Strom/Gas)
│ │ ├── gdpr/ # DSGVO-Löschnachweise
│ │ ├── business-registrations/ # Gewerbeanmeldungen
│ │ ├── commercial-registers/ # Handelsregisterauszüge
│ │ ├── privacy-policies/ # Datenschutzerklärungen
│ │ ├── cancellation-letters/ # Kündigungsschreiben
│ │ ├── cancellation-confirmations/ # Kündigungsbestätigungen
│ │ └── cancellation-*-options/ # Kündigungsdokumente Optionen
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── components/ # UI-Komponenten
│ │ ├── pages/ # Seiten
│ │ │ └── settings/
│ │ │ ├── AuditLogs.tsx # Audit-Protokoll
│ │ │ └── GDPRDashboard.tsx # DSGVO-Dashboard
│ │ ├── hooks/ # Custom Hooks
│ │ ├── services/ # API-Client
│ │ ├── types/ # TypeScript-Typen
│ │ └── App.tsx # Haupt-Komponente
│ └── package.json
├── docker/ # Docker-Deployment
│ ├── Dockerfile # Multi-Stage Build
│ ├── docker-compose.yml # Produktion (MariaDB, App, Caddy)
│ ├── Caddyfile # Reverse-Proxy mit SSL
│ └── entrypoint.sh # Container-Startup
├── docker-compose.yml # MariaDB-Container (Entwicklung)
└── README.md
```
## Berechtigungen
| Rolle | Kunden | Verträge | Benutzer | Plattformen | Audit/DSGVO | Developer |
|-------|--------|----------|----------|-------------|-------------|-----------|
| Admin | CRUD | CRUD | CRUD | CRUD | Vollzugriff | Optional |
| Mitarbeiter | CRUD | CRUD | - | Lesen | Lesen | - |
| Mitarbeiter (Lesen) | Lesen | Lesen | - | Lesen | - | - |
| Kunde | Eigene | Eigene | - | - | - | - |
## Troubleshooting
### Datenbank-Verbindungsfehler
1. Prüfe ob Container läuft: `docker ps`
2. Prüfe die DATABASE_URL in `.env`
3. Warte nach Container-Start ca. 10 Sekunden
### Prisma-Fehler
```bash
# Prisma Client neu generieren
npx prisma generate
# Schema zur Datenbank pushen (ohne Migration)
npx prisma db push
```
### Port bereits belegt
- Backend: `PORT` in `.env` ändern
- Frontend: In `vite.config.ts` anpassen
### Developer-Menü fehlt
```bash
# In der Browser-Console:
fetch('/api/developer/setup', { method: 'POST' }).then(r => r.json()).then(console.log)
# Danach ausloggen und neu einloggen
```
## Vertragstypen
### Standard-Vertragstypen
Folgende Vertragstypen werden bei Installation/Factory-Reset automatisch angelegt:
| Code | Name | Icon | Farbe |
|------|------|------|-------|
| ELECTRICITY | Strom | Zap | #FFC107 |
| GAS | Gas | Flame | #FF5722 |
| DSL | DSL | Wifi | #2196F3 |
| FIBER | Glasfaser | Cable | #9C27B0 |
| CABLE | Kabel Internet (Coax) | Cable | #00BCD4 |
| MOBILE | Mobilfunk | Smartphone | #4CAF50 |
| TV | TV | Tv | #E91E63 |
| CAR_INSURANCE | KFZ-Versicherung | Car | #607D8B |
> **Hinweis:** Vertragstypen können nur von Benutzern mit **Entwicklerzugriff** geändert werden, da Änderungen auch Anpassungen an den Formularen erfordern.
### Vertragstyp-spezifische Felder
Je nach Vertragstyp werden unterschiedliche Felder im Formular angezeigt:
#### Strom & Gas (ELECTRICITY, GAS)
- Zähler-Auswahl
- Jahresverbrauch (kWh/m³)
- Grundpreis, Arbeitspreis
- Bonus
- Vorversorger, Kundennummer beim Vorversorger
#### Internet (DSL, CABLE, FIBER)
- Download/Upload (Mbit/s)
- Router Modell, Seriennummer
- Installationsdatum
- Benutzername, Passwort
- Rufnummern mit SIP-Zugangsdaten
| Vertragstyp | Zusatzfeld |
|-------------|------------|
| **Glasfaser (FIBER)** | Home-ID |
#### Mobilfunk (MOBILE)
- Datenvolumen (GB)
- Inklusiv-Minuten, Inklusiv-SMS
- Gerät-Modell, IMEI
- Multisim-Checkbox
- SIM-Karten (dynamisch erweiterbar):
- Rufnummer, SIM-Kartennummer (ICCID)
- PIN, PUK (verschlüsselt)
- Multisim-Flag, Hauptkarte-Flag
> **Hinweis Multisim:** Nicht buchbar bei Klarmobil, Congstar, Otelo. Benötigt Freenet oder vergleichbar.
#### TV
- Receiver Modell
- Smartcard-Nummer
- Paket/Angebot
#### KFZ-Versicherung (CAR_INSURANCE)
- Kennzeichen, HSN, TSN, FIN/VIN
- Fahrzeugtyp, Erstzulassung
- SF-Klasse (Schadenfreiheitsklasse)
- Versicherungsart (Haftpflicht/Teilkasko/Vollkasko)
- Selbstbeteiligungen (Teilkasko, Vollkasko)
- Versicherungsscheinnummer
- Vorversicherer
### Standard-Anbieter
Folgende Anbieter werden bei Installation/Factory-Reset automatisch angelegt:
| Anbieter | Portal-URL |
|----------|------------|
| Vodafone | https://www.vodafone.de/meinvodafone/account/login |
| Klarmobil | https://www.klarmobil.de/login |
| Otelo | https://www.otelo.de/mein-otelo/login |
| Congstar | https://www.congstar.de/login/ |
| Telekom | https://www.telekom.de/kundencenter/startseite |
| O2 | https://www.o2online.de/ecare/selfcare |
| 1&1 | https://control-center.1und1.de/ |
### Anbieter-spezifische Felder
Einige Felder werden nur bei bestimmten Anbietern angezeigt:
| Anbieter | Vertragstyp | Zusatzfeld |
|----------|-------------|------------|
| **Vodafone** | DSL, Kabel Internet | Aktivierungscode |
> **Hinweis Multisim:** Bei Klarmobil, Congstar und Otelo ist Multisim **nicht** buchbar. Dafür wird Freenet oder ein vergleichbarer Anbieter benötigt.
## Rechnungsverwaltung (Energieverträge)
Für Strom- und Gas-Verträge können Rechnungen verwaltet werden, um den Abrechnungsstatus zu tracken.
### Rechnungstypen
| Typ | Beschreibung |
|-----|--------------|
| **Zwischenrechnung** (INTERIM) | Reguläre Jahresrechnung während der Vertragslaufzeit |
| **Schlussrechnung** (FINAL) | Endabrechnung nach Vertragskündigung/Deaktivierung |
| **Nicht verfügbar** (NOT_AVAILABLE) | Rechnung ist nicht mehr zu bekommen (z.B. Anbieter existiert nicht mehr) |
### Funktionen
- **Rechnungen hinzufügen/bearbeiten/löschen** in der Vertragsdetailansicht
- **Dokument-Upload** (PDF) - Pflicht, außer bei Typ "Nicht verfügbar"
- **Statusanzeige**: Grünes Badge bei Schlussrechnung, Orange bei fehlender Schlussrechnung
- **E-Mail-Anhänge als Rechnung speichern**: Direkt aus dem E-Mail-Client
### E-Mail-Integration
Bei E-Mails, die einem Energievertrag zugeordnet sind:
1. Toggle zwischen "Als Dokument" und "Als Rechnung" im Speichern-Dialog
2. Rechnungsdatum und Typ auswählen
3. Anhang wird automatisch als Rechnungsdokument gespeichert
## Vertrags-Cockpit
Dashboard zur Überwachung offener Aufgaben und fehlender Dokumente.
### Kategorien
| Kategorie | Prüfungen |
|-----------|-----------|
| **Fehlende Dokumente** | Kündigungsschreiben, Kündigungsbestätigung (wenn Kündigung markiert) |
| **Fehlende Rechnungen** | Schluss-/Zwischenrechnungen für Energieverträge |
| **Ablaufende Dokumente** | Ausweise, Bankkarten (nächste 30 Tage) |
### Rechnungsprüfung für Energieverträge
#### Schlussrechnung (gekündigte/deaktivierte Verträge)
| Status | Prüfung |
|--------|---------|
| **CANCELLED** / **DEACTIVATED** | Schlussrechnung oder "Nicht verfügbar" erforderlich |
#### Zwischenrechnung (laufende Verträge)
| Bedingung | Warnung |
|-----------|---------|
| Vertrag > 12 Monate alt, keine Rechnung | "Zwischenrechnung fehlt" |
| Letzte Rechnung > 12 Monate her | "Zwischenrechnung überfällig" |
> **Hinweis Status-Logik:**
> - **EXPIRED** = Laufzeit abgelaufen, aber Vertrag läuft ohne Kündigung weiter → Zwischenrechnung prüfen
> - **CANCELLED** = Aktive Kündigung → Schlussrechnung prüfen
> - **DEACTIVATED** = Manuell beendet → Schlussrechnung prüfen
### Vertrag zurückstellen (Snooze)
Wenn ein Vertrag EXPIRED ist (Laufzeit vorbei, läuft aber weiter), erscheint er im Cockpit. Manchmal ist Bleiben günstiger als Wechseln - aber der Vertrag soll nicht dauerhaft im Cockpit erscheinen.
**Lösung:** Vertrag temporär zurückstellen mit Datum für erneute Prüfung.
#### Snooze aktivieren
Im Cockpit hat jeder Vertrag einen Snooze-Button (Glocke mit Uhr). Optionen:
- **+3 Monate**
- **+6 Monate** (Empfohlen)
- **+12 Monate**
- **Eigenes Datum** (Datepicker)
#### Unterdrückte Warnungen (bei aktivem Snooze)
| Warnung | Beschreibung |
|---------|--------------|
| Kündigungsfrist | Frist läuft ab |
| Vertrag läuft ab | Enddatum naht |
| Kündigungsschreiben fehlt | Bei markierter Kündigung |
| Kündigungsbestätigung fehlt | Bei markierter Kündigung |
#### NICHT unterdrückte Warnungen
Diese Warnungen werden auch bei aktivem Snooze angezeigt:
| Warnung | Beschreibung |
|---------|--------------|
| Schlussrechnung fehlt | Beendeter Vertrag ohne Endabrechnung |
| Zwischenrechnung fehlt/überfällig | Laufender Vertrag ohne Jahresrechnung |
| Zählerstand fehlt | Fehlende Zählerstände bei Energieverträgen |
| Zugangsdaten fehlen | Portal-Passwort, SIP-Daten, etc. |
#### Snooze aufheben
**Im Cockpit:** Bei Verträgen in der Kategorie "Erneute Prüfung fällig" erscheint ein "Snooze aufheben"-Button.
**In Vertragsdetails:** Bei zurückgestellten Verträgen erscheint ein bernsteinfarbenes Badge "Zurückgestellt bis [Datum]" mit X-Button zum Aufheben.
#### Erneute Prüfung fällig
Wenn das Snooze-Datum in der Vergangenheit liegt, erscheint der Vertrag in der neuen Kategorie "Erneute Prüfung fällig" mit Angabe, seit wie vielen Tagen die Prüfung fällig ist.
### Status-Info Modal
An verschiedenen Stellen (Vertragsformular, Vertragsdetails, Vertragsübersicht, Kundenansicht) zeigt ein -Icon neben dem Status eine Erklärung aller Vertragsstatus:
| Status | Bedeutung |
|--------|-----------|
| **Entwurf** | Vertrag wird noch vorbereitet |
| **Ausstehend** | Wartet auf Aktivierung |
| **Aktiv** | Vertrag läuft normal |
| **Abgelaufen** | Laufzeit vorbei, läuft aber ohne Kündigung weiter |
| **Gekündigt** | Aktive Kündigung eingereicht, Vertrag endet |
| **Deaktiviert** | Manuell beendet/archiviert |
## E-Mail-Client
Ein vollständig integrierter E-Mail-Client pro Kunde mit IMAP-Empfang und SMTP-Versand.
### Funktionen
#### E-Mails lesen & verwalten
- **E-Mail-Tab in Kundenansicht** mit Ordnern: Posteingang, Gesendet, Papierkorb
- **Mehrere E-Mail-Konten** pro Kunde (Dropdown zur Auswahl)
- **E-Mail-Detailansicht** mit HTML/Text-Body, Absender, Empfänger, CC, Datum
- **Gelesen/Ungelesen** markieren
- **Favoriten** (Stern) für wichtige E-Mails
- **Papierkorb** mit Wiederherstellen und endgültigem Löschen
#### E-Mails schreiben
- **Neue E-Mail verfassen** mit An, CC, Betreff, Text
- **Antworten** mit zitiertem Originaltext
- **Dateianhänge** (max. 10 MB pro Datei, 25 MB gesamt)
- **SMTP-Versand** über die gewählte StressfreiEmail-Adresse
#### Vertragszuordnung
- **E-Mails zu Verträgen zuordnen** für bessere Nachverfolgung
- **E-Mail-Tab in Vertragsansicht** zeigt nur zugeordnete E-Mails
- **Automatische Zuordnung** bei Versand aus Vertragskontext
- **Manuelle Zuordnung** über Suchfeld und Vertragsauswahl
#### Anhänge
- **Anhangsliste** in E-Mail-Detail
- **Download** einzelner Anhänge
- **Inline-Ansicht** (im Browser öffnen)
### Technische Details
#### Backend-Services
| Service | Beschreibung |
|---------|--------------|
| `imapService.ts` | IMAP-Client (ImapFlow) für E-Mail-Empfang |
| `smtpService.ts` | SMTP-Client (Nodemailer) für E-Mail-Versand |
| `cachedEmail.service.ts` | E-Mail-Caching, Synchronisation, Zuordnung |
#### API-Endpunkte
```
GET /api/customers/:id/emails # E-Mails für Kunde
GET /api/contracts/:id/emails # E-Mails für Vertrag
GET /api/emails/:id # Einzelne E-Mail mit Body
POST /api/stressfrei-emails/:id/sync # IMAP-Synchronisation
POST /api/stressfrei-emails/:id/send # E-Mail senden
POST /api/emails/:id/assign # Vertrag zuordnen
DELETE /api/emails/:id/assign # Zuordnung aufheben
PATCH /api/emails/:id/read # Gelesen/Ungelesen
POST /api/emails/:id/star # Favorit umschalten
DELETE /api/emails/:id # In Papierkorb
POST /api/emails/:id/restore # Aus Papierkorb wiederherstellen
DELETE /api/emails/:id/permanent # Endgültig löschen
GET /api/emails/:id/attachments/:filename # Anhang herunterladen
# Rechnungen (Invoices)
GET /api/invoices/ecd/:ecdId # Rechnungen für EnergyContractDetails
POST /api/invoices/ecd/:ecdId # Rechnung hinzufügen
PUT /api/invoices/ecd/:ecdId/:invoiceId # Rechnung bearbeiten
DELETE /api/invoices/ecd/:ecdId/:invoiceId # Rechnung löschen
POST /api/invoices/:invoiceId/upload # Rechnungsdokument hochladen
# Cockpit
GET /api/contracts/cockpit # Offene Aufgaben abrufen
```
#### Datenbank-Modell
```prisma
model CachedEmail {
id Int @id @default(autoincrement())
stressfreiEmailId Int
stressfreiEmail StressfreiEmail @relation(...)
folder String // INBOX, SENT
messageId String // RFC 5322 Message-ID
uid Int // IMAP UID
subject String?
fromAddress String
fromName String?
toAddresses String @db.Text // JSON Array
ccAddresses String? @db.Text
receivedAt DateTime
textBody String? @db.LongText
htmlBody String? @db.LongText
hasAttachments Boolean @default(false)
attachmentNames String? @db.Text // JSON Array
contractId Int? // Vertragszuordnung
assignedAt DateTime?
assignedBy Int?
isAutoAssigned Boolean @default(false)
isRead Boolean @default(false)
isStarred Boolean @default(false)
isDeleted Boolean @default(false)
deletedAt DateTime?
@@unique([stressfreiEmailId, messageId, folder])
}
```
#### Sicherheit
- **Passwort-Verschlüsselung**: AES-256-GCM für Mailbox-Passwörter
- **Passwort-Reset**: Neues Passwort generieren und beim Provider setzen
- **Verschlüsselungsmodi**: SSL, STARTTLS, oder unverschlüsselt
- **Selbstsignierte Zertifikate**: Konfigurierbar pro Provider
#### Berechtigungen
| Aktion | Berechtigung |
|--------|--------------|
| E-Mails lesen | `customers:read` |
| E-Mails senden, markieren | `customers:update` |
| Anhänge in Dokumente speichern | `customers:update` |
| Vertrag zuordnen | `contracts:update` |
| Löschen, Papierkorb | `emails:delete` |
### Frontend-Komponenten
| Komponente | Beschreibung |
|------------|--------------|
| `EmailClientTab.tsx` | Haupt-Tab mit Konto-Auswahl und Ordnern |
| `EmailList.tsx` | E-Mail-Liste mit Aktionen |
| `EmailDetail.tsx` | E-Mail-Ansicht mit Anhängen |
| `ComposeEmailModal.tsx` | Neue E-Mail / Antworten |
| `TrashEmailList.tsx` | Papierkorb-Verwaltung |
| `AssignToContractModal.tsx` | Vertragszuordnung |
| `ContractEmailsSection.tsx` | E-Mails in Vertragsansicht |
| `SaveAttachmentModal.tsx` | Anhänge in Dokumentfelder speichern |
| `SaveEmailAsPdfModal.tsx` | E-Mail als PDF in Dokumentfelder speichern |
| `InvoicesSection.tsx` | Rechnungsverwaltung in Vertragsdetails |
### Anhänge als Dokumente speichern
E-Mail-Anhänge können direkt in Dokumentfelder des CRM gespeichert werden. Über den blauen Speichern-Button (💾) neben jedem Anhang öffnet sich ein Modal mit allen verfügbaren Zielen.
#### Verfügbare Ziele
| Kategorie | Dokumentfelder |
|-----------|----------------|
| **Kunde** | Datenschutzerklärung |
| **Kunde (Gewerbe)** | + Gewerbeanmeldung, Handelsregisterauszug |
| **Ausweisdokumente** | Dokumentscan (pro Ausweis) |
| **Bankkarten** | Kartenscan (pro Karte) |
| **Vertrag** | Kündigungsschreiben, Kündigungsbestätigung, Kündigungsschreiben (Optionen), Kündigungsbestätigung (Optionen) |
> **Hinweis:** Vertragsdokumente sind nur verfügbar, wenn die E-Mail einem Vertrag zugeordnet ist.
#### Dynamische Konfiguration
Die Dokumentziele werden zentral in `backend/src/config/documentTargets.config.ts` konfiguriert. Neue Dokumentfelder werden automatisch im Modal angezeigt, ohne Frontend-Änderungen.
```typescript
// Beispiel: Neues Feld hinzufügen
{
key: 'newDocument',
label: 'Neues Dokument',
field: 'newDocumentPath', // Prisma-Feld
condition: null, // oder 'BUSINESS' für Geschäftskunden
directory: 'new-documents' // Upload-Verzeichnis
}
```
#### Warnung bei Überschreiben
Wenn bereits ein Dokument im Zielfeld vorhanden ist, wird eine Warnung angezeigt. Das vorhandene Dokument wird beim Speichern automatisch ersetzt und die alte Datei gelöscht.
### Vertragszuordnung aufheben (X-Button)
Der X-Button zum Aufheben der Vertragszuordnung erscheint nur bei **manuell zugeordneten** E-Mails. E-Mails, die direkt aus dem Vertragskontext gesendet wurden (`isAutoAssigned = true`), bleiben dauerhaft mit dem Vertrag verknüpft.
| Ansicht | Vertrags-Badge | X-Button sichtbar |
|---------|----------------|-------------------|
| **Kundenakte - Posteingang** | ✅ | ✅ immer |
| **Kundenakte - Gesendet** | ✅ | nur manuell zugeordnet |
| **Kundenakte - Papierkorb** | ✅ | je nach Original-Ordner |
| **Vertrag - Posteingang** | ✅ | ✅ immer |
| **Vertrag - Gesendet** | ✅ | nur manuell zugeordnet |
| **Vertrag - Papierkorb** | ✅ | je nach Original-Ordner |
| **Detail-Ansicht (beide)** | ✅ | nur manuell zugeordnet |
> **Hinweis:** Bei gesendeten E-Mails gilt:
> - `isAutoAssigned = true`: E-Mail wurde direkt aus dem Vertragskontext gesendet → X-Button ausgeblendet
> - `isAutoAssigned = false`: E-Mail wurde manuell dem Vertrag zugeordnet → X-Button sichtbar
### Vertragsbaum in Kundenansicht
In der Kundendetailansicht werden Verträge als **Baumstruktur** mit Vorgänger-Verknüpfung dargestellt:
```
▼ GAS-ML781A4FYXU │ Gas │ ACTIVE │ 01.01.2025 - 31.12.2026
└─ GAS-ML24GKR...│ Gas │ EXPIRED│ 05.05.2023 - 05.05.2025 (Vorgänger)
└─ GAS-OLD123 │ Gas │ EXPIRED│ 01.01.2021 - 04.05.2023 (Vorgänger)
▶ MOB-ML77W560A73 │ Mobil│ DRAFT │ 02.01.2024 - 02.01.2026
```
**Funktionsweise:**
- **Aktuellste Verträge oben** - Verträge ohne Nachfolger werden als Wurzelknoten angezeigt
- **Standardmäßig eingeklappt** - Klick auf ▶ zeigt die Vorgängerkette
- **Vorgänger eingerückt** - Mit grauem Rand und "(Vorgänger)" Label
- **Verknüpfung über `previousContractId`** - Wird beim Erstellen eines Folgevertrags automatisch gesetzt
> **Hinweis:** In der Hauptvertragsliste (`/contracts`) wird weiterhin die flache Ansicht ohne Baumstruktur verwendet.
## DSGVO-Compliance & Audit-Logging
Umfassendes Audit-Logging-System mit DSGVO-Compliance-Features.
### Audit-Protokoll
Automatische Protokollierung aller API-Zugriffe mit:
| Feld | Beschreibung |
|------|--------------|
| **Benutzer** | User-ID, E-Mail, Rolle |
| **Aktion** | CREATE, READ, UPDATE, DELETE, EXPORT, ANONYMIZE, LOGIN, LOGOUT |
| **Ressource** | Tabelle + ID + lesbare Bezeichnung |
| **Kontext** | Endpoint, HTTP-Methode, IP-Adresse, User-Agent |
| **Änderungen** | Vorher/Nachher-Werte (bei Updates) |
| **Sensitivität** | LOW, MEDIUM, HIGH, CRITICAL |
| **Integrität** | SHA-256 Hash-Kette für Manipulationsschutz |
#### Sensitivitätsstufen
| Stufe | Ressourcen |
|-------|------------|
| **LOW** | Einstellungen, Plattformen, Tarife |
| **MEDIUM** | Verträge, Provider |
| **HIGH** | Kundendaten, Benutzerdaten |
| **CRITICAL** | Authentifizierung, Bankdaten, Ausweisdokumente |
#### Zugriff
- **Einstellungen** → **Audit-Protokoll**
- Filter nach: Datum, Benutzer, Aktion, Ressource, Sensitivität
- Detail-Ansicht mit Vorher/Nachher-Diff
- Export als JSON
- Integritätsprüfung (Hash-Kette verifizieren)
### DSGVO-Dashboard
Zentrale Verwaltung für DSGVO-Anfragen unter **Einstellungen****DSGVO-Dashboard**.
#### Dashboard-Statistiken
- Offene Löschanfragen
- Abgeschlossene Löschungen (letzte 30 Tage)
- Datenexporte (letzte 30 Tage)
- Aktive Einwilligungen
#### Einwilligungsverwaltung (Consents)
| Consent-Typ | Beschreibung |
|-------------|--------------|
| **DATA_PROCESSING** | Grundlegende Datenverarbeitung |
| **MARKETING_EMAIL** | E-Mail-Marketing |
| **MARKETING_PHONE** | Telefonmarketing |
| **DATA_SHARING_PARTNER** | Datenweitergabe an Partner |
Einwilligungen können pro Kunde im Tab "Einwilligungen" verwaltet werden.
#### Löschanfragen (Art. 17)
Workflow für DSGVO-Löschanfragen:
| Status | Beschreibung |
|--------|--------------|
| **PENDING** | Anfrage eingegangen |
| **IN_PROGRESS** | Wird bearbeitet |
| **COMPLETED** | Vollständig gelöscht/anonymisiert |
| **PARTIALLY_COMPLETED** | Teildaten behalten (z.B. aktive Verträge) |
| **REJECTED** | Abgelehnt mit Begründung |
**Anonymisierung statt Löschung:**
- Kundendaten werden anonymisiert (nicht gelöscht)
- Aktive Verträge werden beibehalten
- PDF-Löschnachweis wird generiert
#### Datenexport (Art. 15)
Kunden können alle gespeicherten Daten als JSON exportieren:
- Stammdaten
- Adressen, Bankdaten, Ausweise
- Verträge mit Details
- Zähler und Ablesungen
- Einwilligungen
- Zugriffsprotokolle
### API-Endpunkte
```
# Audit-Logs
GET /api/audit-logs # Logs mit Filtern
GET /api/audit-logs/:id # Einzelnes Log
GET /api/audit-logs/customer/:id # Logs für Kunde
GET /api/audit-logs/export # Export (JSON/CSV)
POST /api/audit-logs/verify # Hash-Kette prüfen
GET /api/audit-logs/retention-policies # Aufbewahrungsfristen
PUT /api/audit-logs/retention-policies/:id
POST /api/audit-logs/cleanup # Manuelle Bereinigung
# DSGVO
GET /api/gdpr/dashboard # Dashboard-Statistiken
GET /api/gdpr/customer/:id/export # Kundendaten-Export
GET /api/gdpr/deletions # Löschanfragen
POST /api/gdpr/deletions # Löschanfrage erstellen
PUT /api/gdpr/deletions/:id/process # Löschanfrage bearbeiten
GET /api/gdpr/customer/:id/consents # Einwilligungen abrufen
PUT /api/gdpr/customer/:id/consents/:type # Einwilligung ändern
GET /api/gdpr/consents/overview # Consent-Übersicht
```
### Aufbewahrungsfristen
| Ressource | Frist | Rechtsgrundlage |
|-----------|-------|-----------------|
| Standard | 10 Jahre | AO §147, HGB §257 |
| Authentifizierung | 2 Jahre | Sicherheit |
| Kundendaten (HIGH) | 10 Jahre | Steuerrecht |
| Verträge | 10 Jahre | Steuerrecht |
| Allgemein (LOW) | 3 Jahre | Verjährung |
### Berechtigungen
| Aktion | Berechtigung |
|--------|--------------|
| Audit-Logs lesen | `audit:read` |
| Audit-Logs exportieren | `audit:export` |
| Audit-Administration | `audit:admin` |
| DSGVO-Export | `gdpr:export` |
| Löschanfrage erstellen | `gdpr:delete` |
| DSGVO-Administration | `gdpr:admin` |
### Technische Details
#### Hash-Kette
Jeder Audit-Log-Eintrag enthält einen SHA-256-Hash über:
- Alle Felder des Eintrags
- Hash des vorherigen Eintrags
Dies ermöglicht die Erkennung von Manipulationen.
#### Sensitive Daten
Folgende Felder werden in Audit-Logs gefiltert:
- `password`, `passwordHash`
- `portalPasswordHash`, `portalPasswordEncrypted`
- `emailPasswordEncrypted`, `internetPasswordEncrypted`
- `sipPasswordEncrypted`, `pin`, `puk`, `apiKey`
#### Performance
- Logging erfolgt asynchron (`setImmediate`)
- API-Response wird nicht blockiert
- Before/After-Werte über Prisma Middleware
## Factory-Defaults: Stammdaten-Kataloge teilen
Das **Factory-Defaults**-System erlaubt den Export und Import von
Stammdaten-Katalogen (Anbieter, Tarife, PDF-Vorlagen usw.) zwischen verschiedenen
OpenCRM-Installationen. Es ist bewusst **streng abgegrenzt** zu Datenbank-Backups:
### Abgrenzung
| | Factory-Defaults | Datenbank-Backup |
|---|---|---|
| Anbieter, Tarife, Kündigungsfristen, Laufzeiten, Kategorien | ✅ | ✅ |
| PDF-Auftragsvorlagen (inkl. Dateien + Feldzuordnungen) | ✅ | ✅ |
| **Kundendaten, Verträge, Dokumente** | ❌ | ✅ |
| **Emails, SMTP-/IMAP-Zugangsdaten** | ❌ | ✅ |
| **System-Einstellungen, Datenschutzerklärungen, Impressum** | ❌ | ✅ |
| Zwischen verschiedenen Installationen teilbar | ✅ | ❌ (zu firmen-spezifisch) |
> **Kurz:** Factory-Defaults = reine Kataloge, Backup = alles.
### Export (Installation A → ZIP)
1. **Einstellungen****Factory-Defaults** öffnen
2. Übersicht prüfen (Anzahl pro Kategorie)
3. Button **„Factory-Defaults exportieren"** klicken
4. ZIP wird als `factory-defaults-YYYY-MM-DD.zip` heruntergeladen
**ZIP-Struktur:**
```
factory-defaults-2026-04-23.zip
├── manifest.json # Version + Datum + Counts
├── providers/
│ └── providers.json # Anbieter inkl. zugehöriger Tarife
├── contract-meta/
│ ├── cancellation-periods.json # Kündigungsfristen (Code + Beschreibung)
│ ├── contract-durations.json # Laufzeiten (Code + Beschreibung)
│ └── contract-categories.json # Kategorien (Strom, Gas, DSL, ...)
└── pdf-templates/
├── pdf-templates.json # Vorlagen-Metadaten + Feldzuordnungen
└── *.pdf # Die eigentlichen PDF-Dateien
```
Die ZIP kann an andere Installationen weitergegeben werden
(Partner, Test-System, neue Installation).
### Import (ZIP → Installation B)
1. ZIP herunterladen bzw. erhalten
2. Inhalt nach `backend/factory-defaults/` entpacken (Unterordnerstruktur beibehalten)
3. Im Backend-Verzeichnis ausführen:
```bash
npm run seed:defaults
```
**Beispiel-Output:**
```
📦 Factory-Defaults werden eingespielt...
✓ Anbieter: 7, Tarife: 12
✓ Kündigungsfristen: 5
✓ Laufzeiten: 4
✓ Vertragskategorien: 8
✓ PDF-Vorlagen: 3
✅ Factory-Defaults erfolgreich eingespielt.
```
### Mehrere ZIPs kombinieren
Du kannst mehrere Exporte in `backend/factory-defaults/` übereinanderlegen
JSON-Dateien werden automatisch gemerged:
```
backend/factory-defaults/
providers/
verivox.json # 40 Anbieter aus Verivox-Paket
check24.json # 30 Anbieter aus Check24-Paket
eigene.json # 5 eigene Anbieter
```
Das Import-Script liest **alle** `*.json` im jeweiligen Unterordner und merged per
unique Key (letzter Eintrag gewinnt). Duplikate sind also unproblematisch.
### Idempotenz
Das Script nutzt ausschließlich Prisma `upsert`:
- **Neue Einträge** werden angelegt
- **Bestehende Einträge** (per unique Key: `name`, `code`) werden aktualisiert
- Nichts wird gelöscht
Du kannst `npm run seed:defaults` also beliebig oft ausführen, ohne Datenverlust
oder Duplikate.
### PDF-Dateien beim Import
Beim Import werden PDF-Vorlagen aus `factory-defaults/pdf-templates/*.pdf` nach
`uploads/pdf-templates/` kopiert und die Pfade in der DB entsprechend gesetzt.
Beim Re-Import wird die alte Datei in `uploads/` entsorgt und durch die neue
ersetzt.
### Berechtigungen
| Aktion | Berechtigung |
|--------|--------------|
| Factory-Defaults Vorschau | `settings:read` |
| Factory-Defaults Export | `settings:update` |
| Factory-Defaults Import (CLI) | Server-Zugang (SSH/Shell) |
### Typischer Einsatzzweck
- **Neue Installation aufsetzen**: Eine Kollegen-ZIP importieren und sofort mit
gepflegtem Anbieter- und Vorlagenkatalog loslegen
- **Vorlagen-Paket teilen**: Eine ZIP mit nur PDF-Vorlagen weitergeben
(die anderen Ordner einfach aus der ZIP entfernen vor dem Entpacken)
- **Anbieter-Paket teilen**: ZIP mit nur `providers/` weitergeben
- **Versionskontrolle**: Die entpackten JSON-Dateien unter Versionskontrolle
stellen (außerhalb von `backend/factory-defaults/`, da der Ordner gitignored ist)
## Changelog
### 1.1.0 (2026-05-01)
**Production-readiness** die Version, die wirklich öffentlich gehen darf.
- 🛡 **Security-Hardening**: 10 Runden statisches + dynamisches Audit, vollständig
dokumentiert in [docs/SECURITY-HARDENING.md](docs/SECURITY-HARDENING.md)
(CORS/Helmet/JWT, IDOR-Schutz an 30+ Endpoints, Mass-Assignment-Whitelists,
Zip-Slip, Path-Traversal, Login-Timing-Side-Channel, XFF-Rate-Limit-Bypass,
Customer-Liste-Leak, SSRF + DNS-Rebinding, Per-File-Ownership statt
freiem `/api/uploads`, JWT-Logout, Audit-Log-Hash-Chain).
- 🚨 **Sicherheits-Monitoring**: neue `SecurityEvent`-Tabelle + Hooks an Login,
Logout, Rate-Limit-Hit, IDOR-Abwehr, SSRF-Block, Password-Reset, JWT-Reject.
Threshold-Detection (Brute-Force, IDOR-Probing, SSRF-Probing) erzeugt
CRITICAL-Events. **Sofort-E-Mail-Alerts** für CRITICAL + **stündlicher Digest**
für HIGH/MEDIUM. UI in Einstellungen → Monitoring mit Filter, Pagination,
Log-leeren (mit optionalem Tage-Filter) und Test-Alert-Button.
- 🔄 **Auto-Vertragsstatus**:
- Lieferbestätigung-Upload → `DRAFT``ACTIVE` + `startDate`
- Kündigungsbestätigung-Upload → `ACTIVE``CANCELLED` + `cancellationConfirmationDate`
(mit Datums-Modal beim Upload)
- Nightly-Cron 02:00: alle `ACTIVE`-Verträge mit `endDate < heute``EXPIRED`
- 🔐 **Lazy bcrypt-Rehash**: Bestandshashes mit Cost 10 werden beim nächsten
Login transparent auf Cost 12 geupgradet.
- 🚪 **Logout-Endpoint** `POST /api/auth/logout`: invalidiert JWTs serverseitig
über `tokenInvalidatedAt`.
- 📦 **`npm audit fix`**: 8 transitive Vulnerabilities gefixt (lodash,
path-to-regexp, undici, minimatch).
### 1.0.0
Erste Release-Version.
- Kunden-, Vertrags-, Adress-, Bankkarten-, Ausweis- und Zählerverwaltung
- Energie-/Telekommunikations-/KFZ-Verträge mit typspezifischen Details
- Vertrags-Cockpit mit Rechnungsprüfung
- E-Mail-Client mit Anhang-Verwaltung
- DSGVO-Compliance: Audit-Log, Einwilligungen, Datenexport, Löschanfragen
- PDF-Auftragsvorlagen-System mit visueller Feldzuordnung
- Factory-Defaults für Stammdaten-Kataloge
- Mandantenfähigkeit über `customerEmailLabel` pro Provider
- Passwort-Reset-Flow + Rate-Limiting + Auto-Geburtstagsgrüße
## Lizenz
MIT