# 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. - 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.