20 KiB
SaaS-Umstrukturierungsplan Kaffeeliste
Stand: 2026-07-11
Dieses Dokument beschreibt den geplanten Umbau der bestehenden Kaffeelisten-App zu einer mehrkundenfaehigen SaaS-Anwendung mit oeffentlicher Landingpage, Kundenregistrierung und geschuetzter App. Die bestehende Bedienlogik und das visuelle Grunddesign sollen bewusst erhalten bleiben.
Zielbild
- Mehrere Kunden koennen eigene Kaffeelisten betreiben.
- Jeder Kunde hat isolierte Daten, Einstellungen, Mitglieder und Buchungen.
- Neue Kunden koennen sich ueber eine oeffentliche Landingpage registrieren.
- Die operative App bleibt optisch nah am aktuellen Bestand: Sidebar, Tabellen, schlichte Formulare, HTML5-UP-Anmutung.
- Authentifizierung, Rollen, Mandantenkontext und Sicherheitspruefungen werden zentralisiert.
- Finanznahe Vorgaenge werden nachvollziehbar und revisionsfreundlich gespeichert.
Nicht-Ziele fuer den ersten Umbau
- Kein kompletter Design-Relaunch.
- Keine verspielte Marketing-App statt der bestehenden Arbeitsoberflaeche.
- Kein gleichzeitiger Neubau aller Randmodule, wenn diese fuer den MVP nicht benoetigt 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: persoenliches Dashboard.stricheintragen.php: Sammelerfassung von Kaffee-Strichen.einzahlung.php: Sammelerfassung von Einzahlungen.kaffeeliste.php: Gesamtuebersicht.mitarbeiterverwalten.php: Mitglieder- und Adminpflege.letzteneintraege.php: Korrektur beziehungsweise Loeschung letzter Buchungen.csvupload.php: Zahlungsimport.exportKaffeeliste.php: PDF-/Listenexport.mailversenden.php: Massenmail.hinweise.php: Hinweise.faq.php: FAQ.
Zentrale technische Beobachtungen:
- Authentifizierung haengt an Windows/IIS
AUTH_USERund LDAP. - Die Mailadresse des Nutzers wird in
functionsLDAP.phpermittelt. - Rollen liegen als Boolean
admindirekt aufkl_Mitarbeiter. - Datenzugriff findet direkt in den einzelnen PHP-Seiten per
sqlsrv_querystatt. - 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.cssund dem HTML5-UP-Layout. - Die App-Navigation sitzt faktisch in
footer.php;nav.phpist leer.
Grundsatzentscheidung
Der Umbau sollte nicht mit einer rein kosmetischen Landingpage starten, sondern mit einem stabilen SaaS-Fundament:
- Bestand dokumentieren und fachliche Ergebnisse einfrieren.
- Mandantenmodell, Auth und Rollen sauber aufbauen.
- Bestehende Fachlogik schrittweise tenant-sicher uebernehmen.
- Landingpage und App-Shell trennen, ohne das App-Design neu zu erfinden.
So bleibt die vertraute Kaffeelisten-Oberflaeche erhalten, waehrend die Datenbasis und Sicherheit SaaS-faehig werden.
Zielarchitektur
Public-Bereich
Der Public-Bereich ist ohne Login erreichbar und laedt keine Mandantendaten.
Empfohlene Routen:
/: Landingpage./preiseoder spaeter/pricing: optional, falls Tarife eingefuehrt werden./faq: oeffentliche FAQ oder FAQ-Auszug./registrieren: Kundenregistrierung./login: Login./passwort-vergessen: Passwort-Reset.
Landingpage-Inhalte:
- Hero mit Name
Kaffeelisteund kurzer Nutzenbeschreibung. - Drei Kernablaeufe: 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.
Geschuetzte App
Die geschuetzte App bleibt die operative Arbeitsoberflaeche.
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: Gesamtuebersicht./app/buchungen: Letzte Eintraege 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:
- Persoenlich: Meine Kaffeeliste, Namensanpassung, FAQ.
- Erfassung: Striche, Einzahlungen.
- Auswertung: Kaffeeliste, Buchungen, Export.
- Administration: Mitglieder, Hinweise, Einstellungen, Importe.
Datenmodell
Empfohlen wird ein Shared-Database/Shared-Schema-Modell mit verpflichtendem
tenant_id auf allen fachlichen Tabellen.
Kernschema:
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:
userssind Login-Konten.participantssind Kaffee-Teilnehmer.- Ein Teilnehmer kann, muss aber nicht, ein Login-Konto haben.
- Rollen gehoeren in
tenant_memberships, nicht direkt auf Teilnehmer. - Buchungen sollten nicht hart geloescht werden.
- Korrekturen laufen ueber Storno- oder Reversal-Eintraege.
- 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, Loeschung, 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 fuer alle schreibenden Aktionen.
- Rate-Limits fuer Login, Registrierung und Passwort-Reset.
- Zentrale Funktionen oder Middleware:
requireLogin,requireTenant,requireRole,csrfToken.
Tenant-Aufloesung:
- Primaer 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.
Spaeter moeglich:
- LDAP/AD oder SSO pro Kunde als optionaler Identity Provider.
- MFA fuer Owner/Admins.
- Row-Level-Security in der Datenbank.
Design-Leitplanken
Das bestehende Design soll erhalten bleiben.
Beibehalten:
- Gruener Akzent
#38761d. - Weiss/Grau als ruhige Grundflaeche.
- Roboto Slab fuer Ueberschriften.
- Open Sans fuer Fliesstext.
- Sidebar fuer die geschuetzte App.
- Tabellen als primaere Darstellung fuer 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 ueber Tabellen.
- Sidebar-Gruppierung und aktive Navigation.
- FAQ strukturieren, aber Inhalte nicht stark veraendern.
Nicht tun:
- Kein komplett neues Farbsystem.
- Keine dekorative Marketingoptik in der App.
- Keine App-Tabellen in Kartenlandschaften aufloesen.
- Keine Navigationsstruktur, die die heutigen Kernablaeufe versteckt.
Meilensteinplan
Uebersicht:
| Meilenstein | Schwerpunkt | Hauptergebnis |
|---|---|---|
| M0 | Baseline | Bestand, Sicherheit und Designreferenz sind dokumentiert |
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prueft sichere Seiten |
| M2 | Technisches Fundament | Public-/App-Struktur, Bootstrap, Session und CSRF stehen |
| M3 | SaaS-Basis | Tenants, User, Registrierung, Login und Rollen funktionieren |
| M4 | Datenmigration | Legacy-Daten sind tenant-sicher im Zielmodell abgebildet |
| M5 | App-Kern | Dashboard, Striche, Einzahlungen, Mitglieder und Liste laufen |
| M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar |
| M7 | Landingpage | Werbliche Seite und Registrierung sind oeffentlich nutzbar |
| M8 | Haertung | Betrieb, Datenschutz, Monitoring und Isolation sind geprueft |
| M9 | Cutover | Produktivumstellung ist vorbereitet und Legacy ist read-only |
M0: Planungs- und Sicherheitsbaseline
Ziel: Den aktuellen Zustand verlaesslich dokumentieren, bevor umgebaut wird.
Arbeitsartefakte:
docs/m0/README.mddocs/m0/code-inventory.mddocs/m0/security-baseline.mddocs/m0/schema-export.sqldocs/m0/golden-master-queries.sqldocs/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-Zustaende 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.
Abhaengigkeiten:
- Zugriff auf echte oder anonymisierte Datenbank.
- Kenntnis der Produktivumgebung.
M1: Golden-Master-Validierung
Ziel: Sicherstellen, dass spaetere 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-Importfaelle sammeln: Treffer, Dubletten, unbekannte Namen. Erledigt im Golden-Master-Check.
- Testfaelle fuer Guthaben, Schulden, Nullsaldo und inaktive Teilnehmer anlegen. Erledigt.
- Sichere GET-Seiten per HTTP-Smoke pruefen. Erledigt mit
scripts/http-smoke.php. - PDF-/Export-Summen als Referenz sichern. Offen, weil die TCPDF-Kopie im Repo unvollstaendig ist.
Ergebnis:
- Golden-Master-Daten.
- Vergleichsqueries oder Vergleichsskript.
- HTTP-Smoke-Test fuer sichere UI-Seiten.
- Akzeptanzkriterien fuer Migration und neue App.
- Dokumentierte offene Punkte: PDF-Export/TCPDF, PHPMailer-Abhaengigkeit, GET-Nebenwirkungen bei Mailversand und Jahresauswertung.
Abhaengigkeiten:
- M0 abgeschlossen.
- PDF-/Mail-/Jahresprozesse werden in M6 gezielt neu gestaltet, statt sie in M1 per GET-Smoke auszufuehren.
M2: Technisches Fundament
Ziel: Eine saubere Basis fuer App, Public-Seiten, Auth und Mandanten schaffen.
Schritte:
- Projektstruktur festlegen: Public-Routes, App-Routes, Views/Templates, Services/Repositories.
- Zentrales Bootstrap fuer Config, DB-Verbindung, Session und Fehlerbehandlung.
- Layouts trennen: Public-Layout und App-Layout.
- Bestehende Assets weiterverwenden.
- CSRF- und Session-Basis einziehen.
- Konfigurationswerte aus Code in Umgebung oder Settings verschieben.
Ergebnis:
- Grundgeruest fuer neue SaaS-App.
- Kein fachlicher Rewrite, aber klare Struktur fuer die Migration.
Abhaengigkeiten:
- Entscheidung, ob PHP nativ weitergefuehrt oder ein Framework genutzt wird.
M3: Mandanten, Registrierung und Login
Ziel: Mehrkundenfaehigkeit und Kunden-Onboarding technisch aktivieren.
Schritte:
- Tabellen fuer
tenants,users,tenant_membershipsundtenant_settingsanlegen. - Registrierung: Tenant + Owner-User + Default-Settings erzeugen.
- Login, Logout, Passwort-Reset und E-Mail-Verifikation bauen.
- Tenant-Aufloesung definieren.
- Rollenpruefung zentralisieren.
- Erste Admin-/Owner-Seite fuer Grundeinstellungen.
Ergebnis:
- Ein neuer Kunde kann sich registrieren und in seine eigene App gelangen.
- Rollen und Tenant-Kontext sind serverseitig verfuegbar.
Abhaengigkeiten:
- M2 abgeschlossen.
M4: Migration des fachlichen Kerns
Ziel: Bestehende Kaffeelisten-Daten tenant-sicher uebernehmen.
Schritte:
- Initialen Tenant fuer den bisherigen Bestand anlegen.
kl_Mitarbeiternachparticipantsmigrieren.admin-Informationen intenant_membershipsoder Admin-Rollen ueberfuehren.kl_confignachtenant_settingsmigrieren.kl_Einzahlungenundkl_Kaffeeverbrauchnachledger_entriesmigrieren.- Legacy-IDs speichern.
- Salden gegen Golden-Master vergleichen.
Ergebnis:
- Bestehender Datenbestand ist im neuen Modell abgebildet.
- Saldenparitaet ist nachweisbar.
Abhaengigkeiten:
- M1 und M3 abgeschlossen.
M5: Geschuetzte App-Funktionen
Ziel: Die operativen Kernseiten im neuen Modell bereitstellen.
Schritte:
- Dashboard
Meine Kaffeelisteumsetzen. - Eigene Stricherfassung umsetzen.
- Zahlungs-/PayPal-Bereich umsetzen.
- Mitgliederverwaltung tenant- und rollenbasiert umsetzen.
- Gesamtuebersicht umsetzen.
- Letzte Eintraege 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.
Abhaengigkeiten:
- M4 abgeschlossen.
M6: Import, Export und Mail
Ziel: Admin- und Treasurer-Flows produktionsreif machen.
Schritte:
- CSV-Import mit Vorschau, Dublettenpruefung und Audit bauen.
- Uploads ausserhalb 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 fuer reale Betriebsablaeufe vollstaendig.
- Import/Export/Mail sind auditierbar.
Abhaengigkeiten:
- M5 fuer die Kernansichten.
M7: Oeffentliche Landingpage
Ziel: Werbliche Einstiegseite und Registrierung verfuegbar machen, ohne die App-Optik zu verwischen.
Schritte:
- Public-Layout mit bestehender Typografie und gruenem Akzent bauen.
- Landingpage-Inhalte erstellen.
- Demo-Screenshot oder Demo-Ansicht einbinden.
- CTA zu Registrierung und Login.
- FAQ-Auszug strukturieren.
- Keine App-Sidebar im Public-Bereich.
Ergebnis:
- Interessenten verstehen das Produkt und koennen sich registrieren.
- Bestehende App bleibt optisch eigenstaendig und arbeitsorientiert.
Abhaengigkeiten:
- M3 fuer Registrierung.
- M5 fuer echte App-Screens oder Demo.
M8: Haertung, Datenschutz und Betrieb
Ziel: Die SaaS-App fuer mehrere Kunden sicher betreiben.
Schritte:
- Backups und Restore-Prozess definieren.
- Monitoring und Fehlerlogging einrichten.
- Audit-Log fuer Admin-Aktionen pruefen.
- Datenexport pro Tenant.
- Loesch-/Anonymisierungsprozess fuer Teilnehmer und Kunden.
- Rate-Limits und Security Headers.
- Mandanten-Isolation testen.
- Rollenmatrix testen.
Ergebnis:
- SaaS ist betrieblich und datenschutzseitig belastbarer.
Abhaengigkeiten:
- M3 bis M6.
M9: Cutover und Legacy-Archiv
Ziel: Produktive Umstellung ohne Datenverlust.
Schritte:
- Legacy fuer Schreibzugriffe sperren.
- Finalen Delta-Export ziehen.
- Migration ausfuehren.
- Rowcounts, Salden, PDF-Stichproben und Importhistorie pruefen.
- 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 fuer Rueckfragen lesbar.
Abhaengigkeiten:
- M8 abgeschlossen.
Empfohlene MVP-Reihenfolge
Fuer den ersten produktnahen MVP sollten nicht alle Randmodule gleichzeitig umgebaut werden.
MVP-Paket:
- Tenant, User, Login, Registrierung.
- Teilnehmer, Settings, Ledger.
- Dashboard, Striche, Einzahlungen, Gesamtuebersicht.
- Mitgliederverwaltung.
- Hinweise.
- CSV-Import, Export.
- Landingpage.
Spaeter:
- Umfrage-Modul.
- SSO/LDAP pro Kunde.
- Billing/Tarife.
- MFA.
- Custom Domains.
- Erweiterte Mandanten-Branding-Optionen.
Validierung
Fachliche Pruefungen:
- 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.
Sicherheitspruefungen:
- 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 ausfuehrbar.
- Ausgabe von Namen, Mails und Hinweisen ist escaped.
UX-Pruefungen:
- Kernseiten bleiben optisch wiedererkennbar.
- Sidebar bleibt fuer App-Nutzer vertraut.
- Landingpage zeigt keine App-Adminnavigation.
- Mobile Darstellung bleibt bedienbar.
- Tabellen bleiben lesbar.
Risiken und Gegenmassnahmen
| Risiko | Gegenmassnahme |
|---|---|
| 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 fuer Buchungen |
| Login und Teilnehmer werden vermischt | users und participants strikt trennen |
| Bestehendes Design driftet weg | Designreferenz und App-Komponenten definieren |
| CSV-Upload unsicher | Upload ausserhalb Webroot, Dateityppruefung, Importvorschau |
| Mailversand nicht nachvollziehbar | Versandlog, Dry-Run, Job-Status |
| Unvollstaendige Vendor-Kopien fuer PDF/Mail | Dependencies sauber vendoren oder ersetzen, Smoke-Tests danach erweitern |
| AD/LDAP blockiert SaaS-Onboarding | E-Mail/Passwort als Basis, SSO spaeter optional |
| Kein DB-Schema im Repo | Schema exportieren und Migrationen einfuehren |
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
treasurerhaben: eigene Rolle oder Teil vonadmin? - 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?
Naechster konkreter Arbeitsschritt
Als naechstes sollte M0 gestartet werden:
- Produktiv-/Testdatenbankschema exportieren.
- Screenshot-Referenz der wichtigsten Seiten erstellen.
- Golden-Master-Auswertungen definieren.
- Entscheidung treffen, ob der neue SaaS-Kern im bestehenden PHP-Stil oder mit einem Framework aufgebaut wird.