Files
opencrm/docs/todo.md
T
duffyduck 2c7a87ccd3 factory-defaults: HTML-Templates + Import über UI
Erweitert das bestehende Factory-Defaults-Bundle um vier HTML-Standardtexte
(Datenschutzerklärung, Impressum, Vollmacht-Vorlage, Website-Datenschutz)
und ergänzt den bisherigen CLI-Only-Import um einen Upload-Pfad in der UI.

Backend:
- collectFactoryDefaults() zieht jetzt auch die Whitelist-AppSettings
- exportFactoryDefaults() legt sie als app-settings/app-settings.json ins ZIP
- importFactoryDefaults(buffer) liest die ZIP idempotent ein – upserts pro
  Kategorie, Whitelist-Filter für AppSettings, Anti-Zip-Slip durch basename
  beim PDF-Lookup
- POST /api/factory-defaults/import (multer memoryStorage, max 50 MB,
  settings:update)
- seed-factory-defaults.ts (CLI) gleichermaßen um seedAppSettings() erweitert

Frontend:
- Import-Card in FactoryDefaults.tsx: Datei-Upload statt CLI-Anleitung
- Erfolgs-Box mit Counts pro Kategorie + Warnings (z.B. fehlende PDFs im ZIP)
- Preview zeigt jetzt auch die Anzahl HTML-Templates

Live verifiziert: Round-Trip Export → DELETE privacyPolicyHtml → Import →
Wert (13.6 KB) wieder vollständig hergestellt, Audit-Log zeigt EXPORT +
UPDATE-Eintrag mit Detail-Counts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:26:33 +02:00

295 lines
15 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 Todo-Liste
---
## 🔜 Offen
### Manuelle Tests (vor Release durchklicken)
Checklisten für Security + Email-Log-System stehen in **[TESTING.md](./TESTING.md)**.
Einmal komplett durchlaufen vor v1.0.0-Release.
### 🚀 SaaS-Ausbau: Instance-per-Customer + Admin-Portal + GoCardless
**Vision:** OpenCRM als SaaS anbieten. Jeder Kunde bekommt seine eigene
isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
über ein zentrales Admin-Portal.
**Architektur-Entscheidung:** Weg C (Instance-per-Customer)
- Pro Kunde eine eigene Docker-Instanz mit eigener DB
- Keine `tenantId` im CRM-Code → keine Security-Risiken durch vergessene Filter
- Komplette Datenisolation (DSGVO-freundlich)
- Updates können gestaffelt ausgerollt werden (erst 10% testen)
- Bei Kündigung: Docker-Image + DB-Export als "Mitnehm-Paket"
**Bewusst NICHT dabei:** eigener Mailserver. Stattdessen Plesk-Integration
(die wir schon haben) Kunde bekommt Mail-Zugang über unseren Plesk bei Bedarf.
---
**Admin-Portal (separate App, neben den CRM-Instanzen):**
- Kundenverwaltung: wer hat welchen Plan, Status (Trial/Active/Suspended/Cancelled)
- "Neuen Kunden anlegen" → Provisioning-Script
- DB anlegen (Master-DB kennt die Mapping)
- Docker-Container starten
- Subdomain konfigurieren (`kundenname.deincrm.de` via Caddy/Traefik)
- Initial-Admin-Account erstellen + Einladungs-Email senden
- Optional: Factory-Defaults für Stammdaten einspielen
- GoCardless-Integration (Webhook + Dashboard)
- Instanz-Management: Pause/Resume bei Zahlungsproblemen
- Logs & Metriken pro Instanz (optional)
- Support-Bereich (Tickets? oder einfach E-Mail)
---
**Abrechnung mit GoCardless (gocardless.com):**
- Zahlungsmethoden: SEPA-Lastschrift (Hauptfokus) + Kreditkarte (über GoCardless Embedded/Success)
- 30 Tage kostenlose Testphase ohne Zahlungsmittel
- Nach Trial: Mandats-Erfassung → regelmäßige Abbuchung
- Mehrere Pläne (z.B. Basic / Pro / Enterprise) mit unterschiedlichen Features
- Webhook-Endpoint im Admin-Portal:
- `payment_confirmed` → Instanz aktiv lassen
- `payment_failed` → Banner im CRM, nach X Tagen pausieren
- `mandate_cancelled` → Kündigungs-Flow
- Rechnungsstellung: GoCardless liefert Zahlungsbelege, aber **echte Rechnungen**
(mit USt-ID, Rechnungsnummer etc.) müssen wir selbst generieren
(evtl. über das existierende PDF-Template-System aus dem CRM nutzen)
---
**Provisioning-Flow (grober Entwurf):**
1. Kunde registriert sich auf Landing Page (Name, Firma, E-Mail, Wunsch-Subdomain)
2. Admin-Portal: Trial-Instanz starten
- DB erstellen, Docker-Container hochfahren, Caddy-Config für Subdomain
- Einladungs-Email mit Admin-Login + Passwort-Reset-Link
3. Tag 25: Erinnerungs-Email "Deine Trial läuft bald ab"
4. Tag 30: Banner im CRM "Jetzt bezahlen oder pausieren"
5. Kunde erfasst GoCardless-Mandat im Admin-Portal-Login
6. Bei erfolgreicher Zahlung: Instanz bleibt aktiv
7. Bei fehlender Zahlung nach 7 Tagen: Instanz pausiert (DB bleibt, UI zeigt Hinweis)
---
**Technische Bausteine für später:**
- Master-DB mit Tenant-Tabelle (Name, Subdomain, DB-Name, Plan, Status, GoCardlessIDs)
- Caddy oder Traefik als Reverse-Proxy mit Auto-SSL (Let's Encrypt)
- Docker-Orchestrierung: einzelne `docker-compose.yml` pro Kunde oder Docker-Swarm/K8s
- Backup-Strategie: pro Tenant separate Backups + zentrale Master-DB-Backups
- Monitoring: ein Fail macht nicht alle down, aber wir müssen es mitbekommen
- Logs zentral: z.B. Loki + Grafana für aggregierte Logs aller Instanzen
---
**Grobe Zeitschätzung:**
- Admin-Portal (MVP): ~1 Woche
- GoCardless-Integration + Webhooks: ~3-5 Tage
- Provisioning-Automatisierung (Docker + Caddy): ~1 Woche
- Landing Page + Checkout: ~3-5 Tage
- Tests + Polishing: ~1 Woche
- **Gesamt: ~3-4 Wochen**
**Vorbereitung JETZT (einfach, macht später Arbeit leichter):**
- ✅ Factory-Defaults System (schon erledigt, hilft beim Provisioning)
- ✅ Domain/Label dynamisch per Provider (schon erledigt)
- Docker-Compose aufräumen, Env-Variablen dokumentieren (klein, ein Tag)
- Backup-Script robust + wiederherstellbar (haben wir schon weitgehend)
---
## ✅ Erledigt
- [x] **📦 Factory-Defaults: HTML-Templates + Import via UI**
- Datenschutzerklärung, Impressum, Vollmacht-Vorlage und Website-Datenschutz
werden jetzt mit ins Factory-Defaults-ZIP gepackt (`app-settings/`-Ordner,
Whitelist-geschützt andere AppSetting-Keys werden ignoriert).
- Import läuft jetzt auch über die UI (Einstellungen → Factory-Defaults →
„ZIP hochladen"). Der CLI-Weg `npm run seed:defaults` bleibt erhalten und
wurde gleichermaßen um die HTML-Templates erweitert.
- Zwei-Wege-Roundtrip live verifiziert: Export → AppSetting löschen →
Import → Wert wieder vollständig hergestellt; Counts in Audit-Log.
- [x] **🐛 Benutzer-Verwaltung: DSGVO- + Entwickler-Zugriff zuweisbar**
- Mass-Assignment-Whitelist (`pickUserUpdate`) hat `hasGdprAccess` /
`hasDeveloperAccess` rausgefiltert → Service erhielt sie nie → Rollen
DSGVO/Developer waren in der UI nicht zuweisbar (Checkbox ohne Wirkung).
- Beide Felder zur Whitelist hinzugefügt + Audit-Log liest die Pre-Werte
jetzt aus den geladenen Rollen (kein False-Positive-Change mehr).
- [x] **🔒 HTTPS-only-Header per Flag (`HTTPS_ENABLED`)**
- HSTS + `upgrade-insecure-requests` (CSP) sperrten den Browser bei
direktem `http://ip:port`-Zugriff aus (`ERR_SSL_PROTOCOL_ERROR`).
- Beide Header default OFF, kommen nur mit `HTTPS_ENABLED=true` (sobald
TLS-Reverse-Proxy davor steht).
- [x] **🗃️ Prisma-Migrations-System (statt `db push`)**
- Initial-Migration `0_init` aus aktuellem Schema generiert
(`prisma migrate diff --from-empty --to-schema-datamodel`).
- 24 alte gedriftete Migrations gelöscht frischer Start.
- `migration_lock.toml` für MySQL hinzugefügt.
- Container-Entrypoint umgebaut:
- Auto-Baseline-Detection: bestehende DB ohne `_prisma_migrations`
`migrate resolve --applied 0_init` läuft automatisch.
- Statt `db push --accept-data-loss` jetzt `migrate deploy` (idempotent,
datenerhaltend, keine stillen DROPs mehr).
- Neuer npm-Script `schema:sync` (lokal/Dev): legt automatisch eine
versionierte Migration mit Zeitstempel-Namen an
(`prisma migrate dev --name auto_$(date +%Y%m%d_%H%M%S)`).
- Workflow ab jetzt: schema.prisma ändern → `npm run schema:sync`
Migration committen → Push → Container-Restart wendet sie automatisch an.
- [x] **🔄 Automatische Vertrags-Status-Übergänge**
- Nightly-Cron (02:00 + Catch-up 60s nach Start): alle Verträge mit
`status=ACTIVE` und `endDate < heute``EXPIRED` (mit Audit-Log).
- Beim Upload der Kündigungsbestätigung (`cancellationConfirmationPath`):
wenn Vertrag aktuell `ACTIVE` → auf `CANCELLED` setzen (Audit-Log).
Frontend fragt per Modal das Bestätigungs-Datum ab (Default: heute),
wird direkt als `cancellationConfirmationDate` gespeichert.
Der "Optionen"-Upload löst den Status-Wechsel bewusst NICHT aus, da er
für Vertragsänderungen (nicht echte Kündigungen) gedacht ist, setzt
aber `cancellationConfirmationOptionsDate` analog.
- Beim Upload einer `Lieferbestätigung` (ContractDocument via direkt-Upload
oder Email-Anhang-Import): wenn Vertrag aktuell `DRAFT` → auf `ACTIVE`
setzen + `startDate` auf das erfasste Lieferdatum (falls leer).
Frontend zeigt Datums-Input conditional, wenn Typ "Lieferbestätigung"
ausgewählt ist.
- Keine neuen Status eingeführt: `cancellationSentDate` vs.
`cancellationConfirmationDate` genügen, um "gesendet vs. bestätigt"
abzubilden. `ACTIVE` bleibt bis zur Bestätigung.
- [x] **🛡️ Security-Hardening vor Production-Deployment (10 Runden)**
- Vollständige Story inkl. aller Live-Test-Tabellen + Trade-offs:
**[SECURITY-HARDENING.md](./SECURITY-HARDENING.md)**
- Erste 2 Runden zusätzlich ausführlich in
[SECURITY-REVIEW.md](./SECURITY-REVIEW.md)
- Highlights:
- Runde 13: CORS, Helmet, JWT-Fallback, IDOR-Welle 1, XSS, Mass
Assignment, Zip-Slip, Path-Traversal, JWT-Algorithm, Rate-Limiter
- Runde 4: 9 Live-IDORs (customer.\*/gdpr.\*) + Error-Handler
- Runde 5: `/api/uploads`-Auth (DSGVO-GAU), Login-Timing,
Privacy-Policy-XSS
- Runde 6: Customer-List-Leak, XFF-Rate-Limit-Bypass,
Self-Grant + Existence-Disclosure
- Runde 7: SSRF-Schutz (Cloud-Metadata-Block), Logout-Endpoint
- Runde 8: DNS-Rebinding-Schutz, Per-File-Ownership-Check
- Runde 9: `npm audit fix` (8 Vulns weg), Audit-Chain-Rehash, keine
neuen Critical-Findings → diminishing returns erreicht
- Runde 10: Security-Monitoring (SecurityEvent-Tabelle + Hooks an
Login/IDOR/SSRF/Reset/Logout/JWT-Reject + Threshold-Detection +
Sofort-Alert für CRITICAL + Hourly-Digest + UI in Einstellungen)
- Deployment-Checkliste komplett (in HARDENING.md)
- [x] **🎉 Version 1.0.0 Feinschliff: Passwort-Reset + Rate-Limiting + Auto-Geburtstagsgrüße**
- **Passwort vergessen-Flow** (Login → "Passwort vergessen?" Link)
- Email-Reset-Token mit 2h Gültigkeit (kryptografisch sicher: 32 Byte Random)
- Funktioniert für Mitarbeiter UND Portal-Kunden (Typ-Auswahl)
- User-Enumeration-Schutz: immer 200 OK, egal ob Email existiert
- Reset-Link per Email mit schönem HTML-Template
- Nach Reset: alle bestehenden Sessions werden gekickt
- **Rate-Limiting** gegen Brute-Force
- Login: 10 Versuche pro 15 Min pro IP (erfolgreiche zählen nicht)
- Passwort-Reset-Anfrage: 5 Versuche pro Stunde pro IP
- **Cron-Job für automatische Geburtstagsgrüße**
- Täglich 08:00 Uhr: alle Kunden mit heutigem Geburtstag + autoBirthdayGreeting=true
- Email-Versand über System-E-Mail, Du/Sie-abhängiger Text
- Catch-up 30s nach Server-Start (falls Server am Geburtstag kurz down war)
- Marker lastBirthdayGreetingYear verhindert Doppel-Versand
- [x] **Mandantenfähigkeit: Domain + Kunden-E-Mail-Label dynamisch pro Provider**
- Neues Feld `customerEmailLabel` am EmailProviderConfig (z.B. "Stressfrei-Wechseln", "Meine-Firma")
- Wenn leer, wird das Label automatisch aus der Domain abgeleitet ("stressfrei-wechseln.de" → "Stressfrei-Wechseln")
- Neuer Frontend-Hook `useProviderSettings()` liefert Domain + Label
- Alle hardcoded "Stressfrei-Wechseln" und `@stressfrei-wechseln.de` Strings durch dynamische Werte ersetzt
(CustomerDetail, ContractForm, ContractDetail, EmailClientTab, Settings)
- Modal-Eingabefeld "Bezeichnung für Kunden-E-Mails" in Provider-Einstellungen
- Notwendig für Multi-Mandanten-Betrieb wenn das CRM an Dritte vermietet wird
- [x] **Factory-Defaults: Export + Import von Stammdaten-Katalogen**
- Enthält: Anbieter, Tarife, Kündigungsfristen, Laufzeiten, Vertragskategorien, PDF-Auftragsvorlagen (+ PDF-Dateien)
- Enthält NICHT: Kundendaten, Verträge, Dokumente, Emails, Einstellungen (dafür gibt es den Datenbank-Backup)
- Neue Einstellungsseite „Factory-Defaults" mit Übersicht (Anzahl pro Kategorie) und Export-Button
- Export: ZIP mit manifest.json + Kategorie-JSONs + PDF-Dateien, Download über Browser
- Import-Script: `npm run seed:defaults` liest `backend/factory-defaults/`, merged mehrere JSONs pro Kategorie, upsertet idempotent + kopiert PDFs in uploads/
- Ordner `backend/factory-defaults/` gitignoriert (außer .gitkeep + README), damit firmen-spezifische Kataloge nicht ins Repo kommen
- [x] **Email-Anhänge → Vertragsdokumente + Rechnungen für alle Vertragstypen**
- Im SaveAttachmentModal (bei einem per Email zugeordneten Vertrag) gibt es jetzt drei Modi:
1. **Als Dokument** (in feste Slots wie Kündigungsschreiben) wie bisher
2. **Als Vertragsdokument** neu, mit Typ-Dropdown (Auftragsformular, Lieferbestätigung, Vertragsunterlagen, Vollmacht, Widerrufsbelehrung, Preisblatt, Sonstiges) + Notizen
3. **Als Rechnung** jetzt für **alle** Vertragstypen (vorher nur Strom/Gas)
- Gleiches gilt für das Speichern der gesamten Email als PDF-Rechnung
- Neuer Backend-Endpoint `saveAttachmentAsContractDocument` für die flexible ContractDocument-Tabelle
- [x] **Geburtstag-Management-Modal in Kundenstammdaten**
- Neuer Button (Cake-Icon) neben Geburtsdatum öffnet Modal
- **Gruß zurücksetzen:** setzt `lastBirthdayGreetingYear` auf null zurück (fürs Debugging + Fallback)
- **Gruß jetzt senden:** per Email (direkt), WhatsApp/Telegram/Signal (öffnet vorbefülltes Fenster)
- Beide Aktionen mit Ja/Nein-Bestätigungsdialog (kein versehentliches Klicken)
- Text respektiert Du/Sie-Einstellung des Kunden
- Checkbox "Automatisch senden" mit Kanal-Dropdown (neue Felder am Customer)
- Audit-Log für Reset + Send
- [x] **Anrede-Verhältnis Du/Sie pro Kunde**
- Neues Feld `useInformalAddress` in Stammdaten (auch bei Firmenkunden)
- Default: Sie (formell)
- Geburtstagsgruß im Portal nutzt die Anrede: "Du"-Kunden bekommen "Herzlichen Glückwunsch, Max!", "Sie"-Kunden "Herzlichen Glückwunsch, Herr Müller!"
- Komplett konsistent auch bei nachträglichen Glückwünschen ("hattest" vs "hatten")
- [x] **Geburtsdatum + Geburtsort auch bei Firmenkunden**
- Felder werden jetzt unabhängig vom Kundentyp angezeigt
- Ermöglicht z.B. Geburtstage für Ansprechpartner bei Firmen
- [x] **Geburtstagskalender + Geburtstagsgruß-Modal**
- Admin: Section im Vertrags-Cockpit mit Kunden, die in den nächsten 30 Tagen oder letzten 7 Tagen Geburtstag haben
- Portal: Modal mit Gruß am Geburtstag (inkl. nachträglichem Glückwunsch bis 7 Tage danach)
- Wird pro Jahr nur einmal angezeigt
- [x] **Typspezifische Zusatzinfos in Vertragslisten**
- Strom/Gas → "Lieferadresse: ..."
- DSL/Glasfaser/Kabel → "Anschlussadresse: ..."
- Mobilfunk → "Rufnummer: ..."
- KFZ → "Kennzeichen: ..."
- Sichtbar in Admin-Liste, Portal-Liste und Kunden-Tab
- [x] **Datenschutzerklärung PDF ↔ Online-Einwilligungen synchronisieren**
- PDF hochgeladen → alle 4 Consents auf GRANTED
- Haken entfernt im Portal → PDF löschen + Tabs sperren
- Entsperrung nur durch alle Haken oder neues PDF
- [x] **Zweitarif-Zähler (HT/NT)** bei Strom + Verbrauchsberechnung
- [x] **Datumsformate vereinheitlichen** (01.01.2026 statt 1.1.2026)
- [x] **Audit-Log aussagekräftig** (Vorher/Nachher bei allen Änderungen)
- [x] **Impressum + Website-Datenschutzerklärung** im Kundenportal
- Editor in Einstellungen
- Vorschlagstexte
- [x] **Consent-Bestätigungs-Flow per Email**
- Alle Hebel müssen gesetzt sein
- Bestätigungsbutton + Bestätigungsemail
- [x] **Vertragsdokumente-Upload** (Auftragsformular, Lieferbestätigung, Vertragsunterlagen als PDF/PNG)
- [x] **Bug: Stressfrei-Email im Auftragsgenerator** (funktioniert jetzt im Vertrag)
- [x] **PDF-Auftragsvorlagen-System**
- Template-Editor in Einstellungen
- PDF hochladen, Formularfelder automatisch auslesen
- CRM-Felder zuordnen (visuell mit Vorschau)
- Seitenweise Sortierung der Felder
- Dynamische Rufnummern-Felder mit Vorwahl-Extraktion
- Nicht zugeordnete Felder bleiben editierbar
- Auftrag generieren aus Vertragsdaten (Button im Vertrags-Detail)
- [x] **Eigentümer-Verwaltung**
- An Adresse gehängt (Firma, Vorname, Nachname, Anschrift, Kontakt)
- Fallback auf Kundendaten wenn leer
- Nur bei Liefer-/Meldeadressen (nicht Rechnung)
- Namens-Kombinationen (Firma + Vorname + Nachname etc.)
- [x] **Gruppenauswahl Liefer-/Rechnungs-/Eigentümer-Adresse** im Auftragsgenerator
- [x] **Objekttyp + Lage + Lage des Anschlusses** bei Festnetz-Verträgen (DSL/Glasfaser/Kabel)
- [x] **Bankverbindung-Fallback** im PDF-Generator (neueste aktive Bankverbindung des Kunden)