# M3 SaaS-Basis Vorbereitung Stand: 2026-07-12 M3 fuehrt Mandanten, Benutzer, Mitgliedschaften und Grundeinstellungen additiv ein. Die bestehende Legacy-App bleibt dabei lauffaehig und wird noch nicht auf das neue Modell umgestellt. ## Ziel - Einen Default-Tenant fuer den aktuellen Bestand erzeugen. - Login-Benutzer und Kaffee-Teilnehmer fachlich trennen. - Rollen tenant-spezifisch vorbereiten. - Bestehende `kl_Mitarbeiter` ohne Datenverlust in `participants` spiegeln. - Die Grundlage fuer Registrierung und Login schaffen, ohne die Legacy-Auth sofort zu ersetzen. ## Nicht-Ziele fuer den ersten M3-Schritt - Kein kompletter Login-Umbau in einem Schritt. - Keine Migration von Einzahlungen und Kaffeeverbrauch nach `ledger_entries`; das gehoert zu M4. - Kein Design- oder Layout-Umbau. - Kein Billing und keine Tarife im ersten Schritt. - Kein produktiver Tenant-Wechsel in bestehenden Legacy-Seiten. ## Umgesetzter erster Schritt Umgesetzte Migrationen: ```text database/migrations/0002_saas_identity_tenants.sql database/migrations/0003_saas_auth_account_fields.sql database/migrations/0004_saas_auth_tokens.sql database/migrations/0005_saas_tenant_resolution.sql ``` Tabellen: - `tenants` - `tenant_settings` - `users` - `tenant_memberships` - `participants` - `tenant_domains` Ergaenzende Skripte: ```text scripts/backfill-default-tenant.php scripts/check-m3-saas-basis.php scripts/check-m3-auth-flow.php scripts/check-m3-settings-flow.php scripts/check-m3-password-email-flow.php scripts/check-m3-tenant-resolution-flow.php ``` Wichtige Regeln: - `users` sind Login-Konten. - `participants` sind Kaffeelisten-Teilnehmer. - Ein `participant` kann optional auf einen `user` zeigen. - Rollen liegen in `tenant_memberships`, nicht in `participants`. - `participants.legacy_mitarbeiter_id` speichert die alte ID aus `kl_Mitarbeiter`. - Geldwerte in neuen Settings als Cent-Integer speichern. - Die Migration ist rein additiv; bestehende Legacy-Seiten fragen weiter die bisherigen `kl_*`-Tabellen ab. ## Default-Tenant Fuer den Bestand wird ein erster Tenant angelegt: ```text slug: default name: Kaffeeliste Bestand status: active timezone: Europe/Berlin locale: de-DE currency_code: EUR ``` Die Werte koennen spaeter in einer Tenant-Einstellungsseite angepasst werden. ## Backfill Ein separates Skript spiegelt die bestehenden Mitarbeiter: ```text scripts/backfill-default-tenant.php ``` Vorgang: 1. Default-Tenant anlegen oder laden. 2. `tenant_settings` aus `kl_config` erzeugen. 3. Fuer jede Zeile aus `kl_Mitarbeiter` einen `participant` anlegen oder aktualisieren. 4. Fuer aktive Admins optional einen `user` und eine `tenant_membership` erzeugen. 5. `admin = 1` als Rolle `admin` abbilden; ein konfigurierter erster Benutzer kann Rolle `owner` erhalten. 6. Verwaiste `participants` mit nicht mehr vorhandener `legacy_mitarbeiter_id` aus dem Default-Tenant entfernen. Der Backfill muss idempotent sein und darf keine bestehenden Legacy-Daten loeschen. Geloescht werden nur SaaS-Spiegelungen, deren Legacy-Mitarbeiter in `kl_Mitarbeiter` nicht mehr existiert. Ausgefuehrter Dev-Stand: - Default-Tenant `default` wurde angelegt. - 9 Legacy-Mitarbeiter wurden als `participants` gespiegelt. - 2 aktive Admin-/Owner-Logins wurden als `users` und `tenant_memberships` angelegt. - Ein zweiter Backfill-Lauf blieb idempotent. ## Registrierung und Login Umgesetzte Dateien: ```text app/database.php app/saas-auth.php login.php register.php konto.php logout.php mandant-auswahl.php passwort-vergessen.php passwort-zuruecksetzen.php email-verifikation-senden.php email-verifizieren.php ``` Umgesetzter Umfang: - Registrierung legt Tenant, Owner-User, Default-Settings, Membership und Owner-Participant in einer Transaktion an. - Login prueft `users.password_hash`, aktive Membership und aktiven Tenant. - Hat ein User genau einen aktiven Mandanten, wird dieser direkt in die Session gelegt. - Hat ein User mehrere aktive Mandanten, fuehrt der Login zur Mandantenauswahl. - Logout beendet die neue PHP-Login-Session. - `konto.php` zeigt den aktuellen SaaS-Kontext fuer den angemeldeten User. - `functions.php` akzeptiert eine SaaS-Login-Session als erste Identitaetsquelle, laesst `DEV_AUTH_EMAIL` und `AUTH_USER` aber als Legacy-Fallback bestehen. - `footer.php` ist fuer nicht angemeldete Public-Seiten sicher und zeigt Links zu Login und Registrierung. - Passwort-Reset erzeugt Single-Use-Tokens und speichert nur Token-Hashes. - E-Mail-Verifikation erzeugt Single-Use-Tokens und setzt `users.email_verified_at`. - Bis zum produktiven Mailversand werden Links nur im Dev-Modus angezeigt beziehungsweise in CLI-Checks direkt verwendet. Noch offen im M3-Auth-Scope: - Weitergehende Rollenmatrix fuer spaetere SaaS-Seiten. - Produktiver Mailversand fuer Reset- und Verifikationslinks. ## Tenant-Aufloesung Entscheidung fuer den Webspace-Betrieb: - Keine Wildcard-Subdomains im ersten Schritt. - Primaere App-Adresse ist eine zentrale App-Domain wie `app.kaffeeliste.de`. - Der aktive Mandant wird nach Login ueber `tenant_memberships` und die PHP- Session gesetzt. - Bei genau einem Mandanten wird automatisch weitergeleitet. - Bei mehreren Mandanten nutzt der User `mandant-auswahl.php`. - `tenant_domains` ist vorbereitet fuer spaeter gezielt eingerichtete feste Domains oder Subdomains, aber nicht Voraussetzung fuer den Start. Noch offen: - `APP_PRIMARY_HOST` in der Zielumgebung setzen. - Produktive Domain-/Zertifikatspruefung fuer einzelne feste Domains definieren. ## Rollen und Grundeinstellungen Umgesetzte Dateien: ```text mandant-einstellungen.php ``` Umgesetzter Umfang: - `saas_user_has_role()`, `saas_require_role()` und `saas_can_manage_tenant_settings()` zentralisieren die erste Rollenpruefung. - Owner und Admin duerfen Tenant-Grundeinstellungen bearbeiten. - Die Seite bearbeitet Kundenname, Zeitzone, Locale, Waehrung, Preis pro Strich, Web-Striche, PayPal-Link, Listenfenster und Schulden-Warnschwelle. - Geldwerte werden in der UI als Euro-Werte erfasst und weiterhin als Cent- Integer gespeichert. - `konto.php` und die Sidebar verlinken die Einstellungen nur fuer passende Rollen. ## Erste Akzeptanzkriterien - Migrationen laufen mehrfach ohne Fehler: erfuellt. - Default-Tenant existiert genau einmal: erfuellt. - `tenant_settings` enthalten Preis pro Strich und bestehende PayPal-Optionen: erfuellt. - Jeder bestehende `kl_Mitarbeiter` hat genau einen `participant`: erfuellt. - `participants.legacy_mitarbeiter_id` ist gesetzt: erfuellt. - Admins werden als tenant-scoped Rolle abgebildet: erfuellt. - Registrierung und Login funktionieren fuer einen neuen Test-Tenant: erfuellt. - Owner-/Admin-Grundeinstellungen koennen aktualisiert werden: erfuellt. - Passwort-Reset und E-Mail-Verifikation funktionieren mit Single-Use-Tokens: erfuellt. - Mandantenauswahl funktioniert fuer User mit mehreren Mandanten: erfuellt. - Golden-Master und HTTP-Smoke bleiben gruen: erfuellt. ## Risiken | Risiko | Gegenmassnahme | | --- | --- | | Login-User und Kaffee-Teilnehmer werden vermischt | `users` und `participants` strikt getrennt halten | | Mehrfacher Backfill erzeugt Duplikate | Eindeutige Constraints und Upsert-Logik | | Legacy-App bricht durch neue Tabellen | M3 nur additiv, keine Legacy-Queries umstellen | | Rollen werden global statt tenant-scoped | Rollen ausschliesslich in `tenant_memberships` speichern | | Falsche Owner-Zuordnung | Owner per Env-Konfiguration oder manuell dokumentierter Entscheidung setzen | ## Empfohlene Reihenfolge 1. Migration `0002_saas_identity_tenants.sql` erstellen: erledigt. 2. Migration `0003_saas_auth_account_fields.sql` erstellen: erledigt. 3. `scripts/backfill-default-tenant.php` erstellen: erledigt. 4. Backfill gegen die Dev-Datenbank ausfuehren: erledigt. 5. Kontrollskript fuer Tenant/Participant/Role-Counts schreiben: erledigt. 6. Login-/Registrierungsrouten bauen: erledigt. 7. Auth-Flow-Kontrollskript schreiben: erledigt. 8. Zentrale Rollenpruefung und Mandant-Einstellungen bauen: erledigt. 9. Settings-Flow-Kontrollskript schreiben: erledigt. 10. Migration `0004_saas_auth_tokens.sql` erstellen: erledigt. 11. Passwort-Reset und E-Mail-Verifikation bauen: erledigt. 12. Password-/E-Mail-Flow-Kontrollskript schreiben: erledigt. 13. Migration `0005_saas_tenant_resolution.sql` erstellen: erledigt. 14. Mandantenauswahl und zentrale Session-Aufloesung bauen: erledigt. 15. Tenant-Resolution-Kontrollskript schreiben: erledigt. 16. Golden-Master und HTTP-Smoke ausfuehren: erledigt. 17. Produktiven Mailversand oder Landingpage vorbereiten: naechster Schritt.