13 KiB
📋 OpenCRM – Todo-Liste
🔜 Offen
Manuelle Tests (vor Release durchklicken)
Checklisten für Security + Email-Log-System stehen in docs/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
tenantIdim 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.devia 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 lassenpayment_failed→ Banner im CRM, nach X Tagen pausierenmandate_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):
- Kunde registriert sich auf Landing Page (Name, Firma, E-Mail, Wunsch-Subdomain)
- Admin-Portal: Trial-Instanz starten
- DB erstellen, Docker-Container hochfahren, Caddy-Config für Subdomain
- Einladungs-Email mit Admin-Login + Passwort-Reset-Link
- Tag 25: Erinnerungs-Email "Deine Trial läuft bald ab"
- Tag 30: Banner im CRM "Jetzt bezahlen oder pausieren"
- Kunde erfasst GoCardless-Mandat im Admin-Portal-Login
- Bei erfolgreicher Zahlung: Instanz bleibt aktiv
- 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.ymlpro 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
-
🔄 Automatische Vertrags-Status-Übergänge
- Nightly-Cron (02:00 + Catch-up 60s nach Start): alle Verträge mit
status=ACTIVEundendDate < heute→EXPIRED(mit Audit-Log). - Beim Upload der Kündigungsbestätigung (
cancellationConfirmationPath): wenn Vertrag aktuellACTIVE→ aufCANCELLEDsetzen (Audit-Log). Frontend fragt per Modal das Bestätigungs-Datum ab (Default: heute), wird direkt alscancellationConfirmationDategespeichert. Der "Optionen"-Upload löst den Status-Wechsel bewusst NICHT aus, da er für Vertragsänderungen (nicht echte Kündigungen) gedacht ist, setzt abercancellationConfirmationOptionsDateanalog. - Beim Upload einer
Lieferbestätigung(ContractDocument via direkt-Upload oder Email-Anhang-Import): wenn Vertrag aktuellDRAFT→ aufACTIVEsetzen +startDateauf das erfasste Lieferdatum (falls leer). Frontend zeigt Datums-Input conditional, wenn Typ "Lieferbestätigung" ausgewählt ist. - Keine neuen Status eingeführt:
cancellationSentDatevs.cancellationConfirmationDategenügen, um "gesendet vs. bestätigt" abzubilden.ACTIVEbleibt bis zur Bestätigung.
- Nightly-Cron (02:00 + Catch-up 60s nach Start): alle Verträge mit
-
🛡️ Security-Hardening vor Production-Deployment (8 Runden)
- Vollständige Story inkl. aller Live-Test-Tabellen + Trade-offs: docs/SECURITY-HARDENING.md
- Erste 2 Runden zusätzlich ausführlich in docs/SECURITY-REVIEW.md
- Highlights:
- Runde 1–3: 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
- Deployment-Checkliste komplett (in HARDENING.md)
-
🎉 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
- Passwort vergessen-Flow (Login → "Passwort vergessen?" Link)
-
Mandantenfähigkeit: Domain + Kunden-E-Mail-Label dynamisch pro Provider
- Neues Feld
customerEmailLabelam 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.deStrings 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
- Neues Feld
-
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:defaultsliestbackend/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
-
Email-Anhänge → Vertragsdokumente + Rechnungen für alle Vertragstypen
- Im SaveAttachmentModal (bei einem per Email zugeordneten Vertrag) gibt es jetzt drei Modi:
- Als Dokument (in feste Slots wie Kündigungsschreiben) – wie bisher
- Als Vertragsdokument – neu, mit Typ-Dropdown (Auftragsformular, Lieferbestätigung, Vertragsunterlagen, Vollmacht, Widerrufsbelehrung, Preisblatt, Sonstiges) + Notizen
- 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
saveAttachmentAsContractDocumentfür die flexible ContractDocument-Tabelle
- Im SaveAttachmentModal (bei einem per Email zugeordneten Vertrag) gibt es jetzt drei Modi:
-
Geburtstag-Management-Modal in Kundenstammdaten
- Neuer Button (Cake-Icon) neben Geburtsdatum öffnet Modal
- Gruß zurücksetzen: setzt
lastBirthdayGreetingYearauf 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
-
Anrede-Verhältnis Du/Sie pro Kunde
- Neues Feld
useInformalAddressin 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")
- Neues Feld
-
Geburtsdatum + Geburtsort auch bei Firmenkunden
- Felder werden jetzt unabhängig vom Kundentyp angezeigt
- Ermöglicht z.B. Geburtstage für Ansprechpartner bei Firmen
-
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
-
Typspezifische Zusatzinfos in Vertragslisten
- Strom/Gas → "Lieferadresse: ..."
- DSL/Glasfaser/Kabel → "Anschlussadresse: ..."
- Mobilfunk → "Rufnummer: ..."
- KFZ → "Kennzeichen: ..."
- Sichtbar in Admin-Liste, Portal-Liste und Kunden-Tab
-
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
-
Zweitarif-Zähler (HT/NT) bei Strom + Verbrauchsberechnung
-
Datumsformate vereinheitlichen (01.01.2026 statt 1.1.2026)
-
Audit-Log aussagekräftig (Vorher/Nachher bei allen Änderungen)
-
Impressum + Website-Datenschutzerklärung im Kundenportal
- Editor in Einstellungen
- Vorschlagstexte
-
Consent-Bestätigungs-Flow per Email
- Alle Hebel müssen gesetzt sein
- Bestätigungsbutton + Bestätigungsemail
-
Vertragsdokumente-Upload (Auftragsformular, Lieferbestätigung, Vertragsunterlagen als PDF/PNG)
-
Bug: Stressfrei-Email im Auftragsgenerator (funktioniert jetzt im Vertrag)
-
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)
-
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.)
-
Gruppenauswahl Liefer-/Rechnungs-/Eigentümer-Adresse im Auftragsgenerator
-
Objekttyp + Lage + Lage des Anschlusses bei Festnetz-Verträgen (DSL/Glasfaser/Kabel)
-
Bankverbindung-Fallback im PDF-Generator (neueste aktive Bankverbindung des Kunden)