# SaaS-Umstrukturierungsplan Kaffeeliste

Stand: 2026-07-13

Dieses Dokument beschreibt den geplanten Umbau der bestehenden Kaffeelisten-App zu
einer mehrkundenfähigen SaaS-Anwendung mit öffentlicher Landingpage,
Kundenregistrierung und geschützter App. Die bestehende Bedienlogik und das
visuelle Grunddesign sollen bewusst erhalten bleiben.

## Zielbild

- Mehrere Kunden können eigene Kaffeelisten betreiben.
- Jeder Kunde hat isolierte Daten, Einstellungen, Mitglieder und Buchungen.
- Neue Kunden können sich über eine öffentliche Landingpage registrieren.
- Die operative App bleibt optisch nah am aktuellen Bestand: Sidebar, Tabellen,
  schlichte Formulare, HTML5-UP-Anmutung.
- Authentifizierung, Rollen, Mandantenkontext und Sicherheitsprüfungen werden
  zentralisiert.
- Finanznahe Vorgänge werden nachvollziehbar und revisionsfreundlich
  gespeichert.

## Nicht-Ziele für den ersten Umbau

- Kein kompletter Design-Relaunch.
- Keine verspielte Marketing-App statt der bestehenden Arbeitsoberfläche.
- Kein gleichzeitiger Neubau aller Randmodule, wenn diese für den MVP nicht
  benötigt werden.
- Kein Dual-Write zwischen Legacy und neuer App als Standardbetrieb.
- Keine Mandanten-ID aus Formularfeldern oder URL-Parametern als alleinige
  Sicherheitsbasis.

## Ist-Zustand

Die aktuelle App ist eine flache PHP/sqlsrv-Legacy-App im Webroot.

Wichtige Bereiche:

- `index.php`: persönliches Dashboard.
- `stricheintragen.php`: Sammelerfassung von Kaffee-Strichen.
- `einzahlung.php`: Sammelerfassung von Einzahlungen.
- `kaffeeliste.php`: Gesamtübersicht.
- `mitarbeiterverwalten.php`: Mitglieder- und Adminpflege.
- `letzteneintraege.php`: Korrektur beziehungsweise Löschung letzter Buchungen.
- `csvupload.php`: Zahlungsimport.
- `exportKaffeeliste.php`: PDF-/Listenexport.
- `mailversenden.php`: Massenmail.
- `hinweise.php`: Hinweise.
- `faq.php`: FAQ.

Zentrale technische Beobachtungen:

- Authentifizierung hängt an Windows/IIS `AUTH_USER` und LDAP.
- Die Mailadresse des Nutzers wird in `functionsLDAP.php` ermittelt.
- Rollen liegen als Boolean `admin` direkt auf `kl_Mitarbeiter`.
- Datenzugriff findet direkt in den einzelnen PHP-Seiten per `sqlsrv_query`
  statt.
- Es gibt kein sichtbares Migrationssystem und kein DB-Schema im Repository.
- Fachliche Tabellen haben aktuell keinen Mandantenbezug.
- Das Design kommt vor allem aus `assets/css/main.css` und dem HTML5-UP-Layout.
- Die App-Navigation sitzt faktisch in `footer.php`; `nav.php` ist leer.

## Grundsatzentscheidung

Der Umbau sollte nicht mit einer rein kosmetischen Landingpage starten, sondern
mit einem stabilen SaaS-Fundament:

1. Bestand dokumentieren und fachliche Ergebnisse einfrieren.
2. Mandantenmodell, Auth und Rollen sauber aufbauen.
3. Bestehende Fachlogik schrittweise tenant-sicher übernehmen.
4. Landingpage und App-Shell trennen, ohne das App-Design neu zu erfinden.

So bleibt die vertraute Kaffeelisten-Oberfläche erhalten, während die
Datenbasis und Sicherheit SaaS-fähig werden.

## Zielarchitektur

### Public-Bereich

Der Public-Bereich ist ohne Login erreichbar und lädt keine Mandantendaten.

Empfohlene Routen:

- `/`: Landingpage.
- `/preise` oder später `/pricing`: optional, falls Tarife eingeführt werden.
- `/faq`: öffentliche FAQ oder FAQ-Auszug.
- `/registrieren` beziehungsweise aktuell `register.php`: Kundenregistrierung.
- `/login` beziehungsweise aktuell `login.php`: Login.
- `/passwort-vergessen` beziehungsweise aktuell `passwort-vergessen.php`:
  Passwort-Reset.

Landingpage-Inhalte:

- Hero mit Name `Kaffeeliste` und kurzer Nutzenbeschreibung.
- Drei Kernabläufe: Kaffee nehmen, Strich setzen, bei Bedarf bezahlen.
- Kurzer Blick auf Funktionen: Mitglieder, Guthaben, Einzahlungen, Export,
  Hinweise.
- App-Screenshot oder Demo-Ansicht im bestehenden Stil.
- FAQ-Auszug.
- Call-to-Action zu Registrierung und Login.

### Geschützte App

Die geschützte App bleibt die operative Arbeitsoberfläche.

Empfohlene Routen:

- `/app`: Dashboard oder Weiterleitung auf `/app/dashboard`.
- `/app/dashboard`: Meine Kaffeeliste.
- `/app/striche`: Eigene Striche erfassen, falls erlaubt.
- `/app/einzahlungen`: Zahlungen und PayPal-Optionen.
- `/app/mitglieder`: Mitgliederverwaltung.
- `/app/liste`: Gesamtübersicht.
- `/app/buchungen`: Letzte Einträge und Korrekturen.
- `/app/importe`: CSV-Importe.
- `/app/export/pdf`: PDF-/Listenexport.
- `/app/hinweise`: Hinweise.
- `/app/einstellungen`: Tenant-Einstellungen.

Die App sollte eine Sidebar behalten, aber besser gruppiert werden:

- Persönlich: Meine Kaffeeliste, Namensanpassung, FAQ.
- Erfassung: Striche, Einzahlungen.
- Auswertung: Kaffeeliste, Buchungen, Export.
- Administration: Mitglieder, Hinweise, Einstellungen, Importe.
- Konto: Kundenkonto und Logout. Public-Links wie Login und Registrierung
  gehören nicht in die App-Sidebar.

### Webspace- und Host-Strategie

Empfohlen für den Start:

- `kaffeeliste.de` oder `www.kaffeeliste.de` für Landingpage, Registrierung
  und Login.
- `app.kaffeeliste.de` für die geschützte App.
- Keine Wildcard-Subdomains für Kunden.
- Mandantenauswahl nach Login über Session und `tenant_memberships`.
- Kundeneigene feste Domains oder Subdomains erst später gezielt einrichten,
  wenn DNS und Zertifikat pro Domain sauber geprüft sind.

Damit bleibt der Betrieb webspace-tauglich: Für den Start reichen zwei feste
Hosts mit normalen Let's-Encrypt-Zertifikaten. Die fachliche Tenant-Auflösung
ist in M3 vorbereitet; produktive Host-Rewrites, Cookie-Domain und Zertifikats-
Details gehören zur M8-Betriebshärtung.

## Datenmodell

Empfohlen wird ein Shared-Database/Shared-Schema-Modell mit verpflichtendem
`tenant_id` auf allen fachlichen Tabellen.

Kernschema:

```text
tenants(
  id, slug, name, status, timezone, locale, currency_code,
  created_at, updated_at
)

tenant_settings(
  tenant_id, mark_price_cents, self_entry_enabled, paypal_enabled,
  paypal_url_template, sheet_window_days, negative_warning_cents,
  positive_highlight_cents, branding_json, updated_by_user_id
)

users(
  id, email_norm, password_hash, name, email_verified_at,
  mfa_enabled, disabled_at, last_login_at, created_at, updated_at
)

tenant_memberships(
  id, tenant_id, user_id, role, status, invited_at, joined_at,
  created_at, updated_at,
  UNIQUE(tenant_id, user_id)
)

participants(
  id, tenant_id, user_id NULL, display_name, email_norm NULL,
  paypal_name NULL, active, legacy_mitarbeiter_id,
  created_at, updated_at,
  UNIQUE(tenant_id, email_norm)
)

ledger_entries(
  id, tenant_id, participant_id, type, amount_cents, marks_count,
  unit_price_cents, booked_at, source, note, created_by_user_id,
  import_batch_id NULL, legacy_table, legacy_id,
  voided_at NULL, reversal_of_entry_id NULL,
  created_at
)

notices(
  id, tenant_id, message, valid_from, valid_until,
  created_by_user_id, deleted_at, created_at, updated_at
)

payment_import_batches(
  id, tenant_id, original_filename, checksum, status,
  created_by_user_id, created_at
)

payment_import_rows(
  id, batch_id, row_number, participant_id NULL, raw_name,
  amount_cents, booked_at, status, raw_json
)

audit_log(
  id, tenant_id, actor_user_id, action, subject_type, subject_id,
  metadata_json, ip, created_at
)

outbound_emails(
  id, tenant_id, participant_id, template, subject, status,
  sent_at, error, created_at
)
```

Wichtige Modellierungsregeln:

- `users` sind Login-Konten.
- `participants` sind Kaffee-Teilnehmer.
- Ein Teilnehmer kann, muss aber nicht, ein Login-Konto haben.
- Rollen gehören in `tenant_memberships`, nicht direkt auf Teilnehmer.
- Buchungen sollten nicht hart gelöscht werden.
- Korrekturen laufen über Storno- oder Reversal-Einträge.
- Legacy-IDs werden bei Migration gespeichert, damit Ergebnisse vergleichbar
  bleiben.
- Geldwerte sollten als Cent-Integer gespeichert werden, nicht als Float.

## Rollenmodell

Empfohlene Rollen:

- `owner`: Kunde verwalten, Billing, Admins, Löschung, Grundeinstellungen.
- `admin`: Mitglieder, Hinweise, fachliche Einstellungen, Auswertungen.
- `treasurer`: Zahlungen, CSV-Import, Korrekturen, Exporte, Mails.
- `member`: eigenes Dashboard, eigene Striche, eigener Anzeigename.
- `viewer`: lesende Auswertungen.

Alle Rollen sind tenant-scoped. Ein User kann also bei Kunde A Admin und bei
Kunde B nur Mitglied sein.

## Authentifizierung und Sicherheit

SaaS-Basis:

- E-Mail/Passwort-Login mit `password_hash`.
- E-Mail-Verifikation.
- Passwort-Reset.
- Sichere Session-Cookies.
- CSRF-Schutz für alle schreibenden Aktionen.
- Rate-Limits für Login, Registrierung und Passwort-Reset.
- Zentrale Funktionen oder Middleware: `requireLogin`, `requireTenant`,
  `requireRole`, `csrfToken`.

Tenant-Auflösung:

- Primär serverseitig aus Subdomain, Custom Domain oder tenantgebundener
  Auswahl nach Login.
- Nicht allein aus Hidden Inputs oder frei manipulierbaren IDs.
- Jede fachliche Query braucht einen Tenant-Scope.

Später möglich:

- LDAP/AD oder SSO pro Kunde als optionaler Identity Provider.
- MFA für Owner/Admins.
- Row-Level-Security in der Datenbank.

## Design-Leitplanken

Das bestehende Design soll erhalten bleiben.

Beibehalten:

- Grüner Akzent `#38761d`.
- Weiß/Grau als ruhige Grundfläche.
- Roboto Slab für Überschriften.
- Open Sans für Fließtext.
- Sidebar für die geschützte App.
- Tabellen als primäre Darstellung für operative Daten.
- Schlichte Formularfelder und Buttons.

Verbessern ohne Relaunch:

- Hinweise als wiederverwendbare Komponente statt Inline-Style.
- Einheitliche Erfolgs-, Fehler- und Warnmeldungen.
- Dashboardwerte als kompakte Statusbereiche.
- Aktionsleisten über Tabellen.
- Sidebar-Gruppierung und aktive Navigation.
- FAQ strukturieren, aber Inhalte nicht stark verändern.

Nicht tun:

- Kein komplett neues Farbsystem.
- Keine dekorative Marketingoptik in der App.
- Keine App-Tabellen in Kartenlandschaften auflösen.
- Keine Navigationsstruktur, die die heutigen Kernabläufe versteckt.

## Meilensteinplan

Übersicht:

| Meilenstein | Schwerpunkt | Hauptergebnis |
| --- | --- | --- |
| M0 | Baseline | Bestand, Sicherheit und Designreferenz sind dokumentiert |
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prüft sichere Seiten |
| M2 | Technisches Fundament | Abgeschlossen: Migrationen, Bootstrap, Session, CSRF-Helper und Legacy-Schreibseitenschutz stehen |
| M3 | SaaS-Basis | Abgeschlossen: Tenants, User, Registrierung, Login, Rollen, Mail-Links und zentrale Mandantenauswahl funktionieren |
| M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritätscheck, Ledger-Service und Preview sind umgesetzt |
| M5 | App-Kern | Gestartet: Gesamtübersicht liest read-only aus dem Ledger |
| M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar |
| M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spätere Ausbaustufen folgen |
| M8 | Härtung | Betrieb, Datenschutz, Monitoring und Isolation sind geprüft |
| M9 | Cutover | Produktivumstellung ist vorbereitet und Legacy ist read-only |

### M0: Planungs- und Sicherheitsbaseline

Ziel:
Den aktuellen Zustand verlässlich dokumentieren, bevor umgebaut wird.

Arbeitsartefakte:

- `docs/m0/README.md`
- `docs/m0/code-inventory.md`
- `docs/m0/security-baseline.md`
- `docs/m0/schema-export.sql`
- `docs/m0/golden-master-queries.sql`
- `docs/m0/screenshot-checklist.md`

Schritte:

- Live-DB-Schema exportieren.
- Tabellen, Spalten, Indizes und Constraints dokumentieren.
- Bekannte Jobs/Skripte erfassen, zum Beispiel Mailversand und Jahresauswertung.
- Secrets inventarisieren und rotieren, insbesondere hart codierte Zugangsdaten.
- Bestehende Seiten und UI-Zustände per Screenshots festhalten.
- Fachliche Kernregeln dokumentieren: Saldo, Jahreswerte, Preis pro Strich,
  100-Tage-Listen, CSV-Deduplizierung, PDF-Ausgabe.

Ergebnis:

- Datenkatalog.
- Prozesslandkarte.
- Designreferenz.
- Liste kritischer Sicherheitsrisiken.

Abhängigkeiten:

- Zugriff auf echte oder anonymisierte Datenbank.
- Kenntnis der Produktivumgebung.

### M1: Golden-Master-Validierung

Ziel:
Sicherstellen, dass spätere neue Berechnungen dieselben Ergebnisse wie der
Bestand liefern.

Schritte:

- Referenzdatensatz aus Legacy erzeugen. Erledigt als rekonstruierter Golden
  Master in der MySQL-Dev-Datenbank.
- Pro Mitglied berechnen: Gesamteinzahlungen, Gesamtausgaben, Gesamtstriche,
  aktueller Stand, Jahreswerte. Erledigt mit `scripts/check-golden-master.php`.
- CSV-Importfälle sammeln: Treffer, Dubletten, unbekannte Namen. Erledigt im
  Golden-Master-Check.
- Testfälle für Guthaben, Schulden, Nullsaldo und inaktive Teilnehmer
  anlegen. Erledigt.
- Sichere GET-Seiten per HTTP-Smoke prüfen. Erledigt mit
  `scripts/http-smoke.php`.
- PDF-/Export-Summen als Referenz sichern. Offen, weil die TCPDF-Kopie im Repo
  unvollständig ist.

Ergebnis:

- Golden-Master-Daten.
- Vergleichsqueries oder Vergleichsskript.
- HTTP-Smoke-Test für sichere UI-Seiten.
- Akzeptanzkriterien für Migration und neue App.
- Dokumentierte offene Punkte: PDF-Export/TCPDF, PHPMailer-Abhängigkeit,
  GET-Nebenwirkungen bei Mailversand und Jahresauswertung.

Abhängigkeiten:

- M0 abgeschlossen.
- PDF-/Mail-/Jahresprozesse werden in M6 gezielt neu gestaltet, statt sie in M1
  per GET-Smoke auszuführen.

### M2: Technisches Fundament

Ziel:
Eine saubere Basis für App, Public-Seiten, Auth und Mandanten schaffen.

Schritte:

- Projektstruktur festlegen: Public-Routes, App-Routes, Views/Templates,
  Services/Repositories.
- Zentrales Bootstrap für Config, DB-Verbindung, Session und Fehlerbehandlung.
  Begonnen mit `app/bootstrap.php`.
- Versionierte Migrationen einführen. Begonnen mit
  `database/migrations/0001_legacy_mysql_baseline.sql` und
  `scripts/migrate.php`.
- Layouts trennen: Public-Layout und App-Layout. Noch offen; Header/Footer
  bleiben vorerst Legacy-Wrapper.
- Bestehende Assets weiterverwenden.
- CSRF- und Session-Basis einziehen. Session und CSRF-Helper sind vorhanden;
  globale Erzwingung erfolgt schrittweise pro POST-Seite. Erste Seiten sind
  abgesichert: `hinweise.php`, `mitarbeiterverwalten.php`,
  `namenanpassen.php`, `index.php`, `stricheintragen.php`, `einzahlung.php`,
  `letzteneintraege.php`, `csvupload.php`.
- CSV-Upload außerhalb des Webroots speichern. In M2 als Legacy-Härtung
  umgesetzt: temporär unter `var/uploads`, Dateityp-/Größenprüfung und
  Löschung nach Verarbeitung.
- Konfigurationswerte aus Code in Umgebung oder Settings verschieben.

Ergebnis:

- Grundgerüst für neue SaaS-App.
- Kein fachlicher Rewrite, aber klare Struktur für die Migration.
- Dokumentation: `docs/m2-technical-foundation.md`.
- Status: abgeschlossen für den M2-Scope.

Abhängigkeiten:

- Entscheidung, ob PHP nativ weitergeführt oder ein Framework genutzt wird.
- M2 bleibt bewusst ohne Tenant-/User-/Rollen-Tabellen; diese starten in M3.

### M3: Mandanten, Registrierung und Login

Ziel:
Mehrkundenfähigkeit und Kunden-Onboarding technisch aktivieren.

Schritte:

- Tabellen für `tenants`, `users`, `tenant_memberships` und
  `tenant_settings` anlegen. Vorbereitung dokumentiert in
  `docs/m3-saas-basis-vorbereitung.md`: erster Schritt erledigt.
- Zusätzlich `participants` als getrennte Kaffee-Teilnehmer-Tabelle anlegen:
  erledigt.
- Default-Tenant für den aktuellen Bestand anlegen: erledigt.
- Bestehende `kl_Mitarbeiter` idempotent in `participants` spiegeln:
  erledigt.
- Registrierung: Tenant + Owner-User + Default-Settings erzeugen: erster
  Flow erledigt.
- Login und Logout bauen: erster Flow erledigt.
- Passwort-Reset und E-Mail-Verifikation bauen: Dev-Flow mit Single-Use-Tokens
  und Mail-Transport-Abstraktion erledigt.
- Tenant-Auflösung definieren: zentrale App-Domain mit Session-Kontext und
  Mandantenauswahl erledigt; feste Domains vorbereitet, keine Wildcards.
- Rollenprüfung zentralisieren: erster Owner/Admin-Check erledigt.
- Erste Admin-/Owner-Seite für Grundeinstellungen: erledigt.
- Erste Public-Landingpage mit CTA zu Login und Registrierung: erledigt.
- Login, Registrierung, Passwort-Reset und E-Mail-Verifikation im Public-Stil:
  erledigt.
- App-Sidebar ohne Public-Login-/Registrierungslinks: erledigt.

Ergebnis:

- Ein neuer Kunde kann sich registrieren und in seine eigene App gelangen.
- Rollen und Tenant-Kontext sind serverseitig verfügbar.
- Reset- und Verifikationslinks können im Dev-Modus geloggt und produktiv über
  einen konfigurierten Mail-Transport versendet werden.
- Status: abgeschlossen für den M3-Scope.

Abhängigkeiten:

- M2 abgeschlossen.

### M4: Migration des fachlichen Kerns

Ziel:
Bestehende Kaffeelisten-Daten tenant-sicher übernehmen.

Schritte:

- Initialen Tenant für den bisherigen Bestand anlegen: erledigt in M3.
- `kl_Mitarbeiter` nach `participants` migrieren: erledigt in M3.
- `admin`-Informationen in `tenant_memberships` oder Admin-Rollen überführen:
  erledigt in M3.
- `kl_config` nach `tenant_settings` migrieren: erledigt in M3.
- `ledger_entries` als tenant-sichere Buchungstabelle anlegen: erledigt.
- `kl_Einzahlungen` und `kl_Kaffeeverbrauch` additiv nach `ledger_entries`
  spiegeln: erster Backfill erledigt.
- Legacy-IDs speichern: erledigt über `legacy_table` und `legacy_id`.
- Salden gegen Golden-Master vergleichen: Kontrollskript erledigt.
- Ledger-Service für Tenant-Summen, Teilnehmer-Summen und letzte Buchungen:
  erledigt.
- Read-only Ledger-Preview für Browservergleich: erledigt.

Ergebnis:

- Bestehender Datenbestand kann im neuen Modell abgebildet werden.
- Saldenparität ist über `scripts/check-m4-ledger-migration.php`
  nachweisbar.
- Erste App-Abfragen können über `app/ledger.php` tenant-sicher aus dem neuen
  Modell lesen.
- `ledger-preview.php` zeigt die neue Ledger-Auswertung ohne Schreibzugriffe.
- Dokumentation: `docs/m4-data-migration.md`.

Abhängigkeiten:

- M1 und M3 abgeschlossen.

### M5: Geschützte App-Funktionen

Ziel:
Die operativen Kernseiten im neuen Modell bereitstellen.

Schritte:

- Dashboard `Meine Kaffeeliste` umsetzen.
- Eigene Stricherfassung umsetzen.
- Zahlungs-/PayPal-Bereich umsetzen.
- Mitgliederverwaltung tenant- und rollenbasiert umsetzen.
- Gesamtübersicht umsetzen: erster read-only Stand in `kaffeeliste.php`
  erledigt.
- Teilnehmerauswertung umsetzen: read-only Stand in `teilnehmerauswertung.php`
  erledigt.
- Letzte Einträge und Korrekturen als Storno statt Delete umsetzen.
- Hinweise als tenant-spezifische Notices umsetzen.

Ergebnis:

- Ein Kunde kann seine Kaffeeliste operativ nutzen.
- App sieht weiterhin nach bestehender Kaffeeliste aus.
- Erster M5-Stand ist in `docs/m5-app-kern.md` dokumentiert.

Abhängigkeiten:

- M4 abgeschlossen.

### M6: Import, Export und Mail

Ziel:
Admin- und Treasurer-Flows produktionsreif machen.

Schritte:

- CSV-Import mit Vorschau, Dublettenprüfung und Audit bauen.
- Uploads außerhalb des Webroots speichern.
- PDF-/Listenexport aus neuem Datenmodell bauen.
- Mailversand als nachvollziehbaren Versandjob mit Dry-Run und Versandlog
  gestalten.
- Jahresauswertung beziehungsweise Jahresbuchungen tenant-sicher abbilden.

Ergebnis:

- Kassenverwaltung ist für reale Betriebsabläufe vollständig.
- Import/Export/Mail sind auditierbar.

Abhängigkeiten:

- M5 für die Kernansichten.

### M7: Öffentliche Landingpage

Ziel:
Werbliche Einstiegseite und Registrierung verfügbar machen, ohne die App-Optik
zu verwischen.

Schritte:

- Public-Layout mit bestehender Typografie und grünem Akzent bauen. Erster
  Stand als `landing.php` und `assets/css/public.css` erledigt.
- Landingpage-Inhalte erstellen. Erster Stand erledigt.
- Demo-Screenshot oder Demo-Ansicht einbinden.
- CTA zu Registrierung und Login. Erledigt.
- Login, Registrierung und Passwort-Reset in den Public-Stil integrieren.
  Erledigt.
- App-Sidebar von Public-Links trennen. Erledigt.
- FAQ-Auszug strukturieren.
- Keine App-Sidebar im Public-Bereich.

Ergebnis:

- Interessenten verstehen das Produkt und können sich registrieren.
- Bestehende App bleibt optisch eigenständig und arbeitsorientiert.

Abhängigkeiten:

- M3 für Registrierung.
- M5 für echte App-Screens oder Demo.

### M8: Härtung, Datenschutz und Betrieb

Ziel:
Die SaaS-App für mehrere Kunden sicher betreiben.

Schritte:

- Backups und Restore-Prozess definieren.
- Monitoring und Fehlerlogging einrichten.
- Audit-Log für Admin-Aktionen prüfen.
- Datenexport pro Tenant.
- Lösch-/Anonymisierungsprozess für Teilnehmer und Kunden.
- Rate-Limits und Security Headers.
- Mandanten-Isolation testen.
- Rollenmatrix testen.

Ergebnis:

- SaaS ist betrieblich und datenschutzseitig belastbarer.

Abhängigkeiten:

- M3 bis M6.

### M9: Cutover und Legacy-Archiv

Ziel:
Produktive Umstellung ohne Datenverlust.

Schritte:

- Legacy für Schreibzugriffe sperren.
- Finalen Delta-Export ziehen.
- Migration ausführen.
- Rowcounts, Salden, PDF-Stichproben und Importhistorie prüfen.
- DNS oder Reverse Proxy umschalten.
- Legacy 30 bis 90 Tage read-only als Archiv behalten.
- Rollback-Fenster und Vorgehen dokumentieren.

Ergebnis:

- Neuer SaaS-Betrieb ist live.
- Alter Bestand bleibt für Rückfragen lesbar.

Abhängigkeiten:

- M8 abgeschlossen.

## Empfohlene MVP-Reihenfolge

Für den ersten produktnahen MVP sollten nicht alle Randmodule gleichzeitig
umgebaut werden.

MVP-Paket:

1. Tenant, User, Login, Registrierung.
2. Mail-Links, zentrale Mandantenauswahl und erste Landingpage.
3. Teilnehmer, Settings, Ledger.
4. Dashboard, Striche, Einzahlungen, Gesamtübersicht.
5. Mitgliederverwaltung.
6. Hinweise.
7. CSV-Import, Export.

Später:

- Umfrage-Modul.
- SSO/LDAP pro Kunde.
- Billing/Tarife.
- MFA.
- Custom Domains.
- Erweiterte Mandanten-Branding-Optionen.

## Validierung

Fachliche Prüfungen:

- Saldo je Teilnehmer alt gegen neu.
- Jahreswerte alt gegen neu.
- Gesamtstriche alt gegen neu.
- Einzahlungen alt gegen neu.
- Export-Summen alt gegen neu.
- CSV-Import: identische Treffer- und Dublettenlogik.

Sicherheitsprüfungen:

- Fremde Tenant-IDs liefern keine Daten.
- Fremde Teilnehmer-IDs liefern keine Daten.
- Admin-Funktionen sind ohne Rolle blockiert.
- Alle POST-Aktionen brauchen CSRF.
- Uploads sind nicht direkt ausführbar.
- Ausgabe von Namen, Mails und Hinweisen ist escaped.

UX-Prüfungen:

- Kernseiten bleiben optisch wiedererkennbar.
- Sidebar bleibt für App-Nutzer vertraut.
- Landingpage zeigt keine App-Adminnavigation.
- Mobile Darstellung bleibt bedienbar.
- Tabellen bleiben lesbar.

## Risiken und Gegenmaßnahmen

| Risiko | Gegenmaßnahme |
| --- | --- |
| Tenant-Leak durch vergessene Query-Filter | Zentrale Repository-/Query-Schicht, Tests mit zwei Tenants |
| Falsche Salden nach Migration | Golden-Master-Vergleich vor Cutover |
| Harte Deletes verlieren Audit-Historie | Storno-/Reversal-Modell für Buchungen |
| Login und Teilnehmer werden vermischt | `users` und `participants` strikt trennen |
| Bestehendes Design driftet weg | Designreferenz und App-Komponenten definieren |
| CSV-Upload unsicher | Upload außerhalb Webroot, Dateitypprüfung, Importvorschau |
| Mailversand nicht nachvollziehbar | Versandlog, Dry-Run, Job-Status |
| Unvollständige Vendor-Kopien für PDF/Mail | Dependencies sauber vendoren oder ersetzen, Smoke-Tests danach erweitern |
| AD/LDAP blockiert SaaS-Onboarding | E-Mail/Passwort als Basis, SSO später optional |
| Kein DB-Schema im Repo | Schema exportieren und Migrationen einführen |

## Offene Entscheidungen

- Soll die neue App weiterhin nativ in PHP entstehen oder mit einem Framework?
- Soll SQL Server bleiben oder mittelfristig eine andere Datenbank genutzt
  werden?
- Wie sollen Kunden-Tenants adressiert werden: Subdomain, Pfad oder Auswahl nach
  Login?
- Welche Rolle soll `treasurer` haben: eigene Rolle oder Teil von `admin`?
- Wird das Umfrage-Modul Teil des SaaS-Produkts oder nur archiviert?
- Braucht der MVP schon Tarife/Billing oder erstmal nur Registrierung?
- Sollen bestehende Kunden per Einladung oder per Self-Service onboarden?

## Nächster konkreter Arbeitsschritt

Als nächstes sollte M0 gestartet werden:

1. Produktiv-/Testdatenbankschema exportieren.
2. Screenshot-Referenz der wichtigsten Seiten erstellen.
3. Golden-Master-Auswertungen definieren.
4. Entscheidung treffen, ob der neue SaaS-Kern im bestehenden PHP-Stil oder mit
   einem Framework aufgebaut wird.
