Deutsche Umlaute in UI und Dokumentation korrigieren

This commit is contained in:
2026-07-14 22:11:33 +02:00
parent f9544f24fd
commit 539117b409
31 changed files with 561 additions and 564 deletions
+11 -11
View File
@@ -1,19 +1,19 @@
# Legacy App Archiv # Legacy App Archiv
Dieses Verzeichnis enthaelt den bisherigen PHP-Bestand der Kaffeeliste. Dieses Verzeichnis enthält den bisherigen PHP-Bestand der Kaffeeliste.
Es bleibt aus drei Gruenden im Repository: Es bleibt aus drei Gründen im Repository:
- als Referenz fuer die bestehende Fachlogik - als Referenz für die bestehende Fachlogik
- als Quelle fuer Datenmigrationen in die SaaS-Version - als Quelle für Datenmigrationen in die SaaS-Version
- als Rueckfalloption waehrend der Uebergangsphase - als Rückfalloption während der Übergangsphase
## Wichtige Legacy-Bereiche ## Wichtige Legacy-Bereiche
- `index.php`: persoenliches Dashboard - `index.php`: persönliches Dashboard
- `stricheintragen.php`: Sammelerfassung fuer Kaffee-Striche - `stricheintragen.php`: Sammelerfassung für Kaffee-Striche
- `einzahlung.php`: Sammelerfassung fuer Einzahlungen - `einzahlung.php`: Sammelerfassung für Einzahlungen
- `kaffeeliste.php`: operative Gesamtuebersicht - `kaffeeliste.php`: operative Gesamtübersicht
- `mitarbeiterverwalten.php`: Mitglieder- und Rollenpflege - `mitarbeiterverwalten.php`: Mitglieder- und Rollenpflege
- `letzteneintraege.php`: Korrektur letzter Buchungen - `letzteneintraege.php`: Korrektur letzter Buchungen
- `hinweise.php`: Banner/Hinweise - `hinweise.php`: Banner/Hinweise
@@ -21,5 +21,5 @@ Es bleibt aus drei Gruenden im Repository:
## Umgang Mit Dem Archiv ## Umgang Mit Dem Archiv
- Keine neuen Produktfunktionen mehr hier entwickeln. - Keine neuen Produktfunktionen mehr hier entwickeln.
- Nur noch fuer Referenz, Datenabgleich oder Notfallbetrieb verwenden. - Nur noch für Referenz, Datenabgleich oder Notfallbetrieb verwenden.
- Neue Arbeit findet ausschliesslich in `../saas-app/` und `../docs/` statt. - Neue Arbeit findet ausschließlich in `../saas-app/` und `../docs/` statt.
+13 -13
View File
@@ -323,13 +323,13 @@ function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): arr
$errors[] = 'Der Kundenname muss zwischen 3 und 255 Zeichen lang sein.'; $errors[] = 'Der Kundenname muss zwischen 3 und 255 Zeichen lang sein.';
} }
if (!in_array($timezone, DateTimeZone::listIdentifiers(), true)) { if (!in_array($timezone, DateTimeZone::listIdentifiers(), true)) {
$errors[] = 'Die Zeitzone ist ungueltig.'; $errors[] = 'Die Zeitzone ist ungültig.';
} }
if (!preg_match('/^[a-z]{2}-[A-Z]{2}$/', $locale)) { if (!preg_match('/^[a-z]{2}-[A-Z]{2}$/', $locale)) {
$errors[] = 'Die Locale muss dem Muster de-DE entsprechen.'; $errors[] = 'Die Locale muss dem Muster de-DE entsprechen.';
} }
if (!preg_match('/^[A-Z]{3}$/', $currencyCode)) { if (!preg_match('/^[A-Z]{3}$/', $currencyCode)) {
$errors[] = 'Der Waehrungscode muss aus drei Grossbuchstaben bestehen.'; $errors[] = 'Der Währungscode muss aus drei Großbuchstaben bestehen.';
} }
if ($markPriceCents === null || $markPriceCents < 1 || $markPriceCents > 10000) { if ($markPriceCents === null || $markPriceCents < 1 || $markPriceCents > 10000) {
$errors[] = 'Der Preis pro Strich muss zwischen 0,01 und 100,00 liegen.'; $errors[] = 'Der Preis pro Strich muss zwischen 0,01 und 100,00 liegen.';
@@ -615,7 +615,7 @@ function saas_request_password_reset(PDO $pdo, string $email, string $tenantSlug
if (!filter_var($emailNorm, FILTER_VALIDATE_EMAIL)) { if (!filter_var($emailNorm, FILTER_VALIDATE_EMAIL)) {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['Bitte eine gueltige E-Mail-Adresse eingeben.'], 'errors' => ['Bitte eine gültige E-Mail-Adresse eingeben.'],
]; ];
} }
@@ -658,7 +658,7 @@ function saas_reset_password_with_token(PDO $pdo, string $token, string $passwor
$tokenRow = saas_fetch_valid_auth_token($pdo, $token, 'password_reset'); $tokenRow = saas_fetch_valid_auth_token($pdo, $token, 'password_reset');
if ($tokenRow === null) { if ($tokenRow === null) {
$errors[] = 'Der Link ist ungueltig oder abgelaufen.'; $errors[] = 'Der Link ist ungültig oder abgelaufen.';
} }
if ($errors !== []) { if ($errors !== []) {
@@ -730,7 +730,7 @@ function saas_verify_email_token(PDO $pdo, string $token): array
if ($tokenRow === null) { if ($tokenRow === null) {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['Der Link ist ungueltig oder abgelaufen.'], 'errors' => ['Der Link ist ungültig oder abgelaufen.'],
]; ];
} }
@@ -748,7 +748,7 @@ function saas_verify_email_token(PDO $pdo, string $token): array
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['Die E-Mail-Adresse konnte nicht bestaetigt werden.'], 'errors' => ['Die E-Mail-Adresse konnte nicht bestätigt werden.'],
]; ];
} }
@@ -765,7 +765,7 @@ function saas_authenticate(PDO $pdo, string $email, string $password, string $te
if (!filter_var($emailNorm, FILTER_VALIDATE_EMAIL) || $password === '') { if (!filter_var($emailNorm, FILTER_VALIDATE_EMAIL) || $password === '') {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['E-Mail oder Passwort ist ungueltig.'], 'errors' => ['E-Mail oder Passwort ist ungültig.'],
]; ];
} }
@@ -785,7 +785,7 @@ function saas_authenticate(PDO $pdo, string $email, string $password, string $te
) { ) {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['E-Mail oder Passwort ist ungueltig.'], 'errors' => ['E-Mail oder Passwort ist ungültig.'],
]; ];
} }
@@ -817,7 +817,7 @@ function saas_authenticate(PDO $pdo, string $email, string $password, string $te
if ($tenantIds === []) { if ($tenantIds === []) {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['Fuer dieses Konto ist kein aktiver Mandant verfuegbar.'], 'errors' => ['Für dieses Konto ist kein aktiver Mandant verfügbar.'],
]; ];
} }
@@ -835,7 +835,7 @@ function saas_authenticate(PDO $pdo, string $email, string $password, string $te
if ($identity === null) { if ($identity === null) {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['Fuer dieses Konto ist kein aktiver Mandant verfuegbar.'], 'errors' => ['Für dieses Konto ist kein aktiver Mandant verfügbar.'],
]; ];
} }
@@ -867,13 +867,13 @@ function saas_register_tenant_owner(PDO $pdo, array $input): array
$errors[] = 'Der Kundenname muss zwischen 3 und 255 Zeichen lang sein.'; $errors[] = 'Der Kundenname muss zwischen 3 und 255 Zeichen lang sein.';
} }
if (!preg_match('/^[a-z0-9][a-z0-9-]{1,98}[a-z0-9]$/', $tenantSlug)) { if (!preg_match('/^[a-z0-9][a-z0-9-]{1,98}[a-z0-9]$/', $tenantSlug)) {
$errors[] = 'Das Kundenkuerzel darf nur Kleinbuchstaben, Zahlen und Bindestriche enthalten.'; $errors[] = 'Das Kundenkürzel darf nur Kleinbuchstaben, Zahlen und Bindestriche enthalten.';
} }
if (strlen($displayName) < 2 || strlen($displayName) > 255) { if (strlen($displayName) < 2 || strlen($displayName) > 255) {
$errors[] = 'Der Name muss zwischen 2 und 255 Zeichen lang sein.'; $errors[] = 'Der Name muss zwischen 2 und 255 Zeichen lang sein.';
} }
if (!filter_var($emailNorm, FILTER_VALIDATE_EMAIL)) { if (!filter_var($emailNorm, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Bitte eine gueltige E-Mail-Adresse eingeben.'; $errors[] = 'Bitte eine gültige E-Mail-Adresse eingeben.';
} }
if (strlen($password) < 8) { if (strlen($password) < 8) {
$errors[] = 'Das Passwort muss mindestens 8 Zeichen lang sein.'; $errors[] = 'Das Passwort muss mindestens 8 Zeichen lang sein.';
@@ -910,7 +910,7 @@ function saas_register_tenant_owner(PDO $pdo, array $input): array
if ((int)$stmt->fetchColumn() > 0) { if ((int)$stmt->fetchColumn() > 0) {
return [ return [
'ok' => false, 'ok' => false,
'errors' => ['Dieses Kundenkuerzel ist bereits vergeben.'], 'errors' => ['Dieses Kundenkürzel ist bereits vergeben.'],
]; ];
} }
+6 -6
View File
@@ -117,8 +117,8 @@ function saas_send_password_reset_mail(string $to, string $token): array
$body = implode("\n", [ $body = implode("\n", [
'Hallo,', 'Hallo,',
'', '',
'fuer dein Kaffeeliste-Konto wurde ein Passwort-Reset angefordert.', 'für dein Kaffeeliste-Konto wurde ein Passwort-Reset angefordert.',
'Oeffne diesen Link, um ein neues Passwort zu setzen:', 'Öffne diesen Link, um ein neues Passwort zu setzen:',
'', '',
$link, $link,
'', '',
@@ -128,7 +128,7 @@ function saas_send_password_reset_mail(string $to, string $token): array
'Deine Kaffeeliste', 'Deine Kaffeeliste',
]); ]);
return saas_send_mail($to, 'Kaffeeliste Passwort zuruecksetzen', $body); return saas_send_mail($to, 'Kaffeeliste Passwort zurücksetzen', $body);
} }
function saas_send_email_verification_mail(string $to, string $token): array function saas_send_email_verification_mail(string $to, string $token): array
@@ -137,8 +137,8 @@ function saas_send_email_verification_mail(string $to, string $token): array
$body = implode("\n", [ $body = implode("\n", [
'Hallo,', 'Hallo,',
'', '',
'bitte bestaetige deine E-Mail-Adresse fuer Kaffeeliste.', 'bitte bestätige deine E-Mail-Adresse für Kaffeeliste.',
'Oeffne dazu diesen Link:', 'Öffne dazu diesen Link:',
'', '',
$link, $link,
'', '',
@@ -147,5 +147,5 @@ function saas_send_email_verification_mail(string $to, string $token): array
'Deine Kaffeeliste', 'Deine Kaffeeliste',
]); ]);
return saas_send_mail($to, 'Kaffeeliste E-Mail bestaetigen', $body); return saas_send_mail($to, 'Kaffeeliste E-Mail bestätigen', $body);
} }
+2 -2
View File
@@ -33,7 +33,7 @@ function csv_prepare_upload(array $file): array
} }
if (($file['size'] ?? 0) <= 0 || $file['size'] > 5 * 1024 * 1024) { if (($file['size'] ?? 0) <= 0 || $file['size'] > 5 * 1024 * 1024) {
return [null, 'Die CSV-Datei ist leer oder groesser als 5 MB.']; return [null, 'Die CSV-Datei ist leer oder größer als 5 MB.'];
} }
$originalName = (string)($file['name'] ?? ''); $originalName = (string)($file['name'] ?? '');
@@ -248,7 +248,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
}elseif($eintrag[3] == 3){ }elseif($eintrag[3] == 3){
echo "Benutzer nicht gefunden"; echo "Benutzer nicht gefunden";
}elseif($eintrag[3] == 4){ }elseif($eintrag[3] == 4){
echo "Ungueltige CSV-Zeile"; echo "Ungültige CSV-Zeile";
} }
echo "</td> echo "</td>
+12 -12
View File
@@ -1,17 +1,17 @@
# Lokaler MySQL-Dev-Start # Lokaler MySQL-Dev-Start
Die Legacy-App kann fuer Entwicklung gegen MySQL laufen. Dafuer gibt es eine Die Legacy-App kann für Entwicklung gegen MySQL laufen. Dafür gibt es eine
Kompatibilitaetsschicht, die die verwendeten `sqlsrv_*`-Funktionen im Dev-Modus Kompatibilitätsschicht, die die verwendeten `sqlsrv_*`-Funktionen im Dev-Modus
auf PDO/MySQL abbildet. auf PDO/MySQL abbildet.
## Voraussetzungen ## Voraussetzungen
- PHP CLI mit `pdo_mysql`. - PHP CLI mit `pdo_mysql`.
- MySQL-Testdatenbank. - MySQL-Testdatenbank.
- Keine Produktivdaten und keine Produktivpasswoerter im Repository. - Keine Produktivdaten und keine Produktivpasswörter im Repository.
In dieser VS-Code-Server-Umgebung wurde PHP lokal unter `.local/` entpackt, In dieser VS-Code-Server-Umgebung wurde PHP lokal unter `.local/` entpackt,
weil `sudo` nicht passwordless verfuegbar war. `.local/` wird von Git ignoriert. weil `sudo` nicht passwordless verfügbar war. `.local/` wird von Git ignoriert.
## Umgebungsvariablen ## Umgebungsvariablen
@@ -26,7 +26,7 @@ export DEV_AUTH_EMAIL="admin@test.local"
export DEV_AUTH_NAME="Test Admin" export DEV_AUTH_NAME="Test Admin"
# Optional: Standard ist var/sessions im Repository. # Optional: Standard ist var/sessions im Repository.
export APP_SESSION_PATH="/path/to/writable/sessions" export APP_SESSION_PATH="/path/to/writable/sessions"
# Optional fuer Passwort-Reset und E-Mail-Verifikation. # Optional für Passwort-Reset und E-Mail-Verifikation.
export APP_BASE_URL="http://127.0.0.1:8080" export APP_BASE_URL="http://127.0.0.1:8080"
export APP_MAIL_TRANSPORT="log" export APP_MAIL_TRANSPORT="log"
export APP_MAIL_LOG_DIR="/path/to/writable/mail-log" export APP_MAIL_LOG_DIR="/path/to/writable/mail-log"
@@ -64,18 +64,18 @@ HOST=127.0.0.1 PORT=8080 scripts/run-dev-server.sh
``` ```
Danach ist die App unter `http://127.0.0.1:8080/` erreichbar. In VS Code kann Danach ist die App unter `http://127.0.0.1:8080/` erreichbar. In VS Code kann
der Port ueber die Ports-Ansicht weitergeleitet werden. der Port über die Ports-Ansicht weitergeleitet werden.
## Hinweise ## Hinweise
- Der Dev-Login umgeht AD/LDAP und nutzt `DEV_AUTH_EMAIL`. - Der Dev-Login umgeht AD/LDAP und nutzt `DEV_AUTH_EMAIL`.
- Der Dev-Mailversand nutzt standardmaessig `APP_MAIL_TRANSPORT=log` und legt - Der Dev-Mailversand nutzt standardmäßig `APP_MAIL_TRANSPORT=log` und legt
Nachrichten unter `var/mail` ab. Fuer Produktion kann zunaechst Nachrichten unter `var/mail` ab. Für Produktion kann zunächst
`APP_MAIL_TRANSPORT=mail` mit passendem `APP_BASE_URL` und Absender gesetzt `APP_MAIL_TRANSPORT=mail` mit passendem `APP_BASE_URL` und Absender gesetzt
werden; eine SMTP-/Provider-Anbindung bleibt ein spaeterer Betriebsausbau. werden; eine SMTP-/Provider-Anbindung bleibt ein späterer Betriebsausbau.
- Die Tabellen werden ueber `database/migrations/` angelegt. - Die Tabellen werden über `database/migrations/` angelegt.
- `database/mysql-dev-schema.sql` bleibt als historische Dev-Schema-Baseline - `database/mysql-dev-schema.sql` bleibt als historische Dev-Schema-Baseline
erhalten; der aktive Weg ist `scripts/migrate.php`. erhalten; der aktive Weg ist `scripts/migrate.php`.
- Session-Dateien liegen standardmaessig unter `var/sessions`; `var/` wird von - Session-Dateien liegen standardmäßig unter `var/sessions`; `var/` wird von
Git ignoriert. Git ignoriert.
- Der Dev-Modus ist nur fuer lokale Tests gedacht, nicht fuer Produktion. - Der Dev-Modus ist nur für lokale Tests gedacht, nicht für Produktion.
+18 -18
View File
@@ -3,7 +3,7 @@
Stand: 2026-07-11 Stand: 2026-07-11
M0 dokumentiert den aktuellen Legacy-Bestand, bevor die SaaS-Umstrukturierung M0 dokumentiert den aktuellen Legacy-Bestand, bevor die SaaS-Umstrukturierung
beginnt. Ziel ist eine belastbare Ausgangsbasis fuer Migration, Sicherheit, beginnt. Ziel ist eine belastbare Ausgangsbasis für Migration, Sicherheit,
Design-Erhalt und fachliche Vergleichstests. Design-Erhalt und fachliche Vergleichstests.
## Status ## Status
@@ -13,24 +13,24 @@ Design-Erhalt und fachliche Vergleichstests.
| Code-Inventar | begonnen | Siehe `code-inventory.md` | | Code-Inventar | begonnen | Siehe `code-inventory.md` |
| Prozesslandkarte | begonnen | In `code-inventory.md` enthalten | | Prozesslandkarte | begonnen | In `code-inventory.md` enthalten |
| Sicherheitsbaseline | begonnen | Siehe `security-baseline.md` | | Sicherheitsbaseline | begonnen | Siehe `security-baseline.md` |
| DB-Schema-Export | nicht aus Alt-DB verfuegbar | Siehe `legacy-db-status.md` | | DB-Schema-Export | nicht aus Alt-DB verfügbar | Siehe `legacy-db-status.md` |
| Golden-Master-Auswertungen | nicht aus Alt-DB verfuegbar | Siehe `legacy-db-status.md` | | Golden-Master-Auswertungen | nicht aus Alt-DB verfügbar | Siehe `legacy-db-status.md` |
| Design-/Screenshot-Referenz | teilweise geliefert | Siehe `screenshot-checklist.md` | | Design-/Screenshot-Referenz | teilweise geliefert | Siehe `screenshot-checklist.md` |
## Lokal abgeschlossen ## Lokal abgeschlossen
- Legacy-Einstiegspunkte und Kernseiten identifiziert. - Legacy-Einstiegspunkte und Kernseiten identifiziert.
- Fachliche Tabellen aus SQL-Strings abgeleitet. - Fachliche Tabellen aus SQL-Strings abgeleitet.
- Schreibende Aktionen und kritische Datenfluesse markiert. - Schreibende Aktionen und kritische Datenflüsse markiert.
- Sicherheitsrisiken fuer M0 inventarisiert. - Sicherheitsrisiken für M0 inventarisiert.
- SQL-Vorlagen fuer Schema- und Golden-Master-Export angelegt. - SQL-Vorlagen für Schema- und Golden-Master-Export angelegt.
- Screenshot-Checkliste fuer Design-Erhalt angelegt. - Screenshot-Checkliste für Design-Erhalt angelegt.
- Erste Screenshot-Referenzen unter `docs/m0/screenshots/` abgelegt. - Erste Screenshot-Referenzen unter `docs/m0/screenshots/` abgelegt.
- Festgelegt: Ein alter MS-SQL-Datenbankstand steht aktuell nicht zur - Festgelegt: Ein alter MS-SQL-Datenbankstand steht aktuell nicht zur
Verfuegung; M0 arbeitet deshalb mit Code, Screenshots und rekonstruierter Verfügung; M0 arbeitet deshalb mit Code, Screenshots und rekonstruierter
MySQL-Dev-Datenbank als Ausgangsbasis. MySQL-Dev-Datenbank als Ausgangsbasis.
## Lokale Einschraenkungen ## Lokale Einschränkungen
- `php` ist in dieser Umgebung nicht im PATH, daher kein lokaler Syntaxcheck und - `php` ist in dieser Umgebung nicht im PATH, daher kein lokaler Syntaxcheck und
kein lokaler App-Start. kein lokaler App-Start.
@@ -40,33 +40,33 @@ Design-Erhalt und fachliche Vergleichstests.
## Blockiert bis Input vorliegt ## Blockiert bis Input vorliegt
Diese Punkte kann ich ohne externe Informationen nicht abschliessen: Diese Punkte kann ich ohne externe Informationen nicht abschließen:
1. Falls spaeter doch noch ein MS-SQL-Dump auftaucht: Schema und 1. Falls später doch noch ein MS-SQL-Dump auftaucht: Schema und
Golden-Master-Werte nachtraeglich exportieren. Golden-Master-Werte nachträglich exportieren.
2. MySQL-Dev-Schema als neue rekonstruierte Baseline weiter verifizieren. 2. MySQL-Dev-Schema als neue rekonstruierte Baseline weiter verifizieren.
3. Fehlende Screenshot-Zustaende nachreichen oder bewusst als optional markieren. 3. Fehlende Screenshot-Zustände nachreichen oder bewusst als optional markieren.
4. Produktivumgebung dokumentieren: PHP-Version, Webserver, SQL-Server-Version, 4. Produktivumgebung dokumentieren: PHP-Version, Webserver, SQL-Server-Version,
Auth-Setup, Cron/Job-Ausfuehrung. Auth-Setup, Cron/Job-Ausführung.
## Was ich von dir brauche ## Was ich von dir brauche
Falls ein alter MS-SQL-Stand spaeter noch auftaucht, bitte eines der folgenden Falls ein alter MS-SQL-Stand später noch auftaucht, bitte eines der folgenden
Pakete bereitstellen: Pakete bereitstellen:
- Idealerweise: einen anonymisierten SQL-Server-Schemaexport plus Rowcounts. - Idealerweise: einen anonymisierten SQL-Server-Schemaexport plus Rowcounts.
- Alternativ: Zugriffsdaten zu einer Test-/Staging-DB, nicht zu Produktion. - Alternativ: Zugriffsdaten zu einer Test-/Staging-DB, nicht zu Produktion.
- Alternativ: die Ergebnisse aus `schema-export.sql` als CSV/Excel/Markdown. - Alternativ: die Ergebnisse aus `schema-export.sql` als CSV/Excel/Markdown.
Zusaetzlich hilfreich: Zusätzlich hilfreich:
- Ein erreichbarer Test-Link zur bestehenden App oder Screenshots der Seiten aus - Ein erreichbarer Test-Link zur bestehenden App oder Screenshots der Seiten aus
`screenshot-checklist.md`. `screenshot-checklist.md`.
- Beispiel-Accounts oder Rollen fuer Admin und normales Mitglied, nur fuer eine - Beispiel-Accounts oder Rollen für Admin und normales Mitglied, nur für eine
Testumgebung. Testumgebung.
- Info, ob hart codierte Zugangsdaten aus Legacy-Skripten bereits rotiert wurden. - Info, ob hart codierte Zugangsdaten aus Legacy-Skripten bereits rotiert wurden.
- Entscheidung, ob M1/M2 nativ in PHP oder mit Framework vorbereitet werden - Entscheidung, ob M1/M2 nativ in PHP oder mit Framework vorbereitet werden
sollen. sollen.
Bitte keine Produktivpasswoerter oder echten personenbezogenen Exportdaten in Bitte keine Produktivpasswörter oder echten personenbezogenen Exportdaten in
das Repository legen. das Repository legen.
+36 -36
View File
@@ -10,28 +10,28 @@ Das echte Datenbankschema muss noch mit `schema-export.sql` verifiziert werden.
| Datei | Rolle | Wichtige Beobachtung | | Datei | Rolle | Wichtige Beobachtung |
| --- | --- | --- | | --- | --- | --- |
| `config.php` | AD-/DB-Konfiguration | Erstellt globale SQL-Server-Verbindung `$conn` | | `config.php` | AD-/DB-Konfiguration | Erstellt globale SQL-Server-Verbindung `$conn` |
| `functions.php` | Auth-/User-Helper | Liest `$_SERVER['AUTH_USER']`, inkludiert LDAP, prueft `kl_Mitarbeiter` | | `functions.php` | Auth-/User-Helper | Liest `$_SERVER['AUTH_USER']`, inkludiert LDAP, prüft `kl_Mitarbeiter` |
| `functionsLDAP.php` | LDAP-Mailauflösung | Ermittelt `$mailadress` aus AD | | `functionsLDAP.php` | LDAP-Mailauflösung | Ermittelt `$mailadress` aus AD |
| `header.php` | HTML-Head und Hinweisbanner | Laedt `assets/css/main.css`, liest `kl_hinweise` | | `header.php` | HTML-Head und Hinweisbanner | Lädt `assets/css/main.css`, liest `kl_hinweise` |
| `footer.php` | App-Sidebar und JS | Enthaelt die sichtbare Navigation und Admin-Menues | | `footer.php` | App-Sidebar und JS | Enthält die sichtbare Navigation und Admin-Menüs |
| `nav.php` | Navigation | Aktuell praktisch leer | | `nav.php` | Navigation | Aktuell praktisch leer |
| `headerline.php` | Headerfragment | Enthaelt NUL-Zeichen, fachlich unklar | | `headerline.php` | Headerfragment | Enthält NUL-Zeichen, fachlich unklar |
| `assets/css/main.css` | Hauptdesign | HTML5-UP Editorial, gruener Akzent, Tabellen, Buttons, Sidebar | | `assets/css/main.css` | Hauptdesign | HTML5-UP Editorial, grüner Akzent, Tabellen, Buttons, Sidebar |
| `DataTables/` | Tabellen-Assets | Wird teilweise eingebunden, aber nicht konsequent initialisiert | | `DataTables/` | Tabellen-Assets | Wird teilweise eingebunden, aber nicht konsequent initialisiert |
| `PHPMailer/` | Mailversand | Fuer Rundmails/Jahresauswertung genutzt | | `PHPMailer/` | Mailversand | Für Rundmails/Jahresauswertung genutzt |
| `TCPDF/` | PDF-Export | Fuer Kaffeelisten-Export genutzt | | `TCPDF/` | PDF-Export | Für Kaffeelisten-Export genutzt |
## Fachliche Kernseiten ## Fachliche Kernseiten
| Datei | Prozess | Tabellen | | Datei | Prozess | Tabellen |
| --- | --- | --- | | --- | --- | --- |
| `index.php` | Persoenliches Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal-Links | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_config` | | `index.php` | Persönliches Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal-Links | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_config` |
| `kaffeeliste.php` | Admin-Gesamtuebersicht mit Salden | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` | | `kaffeeliste.php` | Admin-Gesamtübersicht mit Salden | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
| `stricheintragen.php` | Sammelerfassung von Strichen | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_config` | | `stricheintragen.php` | Sammelerfassung von Strichen | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_config` |
| `einzahlung.php` | Sammelerfassung von Einzahlungen | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` | | `einzahlung.php` | Sammelerfassung von Einzahlungen | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
| `mitarbeiterverwalten.php` | Mitglieder anlegen, bearbeiten, aktivieren, deaktivieren | `kl_Mitarbeiter` | | `mitarbeiterverwalten.php` | Mitglieder anlegen, bearbeiten, aktivieren, deaktivieren | `kl_Mitarbeiter` |
| `namenanpassen.php` | Anzeigenamen aendern | `kl_Mitarbeiter` | | `namenanpassen.php` | Anzeigenamen ändern | `kl_Mitarbeiter` |
| `letzteneintraege.php` | Letzte Buchungen anzeigen und loeschen | `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_Mitarbeiter` | | `letzteneintraege.php` | Letzte Buchungen anzeigen und löschen | `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_Mitarbeiter` |
| `csvupload.php` | Zahlungs-CSV importieren | `kl_Mitarbeiter`, `kl_Einzahlungen` | | `csvupload.php` | Zahlungs-CSV importieren | `kl_Mitarbeiter`, `kl_Einzahlungen` |
| `exportKaffeeliste.php` | PDF-/Listenexport | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_Einzahlungen`, `kl_config` | | `exportKaffeeliste.php` | PDF-/Listenexport | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_Einzahlungen`, `kl_config` |
| `mailausgebe.php` | Mailausgabe/Adressliste | `kl_Mitarbeiter` | | `mailausgebe.php` | Mailausgabe/Adressliste | `kl_Mitarbeiter` |
@@ -44,7 +44,7 @@ Das echte Datenbankschema muss noch mit `schema-export.sql` verifiziert werden.
## Abgeleitete Tabellen und Spalten ## Abgeleitete Tabellen und Spalten
Diese Spalten sind aus dem Code abgeleitet und muessen mit dem echten DB-Schema Diese Spalten sind aus dem Code abgeleitet und müssen mit dem echten DB-Schema
abgeglichen werden. abgeglichen werden.
| Tabelle | Abgeleitete Spalten | | Tabelle | Abgeleitete Spalten |
@@ -64,13 +64,13 @@ abgeglichen werden.
1. Seite inkludiert `functions.php`. 1. Seite inkludiert `functions.php`.
2. `functions.php` liest `$_SERVER['AUTH_USER']`. 2. `functions.php` liest `$_SERVER['AUTH_USER']`.
3. `functionsLDAP.php` ermittelt Mailadresse aus LDAP. 3. `functionsLDAP.php` ermittelt Mailadresse aus LDAP.
4. Zugriff wird ueber `kl_Mitarbeiter.Email`, `aktiv` und `admin` geprueft. 4. Zugriff wird über `kl_Mitarbeiter.Email`, `aktiv` und `admin` geprüft.
M0-Feststellung: M0-Feststellung:
Login-Konto, Kaffee-Teilnehmer und Rolle sind im Legacy-Modell gekoppelt. Fuer Login-Konto, Kaffee-Teilnehmer und Rolle sind im Legacy-Modell gekoppelt. Für
SaaS muessen `users`, `participants` und `tenant_memberships` getrennt werden. SaaS müssen `users`, `participants` und `tenant_memberships` getrennt werden.
### Persoenliches Dashboard ### Persönliches Dashboard
Quelle: `index.php`. Quelle: `index.php`.
@@ -81,7 +81,7 @@ Berechnungen:
- Gesamtausgabe: Summe `kl_Kaffeeverbrauch.Kosten`. - Gesamtausgabe: Summe `kl_Kaffeeverbrauch.Kosten`.
- Gesamtstriche: Summe `kl_Kaffeeverbrauch.AnzahlStriche`. - Gesamtstriche: Summe `kl_Kaffeeverbrauch.AnzahlStriche`.
- Aktueller Stand: Einzahlungen minus Ausgaben. - Aktueller Stand: Einzahlungen minus Ausgaben.
- Jahreswerte: dieselben Summen fuer aktuelles Jahr. - Jahreswerte: dieselben Summen für aktuelles Jahr.
- Bei aktivem `strichperweb`: eigener Strich-Eintrag. - Bei aktivem `strichperweb`: eigener Strich-Eintrag.
- Bei aktivem `paypaluse`: PayPal-Links anzeigen. - Bei aktivem `paypaluse`: PayPal-Links anzeigen.
@@ -123,19 +123,19 @@ M0-Feststellung:
Das `admin`-Flag ist fachlich eine Rolleninformation und sollte in der SaaS-App Das `admin`-Flag ist fachlich eine Rolleninformation und sollte in der SaaS-App
in `tenant_memberships.role` wandern. in `tenant_memberships.role` wandern.
### Korrektur letzter Eintraege ### Korrektur letzter Einträge
Quelle: `letzteneintraege.php`. Quelle: `letzteneintraege.php`.
Aktionen: Aktionen:
- Letzte 100 Einzahlungen anzeigen. - Letzte 100 Einzahlungen anzeigen.
- Einzahlung hart loeschen. - Einzahlung hart löschen.
- Letzte 100 Strich-Eintraege anzeigen. - Letzte 100 Strich-Einträge anzeigen.
- Strich-Eintrag hart loeschen. - Strich-Eintrag hart löschen.
M0-Feststellung: M0-Feststellung:
Finanznahe Korrekturen sollten im Zielmodell nicht hart loeschen, sondern als Finanznahe Korrekturen sollten im Zielmodell nicht hart löschen, sondern als
Storno/Reversal mit Audit-Trail gespeichert werden. Storno/Reversal mit Audit-Trail gespeichert werden.
### CSV-Import ### CSV-Import
@@ -148,8 +148,8 @@ Ablauf:
- CSV mit Komma-Trennung lesen. - CSV mit Komma-Trennung lesen.
- Name aus Spalte 3, Betrag aus Spalte 7, Datum aus Spalte 0. - Name aus Spalte 3, Betrag aus Spalte 7, Datum aus Spalte 0.
- Mitarbeiter per Name oder `paypalname` finden. - Mitarbeiter per Name oder `paypalname` finden.
- Dublette anhand Mitarbeiter, Betrag und Datumstag pruefen. - Dublette anhand Mitarbeiter, Betrag und Datumstag prüfen.
- Einzahlung einfuegen. - Einzahlung einfügen.
- Ergebnis in Erfolg/Fehler klassifizieren. - Ergebnis in Erfolg/Fehler klassifizieren.
### Jahresauswertung ### Jahresauswertung
@@ -160,33 +160,33 @@ Ablauf:
- Gesamtstriche des aktuellen Jahres je aktivem Mitarbeiter berechnen. - Gesamtstriche des aktuellen Jahres je aktivem Mitarbeiter berechnen.
- Konstante Anzahl neuer Striche/Gutschrift proportional verteilen. - Konstante Anzahl neuer Striche/Gutschrift proportional verteilen.
- Einzahlung je Mitarbeiter einfuegen. - Einzahlung je Mitarbeiter einfügen.
- Mail an Mitarbeiter senden. - Mail an Mitarbeiter senden.
M0-Feststellung: M0-Feststellung:
Das ist ein Job/Skript mit direktem Schreibzugriff und Mailversand. Fuer SaaS Das ist ein Job/Skript mit direktem Schreibzugriff und Mailversand. Für SaaS
braucht es Dry-Run, Audit, Tenant-Scope und expliziten Operator. braucht es Dry-Run, Audit, Tenant-Scope und expliziten Operator.
## Offene Schemafragen ## Offene Schemafragen
- Gibt es Primary Keys, Foreign Keys und Indizes fuer die `kl_`-Tabellen? - Gibt es Primary Keys, Foreign Keys und Indizes für die `kl_`-Tabellen?
- Sind Datentypen fuer Geldwerte `decimal`, `money`, `float` oder etwas anderes? - Sind Datentypen für Geldwerte `decimal`, `money`, `float` oder etwas anderes?
- Gibt es historische Spalten, die im Code nicht sichtbar sind? - Gibt es historische Spalten, die im Code nicht sichtbar sind?
- Gibt es Trigger oder Stored Procedures? - Gibt es Trigger oder Stored Procedures?
- Gibt es weitere Jobs ausser `jahresauswertung.php`? - Gibt es weitere Jobs außer `jahresauswertung.php`?
- Sind Umfragetabellen produktiv relevant oder Archiv/Randmodul? - Sind Umfragetabellen produktiv relevant oder Archiv/Randmodul?
## Technische Legacy-Inkonsistenzen ## Technische Legacy-Inkonsistenzen
- `csvupload.php` sucht in `getMitarbeiterID` nach `Name = ? or paypalname = ?`, - `csvupload.php` sucht in `getMitarbeiterID` nach `Name = ? or paypalname = ?`,
uebergibt aber nur einen Parameter. Das sollte vor der Uebernahme des übergibt aber nur einen Parameter. Das sollte vor der Übernahme des
CSV-Flows verifiziert werden. CSV-Flows verifiziert werden.
- `einzahlung.php` prueft auf `$_GET["action"]`, die Buttons erzeugen aber - `einzahlung.php` prüft auf `$_GET["action"]`, die Buttons erzeugen aber
URLs mit `aktion=...`. Der Vorderseite-/Rueckseite-Filter kann dadurch URLs mit `aktion=...`. Der Vorderseite-/Rückseite-Filter kann dadurch
abweichend vom erwarteten Verhalten laufen. abweichend vom erwarteten Verhalten laufen.
- `exportKaffeeliste.php` referenziert `tcpdf/tcpdf.php`, waehrend der Ordner im - `exportKaffeeliste.php` referenziert `tcpdf/tcpdf.php`, während der Ordner im
Repository `TCPDF/` heisst. Auf case-sensitiven Dateisystemen kann das Repository `TCPDF/` heißt. Auf case-sensitiven Dateisystemen kann das
brechen. brechen.
- `headerline.php` ist fachlich leer beziehungsweise mit NUL-Zeichen gefuellt. - `headerline.php` ist fachlich leer beziehungsweise mit NUL-Zeichen gefüllt.
Vor der neuen App-Shell sollte geklaert werden, ob die Datei entfernt werden Vor der neuen App-Shell sollte geklärt werden, ob die Datei entfernt werden
kann. kann.
+5 -6
View File
@@ -3,10 +3,10 @@ M0 Golden-Master Queries
Ziel: Ziel:
- Fachliche Legacy-Ergebnisse exportieren, damit Migration und neue SaaS-App - Fachliche Legacy-Ergebnisse exportieren, damit Migration und neue SaaS-App
gegen denselben Stand verglichen werden koennen. gegen denselben Stand verglichen werden können.
Ausfuehrung: Ausführung:
- In der Legacy-Datenbank ausfuehren. - In der Legacy-Datenbank ausführen.
- Ergebnisse je Query als CSV speichern. - Ergebnisse je Query als CSV speichern.
- Keine personenbezogenen Exporte ins Repo committen. - Keine personenbezogenen Exporte ins Repo committen.
*/ */
@@ -94,7 +94,7 @@ FROM kl_Einzahlungen e
JOIN kl_Mitarbeiter m ON m.MitarbeiterID = e.MitarbeiterID JOIN kl_Mitarbeiter m ON m.MitarbeiterID = e.MitarbeiterID
ORDER BY e.Datum DESC, e.EinzahlungsID DESC; ORDER BY e.Datum DESC, e.EinzahlungsID DESC;
-- 5. Letzte Strich-/Verbrauchseintraege je Teilnehmer -- 5. Letzte Strich-/Verbrauchseinträge je Teilnehmer
SELECT SELECT
v.VerbrauchID, v.VerbrauchID,
v.MitarbeiterID, v.MitarbeiterID,
@@ -142,7 +142,7 @@ GROUP BY m.MitarbeiterID, m.Name, m.Email
HAVING SUM(v.AnzahlStriche) >= 10 HAVING SUM(v.AnzahlStriche) >= 10
ORDER BY m.Name; ORDER BY m.Name;
-- 9. 100-Tage-Rueckseite analog Legacy-Stricherfassung -- 9. 100-Tage-Rückseite analog Legacy-Stricherfassung
SELECT SELECT
m.MitarbeiterID, m.MitarbeiterID,
m.Name, m.Name,
@@ -182,4 +182,3 @@ WHERE YEAR(v.Datum) = @CurrentYear
AND m.aktiv = 1 AND m.aktiv = 1
GROUP BY m.MitarbeiterID, m.Name, m.Email GROUP BY m.MitarbeiterID, m.Name, m.Email
ORDER BY GesamtStriche DESC, m.Name; ORDER BY GesamtStriche DESC, m.Name;
+14 -15
View File
@@ -4,44 +4,43 @@ Stand: 2026-07-11
## Entscheidung ## Entscheidung
Ein alter MS-SQL-Datenbankstand steht aktuell nicht zur Verfuegung. Deshalb Ein alter MS-SQL-Datenbankstand steht aktuell nicht zur Verfügung. Deshalb
koennen `schema-export.sql` und `golden-master-queries.sql` derzeit nicht gegen können `schema-export.sql` und `golden-master-queries.sql` derzeit nicht gegen
eine echte Legacy-Datenbank ausgefuehrt werden. eine echte Legacy-Datenbank ausgeführt werden.
Fuer die weitere Umstrukturierung gilt daher: Für die weitere Umstrukturierung gilt daher:
- Der Legacy-Code ist die primaere Quelle fuer das fachliche Verhalten. - Der Legacy-Code ist die primäre Quelle für das fachliche Verhalten.
- Die Screenshots unter `docs/m0/screenshots/` sind die visuelle Referenz. - Die Screenshots unter `docs/m0/screenshots/` sind die visuelle Referenz.
- Die MySQL-Testdatenbank ist die rekonstruierte Entwicklungsbasis. - Die MySQL-Testdatenbank ist die rekonstruierte Entwicklungsbasis.
- Fachliche Golden-Master-Werte werden aus rekonstruierten Testfaellen statt aus - Fachliche Golden-Master-Werte werden aus rekonstruierten Testfällen statt aus
historischer Produktivdatenbank aufgebaut. historischer Produktivdatenbank aufgebaut.
## Konsequenz fuer M1 ## Konsequenz für M1
M1 wird nicht als Vergleich gegen echte Alt-Daten gestartet, sondern als M1 wird nicht als Vergleich gegen echte Alt-Daten gestartet, sondern als
rekonstruierter Golden-Master: rekonstruierter Golden-Master:
1. Fachliche Regeln aus dem Code ableiten. 1. Fachliche Regeln aus dem Code ableiten.
2. Kleine, kontrollierte Testdaten erzeugen. 2. Kleine, kontrollierte Testdaten erzeugen.
3. Erwartete Salden, Jahreswerte, Striche, Einzahlungen und CSV-Importfaelle 3. Erwartete Salden, Jahreswerte, Striche, Einzahlungen und CSV-Importfälle
manuell beziehungsweise per Testskript definieren. manuell beziehungsweise per Testskript definieren.
4. Neue Implementierung gegen diese erwarteten Werte testen. 4. Neue Implementierung gegen diese erwarteten Werte testen.
Wenn spaeter doch noch ein MS-SQL-Dump oder Schemaexport auftaucht, werden die Wenn später doch noch ein MS-SQL-Dump oder Schemaexport auftaucht, werden die
urspruenglichen M0-SQL-Dateien nachtraeglich genutzt und die Baseline ergaenzt. ursprünglichen M0-SQL-Dateien nachträglich genutzt und die Baseline ergänzt.
## Aktuelle rekonstruierte Basis ## Aktuelle rekonstruierte Basis
- MySQL-Dev-Schema: `database/mysql-dev-schema.sql` - MySQL-Dev-Schema: `database/mysql-dev-schema.sql`
- Initialisierung: `scripts/init-mysql-dev.php` - Initialisierung: `scripts/init-mysql-dev.php`
- Dev-Start: `scripts/run-dev-server.sh` - Dev-Start: `scripts/run-dev-server.sh`
- MySQL-Kompatibilitaet: `lib/sqlsrv_mysql_compat.php` - MySQL-Kompatibilität: `lib/sqlsrv_mysql_compat.php`
## Offene Punkte ## Offene Punkte
- Soll die MySQL-Testdatenbank ab jetzt die verbindliche Entwicklungsdatenbank - Soll die MySQL-Testdatenbank ab jetzt die verbindliche Entwicklungsdatenbank
fuer M1/M2 sein? für M1/M2 sein?
- Soll das Ziel weiterhin eine angepasste Legacy-PHP-App sein oder ein sauberer - Soll das Ziel weiterhin eine angepasste Legacy-PHP-App sein oder ein sauberer
SaaS-Neuaufbau mit uebernommener Fachlogik? SaaS-Neuaufbau mit übernommener Fachlogik?
- Welche Testfaelle muessen fachlich auf jeden Fall abgedeckt werden? - Welche Testfälle müssen fachlich auf jeden Fall abgedeckt werden?
+1 -2
View File
@@ -6,7 +6,7 @@ Ziel:
aus der Legacy-Datenbank exportieren. aus der Legacy-Datenbank exportieren.
Hinweise: Hinweise:
- In der Ziel-Datenbank ausfuehren. - In der Ziel-Datenbank ausführen.
- Ergebnisse als CSV/Excel speichern und nicht mit Produktiv-Secrets ins Repo - Ergebnisse als CSV/Excel speichern und nicht mit Produktiv-Secrets ins Repo
legen. legen.
- Falls nur Kaffeelisten-Tabellen exportiert werden sollen, den WHERE-Block bei - Falls nur Kaffeelisten-Tabellen exportiert werden sollen, den WHERE-Block bei
@@ -149,4 +149,3 @@ WHERE o.type IN ('P', 'FN', 'IF', 'TF')
OR OBJECT_DEFINITION(o.object_id) LIKE '%CoffeeSurvey%' OR OBJECT_DEFINITION(o.object_id) LIKE '%CoffeeSurvey%'
) )
ORDER BY s.name, o.name; ORDER BY s.name, o.name;
+14 -14
View File
@@ -3,7 +3,7 @@
Stand: 2026-07-11 Stand: 2026-07-11
Diese Checkliste friert die bestehende Optik ein, damit die SaaS-App vertraut Diese Checkliste friert die bestehende Optik ein, damit die SaaS-App vertraut
bleibt. Wenn die Legacy-App lokal nicht laeuft, koennen Screenshots aus der bleibt. Wenn die Legacy-App lokal nicht läuft, können Screenshots aus der
aktuellen Umgebung geliefert werden. aktuellen Umgebung geliefert werden.
## Allgemeine Vorgaben ## Allgemeine Vorgaben
@@ -11,24 +11,24 @@ aktuellen Umgebung geliefert werden.
- Je Seite Desktop mit ca. 1440 px Breite aufnehmen. - Je Seite Desktop mit ca. 1440 px Breite aufnehmen.
- Je Kernseite mobile Ansicht mit ca. 390 px Breite aufnehmen. - Je Kernseite mobile Ansicht mit ca. 390 px Breite aufnehmen.
- Je relevanter Rolle aufnehmen: normales Mitglied und Admin. - Je relevanter Rolle aufnehmen: normales Mitglied und Admin.
- Keine echten personenbezogenen Daten veroeffentlichen. - Keine echten personenbezogenen Daten veröffentlichen.
- Dateinamen nach Muster `m0-<rolle>-<seite>-<viewport>.png`. - Dateinamen nach Muster `m0-<rolle>-<seite>-<viewport>.png`.
## Kernseiten ## Kernseiten
| Prioritaet | Seite | Rolle | Status | Zweck | | Priorität | Seite | Rolle | Status | Zweck |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| Hoch | `index.php` | Mitglied | vorhanden: `m0-member-dashboard-desktop.png`, `m0-member-dashboard-mobile.png` | Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal | | Hoch | `index.php` | Mitglied | vorhanden: `m0-member-dashboard-desktop.png`, `m0-member-dashboard-mobile.png` | Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal |
| Hoch | `index.php` | Admin | aktuell identisch wie Mitglied | Dashboard mit Admin-Sidebar | | Hoch | `index.php` | Admin | aktuell identisch wie Mitglied | Dashboard mit Admin-Sidebar |
| Hoch | `kaffeeliste.php` | Admin | vorhanden: `m0-admin-kaffeeliste-desktop.png` | Gesamtuebersicht und Aktionsleiste | | Hoch | `kaffeeliste.php` | Admin | vorhanden: `m0-admin-kaffeeliste-desktop.png` | Gesamtübersicht und Aktionsleiste |
| Hoch | `stricheintragen.php` | Admin | vorhanden: `m0-admin-stricheintragen-desktop.png` | Sammelerfassung Striche | | Hoch | `stricheintragen.php` | Admin | vorhanden: `m0-admin-stricheintragen-desktop.png` | Sammelerfassung Striche |
| Hoch | `einzahlung.php` | Admin | vorhanden: `m0-admin-einzahlung-desktop.png` | Sammelerfassung Einzahlungen | | Hoch | `einzahlung.php` | Admin | vorhanden: `m0-admin-einzahlung-desktop.png` | Sammelerfassung Einzahlungen |
| Hoch | `mitarbeiterverwalten.php` | Admin | vorhanden: `m0-admin-mitglieder-desktop.png` | Mitgliederliste und Formular | | Hoch | `mitarbeiterverwalten.php` | Admin | vorhanden: `m0-admin-mitglieder-desktop.png` | Mitgliederliste und Formular |
| Hoch | `letzteneintraege.php` | Admin | vorhanden: `m0-admin-letzteneintraege-desktop.png` | Korrekturansicht mit Loeschaktionen | | Hoch | `letzteneintraege.php` | Admin | vorhanden: `m0-admin-letzteneintraege-desktop.png` | Korrekturansicht mit Löschaktionen |
| Mittel | `csvupload.php` | Admin | vorhanden: `m0-admin-csv-upload-desktop.png` | CSV-Upload-Formular und Ergebniszustand | | Mittel | `csvupload.php` | Admin | vorhanden: `m0-admin-csv-upload-desktop.png` | CSV-Upload-Formular und Ergebniszustand |
| Mittel | `hinweise.php` | Admin | vorhanden: `m0-admin-hinweise-desktop.png` | Hinweise anlegen und Liste | | Mittel | `hinweise.php` | Admin | vorhanden: `m0-admin-hinweise-desktop.png` | Hinweise anlegen und Liste |
| Mittel | `faq.php` | Mitglied | vorhanden: `m0-member-faq-desktop.png` | FAQ-Inhalte und Typografie | | Mittel | `faq.php` | Mitglied | vorhanden: `m0-member-faq-desktop.png` | FAQ-Inhalte und Typografie |
| Mittel | `namenanpassen.php` | Mitglied | offen | Formular fuer Anzeigenamen | | Mittel | `namenanpassen.php` | Mitglied | offen | Formular für Anzeigenamen |
| Niedrig | `teilnehmerauswertung.php` | Admin | offen | Detailauswertung pro Teilnehmer | | Niedrig | `teilnehmerauswertung.php` | Admin | offen | Detailauswertung pro Teilnehmer |
| Niedrig | `mailausgebe.php` | Admin | offen | Mail-/Adressausgabe | | Niedrig | `mailausgebe.php` | Admin | offen | Mail-/Adressausgabe |
| Niedrig | `umfrage.php` | Mitglied | offen, falls SaaS-relevant | Randmodul, falls SaaS-relevant | | Niedrig | `umfrage.php` | Mitglied | offen, falls SaaS-relevant | Randmodul, falls SaaS-relevant |
@@ -49,9 +49,9 @@ docs/m0/screenshots/m0-member-dashboard-mobile.png
docs/m0/screenshots/m0-member-faq-desktop.png docs/m0/screenshots/m0-member-faq-desktop.png
``` ```
## Zustaende ## Zustände
Diese UI-Zustaende sollten, wenn moeglich, separat gesichert werden: Diese UI-Zustände sollten, wenn möglich, separat gesichert werden:
- Positiver Kontostand. - Positiver Kontostand.
- Negativer Kontostand. - Negativer Kontostand.
@@ -62,14 +62,14 @@ Diese UI-Zustaende sollten, wenn moeglich, separat gesichert werden:
- CSV-Import mit Erfolg, Dublette und unbekanntem Nutzer. - CSV-Import mit Erfolg, Dublette und unbekanntem Nutzer.
- Hinweisbanner aktiv. - Hinweisbanner aktiv.
- Leere Tabellen oder keine Daten. - Leere Tabellen oder keine Daten.
- Mobile Sidebar geoeffnet und geschlossen. - Mobile Sidebar geöffnet und geschlossen.
## Designmerkmale, die erhalten bleiben sollen ## Designmerkmale, die erhalten bleiben sollen
- Gruener Akzent fuer Links, Buttons und Linien. - Grüner Akzent für Links, Buttons und Linien.
- Roboto-Slab-Ueberschriften. - Roboto-Slab-Überschriften.
- Open-Sans-Fliesstext. - Open-Sans-Fließtext.
- Weisse Hauptflaeche. - Weiße Hauptfläche.
- Graue Sidebar in der App. - Graue Sidebar in der App.
- Tabellen als zentrale Datenansicht. - Tabellen als zentrale Datenansicht.
- Schlichte Formularfelder und Buttons. - Schlichte Formularfelder und Buttons.
@@ -84,5 +84,5 @@ docs/m0/screenshots/
``` ```
Vor dem Commit sollten Screenshots anonymisiert sein. Wenn echte Namen, Vor dem Commit sollten Screenshots anonymisiert sein. Wenn echte Namen,
E-Mails, PayPal-Daten oder Salden sichtbar sind, besser ausserhalb des Repos E-Mails, PayPal-Daten oder Salden sichtbar sind, besser außerhalb des Repos
ablegen und nur als Referenz verwenden. ablegen und nur als Referenz verwenden.
+18 -18
View File
@@ -3,17 +3,17 @@
Stand: 2026-07-11 Stand: 2026-07-11
Diese Baseline ist eine statische Erstbewertung. Sie ersetzt kein Penetration Diese Baseline ist eine statische Erstbewertung. Sie ersetzt kein Penetration
Testing, markiert aber die wichtigsten Risiken fuer die SaaS-Umstrukturierung. Testing, markiert aber die wichtigsten Risiken für die SaaS-Umstrukturierung.
## Kritische Sofortthemen ## Kritische Sofortthemen
| Risiko | Quelle | Auswirkung | Empfehlung | | Risiko | Quelle | Auswirkung | Empfehlung |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Hart codierte DB-Zugangsdaten | `jahresauswertung.php` Zeilen 4-8 | Secret-Leak, direkte Produktiv-DB-Gefahr | Zugangsdaten rotieren, Skript deaktivieren oder auf Env-Konfiguration umstellen | | Hart codierte DB-Zugangsdaten | `jahresauswertung.php` Zeilen 4-8 | Secret-Leak, direkte Produktiv-DB-Gefahr | Zugangsdaten rotieren, Skript deaktivieren oder auf Env-Konfiguration umstellen |
| Schreibende Seiten ohne eigene Rollenpruefung | `stricheintragen.php`, `einzahlung.php`, `mailversenden.php`, `exportKaffeeliste.php` | Direkter URL-Aufruf kann Aktionen erlauben | Jede Seite serverseitig mit `requireRole` absichern | | Schreibende Seiten ohne eigene Rollenprüfung | `stricheintragen.php`, `einzahlung.php`, `mailversenden.php`, `exportKaffeeliste.php` | Direkter URL-Aufruf kann Aktionen erlauben | Jede Seite serverseitig mit `requireRole` absichern |
| CSRF-Schutz nur teilweise vorhanden | viele POST-/Delete-Formulare; M2 hat `hinweise.php`, `mitarbeiterverwalten.php`, `namenanpassen.php`, `index.php`, `stricheintragen.php`, `einzahlung.php`, `letzteneintraege.php`, `csvupload.php` abgesichert | Ungewollte Buchungen, Loeschungen, Imports | CSRF fuer alle verbleibenden schreibenden Aktionen | | CSRF-Schutz nur teilweise vorhanden | viele POST-/Delete-Formulare; M2 hat `hinweise.php`, `mitarbeiterverwalten.php`, `namenanpassen.php`, `index.php`, `stricheintragen.php`, `einzahlung.php`, `letzteneintraege.php`, `csvupload.php` abgesichert | Ungewollte Buchungen, Löschungen, Imports | CSRF für alle verbleibenden schreibenden Aktionen |
| Harte Deletes fuer Buchungen | `letzteneintraege.php` | Audit-Historie und Revisionsfaehigkeit gehen verloren | Storno-/Reversal-Modell statt Delete | | Harte Deletes für Buchungen | `letzteneintraege.php` | Audit-Historie und Revisionsfähigkeit gehen verloren | Storno-/Reversal-Modell statt Delete |
| CSV-Upload nur teilweise gehaertet | `csvupload.php`; M2 speichert temporaer unter `var/uploads`, prueft Dateityp und loescht nach Import | Ohne Importvorschau/Audit bleiben Fehlimporte schwer nachvollziehbar | Importvorschau, Batch-/Audit-Log und detaillierte Zeilenfehler in M6 | | CSV-Upload nur teilweise gehärtet | `csvupload.php`; M2 speichert temporär unter `var/uploads`, prüft Dateityp und löscht nach Import | Ohne Importvorschau/Audit bleiben Fehlimporte schwer nachvollziehbar | Importvorschau, Batch-/Audit-Log und detaillierte Zeilenfehler in M6 |
| Kein Tenant-Scope | alle fachlichen Queries | Zentrales SaaS-Leak-Risiko | `tenant_id` verpflichtend und Query-Schicht testen | | Kein Tenant-Scope | alle fachlichen Queries | Zentrales SaaS-Leak-Risiko | `tenant_id` verpflichtend und Query-Schicht testen |
## Weitere Befunde ## Weitere Befunde
@@ -25,10 +25,10 @@ Testing, markiert aber die wichtigsten Risiken fuer die SaaS-Umstrukturierung.
| Datumsformat `Y-d-m H:i:s` | `index.php`, `stricheintragen.php`, `einzahlung.php`, `hinweise.php` | ISO/DB-kompatibel `Y-m-d H:i:s` oder DB-Zeit verwenden | | Datumsformat `Y-d-m H:i:s` | `index.php`, `stricheintragen.php`, `einzahlung.php`, `hinweise.php` | ISO/DB-kompatibel `Y-m-d H:i:s` oder DB-Zeit verwenden |
| Ausgabe von Namen/E-Mails teils unescaped | mehrere Tabellen, z.B. Mitgliederverwaltung | `htmlspecialchars` zentral erzwingen | | Ausgabe von Namen/E-Mails teils unescaped | mehrere Tabellen, z.B. Mitgliederverwaltung | `htmlspecialchars` zentral erzwingen |
| Fehlerausgabe mit `sqlsrv_errors()` an Nutzer | mehrere Dateien | Logging intern, neutrale Fehlermeldung extern | | Fehlerausgabe mit `sqlsrv_errors()` an Nutzer | mehrere Dateien | Logging intern, neutrale Fehlermeldung extern |
| CSV-Mitarbeitersuche mit mutmasslich falscher Parameteranzahl | `csvupload.php` `getMitarbeiterID`; in M2 korrigiert | Mit Golden-Master weiter pruefen | | CSV-Mitarbeitersuche mit mutmaßlich falscher Parameteranzahl | `csvupload.php` `getMitarbeiterID`; in M2 korrigiert | Mit Golden-Master weiter prüfen |
| Basis-Auth-Beispiel mit Platzhalter-Passwort | `umfrageergebnisse.php` Kommentarblock | Entfernen oder echte Auth-Middleware nutzen | | Basis-Auth-Beispiel mit Platzhalter-Passwort | `umfrageergebnisse.php` Kommentarblock | Entfernen oder echte Auth-Middleware nutzen |
| App-Navigation ist in `footer.php` | Layoutstruktur | Trennung in App-Shell und Public-Shell | | App-Navigation ist in `footer.php` | Layoutstruktur | Trennung in App-Shell und Public-Shell |
| `headerline.php` enthaelt NUL-Zeichen | `headerline.php` | Datei pruefen/entfernen, wenn ungenutzt | | `headerline.php` enthält NUL-Zeichen | `headerline.php` | Datei prüfen/entfernen, wenn ungenutzt |
| Massenmail ohne Versandlog | `mailversenden.php`, `jahresauswertung.php` | Job-Modell mit Dry-Run, Audit, Rate-Limit | | Massenmail ohne Versandlog | `mailversenden.php`, `jahresauswertung.php` | Job-Modell mit Dry-Run, Audit, Rate-Limit |
## Rollen- und Zugriffsrisiken ## Rollen- und Zugriffsrisiken
@@ -39,29 +39,29 @@ Aktuell gilt:
gesteuert. gesteuert.
- Einige Admin-Zielseiten haben eigene Checks. - Einige Admin-Zielseiten haben eigene Checks.
- Einige schreibende oder sensible Dateien verlassen sich nicht durchgehend auf - Einige schreibende oder sensible Dateien verlassen sich nicht durchgehend auf
eine eigene Rollenpruefung. eine eigene Rollenprüfung.
Fuer SaaS gilt: Für SaaS gilt:
- Menue-Ausblendung ist keine Berechtigungspruefung. - Menü-Ausblendung ist keine Berechtigungsprüfung.
- Jede Route braucht serverseitige Auth- und Rollenpruefung. - Jede Route braucht serverseitige Auth- und Rollenprüfung.
- Jede fachliche Route braucht Tenant-Kontext. - Jede fachliche Route braucht Tenant-Kontext.
- IDs aus Requests muessen zum aktuellen Tenant gehoeren. - IDs aus Requests müssen zum aktuellen Tenant gehören.
## Empfohlene Reihenfolge fuer Sicherheitsarbeit ## Empfohlene Reihenfolge für Sicherheitsarbeit
1. Secrets rotieren und aus dem Code entfernen. 1. Secrets rotieren und aus dem Code entfernen.
2. Legacy-Schreibseiten bis zum Umbau hinter explizite Admin-Pruefung setzen. 2. Legacy-Schreibseiten bis zum Umbau hinter explizite Admin-Prüfung setzen.
3. CSRF-Schutz schrittweise auf alle verbleibenden Legacy-Schreibseiten ausrollen. 3. CSRF-Schutz schrittweise auf alle verbleibenden Legacy-Schreibseiten ausrollen.
4. Finanzdaten im Zielmodell nur noch stornieren, nicht loeschen. 4. Finanzdaten im Zielmodell nur noch stornieren, nicht löschen.
5. CSV-Import mit Vorschau, Audit und Zeilenfehlern modellieren. 5. CSV-Import mit Vorschau, Audit und Zeilenfehlern modellieren.
6. Einheitliche Escape-/View-Helfer einfuehren. 6. Einheitliche Escape-/View-Helfer einführen.
7. Tenant-Isolation mit Tests gegen zwei Tenants absichern. 7. Tenant-Isolation mit Tests gegen zwei Tenants absichern.
## Nicht im Repo speichern ## Nicht im Repo speichern
- Produktivpasswoerter. - Produktivpasswörter.
- Echte Nutzerlisten. - Echte Nutzerlisten.
- Bank-/PayPal-Exportdaten mit Personenbezug. - Bank-/PayPal-Exportdaten mit Personenbezug.
- AD-/LDAP-Service-Account-Daten. - AD-/LDAP-Service-Account-Daten.
- Vollstaendige Produktiv-Dumps. - Vollständige Produktiv-Dumps.
+54 -54
View File
@@ -2,14 +2,14 @@
Stand: 2026-07-12 Stand: 2026-07-12
Da kein alter MS-SQL-Datenbankstand verfuegbar ist, wird M1 als rekonstruierter Da kein alter MS-SQL-Datenbankstand verfügbar ist, wird M1 als rekonstruierter
Golden Master aufgebaut. Die Referenz entsteht aus Legacy-Code, Screenshots und Golden Master aufgebaut. Die Referenz entsteht aus Legacy-Code, Screenshots und
kontrollierten Testdaten in der MySQL-Dev-Datenbank. kontrollierten Testdaten in der MySQL-Dev-Datenbank.
## Ziel ## Ziel
Die wichtigsten fachlichen Regeln werden mit kleinen, nachvollziehbaren Die wichtigsten fachlichen Regeln werden mit kleinen, nachvollziehbaren
Testfaellen festgeschrieben. Diese Tests dienen spaeter als Sicherheitsnetz fuer Testfällen festgeschrieben. Diese Tests dienen später als Sicherheitsnetz für
die SaaS-Umstrukturierung. die SaaS-Umstrukturierung.
## Testdaten-Gruppen ## Testdaten-Gruppen
@@ -28,7 +28,7 @@ die SaaS-Umstrukturierung.
- Mehrere Einzahlungen an unterschiedlichen Tagen. - Mehrere Einzahlungen an unterschiedlichen Tagen.
- Strichbuchung mit einem Strich. - Strichbuchung mit einem Strich.
- Strichbuchung mit zwei Strichen. - Strichbuchung mit zwei Strichen.
- Sammelerfassung fuer mehrere Teilnehmer. - Sammelerfassung für mehrere Teilnehmer.
- Web-Strich-Eintrag mit `Eintragsart = 2`. - Web-Strich-Eintrag mit `Eintragsart = 2`.
### Salden ### Salden
@@ -36,13 +36,13 @@ die SaaS-Umstrukturierung.
- Positiver Kontostand. - Positiver Kontostand.
- Negativer Kontostand. - Negativer Kontostand.
- Nullsaldo. - Nullsaldo.
- Jahreswerte fuer aktuelles Jahr. - Jahreswerte für aktuelles Jahr.
- Altdaten aus Vorjahr, die nicht in Jahreswerte fallen. - Altdaten aus Vorjahr, die nicht in Jahreswerte fallen.
### CSV-Import ### CSV-Import
- Treffer ueber Name. - Treffer über Name.
- Treffer ueber `paypalname`. - Treffer über `paypalname`.
- Dublette gleicher Teilnehmer, Betrag und Datum. - Dublette gleicher Teilnehmer, Betrag und Datum.
- Unbekannter Name. - Unbekannter Name.
- Negativer oder leerer Betrag. - Negativer oder leerer Betrag.
@@ -51,11 +51,11 @@ die SaaS-Umstrukturierung.
- Aktiver Hinweis. - Aktiver Hinweis.
- Abgelaufener Hinweis. - Abgelaufener Hinweis.
- Neuer Hinweis ueber Admin-Formular. - Neuer Hinweis über Admin-Formular.
## Erwartete Referenzwerte ## Erwartete Referenzwerte
Fuer jeden Testfall sollen erwartete Werte dokumentiert werden: Für jeden Testfall sollen erwartete Werte dokumentiert werden:
- Summe Einzahlungen. - Summe Einzahlungen.
- Summe Kosten. - Summe Kosten.
@@ -72,25 +72,25 @@ Fuer jeden Testfall sollen erwartete Werte dokumentiert werden:
Seed-Skript anlegen. Erledigt mit `scripts/seed-golden-master.php`. Seed-Skript anlegen. Erledigt mit `scripts/seed-golden-master.php`.
2. `scripts/init-mysql-dev.php` so erweitern, dass es die Testdaten idempotent 2. `scripts/init-mysql-dev.php` so erweitern, dass es die Testdaten idempotent
anlegt. Entschieden: Golden-Master-Daten bleiben in einem separaten Skript, anlegt. Entschieden: Golden-Master-Daten bleiben in einem separaten Skript,
damit normale Dev-Daten nicht zwangsweise ueberschrieben werden. damit normale Dev-Daten nicht zwangsweise überschrieben werden.
3. Ein erstes Vergleichsskript `scripts/check-golden-master.php` erstellen. 3. Ein erstes Vergleichsskript `scripts/check-golden-master.php` erstellen.
Erledigt. Erledigt.
4. Die wichtigsten Seiten per HTTP abrufen und auf erwartete Texte/Werte 4. Die wichtigsten Seiten per HTTP abrufen und auf erwartete Texte/Werte
pruefen. Erledigt mit `scripts/http-smoke.php`. prüfen. Erledigt mit `scripts/http-smoke.php`.
5. Danach M2 starten: technisches Fundament fuer SaaS-Struktur. M1 ist als 5. Danach M2 starten: technisches Fundament für SaaS-Struktur. M1 ist als
Sicherheitsnetz nutzbar; offene Dependency-Themen sind unten dokumentiert. Sicherheitsnetz nutzbar; offene Dependency-Themen sind unten dokumentiert.
## Skripte ## Skripte
- `scripts/dev-db.php`: gemeinsame DB-Verbindung fuer Dev-Skripte. - `scripts/dev-db.php`: gemeinsame DB-Verbindung für Dev-Skripte.
- `scripts/golden-master-data.php`: definierte Testdaten und erwartete Werte. - `scripts/golden-master-data.php`: definierte Testdaten und erwartete Werte.
- `scripts/seed-golden-master.php`: legt Golden-Master-Testdaten idempotent an. - `scripts/seed-golden-master.php`: legt Golden-Master-Testdaten idempotent an.
- `scripts/check-golden-master.php`: prueft Salden, Jahreswerte, Rollen, - `scripts/check-golden-master.php`: prüft Salden, Jahreswerte, Rollen,
Listenfilter, CSV-Lookup und Hinweise. Listenfilter, CSV-Lookup und Hinweise.
- `scripts/http-smoke.php`: ruft sichere GET-Seiten per HTTP ab und prueft auf - `scripts/http-smoke.php`: ruft sichere GET-Seiten per HTTP ab und prüft auf
HTTP 200, erwartete Texte und PHP-Fehlermarker. HTTP 200, erwartete Texte und PHP-Fehlermarker.
## Ausfuehrung ## Ausführung
Mit normal installierter PHP-CLI: Mit normal installierter PHP-CLI:
@@ -122,7 +122,7 @@ LD_LIBRARY_PATH="$PWD/.local/php/usr/lib/x86_64-linux-gnu" \
Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`, Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
`DB_USER` und `DB_PASS`. `DB_USER` und `DB_PASS`.
## Abgedeckte Testfaelle ## Abgedeckte Testfälle
| Testfall | Fixture | | Testfall | Fixture |
| --- | --- | | --- | --- |
@@ -131,77 +131,77 @@ Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
| Negativer Saldo | `gm-negative@test.local` | | Negativer Saldo | `gm-negative@test.local` |
| Nullsaldo | `gm-zero@test.local` | | Nullsaldo | `gm-zero@test.local` |
| Inaktives Mitglied | `gm-inactive@test.local` | | Inaktives Mitglied | `gm-inactive@test.local` |
| PayPal-Name fuer CSV-Lookup | `gm-paypal@test.local` | | PayPal-Name für CSV-Lookup | `gm-paypal@test.local` |
| Mitglied ohne Buchungen | `gm-empty@test.local` | | Mitglied ohne Buchungen | `gm-empty@test.local` |
| Vieltrinker fuer Vorderseite | `gm-heavy@test.local` | | Vieltrinker für Vorderseite | `gm-heavy@test.local` |
| Aktiver Hinweis | `[GM] Aktiver Hinweis fuer Golden-Master-Test` | | Aktiver Hinweis | `[GM] Aktiver Hinweis für Golden-Master-Test` |
| Abgelaufener Hinweis | `[GM] Abgelaufener Hinweis fuer Golden-Master-Test` | | Abgelaufener Hinweis | `[GM] Abgelaufener Hinweis für Golden-Master-Test` |
## HTTP-Smoke-Abdeckung ## HTTP-Smoke-Abdeckung
Der HTTP-Smoke-Test prueft nur GET-Seiten ohne bekannte Schreib- oder Der HTTP-Smoke-Test prüft nur GET-Seiten ohne bekannte Schreib- oder
Versand-Nebenwirkungen. Versand-Nebenwirkungen.
| Seite | Erwartung | | Seite | Erwartung |
| --- | --- | | --- | --- |
| `index.php` | Dashboard laedt fuer `Test Admin` | | `index.php` | Dashboard lädt für `Test Admin` |
| `stricheintragen.php` | Stricherfassung laedt | | `stricheintragen.php` | Stricherfassung lädt |
| `einzahlung.php` | Einzahlungserfassung laedt | | `einzahlung.php` | Einzahlungserfassung lädt |
| `kaffeeliste.php` | Gesamtuebersicht laedt | | `kaffeeliste.php` | Gesamtübersicht lädt |
| `mitarbeiterverwalten.php` | Mitgliederverwaltung inkl. `PayPal-Name` laedt | | `mitarbeiterverwalten.php` | Mitgliederverwaltung inkl. `PayPal-Name` lädt |
| `letzteneintraege.php` | letzte Einzahlungen und Striche laden | | `letzteneintraege.php` | letzte Einzahlungen und Striche laden |
| `csvupload.php` | CSV-Upload-Formular laedt | | `csvupload.php` | CSV-Upload-Formular lädt |
| `hinweise.php` | Hinweisverwaltung laedt | | `hinweise.php` | Hinweisverwaltung lädt |
| `faq.php` | FAQ laedt | | `faq.php` | FAQ lädt |
| `namenanpassen.php` | Namensanpassung laedt | | `namenanpassen.php` | Namensanpassung lädt |
| `teilnehmerauswertung.php?user_id=1` | Teilnehmerauswertung fuer Dev-Admin laedt | | `teilnehmerauswertung.php?user_id=1` | Teilnehmerauswertung für Dev-Admin lädt |
| `mailausgebe.php` | Mailadress-Ausgabe laedt | | `mailausgebe.php` | Mailadress-Ausgabe lädt |
| `umfrage.php` | geschlossene Umfrage laedt | | `umfrage.php` | geschlossene Umfrage lädt |
| `umfrageergebnisse.php` | Umfrageauswertung laedt | | `umfrageergebnisse.php` | Umfrageauswertung lädt |
## Akzeptanzkriterien ## Akzeptanzkriterien
- Testdaten sind reproduzierbar. - Testdaten sind reproduzierbar.
- Tests koennen mehrfach laufen, ohne Duplikate zu erzeugen. - Tests können mehrfach laufen, ohne Duplikate zu erzeugen.
- Salden und Jahreswerte sind automatisch pruefbar. - Salden und Jahreswerte sind automatisch prüfbar.
- CSV-Importfaelle sind fachlich abgedeckt. - CSV-Importfälle sind fachlich abgedeckt.
- Die wichtigsten UI-Seiten liefern HTTP 200 ohne PHP-Warnings. - Die wichtigsten UI-Seiten liefern HTTP 200 ohne PHP-Warnings.
- Seiten mit Schreib- oder Versand-Nebenwirkungen werden nicht automatisch per - Seiten mit Schreib- oder Versand-Nebenwirkungen werden nicht automatisch per
GET ausgefuehrt, sondern als Risiko dokumentiert. GET ausgeführt, sondern als Risiko dokumentiert.
## Gefundene Fehler und Bewertung ## Gefundene Fehler und Bewertung
Im M1-Abschlusslauf wurden folgende Punkte geprueft: Im M1-Abschlusslauf wurden folgende Punkte geprüft:
- `number_format(null)` in `kaffeeliste.php`: behoben; leere Summen werden als - `number_format(null)` in `kaffeeliste.php`: behoben; leere Summen werden als
`0` behandelt. `0` behandelt.
- `paypalname` in der Mitgliederverwaltung: eingepflegt und im Golden Master - `paypalname` in der Mitgliederverwaltung: eingepflegt und im Golden Master
abgedeckt. abgedeckt.
- `umfrage.php`: `mb_strtolower()` ist in der lokalen PHP-Umgebung nicht - `umfrage.php`: `mb_strtolower()` ist in der lokalen PHP-Umgebung nicht
verfuegbar; Fallback auf `strtolower()` ergaenzt. verfügbar; Fallback auf `strtolower()` ergänzt.
- `exportKaffeeliste.php`: Pfad auf `TCPDF/tcpdf.php` korrigiert. Der Export - `exportKaffeeliste.php`: Pfad auf `TCPDF/tcpdf.php` korrigiert. Der Export
bleibt trotzdem als bekannter Dependency-Fehler offen, weil die TCPDF-Kopie im bleibt trotzdem als bekannter Dependency-Fehler offen, weil die TCPDF-Kopie im
Repo unvollstaendig ist und `TCPDF/include/tcpdf_font_data.php` fehlt. Repo unvollständig ist und `TCPDF/include/tcpdf_font_data.php` fehlt.
- `mailversenden.php`: nicht automatisch per HTTP ausgefuehrt, weil ein GET bei - `mailversenden.php`: nicht automatisch per HTTP ausgeführt, weil ein GET bei
vollstaendiger PHPMailer-Installation E-Mails versenden kann. Zusaetzlich ist vollständiger PHPMailer-Installation E-Mails versenden kann. Zusätzlich ist
die PHPMailer-Kopie im Repo unvollstaendig. die PHPMailer-Kopie im Repo unvollständig.
- `jahresauswertung.php`: nicht automatisch per HTTP ausgefuehrt, weil ein GET - `jahresauswertung.php`: nicht automatisch per HTTP ausgeführt, weil ein GET
Jahresbuchungen schreiben und E-Mails versenden kann. Zusaetzlich ist die Jahresbuchungen schreiben und E-Mails versenden kann. Zusätzlich ist die
PHPMailer-Kopie im Repo unvollstaendig. PHPMailer-Kopie im Repo unvollständig.
## Aktueller Pruefstatus ## Aktueller Prüfstatus
Stand 2026-07-12: Stand 2026-07-12:
- `scripts/seed-golden-master.php` erfolgreich gegen die MySQL-Dev-Datenbank - `scripts/seed-golden-master.php` erfolgreich gegen die MySQL-Dev-Datenbank
ausgefuehrt. ausgeführt.
- `scripts/check-golden-master.php` erfolgreich ausgefuehrt. - `scripts/check-golden-master.php` erfolgreich ausgeführt.
- Zweiter Seed+Check-Lauf erfolgreich, Idempotenz bestaetigt. - Zweiter Seed+Check-Lauf erfolgreich, Idempotenz bestätigt.
- Ergebnis: 104 Assertions bestanden. - Ergebnis: 104 Assertions bestanden.
- `scripts/http-smoke.php` erfolgreich gegen `http://127.0.0.1:8080` - `scripts/http-smoke.php` erfolgreich gegen `http://127.0.0.1:8080`
ausgefuehrt. ausgeführt.
- Ergebnis: 14 sichere Seiten bestanden ohne `Deprecated`, `Warning`, - Ergebnis: 14 sichere Seiten bestanden ohne `Deprecated`, `Warning`,
`Fatal error`, `Parse error`, `Notice` oder `Uncaught Error`. `Fatal error`, `Parse error`, `Notice` oder `Uncaught Error`.
- Bekannter offener Punkt: PDF-Export wegen unvollstaendiger TCPDF-Abhaengigkeit. - Bekannter offener Punkt: PDF-Export wegen unvollständiger TCPDF-Abhängigkeit.
- Bewusst nicht automatisch ausgefuehrt: `mailversenden.php` und - Bewusst nicht automatisch ausgeführt: `mailversenden.php` und
`jahresauswertung.php`, da GET dort Nebenwirkungen haben kann. `jahresauswertung.php`, da GET dort Nebenwirkungen haben kann.
+53 -53
View File
@@ -2,16 +2,16 @@
Stand: 2026-07-13 Stand: 2026-07-13
M2 fuehrt die technischen Grundbausteine fuer den SaaS-Umbau ein, ohne das M2 führt die technischen Grundbausteine für den SaaS-Umbau ein, ohne das
Legacy-Verhalten oder das bestehende Design zu veraendern. Legacy-Verhalten oder das bestehende Design zu verändern.
Status: abgeschlossen fuer den M2-Scope. Bewusst ausgelagerte Themen sind unten Status: abgeschlossen für den M2-Scope. Bewusst ausgelagerte Themen sind unten
als Grenzen und M3-/M6-Uebergabe dokumentiert. als Grenzen und M3-/M6-Übergabe dokumentiert.
## Ziel ## Ziel
- Versionierte Datenbankmigrationen statt loser Schema-Ausfuehrung. - Versionierte Datenbankmigrationen statt loser Schema-Ausführung.
- Zentrales Bootstrap fuer Umgebung, Session und CSRF-Helper. - Zentrales Bootstrap für Umgebung, Session und CSRF-Helper.
- Bestehende Legacy-Seiten bleiben kompatibel. - Bestehende Legacy-Seiten bleiben kompatibel.
- Layout-Trennung wird vorbereitet, aber noch nicht in bestehende Seiten - Layout-Trennung wird vorbereitet, aber noch nicht in bestehende Seiten
hineingezogen. hineingezogen.
@@ -28,47 +28,47 @@ als Grenzen und M3-/M6-Uebergabe dokumentiert.
- `app_csrf_field()` - `app_csrf_field()`
- `app_verify_csrf()` - `app_verify_csrf()`
- `app_require_csrf()` - `app_require_csrf()`
- `config.php` laedt den Bootstrap und startet die Session mit sicheren - `config.php` lädt den Bootstrap und startet die Session mit sicheren
Cookie-Optionen. Cookie-Optionen.
- Sessions werden standardmaessig unter `var/sessions` abgelegt, weil die - Sessions werden standardmäßig unter `var/sessions` abgelegt, weil die
lokale PHP-Umgebung keinen beschreibbaren Systempfad garantiert. `var/` ist lokale PHP-Umgebung keinen beschreibbaren Systempfad garantiert. `var/` ist
von Git ignoriert. Der Pfad kann ueber `APP_SESSION_PATH` ueberschrieben von Git ignoriert. Der Pfad kann über `APP_SESSION_PATH` überschrieben
werden. werden.
- CSRF ist bewusst noch nicht global erzwungen. Die vorhandenen POST-Seiten - CSRF ist bewusst noch nicht global erzwungen. Die vorhandenen POST-Seiten
werden spaeter einzeln umgestellt, damit keine Formulare oder Spezialflows werden später einzeln umgestellt, damit keine Formulare oder Spezialflows
brechen. brechen.
### CSRF-Rollout ### CSRF-Rollout
Erste Legacy-POST-Seiten sind opt-in abgesichert: Erste Legacy-POST-Seiten sind opt-in abgesichert:
- `hinweise.php`: Hinweis anlegen und Hinweis loeschen. Der bisherige - `hinweise.php`: Hinweis anlegen und Hinweis löschen. Der bisherige
GET-Loeschlink wurde durch ein POST-Formular mit CSRF-Token ersetzt. GET-Löschlink wurde durch ein POST-Formular mit CSRF-Token ersetzt.
- `mitarbeiterverwalten.php`: Mitglied anlegen, Bearbeitungsformular oeffnen, - `mitarbeiterverwalten.php`: Mitglied anlegen, Bearbeitungsformular öffnen,
Mitglied speichern, aktivieren und deaktivieren. Mitglied speichern, aktivieren und deaktivieren.
- `namenanpassen.php`: Anzeigenamen aktualisieren. - `namenanpassen.php`: Anzeigenamen aktualisieren.
- `index.php`: eigene Web-Striche eintragen. - `index.php`: eigene Web-Striche eintragen.
- `stricheintragen.php`: Sammelerfassung von Strichen. - `stricheintragen.php`: Sammelerfassung von Strichen.
- `einzahlung.php`: Sammelerfassung von Einzahlungen. - `einzahlung.php`: Sammelerfassung von Einzahlungen.
- `letzteneintraege.php`: letzte Einzahlungen und Strich-Eintraege loeschen. - `letzteneintraege.php`: letzte Einzahlungen und Strich-Einträge löschen.
- `csvupload.php`: CSV-Zahlungsimport. - `csvupload.php`: CSV-Zahlungsimport.
Noch offen: Noch offen:
- Spezialprozesse: `mailversenden.php`, `jahresauswertung.php`. - Spezialprozesse: `mailversenden.php`, `jahresauswertung.php`.
### CSV-Upload-Haertung ### CSV-Upload-Härtung
- `csvupload.php` nutzt jetzt CSRF. - `csvupload.php` nutzt jetzt CSRF.
- Uploads werden unter `var/uploads` gespeichert und nach der Verarbeitung - Uploads werden unter `var/uploads` gespeichert und nach der Verarbeitung
geloescht. Damit liegen importierte Dateien nicht mehr im Webroot. gelöscht. Damit liegen importierte Dateien nicht mehr im Webroot.
- Dateiendung, Dateigroesse und MIME-Typ werden vor der Verarbeitung geprueft. - Dateiendung, Dateigröße und MIME-Typ werden vor der Verarbeitung geprüft.
- Hochgeladene Dateien bekommen serverseitig erzeugte Zufallsnamen. - Hochgeladene Dateien bekommen serverseitig erzeugte Zufallsnamen.
- CSV-Auswertungswerte werden HTML-escaped ausgegeben. - CSV-Auswertungswerte werden HTML-escaped ausgegeben.
- Die PayPal-Namenssuche nutzt `Name` und `paypalname` mit expliziter - Die PayPal-Namenssuche nutzt `Name` und `paypalname` mit expliziter
Parameterbindung. Parameterbindung.
Offen fuer M6: Offen für M6:
- Importvorschau vor dem Schreiben. - Importvorschau vor dem Schreiben.
- Import-Batch/Audit-Log. - Import-Batch/Audit-Log.
@@ -78,19 +78,19 @@ Offen fuer M6:
- `database/migrations/0001_legacy_mysql_baseline.sql` bildet die bisherige - `database/migrations/0001_legacy_mysql_baseline.sql` bildet die bisherige
MySQL-Dev-Baseline als erste versionierte Migration ab. MySQL-Dev-Baseline als erste versionierte Migration ab.
- `scripts/dev-db.php` verwaltet `schema_migrations` und fuehrt neue - `scripts/dev-db.php` verwaltet `schema_migrations` und führt neue
Migrationen idempotent aus. Migrationen idempotent aus.
- `scripts/migrate.php` ist der direkte Runner fuer Migrationen. - `scripts/migrate.php` ist der direkte Runner für Migrationen.
- `scripts/init-mysql-dev.php` nutzt ab jetzt ebenfalls die Migrationslogik. - `scripts/init-mysql-dev.php` nutzt ab jetzt ebenfalls die Migrationslogik.
### Layout ### Layout
- `header.php` und `footer.php` bleiben vorerst Legacy-Wrapper. - `header.php` und `footer.php` bleiben vorerst Legacy-Wrapper.
- Die spaetere Trennung in Public-Layout und App-Layout wird erst umgesetzt, - Die spätere Trennung in Public-Layout und App-Layout wird erst umgesetzt,
wenn die neue Route-/View-Struktur steht. wenn die neue Route-/View-Struktur steht.
- Die bestehende HTML5-UP-Struktur, Sidebar und Assets bleiben unveraendert. - Die bestehende HTML5-UP-Struktur, Sidebar und Assets bleiben unverändert.
## Ausfuehrung ## Ausführung
Mit normaler PHP-CLI: Mit normaler PHP-CLI:
@@ -111,43 +111,43 @@ LD_LIBRARY_PATH="$PWD/.local/php/usr/lib/x86_64-linux-gnu:$PWD/.local/php/usr/li
``` ```
Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`, Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
`DB_USER` und `DB_PASS`. `scripts/init-mysql-dev.php` braucht zusaetzlich `DB_USER` und `DB_PASS`. `scripts/init-mysql-dev.php` braucht zusätzlich
`DEV_AUTH_EMAIL`. `DEV_AUTH_EMAIL`.
## Aktueller Pruefstatus ## Aktueller Prüfstatus
- Migration `0001_legacy_mysql_baseline.sql` erfolgreich angewendet. - Migration `0001_legacy_mysql_baseline.sql` erfolgreich angewendet.
- Zweiter Migrationslauf meldet: Datenbank ist aktuell. - Zweiter Migrationslauf meldet: Datenbank ist aktuell.
- PHP-Syntax fuer Bootstrap, Migrationen, Init-Skript und Config ist sauber. - PHP-Syntax für Bootstrap, Migrationen, Init-Skript und Config ist sauber.
- Session-Start laeuft in der lokalen Dev-Umgebung ohne PHP-Warnings ueber - Session-Start läuft in der lokalen Dev-Umgebung ohne PHP-Warnings über
`var/sessions`. `var/sessions`.
- CSRF negative Tests: `hinweise.php`, `mitarbeiterverwalten.php` und - CSRF negative Tests: `hinweise.php`, `mitarbeiterverwalten.php` und
`namenanpassen.php` liefern bei POST ohne Token HTTP 419. `namenanpassen.php` liefern bei POST ohne Token HTTP 419.
- CSRF positive Tests: gueltige Token funktionieren fuer Hinweis-Anlage, - CSRF positive Tests: gültige Token funktionieren für Hinweis-Anlage,
Mitglieder-Bearbeitungsformular und Namensanpassung. Der temporaere Mitglieder-Bearbeitungsformular und Namensanpassung. Der temporäre
Testhinweis wurde wieder entfernt. Testhinweis wurde wieder entfernt.
- CSRF negative Tests fuer Buchungsflows: `index.php`, `stricheintragen.php` - CSRF negative Tests für Buchungsflows: `index.php`, `stricheintragen.php`
und `einzahlung.php` liefern bei POST ohne Token HTTP 419. und `einzahlung.php` liefern bei POST ohne Token HTTP 419.
- CSRF positive Tests fuer Buchungsflows: gueltige Token funktionieren fuer - CSRF positive Tests für Buchungsflows: gültige Token funktionieren für
eigene Web-Striche, Sammelstriche und Sammeleinzahlungen. Die temporaeren eigene Web-Striche, Sammelstriche und Sammeleinzahlungen. Die temporären
Testbuchungen wurden wieder entfernt. Testbuchungen wurden wieder entfernt.
- CSRF negative Tests fuer Korrektur-/Loeschflows: `letzteneintraege.php` - CSRF negative Tests für Korrektur-/Löschflows: `letzteneintraege.php`
liefert bei POST ohne Token HTTP 419. liefert bei POST ohne Token HTTP 419.
- CSRF positive Tests fuer Korrektur-/Loeschflows: gueltige Token funktionieren - CSRF positive Tests für Korrektur-/Löschflows: gültige Token funktionieren
fuer das Loeschen temporaerer Einzahlungs- und Strich-Testeintraege. für das Löschen temporärer Einzahlungs- und Strich-Testeinträge.
- CSRF negative Test fuer CSV-Upload: POST ohne Token liefert HTTP 419. - CSRF negative Test für CSV-Upload: POST ohne Token liefert HTTP 419.
- CSV-Upload positive Tests: gueltiges Token verarbeitet eine CSV-Datei, erkennt - CSV-Upload positive Tests: gültiges Token verarbeitet eine CSV-Datei, erkennt
eine PayPal-Alias-Dublette und hinterlaesst keine Datei in `var/uploads`. eine PayPal-Alias-Dublette und hinterlässt keine Datei in `var/uploads`.
- CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen. - CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen.
- Golden Master weiterhin gruen mit 104 Assertions. - Golden Master weiterhin grün mit 104 Assertions.
- HTTP-Smoke weiterhin gruen mit 23 sicheren Seiten inklusive Landingpage, Login, - HTTP-Smoke weiterhin grün mit 23 sicheren Seiten inklusive Landingpage, Login,
Registrierung, Passwort-Reset, E-Mail-Verifikation und geschuetzter Registrierung, Passwort-Reset, E-Mail-Verifikation und geschützter
Mandant-Einstellungen, geschuetzter Mandantenauswahl sowie Ledger-Preview. Mandant-Einstellungen, geschützter Mandantenauswahl sowie Ledger-Preview.
## Bewusste Grenzen ## Bewusste Grenzen
- Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehoeren zu M3. - Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehören zu M3.
- Keine globale CSRF-Erzwingung fuer Spezialprozesse. `mailversenden.php` und - Keine globale CSRF-Erzwingung für Spezialprozesse. `mailversenden.php` und
`jahresauswertung.php` werden in M6 als Jobs mit Dry-Run, Audit und `jahresauswertung.php` werden in M6 als Jobs mit Dry-Run, Audit und
Versandlog neu betrachtet. Versandlog neu betrachtet.
- Kein Layout-Umbau in M2. Die visuelle Struktur bleibt stabil; Public-/App- - Kein Layout-Umbau in M2. Die visuelle Struktur bleibt stabil; Public-/App-
@@ -157,26 +157,26 @@ Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
## Abschlusskriterien ## Abschlusskriterien
- Migrationsrunner ist vorhanden und idempotent. - Migrationsrunner ist vorhanden und idempotent.
- Bootstrap, Session und CSRF-Helper sind zentral verfuegbar. - Bootstrap, Session und CSRF-Helper sind zentral verfügbar.
- Die relevanten Legacy-Schreibseiten sind CSRF-geschuetzt. - Die relevanten Legacy-Schreibseiten sind CSRF-geschützt.
- CSV-Uploads liegen nicht mehr im Webroot. - CSV-Uploads liegen nicht mehr im Webroot.
- Bestehende Legacy-Seiten bleiben per HTTP-Smoke erreichbar. - Bestehende Legacy-Seiten bleiben per HTTP-Smoke erreichbar.
- Golden-Master-Fachwerte bleiben unveraendert. - Golden-Master-Fachwerte bleiben unverändert.
## Uebergabe an M3 ## Übergabe an M3
M3 startet mit additiven SaaS-Tabellen und einer Default-Tenant-Abbildung. Die M3 startet mit additiven SaaS-Tabellen und einer Default-Tenant-Abbildung. Die
Legacy-Seiten sollen dabei weiterhin ueber die bestehenden Tabellen laufen, bis Legacy-Seiten sollen dabei weiterhin über die bestehenden Tabellen laufen, bis
der fachliche Kern in M4/M5 schrittweise auf das Zielmodell umgestellt wird. der fachliche Kern in M4/M5 schrittweise auf das Zielmodell umgestellt wird.
M3 wurde inzwischen gestartet. Umgesetzte Details stehen in M3 wurde inzwischen gestartet. Umgesetzte Details stehen in
`docs/m3-saas-basis-vorbereitung.md`. Der urspruengliche Startpunkt war: `docs/m3-saas-basis-vorbereitung.md`. Der ursprüngliche Startpunkt war:
1. `tenants`, `tenant_settings`, `users`, `tenant_memberships` und 1. `tenants`, `tenant_settings`, `users`, `tenant_memberships` und
`participants` als neue Migration anlegen. `participants` als neue Migration anlegen.
2. Einen Default-Tenant fuer die bestehende Kaffeeliste definieren. 2. Einen Default-Tenant für die bestehende Kaffeeliste definieren.
3. Bestehende `kl_Mitarbeiter` in `participants` spiegeln, inklusive 3. Bestehende `kl_Mitarbeiter` in `participants` spiegeln, inklusive
`legacy_mitarbeiter_id`. `legacy_mitarbeiter_id`.
4. Admins aus `kl_Mitarbeiter.admin` als `tenant_memberships.role = 'admin'` 4. Admins aus `kl_Mitarbeiter.admin` als `tenant_memberships.role = 'admin'`
beziehungsweise fuer den ersten Hauptnutzer als `owner` abbilden. beziehungsweise für den ersten Hauptnutzer als `owner` abbilden.
5. Erst danach Login/Registrierung und Public-/App-Routen anschliessen. 5. Erst danach Login/Registrierung und Public-/App-Routen anschließen.
+61 -61
View File
@@ -2,27 +2,27 @@
Stand: 2026-07-13 Stand: 2026-07-13
M3 fuehrt Mandanten, Benutzer, Mitgliedschaften und Grundeinstellungen additiv M3 führt Mandanten, Benutzer, Mitgliedschaften und Grundeinstellungen additiv
ein. Die bestehende Legacy-App bleibt dabei lauffaehig und wird noch nicht auf ein. Die bestehende Legacy-App bleibt dabei lauffähig und wird noch nicht auf
das neue Modell umgestellt. das neue Modell umgestellt.
Status: abgeschlossen fuer den M3-Scope. M4 startet mit der additiven Status: abgeschlossen für den M3-Scope. M4 startet mit der additiven
Migration von Einzahlungen und Kaffeeverbrauch nach `ledger_entries`. Migration von Einzahlungen und Kaffeeverbrauch nach `ledger_entries`.
## Ziel ## Ziel
- Einen Default-Tenant fuer den aktuellen Bestand erzeugen. - Einen Default-Tenant für den aktuellen Bestand erzeugen.
- Login-Benutzer und Kaffee-Teilnehmer fachlich trennen. - Login-Benutzer und Kaffee-Teilnehmer fachlich trennen.
- Rollen tenant-spezifisch vorbereiten. - Rollen tenant-spezifisch vorbereiten.
- Bestehende `kl_Mitarbeiter` ohne Datenverlust in `participants` spiegeln. - Bestehende `kl_Mitarbeiter` ohne Datenverlust in `participants` spiegeln.
- Die Grundlage fuer Registrierung und Login schaffen, ohne die Legacy-Auth - Die Grundlage für Registrierung und Login schaffen, ohne die Legacy-Auth
sofort zu ersetzen. sofort zu ersetzen.
## Nicht-Ziele fuer den ersten M3-Schritt ## Nicht-Ziele für den ersten M3-Schritt
- Kein kompletter Login-Umbau in einem Schritt. - Kein kompletter Login-Umbau in einem Schritt.
- Keine Migration von Einzahlungen und Kaffeeverbrauch nach `ledger_entries`; - Keine Migration von Einzahlungen und Kaffeeverbrauch nach `ledger_entries`;
das gehoert zu M4. das gehört zu M4.
- Kein Design- oder Layout-Umbau. - Kein Design- oder Layout-Umbau.
- Kein Billing und keine Tarife im ersten Schritt. - Kein Billing und keine Tarife im ersten Schritt.
- Kein produktiver Tenant-Wechsel in bestehenden Legacy-Seiten. - Kein produktiver Tenant-Wechsel in bestehenden Legacy-Seiten.
@@ -47,7 +47,7 @@ Tabellen:
- `participants` - `participants`
- `tenant_domains` - `tenant_domains`
Ergaenzende Skripte: Ergänzende Skripte:
```text ```text
scripts/backfill-default-tenant.php scripts/backfill-default-tenant.php
@@ -73,7 +73,7 @@ Wichtige Regeln:
## Default-Tenant ## Default-Tenant
Fuer den Bestand wird ein erster Tenant angelegt: Für den Bestand wird ein erster Tenant angelegt:
```text ```text
slug: default slug: default
@@ -84,7 +84,7 @@ locale: de-DE
currency_code: EUR currency_code: EUR
``` ```
Die Werte koennen spaeter in einer Tenant-Einstellungsseite angepasst werden. Die Werte können später in einer Tenant-Einstellungsseite angepasst werden.
## Backfill ## Backfill
@@ -98,9 +98,9 @@ Vorgang:
1. Default-Tenant anlegen oder laden. 1. Default-Tenant anlegen oder laden.
2. `tenant_settings` aus `kl_config` erzeugen. 2. `tenant_settings` aus `kl_config` erzeugen.
3. Fuer jede Zeile aus `kl_Mitarbeiter` einen `participant` anlegen oder 3. Für jede Zeile aus `kl_Mitarbeiter` einen `participant` anlegen oder
aktualisieren. aktualisieren.
4. Fuer aktive Admins optional einen `user` und eine `tenant_membership` 4. Für aktive Admins optional einen `user` und eine `tenant_membership`
erzeugen. erzeugen.
5. `admin = 1` als Rolle `admin` abbilden; ein konfigurierter erster Benutzer 5. `admin = 1` als Rolle `admin` abbilden; ein konfigurierter erster Benutzer
kann Rolle `owner` erhalten. kann Rolle `owner` erhalten.
@@ -108,10 +108,10 @@ Vorgang:
aus dem Default-Tenant entfernen. aus dem Default-Tenant entfernen.
Der Backfill muss idempotent sein und darf keine bestehenden Legacy-Daten Der Backfill muss idempotent sein und darf keine bestehenden Legacy-Daten
loeschen. Geloescht werden nur SaaS-Spiegelungen, deren Legacy-Mitarbeiter in löschen. Gelöscht werden nur SaaS-Spiegelungen, deren Legacy-Mitarbeiter in
`kl_Mitarbeiter` nicht mehr existiert. `kl_Mitarbeiter` nicht mehr existiert.
Ausgefuehrter Dev-Stand: Ausgeführter Dev-Stand:
- Default-Tenant `default` wurde angelegt. - Default-Tenant `default` wurde angelegt.
- 9 Legacy-Mitarbeiter wurden als `participants` gespiegelt. - 9 Legacy-Mitarbeiter wurden als `participants` gespiegelt.
@@ -145,15 +145,15 @@ Umgesetzter Umfang:
- Registrierung legt Tenant, Owner-User, Default-Settings, Membership und - Registrierung legt Tenant, Owner-User, Default-Settings, Membership und
Owner-Participant in einer Transaktion an. Owner-Participant in einer Transaktion an.
- Login prueft `users.password_hash`, aktive Membership und aktiven Tenant. - Login prüft `users.password_hash`, aktive Membership und aktiven Tenant.
- Hat ein User genau einen aktiven Mandanten, wird dieser direkt in die Session - Hat ein User genau einen aktiven Mandanten, wird dieser direkt in die Session
gelegt. gelegt.
- Hat ein User mehrere aktive Mandanten, fuehrt der Login zur - Hat ein User mehrere aktive Mandanten, führt der Login zur
Mandantenauswahl. Mandantenauswahl.
- Logout beendet die neue PHP-Login-Session. - Logout beendet die neue PHP-Login-Session.
- `konto.php` zeigt den aktuellen SaaS-Kontext fuer den angemeldeten User. - `konto.php` zeigt den aktuellen SaaS-Kontext für den angemeldeten User.
- `functions.php` akzeptiert eine SaaS-Login-Session als erste Identitaetsquelle, - `functions.php` akzeptiert eine SaaS-Login-Session als erste Identitätsquelle,
laesst `DEV_AUTH_EMAIL` und `AUTH_USER` aber als Legacy-Fallback bestehen. lässt `DEV_AUTH_EMAIL` und `AUTH_USER` aber als Legacy-Fallback bestehen.
- `footer.php` bleibt App-Sidebar und zeigt keine Public-Login- oder - `footer.php` bleibt App-Sidebar und zeigt keine Public-Login- oder
Registrierungslinks mehr. Registrierungslinks mehr.
- Login, Registrierung, Passwort-Reset und E-Mail-Verifikation nutzen das - Login, Registrierung, Passwort-Reset und E-Mail-Verifikation nutzen das
@@ -161,7 +161,7 @@ Umgesetzter Umfang:
- Passwort-Reset erzeugt Single-Use-Tokens und speichert nur Token-Hashes. - Passwort-Reset erzeugt Single-Use-Tokens und speichert nur Token-Hashes.
- E-Mail-Verifikation erzeugt Single-Use-Tokens und setzt - E-Mail-Verifikation erzeugt Single-Use-Tokens und setzt
`users.email_verified_at`. `users.email_verified_at`.
- Reset- und Verifikationslinks werden ueber `app/saas-mail.php` versendet. - Reset- und Verifikationslinks werden über `app/saas-mail.php` versendet.
Der Dev-Standard schreibt Mails nach `var/mail`; produktiv kann auf PHP Der Dev-Standard schreibt Mails nach `var/mail`; produktiv kann auf PHP
`mail()` umgestellt werden. `mail()` umgestellt werden.
- Dev-Links werden weiterhin nur im Dev-Modus angezeigt, damit lokale Checks - Dev-Links werden weiterhin nur im Dev-Modus angezeigt, damit lokale Checks
@@ -169,7 +169,7 @@ Umgesetzter Umfang:
Noch offen im M3-Auth-Scope: Noch offen im M3-Auth-Scope:
- Weitergehende Rollenmatrix fuer spaetere SaaS-Seiten. - Weitergehende Rollenmatrix für spätere SaaS-Seiten.
- Produktive SMTP-/Provider-Anbindung inklusive Bounce-/Fehlerprotokoll. - Produktive SMTP-/Provider-Anbindung inklusive Bounce-/Fehlerprotokoll.
## Public-Landingpage ## Public-Landingpage
@@ -184,35 +184,35 @@ assets/css/public.css
Umgesetzter Umfang: Umgesetzter Umfang:
- Oeffentliche Landingpage ohne Legacy-DB-Zugriff. - Öffentliche Landingpage ohne Legacy-DB-Zugriff.
- Hero mit generiertem Bild-Asset, bestehender Typografie und gruenem Akzent. - Hero mit generiertem Bild-Asset, bestehender Typografie und grünem Akzent.
- CTA zu Login und Registrierung. - CTA zu Login und Registrierung.
- Login, Registrierung und oeffentliche Auth-Hilfsseiten sind optisch in den - Login, Registrierung und öffentliche Auth-Hilfsseiten sind optisch in den
Public-Bereich integriert. Public-Bereich integriert.
- Kurzabschnitt zu Stricherfassung, Mandantenfaehigkeit und Webspace-Betrieb. - Kurzabschnitt zu Stricherfassung, Mandantenfähigkeit und Webspace-Betrieb.
- Die geschuetzte App-Sidebar bleibt von der Public-Seite getrennt. - Die geschützte App-Sidebar bleibt von der Public-Seite getrennt.
## Tenant-Aufloesung ## Tenant-Auflösung
Entscheidung fuer den Webspace-Betrieb: Entscheidung für den Webspace-Betrieb:
- Keine Wildcard-Subdomains im ersten Schritt. - Keine Wildcard-Subdomains im ersten Schritt.
- Primaere App-Adresse ist eine zentrale App-Domain wie `app.kaffeeliste.de`. - Primäre App-Adresse ist eine zentrale App-Domain wie `app.kaffeeliste.de`.
- Die Public-Seite kann ueber `kaffeeliste.de` beziehungsweise - Die Public-Seite kann über `kaffeeliste.de` beziehungsweise
`www.kaffeeliste.de` laufen. `www.kaffeeliste.de` laufen.
- Der aktive Mandant wird nach Login ueber `tenant_memberships` und die PHP- - Der aktive Mandant wird nach Login über `tenant_memberships` und die PHP-
Session gesetzt. Session gesetzt.
- Bei genau einem Mandanten wird automatisch weitergeleitet. - Bei genau einem Mandanten wird automatisch weitergeleitet.
- Bei mehreren Mandanten nutzt der User `mandant-auswahl.php`. - Bei mehreren Mandanten nutzt der User `mandant-auswahl.php`.
- `tenant_domains` ist vorbereitet fuer spaeter gezielt eingerichtete feste - `tenant_domains` ist vorbereitet für später gezielt eingerichtete feste
Domains oder Subdomains, aber nicht Voraussetzung fuer den Start. Domains oder Subdomains, aber nicht Voraussetzung für den Start.
Noch offen: Noch offen:
- `APP_PRIMARY_HOST` in der Zielumgebung setzen. - `APP_PRIMARY_HOST` in der Zielumgebung setzen.
- Optional `APP_PUBLIC_HOST` beziehungsweise Host-Rewrite fuer die Landingpage - Optional `APP_PUBLIC_HOST` beziehungsweise Host-Rewrite für die Landingpage
in der Zielumgebung definieren. in der Zielumgebung definieren.
- Produktive Domain-/Zertifikatspruefung fuer einzelne feste Domains - Produktive Domain-/Zertifikatsprüfung für einzelne feste Domains
definieren. definieren.
## Rollen und Grundeinstellungen ## Rollen und Grundeinstellungen
@@ -226,43 +226,43 @@ mandant-einstellungen.php
Umgesetzter Umfang: Umgesetzter Umfang:
- `saas_user_has_role()`, `saas_require_role()` und - `saas_user_has_role()`, `saas_require_role()` und
`saas_can_manage_tenant_settings()` zentralisieren die erste Rollenpruefung. `saas_can_manage_tenant_settings()` zentralisieren die erste Rollenprüfung.
- Owner und Admin duerfen Tenant-Grundeinstellungen bearbeiten. - Owner und Admin dürfen Tenant-Grundeinstellungen bearbeiten.
- Die Seite bearbeitet Kundenname, Zeitzone, Locale, Waehrung, Preis pro - Die Seite bearbeitet Kundenname, Zeitzone, Locale, Währung, Preis pro
Strich, Web-Striche, PayPal-Link, Listenfenster und Schulden-Warnschwelle. Strich, Web-Striche, PayPal-Link, Listenfenster und Schulden-Warnschwelle.
- Geldwerte werden in der UI als Euro-Werte erfasst und weiterhin als Cent- - Geldwerte werden in der UI als Euro-Werte erfasst und weiterhin als Cent-
Integer gespeichert. Integer gespeichert.
- `konto.php` und die Sidebar verlinken die Einstellungen nur fuer passende - `konto.php` und die Sidebar verlinken die Einstellungen nur für passende
Rollen. Rollen.
## Erste Akzeptanzkriterien ## Erste Akzeptanzkriterien
- Migrationen laufen mehrfach ohne Fehler: erfuellt. - Migrationen laufen mehrfach ohne Fehler: erfüllt.
- Default-Tenant existiert genau einmal: erfuellt. - Default-Tenant existiert genau einmal: erfüllt.
- `tenant_settings` enthalten Preis pro Strich und bestehende PayPal-Optionen: - `tenant_settings` enthalten Preis pro Strich und bestehende PayPal-Optionen:
erfuellt. erfüllt.
- Jeder bestehende `kl_Mitarbeiter` hat genau einen `participant`: erfuellt. - Jeder bestehende `kl_Mitarbeiter` hat genau einen `participant`: erfüllt.
- `participants.legacy_mitarbeiter_id` ist gesetzt: erfuellt. - `participants.legacy_mitarbeiter_id` ist gesetzt: erfüllt.
- Admins werden als tenant-scoped Rolle abgebildet: erfuellt. - Admins werden als tenant-scoped Rolle abgebildet: erfüllt.
- Registrierung und Login funktionieren fuer einen neuen Test-Tenant: - Registrierung und Login funktionieren für einen neuen Test-Tenant:
erfuellt. erfüllt.
- Owner-/Admin-Grundeinstellungen koennen aktualisiert werden: erfuellt. - Owner-/Admin-Grundeinstellungen können aktualisiert werden: erfüllt.
- Passwort-Reset und E-Mail-Verifikation funktionieren mit Single-Use-Tokens: - Passwort-Reset und E-Mail-Verifikation funktionieren mit Single-Use-Tokens:
erfuellt. erfüllt.
- Reset- und Verifikationslinks werden im Dev-/Testmodus als Mail-Log erzeugt: - Reset- und Verifikationslinks werden im Dev-/Testmodus als Mail-Log erzeugt:
erfuellt. erfüllt.
- Mandantenauswahl funktioniert fuer User mit mehreren Mandanten: erfuellt. - Mandantenauswahl funktioniert für User mit mehreren Mandanten: erfüllt.
- Oeffentliche Landingpage ist per HTTP-Smoke erreichbar: erfuellt. - Öffentliche Landingpage ist per HTTP-Smoke erreichbar: erfüllt.
- Golden-Master und HTTP-Smoke bleiben gruen: erfuellt. - Golden-Master und HTTP-Smoke bleiben grün: erfüllt.
## Risiken ## Risiken
| Risiko | Gegenmassnahme | | Risiko | Gegenmaßnahme |
| --- | --- | | --- | --- |
| Login-User und Kaffee-Teilnehmer werden vermischt | `users` und `participants` strikt getrennt halten | | Login-User und Kaffee-Teilnehmer werden vermischt | `users` und `participants` strikt getrennt halten |
| Mehrfacher Backfill erzeugt Duplikate | Eindeutige Constraints und Upsert-Logik | | Mehrfacher Backfill erzeugt Duplikate | Eindeutige Constraints und Upsert-Logik |
| Legacy-App bricht durch neue Tabellen | M3 nur additiv, keine Legacy-Queries umstellen | | 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 | | Rollen werden global statt tenant-scoped | Rollen ausschließlich in `tenant_memberships` speichern |
| Falsche Owner-Zuordnung | Owner per Env-Konfiguration oder manuell dokumentierter Entscheidung setzen | | Falsche Owner-Zuordnung | Owner per Env-Konfiguration oder manuell dokumentierter Entscheidung setzen |
## Empfohlene Reihenfolge ## Empfohlene Reihenfolge
@@ -270,24 +270,24 @@ Umgesetzter Umfang:
1. Migration `0002_saas_identity_tenants.sql` erstellen: erledigt. 1. Migration `0002_saas_identity_tenants.sql` erstellen: erledigt.
2. Migration `0003_saas_auth_account_fields.sql` erstellen: erledigt. 2. Migration `0003_saas_auth_account_fields.sql` erstellen: erledigt.
3. `scripts/backfill-default-tenant.php` erstellen: erledigt. 3. `scripts/backfill-default-tenant.php` erstellen: erledigt.
4. Backfill gegen die Dev-Datenbank ausfuehren: erledigt. 4. Backfill gegen die Dev-Datenbank ausführen: erledigt.
5. Kontrollskript fuer Tenant/Participant/Role-Counts schreiben: erledigt. 5. Kontrollskript für Tenant/Participant/Role-Counts schreiben: erledigt.
6. Login-/Registrierungsrouten bauen: erledigt. 6. Login-/Registrierungsrouten bauen: erledigt.
7. Auth-Flow-Kontrollskript schreiben: erledigt. 7. Auth-Flow-Kontrollskript schreiben: erledigt.
8. Zentrale Rollenpruefung und Mandant-Einstellungen bauen: erledigt. 8. Zentrale Rollenprüfung und Mandant-Einstellungen bauen: erledigt.
9. Settings-Flow-Kontrollskript schreiben: erledigt. 9. Settings-Flow-Kontrollskript schreiben: erledigt.
10. Migration `0004_saas_auth_tokens.sql` erstellen: erledigt. 10. Migration `0004_saas_auth_tokens.sql` erstellen: erledigt.
11. Passwort-Reset und E-Mail-Verifikation bauen: erledigt. 11. Passwort-Reset und E-Mail-Verifikation bauen: erledigt.
12. Password-/E-Mail-Flow-Kontrollskript schreiben: erledigt. 12. Password-/E-Mail-Flow-Kontrollskript schreiben: erledigt.
13. Migration `0005_saas_tenant_resolution.sql` erstellen: erledigt. 13. Migration `0005_saas_tenant_resolution.sql` erstellen: erledigt.
14. Mandantenauswahl und zentrale Session-Aufloesung bauen: erledigt. 14. Mandantenauswahl und zentrale Session-Auflösung bauen: erledigt.
15. Tenant-Resolution-Kontrollskript schreiben: erledigt. 15. Tenant-Resolution-Kontrollskript schreiben: erledigt.
16. Golden-Master und HTTP-Smoke ausfuehren: erledigt. 16. Golden-Master und HTTP-Smoke ausführen: erledigt.
17. Mail-Transport-Abstraktion und Mail-Flow-Kontrollskript bauen: erledigt. 17. Mail-Transport-Abstraktion und Mail-Flow-Kontrollskript bauen: erledigt.
18. Public-Landingpage mit erstem Hero-Asset vorbereiten: erledigt. 18. Public-Landingpage mit erstem Hero-Asset vorbereiten: erledigt.
19. M3-Abschlusscheck dokumentieren und M4-Datenmigration starten: erledigt. 19. M3-Abschlusscheck dokumentieren und M4-Datenmigration starten: erledigt.
## Uebergabe an M4 ## Übergabe an M4
M4 ist in `docs/m4-data-migration.md` dokumentiert. Der erste M4-Schritt ist M4 ist in `docs/m4-data-migration.md` dokumentiert. Der erste M4-Schritt ist
bewusst additiv: bewusst additiv:
+39 -39
View File
@@ -2,20 +2,20 @@
Stand: 2026-07-14 Stand: 2026-07-14
M4 uebernimmt die finanznahen Legacy-Buchungen additiv in das neue M4 übernimmt die finanznahen Legacy-Buchungen additiv in das neue
tenant-sichere Ledger-Modell. Die bestehenden Legacy-Seiten lesen und schreiben tenant-sichere Ledger-Modell. Die bestehenden Legacy-Seiten lesen und schreiben
weiterhin die bisherigen `kl_*`-Tabellen; das Ledger dient zunaechst als weiterhin die bisherigen `kl_*`-Tabellen; das Ledger dient zunächst als
vergleichbare, pruefbare Zielstruktur. Erste App-Abfragen koennen nun ueber vergleichbare, prüfbare Zielstruktur. Erste App-Abfragen können nun über
einen zentralen Ledger-Service gegen `ledger_entries` laufen. einen zentralen Ledger-Service gegen `ledger_entries` laufen.
## Ziel ## Ziel
- `kl_Einzahlungen` nach `ledger_entries` spiegeln. - `kl_Einzahlungen` nach `ledger_entries` spiegeln.
- `kl_Kaffeeverbrauch` nach `ledger_entries` spiegeln. - `kl_Kaffeeverbrauch` nach `ledger_entries` spiegeln.
- Legacy-IDs erhalten, damit Paritaet und spaetere Delta-Migration moeglich - Legacy-IDs erhalten, damit Parität und spätere Delta-Migration möglich
bleiben. bleiben.
- Summen gegen den Golden Master pruefen. - Summen gegen den Golden Master prüfen.
- Keine bestehenden Legacy-Buchungen loeschen oder veraendern. - Keine bestehenden Legacy-Buchungen löschen oder verändern.
## Nicht-Ziele ## Nicht-Ziele
@@ -36,7 +36,7 @@ Neue Tabelle:
- `ledger_entries` - `ledger_entries`
Ergaenzende Skripte: Ergänzende Skripte:
```text ```text
scripts/backfill-ledger-entries.php scripts/backfill-ledger-entries.php
@@ -44,20 +44,20 @@ scripts/check-m4-ledger-migration.php
scripts/check-m4-ledger-service.php scripts/check-m4-ledger-service.php
``` ```
Ergaenzende App-Dateien: Ergänzende App-Dateien:
```text ```text
app/ledger.php app/ledger.php
ledger-preview.php ledger-preview.php
``` ```
Ausgefuehrter Dev-Stand: Ausgeführter Dev-Stand:
- Migration `0006_saas_ledger_entries.sql` wurde angewendet. - Migration `0006_saas_ledger_entries.sql` wurde angewendet.
- 9 Legacy-Einzahlungen wurden als `payment` gespiegelt. - 9 Legacy-Einzahlungen wurden als `payment` gespiegelt.
- 12 Legacy-Kaffeeverbrauch-Zeilen wurden als `consumption` gespiegelt. - 12 Legacy-Kaffeeverbrauch-Zeilen wurden als `consumption` gespiegelt.
- Ein zweiter Backfill-Lauf blieb idempotent und erzeugte keine Dubletten. - Ein zweiter Backfill-Lauf blieb idempotent und erzeugte keine Dubletten.
- `app/ledger.php` stellt zentrale Abfragen fuer Tenant-Summen, - `app/ledger.php` stellt zentrale Abfragen für Tenant-Summen,
Teilnehmer-Summen, einzelne Teilnehmer und letzte Buchungen bereit. Teilnehmer-Summen, einzelne Teilnehmer und letzte Buchungen bereit.
- `ledger-preview.php` zeigt eine read-only App-Vorschau auf Tenant-Summen, - `ledger-preview.php` zeigt eine read-only App-Vorschau auf Tenant-Summen,
aktive Teilnehmer und die letzten Ledger-Buchungen. aktive Teilnehmer und die letzten Ledger-Buchungen.
@@ -109,16 +109,16 @@ ledger_entries.legacy_id = VerbrauchID
Verbrauch wird negativ gespeichert. Damit ergibt `SUM(amount_cents)` direkt den Verbrauch wird negativ gespeichert. Damit ergibt `SUM(amount_cents)` direkt den
aktuellen Saldo eines Teilnehmers. aktuellen Saldo eines Teilnehmers.
## Idempotenz und Aufraeumen ## Idempotenz und Aufräumen
Das Backfill-Skript nutzt `tenant_id`, `legacy_table` und `legacy_id` als Das Backfill-Skript nutzt `tenant_id`, `legacy_table` und `legacy_id` als
eindeutige Quelle. Wiederholte Laeufe aktualisieren vorhandene Ledger-Zeilen. eindeutige Quelle. Wiederholte Läufe aktualisieren vorhandene Ledger-Zeilen.
Fuer den Default-Tenant werden nur solche Legacy-Spiegelungen entfernt, deren Für den Default-Tenant werden nur solche Legacy-Spiegelungen entfernt, deren
urspruengliche Legacy-Zeile nicht mehr existiert. Fachliche Daten werden dabei ursprüngliche Legacy-Zeile nicht mehr existiert. Fachliche Daten werden dabei
nicht aus den `kl_*`-Tabellen geloescht. nicht aus den `kl_*`-Tabellen gelöscht.
## Ausfuehrung ## Ausführung
```bash ```bash
php scripts/backfill-default-tenant.php php scripts/backfill-default-tenant.php
@@ -133,33 +133,33 @@ Installation aus `.local/` verwendet werden.
## Akzeptanzkriterien ## Akzeptanzkriterien
- Migration `0006_saas_ledger_entries.sql` laeuft erfolgreich: erfuellt. - Migration `0006_saas_ledger_entries.sql` läuft erfolgreich: erfüllt.
- Jede Legacy-Einzahlung hat genau eine Ledger-Zeile: erfuellt. - Jede Legacy-Einzahlung hat genau eine Ledger-Zeile: erfüllt.
- Jeder Legacy-Kaffeeverbrauch hat genau eine Ledger-Zeile: erfuellt. - Jeder Legacy-Kaffeeverbrauch hat genau eine Ledger-Zeile: erfüllt.
- Legacy-IDs sind je Tenant eindeutig: erfuellt. - Legacy-IDs sind je Tenant eindeutig: erfüllt.
- Einzahlungen sind positiv, Verbrauch ist negativ: erfuellt. - Einzahlungen sind positiv, Verbrauch ist negativ: erfüllt.
- Verbrauch behaelt Strichanzahl und Preis pro Strich: erfuellt. - Verbrauch behält Strichanzahl und Preis pro Strich: erfüllt.
- Web-Striche aus `Eintragsart = 2` bleiben als `source = legacy_web` - Web-Striche aus `Eintragsart = 2` bleiben als `source = legacy_web`
erkennbar: erfuellt. erkennbar: erfüllt.
- Ledger-Summen entsprechen dem Golden Master: erfuellt. - Ledger-Summen entsprechen dem Golden Master: erfüllt.
- Zentrale Ledger-Abfragen liefern dieselben Teilnehmer-Summen wie der Golden - Zentrale Ledger-Abfragen liefern dieselben Teilnehmer-Summen wie der Golden
Master: erfuellt. Master: erfüllt.
- Tenant-Summen, aktive Teilnehmer, Einzelteilnehmer und letzte Buchungen sind - Tenant-Summen, aktive Teilnehmer, Einzelteilnehmer und letzte Buchungen sind
ueber `app/ledger.php` abrufbar: erfuellt. über `app/ledger.php` abrufbar: erfüllt.
- Read-only Ledger-Preview ist im Browser erreichbar: erfuellt. - Read-only Ledger-Preview ist im Browser erreichbar: erfüllt.
## Aktueller Pruefstatus ## Aktueller Prüfstatus
- M4 Ledger-Migration: gruen mit 73 Assertions. - M4 Ledger-Migration: grün mit 73 Assertions.
- M4 Ledger-Service: gruen mit 110 Assertions. - M4 Ledger-Service: grün mit 110 Assertions.
- M3 SaaS-Basis: weiterhin gruen. - M3 SaaS-Basis: weiterhin grün.
- M3 Tenant-Aufloesung: weiterhin gruen. - M3 Tenant-Auflösung: weiterhin grün.
- M3 Passwort/E-Mail: weiterhin gruen. - M3 Passwort/E-Mail: weiterhin grün.
- M3 Auth-Flow: weiterhin gruen. - M3 Auth-Flow: weiterhin grün.
- M3 Settings-Flow: weiterhin gruen. - M3 Settings-Flow: weiterhin grün.
- M3 Mail-Flow: weiterhin gruen. - M3 Mail-Flow: weiterhin grün.
- Golden Master: weiterhin gruen mit 104 Assertions. - Golden Master: weiterhin grün mit 104 Assertions.
- HTTP-Smoke: weiterhin gruen mit 23 geprueften Seiten inklusive - HTTP-Smoke: weiterhin grün mit 23 geprüften Seiten inklusive
`ledger-preview.php`. `ledger-preview.php`.
## Noch offen in M4 ## Noch offen in M4
@@ -167,4 +167,4 @@ Installation aus `.local/` verwendet werden.
- Tenant-sichere Dashboard-, Strich-, Einzahlungs- und Listenqueries schrittweise - Tenant-sichere Dashboard-, Strich-, Einzahlungs- und Listenqueries schrittweise
von Legacy-Tabellen auf das Ledger umstellen. von Legacy-Tabellen auf das Ledger umstellen.
- Storno-/Reversal-Modell in der UI statt harter Deletes. - Storno-/Reversal-Modell in der UI statt harter Deletes.
- Delta-Strategie fuer den finalen Cutover. - Delta-Strategie für den finalen Cutover.
+14 -14
View File
@@ -4,13 +4,13 @@ Stand: 2026-07-14
M5 stellt die operativen App-Seiten schrittweise auf das neue tenant-sichere M5 stellt die operativen App-Seiten schrittweise auf das neue tenant-sichere
Modell um. Der Start erfolgt bewusst read-only, damit Summen, Links und Modell um. Der Start erfolgt bewusst read-only, damit Summen, Links und
Darstellung gegen die bestehende Legacy-Oberflaeche vergleichbar bleiben. Darstellung gegen die bestehende Legacy-Oberfläche vergleichbar bleiben.
## Ziel ## Ziel
- Kernseiten aus `app/ledger.php` lesen lassen. - Kernseiten aus `app/ledger.php` lesen lassen.
- Bestehendes Tabellenlayout und Bediengefuehl erhalten. - Bestehendes Tabellenlayout und Bediengefühl erhalten.
- Schreibende Legacy-Flows erst nach stabiler Leseparitaet umstellen. - Schreibende Legacy-Flows erst nach stabiler Leseparität umstellen.
- Tenant-Kontext serverseitig bestimmen, nicht aus Formularfeldern. - Tenant-Kontext serverseitig bestimmen, nicht aus Formularfeldern.
## Umgesetzter erster Schritt ## Umgesetzter erster Schritt
@@ -23,18 +23,18 @@ kaffeeliste.php
Umgesetzter Umfang: Umgesetzter Umfang:
- Die Gesamtuebersicht liest aktive Teilnehmer aus - Die Gesamtübersicht liest aktive Teilnehmer aus
`ledger_fetch_participant_summaries()`. `ledger_fetch_participant_summaries()`.
- Angezeigte Werte bleiben fachlich gleich: aktueller Stand, Gesamtausgabe, - Angezeigte Werte bleiben fachlich gleich: aktueller Stand, Gesamtausgabe,
Gesamtstriche und Gesamteinzahlungen. Gesamtstriche und Gesamteinzahlungen.
- Links zur bestehenden `teilnehmerauswertung.php` bleiben ueber - Links zur bestehenden `teilnehmerauswertung.php` bleiben über
`participants.legacy_mitarbeiter_id` erhalten. `participants.legacy_mitarbeiter_id` erhalten.
- Owner, Admin und Treasurer duerfen die SaaS-Ansicht lesen. - Owner, Admin und Treasurer dürfen die SaaS-Ansicht lesen.
- Der lokale Legacy-/Dev-Admin-Fallback nutzt nur ohne SaaS-Session den - Der lokale Legacy-/Dev-Admin-Fallback nutzt nur ohne SaaS-Session den
Default-Tenant. Default-Tenant.
- Die Sidebar zeigt Legacy-Schreibseiten weiterhin nur fuer Legacy-Admins; - Die Sidebar zeigt Legacy-Schreibseiten weiterhin nur für Legacy-Admins;
Ledger-Leseansichten sind fuer passende SaaS-Rollen sichtbar. Ledger-Leseansichten sind für passende SaaS-Rollen sichtbar.
- Export, letzte Eintraege, CSV-Upload und Info-Mail bleiben noch Legacy-Flows. - Export, letzte Einträge, CSV-Upload und Info-Mail bleiben noch Legacy-Flows.
## Noch offen ## Noch offen
@@ -44,9 +44,9 @@ Umgesetzter Umfang:
Storno-/Reversal-Strategie vorbereiten. Storno-/Reversal-Strategie vorbereiten.
- Export, Mail und Jahresprozesse bleiben M6-Themen. - Export, Mail und Jahresprozesse bleiben M6-Themen.
## Aktueller Pruefstatus ## Aktueller Prüfstatus
- M4 Ledger-Migration: gruen mit 73 Assertions. - M4 Ledger-Migration: grün mit 73 Assertions.
- M4 Ledger-Service: gruen mit 110 Assertions. - M4 Ledger-Service: grün mit 110 Assertions.
- Golden Master: gruen mit 104 Assertions. - Golden Master: grün mit 104 Assertions.
- HTTP-Smoke: gruen mit 23 geprueften Seiten. - HTTP-Smoke: grün mit 23 geprüften Seiten.
+151 -151
View File
@@ -3,28 +3,28 @@
Stand: 2026-07-13 Stand: 2026-07-13
Dieses Dokument beschreibt den geplanten Umbau der bestehenden Kaffeelisten-App zu Dieses Dokument beschreibt den geplanten Umbau der bestehenden Kaffeelisten-App zu
einer mehrkundenfaehigen SaaS-Anwendung mit oeffentlicher Landingpage, einer mehrkundenfähigen SaaS-Anwendung mit öffentlicher Landingpage,
Kundenregistrierung und geschuetzter App. Die bestehende Bedienlogik und das Kundenregistrierung und geschützter App. Die bestehende Bedienlogik und das
visuelle Grunddesign sollen bewusst erhalten bleiben. visuelle Grunddesign sollen bewusst erhalten bleiben.
## Zielbild ## Zielbild
- Mehrere Kunden koennen eigene Kaffeelisten betreiben. - Mehrere Kunden können eigene Kaffeelisten betreiben.
- Jeder Kunde hat isolierte Daten, Einstellungen, Mitglieder und Buchungen. - Jeder Kunde hat isolierte Daten, Einstellungen, Mitglieder und Buchungen.
- Neue Kunden koennen sich ueber eine oeffentliche Landingpage registrieren. - Neue Kunden können sich über eine öffentliche Landingpage registrieren.
- Die operative App bleibt optisch nah am aktuellen Bestand: Sidebar, Tabellen, - Die operative App bleibt optisch nah am aktuellen Bestand: Sidebar, Tabellen,
schlichte Formulare, HTML5-UP-Anmutung. schlichte Formulare, HTML5-UP-Anmutung.
- Authentifizierung, Rollen, Mandantenkontext und Sicherheitspruefungen werden - Authentifizierung, Rollen, Mandantenkontext und Sicherheitsprüfungen werden
zentralisiert. zentralisiert.
- Finanznahe Vorgaenge werden nachvollziehbar und revisionsfreundlich - Finanznahe Vorgänge werden nachvollziehbar und revisionsfreundlich
gespeichert. gespeichert.
## Nicht-Ziele fuer den ersten Umbau ## Nicht-Ziele für den ersten Umbau
- Kein kompletter Design-Relaunch. - Kein kompletter Design-Relaunch.
- Keine verspielte Marketing-App statt der bestehenden Arbeitsoberflaeche. - Keine verspielte Marketing-App statt der bestehenden Arbeitsoberfläche.
- Kein gleichzeitiger Neubau aller Randmodule, wenn diese fuer den MVP nicht - Kein gleichzeitiger Neubau aller Randmodule, wenn diese für den MVP nicht
benoetigt werden. benötigt werden.
- Kein Dual-Write zwischen Legacy und neuer App als Standardbetrieb. - Kein Dual-Write zwischen Legacy und neuer App als Standardbetrieb.
- Keine Mandanten-ID aus Formularfeldern oder URL-Parametern als alleinige - Keine Mandanten-ID aus Formularfeldern oder URL-Parametern als alleinige
Sicherheitsbasis. Sicherheitsbasis.
@@ -35,12 +35,12 @@ Die aktuelle App ist eine flache PHP/sqlsrv-Legacy-App im Webroot.
Wichtige Bereiche: Wichtige Bereiche:
- `index.php`: persoenliches Dashboard. - `index.php`: persönliches Dashboard.
- `stricheintragen.php`: Sammelerfassung von Kaffee-Strichen. - `stricheintragen.php`: Sammelerfassung von Kaffee-Strichen.
- `einzahlung.php`: Sammelerfassung von Einzahlungen. - `einzahlung.php`: Sammelerfassung von Einzahlungen.
- `kaffeeliste.php`: Gesamtuebersicht. - `kaffeeliste.php`: Gesamtübersicht.
- `mitarbeiterverwalten.php`: Mitglieder- und Adminpflege. - `mitarbeiterverwalten.php`: Mitglieder- und Adminpflege.
- `letzteneintraege.php`: Korrektur beziehungsweise Loeschung letzter Buchungen. - `letzteneintraege.php`: Korrektur beziehungsweise Löschung letzter Buchungen.
- `csvupload.php`: Zahlungsimport. - `csvupload.php`: Zahlungsimport.
- `exportKaffeeliste.php`: PDF-/Listenexport. - `exportKaffeeliste.php`: PDF-/Listenexport.
- `mailversenden.php`: Massenmail. - `mailversenden.php`: Massenmail.
@@ -49,7 +49,7 @@ Wichtige Bereiche:
Zentrale technische Beobachtungen: Zentrale technische Beobachtungen:
- Authentifizierung haengt an Windows/IIS `AUTH_USER` und LDAP. - Authentifizierung hängt an Windows/IIS `AUTH_USER` und LDAP.
- Die Mailadresse des Nutzers wird in `functionsLDAP.php` ermittelt. - Die Mailadresse des Nutzers wird in `functionsLDAP.php` ermittelt.
- Rollen liegen als Boolean `admin` direkt auf `kl_Mitarbeiter`. - Rollen liegen als Boolean `admin` direkt auf `kl_Mitarbeiter`.
- Datenzugriff findet direkt in den einzelnen PHP-Seiten per `sqlsrv_query` - Datenzugriff findet direkt in den einzelnen PHP-Seiten per `sqlsrv_query`
@@ -66,23 +66,23 @@ mit einem stabilen SaaS-Fundament:
1. Bestand dokumentieren und fachliche Ergebnisse einfrieren. 1. Bestand dokumentieren und fachliche Ergebnisse einfrieren.
2. Mandantenmodell, Auth und Rollen sauber aufbauen. 2. Mandantenmodell, Auth und Rollen sauber aufbauen.
3. Bestehende Fachlogik schrittweise tenant-sicher uebernehmen. 3. Bestehende Fachlogik schrittweise tenant-sicher übernehmen.
4. Landingpage und App-Shell trennen, ohne das App-Design neu zu erfinden. 4. Landingpage und App-Shell trennen, ohne das App-Design neu zu erfinden.
So bleibt die vertraute Kaffeelisten-Oberflaeche erhalten, waehrend die So bleibt die vertraute Kaffeelisten-Oberfläche erhalten, während die
Datenbasis und Sicherheit SaaS-faehig werden. Datenbasis und Sicherheit SaaS-fähig werden.
## Zielarchitektur ## Zielarchitektur
### Public-Bereich ### Public-Bereich
Der Public-Bereich ist ohne Login erreichbar und laedt keine Mandantendaten. Der Public-Bereich ist ohne Login erreichbar und lädt keine Mandantendaten.
Empfohlene Routen: Empfohlene Routen:
- `/`: Landingpage. - `/`: Landingpage.
- `/preise` oder spaeter `/pricing`: optional, falls Tarife eingefuehrt werden. - `/preise` oder später `/pricing`: optional, falls Tarife eingeführt werden.
- `/faq`: oeffentliche FAQ oder FAQ-Auszug. - `/faq`: öffentliche FAQ oder FAQ-Auszug.
- `/registrieren` beziehungsweise aktuell `register.php`: Kundenregistrierung. - `/registrieren` beziehungsweise aktuell `register.php`: Kundenregistrierung.
- `/login` beziehungsweise aktuell `login.php`: Login. - `/login` beziehungsweise aktuell `login.php`: Login.
- `/passwort-vergessen` beziehungsweise aktuell `passwort-vergessen.php`: - `/passwort-vergessen` beziehungsweise aktuell `passwort-vergessen.php`:
@@ -91,16 +91,16 @@ Empfohlene Routen:
Landingpage-Inhalte: Landingpage-Inhalte:
- Hero mit Name `Kaffeeliste` und kurzer Nutzenbeschreibung. - Hero mit Name `Kaffeeliste` und kurzer Nutzenbeschreibung.
- Drei Kernablaeufe: Kaffee nehmen, Strich setzen, bei Bedarf bezahlen. - Drei Kernabläufe: Kaffee nehmen, Strich setzen, bei Bedarf bezahlen.
- Kurzer Blick auf Funktionen: Mitglieder, Guthaben, Einzahlungen, Export, - Kurzer Blick auf Funktionen: Mitglieder, Guthaben, Einzahlungen, Export,
Hinweise. Hinweise.
- App-Screenshot oder Demo-Ansicht im bestehenden Stil. - App-Screenshot oder Demo-Ansicht im bestehenden Stil.
- FAQ-Auszug. - FAQ-Auszug.
- Call-to-Action zu Registrierung und Login. - Call-to-Action zu Registrierung und Login.
### Geschuetzte App ### Geschützte App
Die geschuetzte App bleibt die operative Arbeitsoberflaeche. Die geschützte App bleibt die operative Arbeitsoberfläche.
Empfohlene Routen: Empfohlene Routen:
@@ -109,8 +109,8 @@ Empfohlene Routen:
- `/app/striche`: Eigene Striche erfassen, falls erlaubt. - `/app/striche`: Eigene Striche erfassen, falls erlaubt.
- `/app/einzahlungen`: Zahlungen und PayPal-Optionen. - `/app/einzahlungen`: Zahlungen und PayPal-Optionen.
- `/app/mitglieder`: Mitgliederverwaltung. - `/app/mitglieder`: Mitgliederverwaltung.
- `/app/liste`: Gesamtuebersicht. - `/app/liste`: Gesamtübersicht.
- `/app/buchungen`: Letzte Eintraege und Korrekturen. - `/app/buchungen`: Letzte Einträge und Korrekturen.
- `/app/importe`: CSV-Importe. - `/app/importe`: CSV-Importe.
- `/app/export/pdf`: PDF-/Listenexport. - `/app/export/pdf`: PDF-/Listenexport.
- `/app/hinweise`: Hinweise. - `/app/hinweise`: Hinweise.
@@ -118,29 +118,29 @@ Empfohlene Routen:
Die App sollte eine Sidebar behalten, aber besser gruppiert werden: Die App sollte eine Sidebar behalten, aber besser gruppiert werden:
- Persoenlich: Meine Kaffeeliste, Namensanpassung, FAQ. - Persönlich: Meine Kaffeeliste, Namensanpassung, FAQ.
- Erfassung: Striche, Einzahlungen. - Erfassung: Striche, Einzahlungen.
- Auswertung: Kaffeeliste, Buchungen, Export. - Auswertung: Kaffeeliste, Buchungen, Export.
- Administration: Mitglieder, Hinweise, Einstellungen, Importe. - Administration: Mitglieder, Hinweise, Einstellungen, Importe.
- Konto: Kundenkonto und Logout. Public-Links wie Login und Registrierung - Konto: Kundenkonto und Logout. Public-Links wie Login und Registrierung
gehoeren nicht in die App-Sidebar. gehören nicht in die App-Sidebar.
### Webspace- und Host-Strategie ### Webspace- und Host-Strategie
Empfohlen fuer den Start: Empfohlen für den Start:
- `kaffeeliste.de` oder `www.kaffeeliste.de` fuer Landingpage, Registrierung - `kaffeeliste.de` oder `www.kaffeeliste.de` für Landingpage, Registrierung
und Login. und Login.
- `app.kaffeeliste.de` fuer die geschuetzte App. - `app.kaffeeliste.de` für die geschützte App.
- Keine Wildcard-Subdomains fuer Kunden. - Keine Wildcard-Subdomains für Kunden.
- Mandantenauswahl nach Login ueber Session und `tenant_memberships`. - Mandantenauswahl nach Login über Session und `tenant_memberships`.
- Kundeneigene feste Domains oder Subdomains erst spaeter gezielt einrichten, - Kundeneigene feste Domains oder Subdomains erst später gezielt einrichten,
wenn DNS und Zertifikat pro Domain sauber geprueft sind. wenn DNS und Zertifikat pro Domain sauber geprüft sind.
Damit bleibt der Betrieb webspace-tauglich: Fuer den Start reichen zwei feste Damit bleibt der Betrieb webspace-tauglich: Für den Start reichen zwei feste
Hosts mit normalen Let's-Encrypt-Zertifikaten. Die fachliche Tenant-Aufloesung Hosts mit normalen Let's-Encrypt-Zertifikaten. Die fachliche Tenant-Auflösung
ist in M3 vorbereitet; produktive Host-Rewrites, Cookie-Domain und Zertifikats- ist in M3 vorbereitet; produktive Host-Rewrites, Cookie-Domain und Zertifikats-
Details gehoeren zur M8-Betriebshaertung. Details gehören zur M8-Betriebshärtung.
## Datenmodell ## Datenmodell
@@ -218,9 +218,9 @@ Wichtige Modellierungsregeln:
- `users` sind Login-Konten. - `users` sind Login-Konten.
- `participants` sind Kaffee-Teilnehmer. - `participants` sind Kaffee-Teilnehmer.
- Ein Teilnehmer kann, muss aber nicht, ein Login-Konto haben. - Ein Teilnehmer kann, muss aber nicht, ein Login-Konto haben.
- Rollen gehoeren in `tenant_memberships`, nicht direkt auf Teilnehmer. - Rollen gehören in `tenant_memberships`, nicht direkt auf Teilnehmer.
- Buchungen sollten nicht hart geloescht werden. - Buchungen sollten nicht hart gelöscht werden.
- Korrekturen laufen ueber Storno- oder Reversal-Eintraege. - Korrekturen laufen über Storno- oder Reversal-Einträge.
- Legacy-IDs werden bei Migration gespeichert, damit Ergebnisse vergleichbar - Legacy-IDs werden bei Migration gespeichert, damit Ergebnisse vergleichbar
bleiben. bleiben.
- Geldwerte sollten als Cent-Integer gespeichert werden, nicht als Float. - Geldwerte sollten als Cent-Integer gespeichert werden, nicht als Float.
@@ -229,7 +229,7 @@ Wichtige Modellierungsregeln:
Empfohlene Rollen: Empfohlene Rollen:
- `owner`: Kunde verwalten, Billing, Admins, Loeschung, Grundeinstellungen. - `owner`: Kunde verwalten, Billing, Admins, Löschung, Grundeinstellungen.
- `admin`: Mitglieder, Hinweise, fachliche Einstellungen, Auswertungen. - `admin`: Mitglieder, Hinweise, fachliche Einstellungen, Auswertungen.
- `treasurer`: Zahlungen, CSV-Import, Korrekturen, Exporte, Mails. - `treasurer`: Zahlungen, CSV-Import, Korrekturen, Exporte, Mails.
- `member`: eigenes Dashboard, eigene Striche, eigener Anzeigename. - `member`: eigenes Dashboard, eigene Striche, eigener Anzeigename.
@@ -246,22 +246,22 @@ SaaS-Basis:
- E-Mail-Verifikation. - E-Mail-Verifikation.
- Passwort-Reset. - Passwort-Reset.
- Sichere Session-Cookies. - Sichere Session-Cookies.
- CSRF-Schutz fuer alle schreibenden Aktionen. - CSRF-Schutz für alle schreibenden Aktionen.
- Rate-Limits fuer Login, Registrierung und Passwort-Reset. - Rate-Limits für Login, Registrierung und Passwort-Reset.
- Zentrale Funktionen oder Middleware: `requireLogin`, `requireTenant`, - Zentrale Funktionen oder Middleware: `requireLogin`, `requireTenant`,
`requireRole`, `csrfToken`. `requireRole`, `csrfToken`.
Tenant-Aufloesung: Tenant-Auflösung:
- Primaer serverseitig aus Subdomain, Custom Domain oder tenantgebundener - Primär serverseitig aus Subdomain, Custom Domain oder tenantgebundener
Auswahl nach Login. Auswahl nach Login.
- Nicht allein aus Hidden Inputs oder frei manipulierbaren IDs. - Nicht allein aus Hidden Inputs oder frei manipulierbaren IDs.
- Jede fachliche Query braucht einen Tenant-Scope. - Jede fachliche Query braucht einen Tenant-Scope.
Spaeter moeglich: Später möglich:
- LDAP/AD oder SSO pro Kunde als optionaler Identity Provider. - LDAP/AD oder SSO pro Kunde als optionaler Identity Provider.
- MFA fuer Owner/Admins. - MFA für Owner/Admins.
- Row-Level-Security in der Datenbank. - Row-Level-Security in der Datenbank.
## Design-Leitplanken ## Design-Leitplanken
@@ -270,12 +270,12 @@ Das bestehende Design soll erhalten bleiben.
Beibehalten: Beibehalten:
- Gruener Akzent `#38761d`. - Grüner Akzent `#38761d`.
- Weiss/Grau als ruhige Grundflaeche. - Weiß/Grau als ruhige Grundfläche.
- Roboto Slab fuer Ueberschriften. - Roboto Slab für Überschriften.
- Open Sans fuer Fliesstext. - Open Sans für Fließtext.
- Sidebar fuer die geschuetzte App. - Sidebar für die geschützte App.
- Tabellen als primaere Darstellung fuer operative Daten. - Tabellen als primäre Darstellung für operative Daten.
- Schlichte Formularfelder und Buttons. - Schlichte Formularfelder und Buttons.
Verbessern ohne Relaunch: Verbessern ohne Relaunch:
@@ -283,38 +283,38 @@ Verbessern ohne Relaunch:
- Hinweise als wiederverwendbare Komponente statt Inline-Style. - Hinweise als wiederverwendbare Komponente statt Inline-Style.
- Einheitliche Erfolgs-, Fehler- und Warnmeldungen. - Einheitliche Erfolgs-, Fehler- und Warnmeldungen.
- Dashboardwerte als kompakte Statusbereiche. - Dashboardwerte als kompakte Statusbereiche.
- Aktionsleisten ueber Tabellen. - Aktionsleisten über Tabellen.
- Sidebar-Gruppierung und aktive Navigation. - Sidebar-Gruppierung und aktive Navigation.
- FAQ strukturieren, aber Inhalte nicht stark veraendern. - FAQ strukturieren, aber Inhalte nicht stark verändern.
Nicht tun: Nicht tun:
- Kein komplett neues Farbsystem. - Kein komplett neues Farbsystem.
- Keine dekorative Marketingoptik in der App. - Keine dekorative Marketingoptik in der App.
- Keine App-Tabellen in Kartenlandschaften aufloesen. - Keine App-Tabellen in Kartenlandschaften auflösen.
- Keine Navigationsstruktur, die die heutigen Kernablaeufe versteckt. - Keine Navigationsstruktur, die die heutigen Kernabläufe versteckt.
## Meilensteinplan ## Meilensteinplan
Uebersicht: Übersicht:
| Meilenstein | Schwerpunkt | Hauptergebnis | | Meilenstein | Schwerpunkt | Hauptergebnis |
| --- | --- | --- | | --- | --- | --- |
| M0 | Baseline | Bestand, Sicherheit und Designreferenz sind dokumentiert | | M0 | Baseline | Bestand, Sicherheit und Designreferenz sind dokumentiert |
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prueft sichere Seiten | | 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 | | 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 | | M3 | SaaS-Basis | Abgeschlossen: Tenants, User, Registrierung, Login, Rollen, Mail-Links und zentrale Mandantenauswahl funktionieren |
| M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritaetscheck, Ledger-Service und Preview sind umgesetzt | | M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritätscheck, Ledger-Service und Preview sind umgesetzt |
| M5 | App-Kern | Gestartet: Gesamtuebersicht liest read-only aus dem Ledger | | M5 | App-Kern | Gestartet: Gesamtübersicht liest read-only aus dem Ledger |
| M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar | | M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar |
| M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spaetere Ausbaustufen folgen | | M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spätere Ausbaustufen folgen |
| M8 | Haertung | Betrieb, Datenschutz, Monitoring und Isolation sind geprueft | | M8 | Härtung | Betrieb, Datenschutz, Monitoring und Isolation sind geprüft |
| M9 | Cutover | Produktivumstellung ist vorbereitet und Legacy ist read-only | | M9 | Cutover | Produktivumstellung ist vorbereitet und Legacy ist read-only |
### M0: Planungs- und Sicherheitsbaseline ### M0: Planungs- und Sicherheitsbaseline
Ziel: Ziel:
Den aktuellen Zustand verlaesslich dokumentieren, bevor umgebaut wird. Den aktuellen Zustand verlässlich dokumentieren, bevor umgebaut wird.
Arbeitsartefakte: Arbeitsartefakte:
@@ -331,7 +331,7 @@ Schritte:
- Tabellen, Spalten, Indizes und Constraints dokumentieren. - Tabellen, Spalten, Indizes und Constraints dokumentieren.
- Bekannte Jobs/Skripte erfassen, zum Beispiel Mailversand und Jahresauswertung. - Bekannte Jobs/Skripte erfassen, zum Beispiel Mailversand und Jahresauswertung.
- Secrets inventarisieren und rotieren, insbesondere hart codierte Zugangsdaten. - Secrets inventarisieren und rotieren, insbesondere hart codierte Zugangsdaten.
- Bestehende Seiten und UI-Zustaende per Screenshots festhalten. - Bestehende Seiten und UI-Zustände per Screenshots festhalten.
- Fachliche Kernregeln dokumentieren: Saldo, Jahreswerte, Preis pro Strich, - Fachliche Kernregeln dokumentieren: Saldo, Jahreswerte, Preis pro Strich,
100-Tage-Listen, CSV-Deduplizierung, PDF-Ausgabe. 100-Tage-Listen, CSV-Deduplizierung, PDF-Ausgabe.
@@ -342,7 +342,7 @@ Ergebnis:
- Designreferenz. - Designreferenz.
- Liste kritischer Sicherheitsrisiken. - Liste kritischer Sicherheitsrisiken.
Abhaengigkeiten: Abhängigkeiten:
- Zugriff auf echte oder anonymisierte Datenbank. - Zugriff auf echte oder anonymisierte Datenbank.
- Kenntnis der Produktivumgebung. - Kenntnis der Produktivumgebung.
@@ -350,7 +350,7 @@ Abhaengigkeiten:
### M1: Golden-Master-Validierung ### M1: Golden-Master-Validierung
Ziel: Ziel:
Sicherstellen, dass spaetere neue Berechnungen dieselben Ergebnisse wie der Sicherstellen, dass spätere neue Berechnungen dieselben Ergebnisse wie der
Bestand liefern. Bestand liefern.
Schritte: Schritte:
@@ -359,42 +359,42 @@ Schritte:
Master in der MySQL-Dev-Datenbank. Master in der MySQL-Dev-Datenbank.
- Pro Mitglied berechnen: Gesamteinzahlungen, Gesamtausgaben, Gesamtstriche, - Pro Mitglied berechnen: Gesamteinzahlungen, Gesamtausgaben, Gesamtstriche,
aktueller Stand, Jahreswerte. Erledigt mit `scripts/check-golden-master.php`. aktueller Stand, Jahreswerte. Erledigt mit `scripts/check-golden-master.php`.
- CSV-Importfaelle sammeln: Treffer, Dubletten, unbekannte Namen. Erledigt im - CSV-Importfälle sammeln: Treffer, Dubletten, unbekannte Namen. Erledigt im
Golden-Master-Check. Golden-Master-Check.
- Testfaelle fuer Guthaben, Schulden, Nullsaldo und inaktive Teilnehmer - Testfälle für Guthaben, Schulden, Nullsaldo und inaktive Teilnehmer
anlegen. Erledigt. anlegen. Erledigt.
- Sichere GET-Seiten per HTTP-Smoke pruefen. Erledigt mit - Sichere GET-Seiten per HTTP-Smoke prüfen. Erledigt mit
`scripts/http-smoke.php`. `scripts/http-smoke.php`.
- PDF-/Export-Summen als Referenz sichern. Offen, weil die TCPDF-Kopie im Repo - PDF-/Export-Summen als Referenz sichern. Offen, weil die TCPDF-Kopie im Repo
unvollstaendig ist. unvollständig ist.
Ergebnis: Ergebnis:
- Golden-Master-Daten. - Golden-Master-Daten.
- Vergleichsqueries oder Vergleichsskript. - Vergleichsqueries oder Vergleichsskript.
- HTTP-Smoke-Test fuer sichere UI-Seiten. - HTTP-Smoke-Test für sichere UI-Seiten.
- Akzeptanzkriterien fuer Migration und neue App. - Akzeptanzkriterien für Migration und neue App.
- Dokumentierte offene Punkte: PDF-Export/TCPDF, PHPMailer-Abhaengigkeit, - Dokumentierte offene Punkte: PDF-Export/TCPDF, PHPMailer-Abhängigkeit,
GET-Nebenwirkungen bei Mailversand und Jahresauswertung. GET-Nebenwirkungen bei Mailversand und Jahresauswertung.
Abhaengigkeiten: Abhängigkeiten:
- M0 abgeschlossen. - M0 abgeschlossen.
- PDF-/Mail-/Jahresprozesse werden in M6 gezielt neu gestaltet, statt sie in M1 - PDF-/Mail-/Jahresprozesse werden in M6 gezielt neu gestaltet, statt sie in M1
per GET-Smoke auszufuehren. per GET-Smoke auszuführen.
### M2: Technisches Fundament ### M2: Technisches Fundament
Ziel: Ziel:
Eine saubere Basis fuer App, Public-Seiten, Auth und Mandanten schaffen. Eine saubere Basis für App, Public-Seiten, Auth und Mandanten schaffen.
Schritte: Schritte:
- Projektstruktur festlegen: Public-Routes, App-Routes, Views/Templates, - Projektstruktur festlegen: Public-Routes, App-Routes, Views/Templates,
Services/Repositories. Services/Repositories.
- Zentrales Bootstrap fuer Config, DB-Verbindung, Session und Fehlerbehandlung. - Zentrales Bootstrap für Config, DB-Verbindung, Session und Fehlerbehandlung.
Begonnen mit `app/bootstrap.php`. Begonnen mit `app/bootstrap.php`.
- Versionierte Migrationen einfuehren. Begonnen mit - Versionierte Migrationen einführen. Begonnen mit
`database/migrations/0001_legacy_mysql_baseline.sql` und `database/migrations/0001_legacy_mysql_baseline.sql` und
`scripts/migrate.php`. `scripts/migrate.php`.
- Layouts trennen: Public-Layout und App-Layout. Noch offen; Header/Footer - Layouts trennen: Public-Layout und App-Layout. Noch offen; Header/Footer
@@ -405,36 +405,36 @@ Schritte:
abgesichert: `hinweise.php`, `mitarbeiterverwalten.php`, abgesichert: `hinweise.php`, `mitarbeiterverwalten.php`,
`namenanpassen.php`, `index.php`, `stricheintragen.php`, `einzahlung.php`, `namenanpassen.php`, `index.php`, `stricheintragen.php`, `einzahlung.php`,
`letzteneintraege.php`, `csvupload.php`. `letzteneintraege.php`, `csvupload.php`.
- CSV-Upload ausserhalb des Webroots speichern. In M2 als Legacy-Haertung - CSV-Upload außerhalb des Webroots speichern. In M2 als Legacy-Härtung
umgesetzt: temporaer unter `var/uploads`, Dateityp-/Groessenpruefung und umgesetzt: temporär unter `var/uploads`, Dateityp-/Größenprüfung und
Loeschung nach Verarbeitung. Löschung nach Verarbeitung.
- Konfigurationswerte aus Code in Umgebung oder Settings verschieben. - Konfigurationswerte aus Code in Umgebung oder Settings verschieben.
Ergebnis: Ergebnis:
- Grundgeruest fuer neue SaaS-App. - Grundgerüst für neue SaaS-App.
- Kein fachlicher Rewrite, aber klare Struktur fuer die Migration. - Kein fachlicher Rewrite, aber klare Struktur für die Migration.
- Dokumentation: `docs/m2-technical-foundation.md`. - Dokumentation: `docs/m2-technical-foundation.md`.
- Status: abgeschlossen fuer den M2-Scope. - Status: abgeschlossen für den M2-Scope.
Abhaengigkeiten: Abhängigkeiten:
- Entscheidung, ob PHP nativ weitergefuehrt oder ein Framework genutzt wird. - Entscheidung, ob PHP nativ weitergeführt oder ein Framework genutzt wird.
- M2 bleibt bewusst ohne Tenant-/User-/Rollen-Tabellen; diese starten in M3. - M2 bleibt bewusst ohne Tenant-/User-/Rollen-Tabellen; diese starten in M3.
### M3: Mandanten, Registrierung und Login ### M3: Mandanten, Registrierung und Login
Ziel: Ziel:
Mehrkundenfaehigkeit und Kunden-Onboarding technisch aktivieren. Mehrkundenfähigkeit und Kunden-Onboarding technisch aktivieren.
Schritte: Schritte:
- Tabellen fuer `tenants`, `users`, `tenant_memberships` und - Tabellen für `tenants`, `users`, `tenant_memberships` und
`tenant_settings` anlegen. Vorbereitung dokumentiert in `tenant_settings` anlegen. Vorbereitung dokumentiert in
`docs/m3-saas-basis-vorbereitung.md`: erster Schritt erledigt. `docs/m3-saas-basis-vorbereitung.md`: erster Schritt erledigt.
- Zusaetzlich `participants` als getrennte Kaffee-Teilnehmer-Tabelle anlegen: - Zusätzlich `participants` als getrennte Kaffee-Teilnehmer-Tabelle anlegen:
erledigt. erledigt.
- Default-Tenant fuer den aktuellen Bestand anlegen: erledigt. - Default-Tenant für den aktuellen Bestand anlegen: erledigt.
- Bestehende `kl_Mitarbeiter` idempotent in `participants` spiegeln: - Bestehende `kl_Mitarbeiter` idempotent in `participants` spiegeln:
erledigt. erledigt.
- Registrierung: Tenant + Owner-User + Default-Settings erzeugen: erster - Registrierung: Tenant + Owner-User + Default-Settings erzeugen: erster
@@ -442,10 +442,10 @@ Schritte:
- Login und Logout bauen: erster Flow erledigt. - Login und Logout bauen: erster Flow erledigt.
- Passwort-Reset und E-Mail-Verifikation bauen: Dev-Flow mit Single-Use-Tokens - Passwort-Reset und E-Mail-Verifikation bauen: Dev-Flow mit Single-Use-Tokens
und Mail-Transport-Abstraktion erledigt. und Mail-Transport-Abstraktion erledigt.
- Tenant-Aufloesung definieren: zentrale App-Domain mit Session-Kontext und - Tenant-Auflösung definieren: zentrale App-Domain mit Session-Kontext und
Mandantenauswahl erledigt; feste Domains vorbereitet, keine Wildcards. Mandantenauswahl erledigt; feste Domains vorbereitet, keine Wildcards.
- Rollenpruefung zentralisieren: erster Owner/Admin-Check erledigt. - Rollenprüfung zentralisieren: erster Owner/Admin-Check erledigt.
- Erste Admin-/Owner-Seite fuer Grundeinstellungen: erledigt. - Erste Admin-/Owner-Seite für Grundeinstellungen: erledigt.
- Erste Public-Landingpage mit CTA zu Login und Registrierung: erledigt. - Erste Public-Landingpage mit CTA zu Login und Registrierung: erledigt.
- Login, Registrierung, Passwort-Reset und E-Mail-Verifikation im Public-Stil: - Login, Registrierung, Passwort-Reset und E-Mail-Verifikation im Public-Stil:
erledigt. erledigt.
@@ -454,51 +454,51 @@ Schritte:
Ergebnis: Ergebnis:
- Ein neuer Kunde kann sich registrieren und in seine eigene App gelangen. - Ein neuer Kunde kann sich registrieren und in seine eigene App gelangen.
- Rollen und Tenant-Kontext sind serverseitig verfuegbar. - Rollen und Tenant-Kontext sind serverseitig verfügbar.
- Reset- und Verifikationslinks koennen im Dev-Modus geloggt und produktiv ueber - Reset- und Verifikationslinks können im Dev-Modus geloggt und produktiv über
einen konfigurierten Mail-Transport versendet werden. einen konfigurierten Mail-Transport versendet werden.
- Status: abgeschlossen fuer den M3-Scope. - Status: abgeschlossen für den M3-Scope.
Abhaengigkeiten: Abhängigkeiten:
- M2 abgeschlossen. - M2 abgeschlossen.
### M4: Migration des fachlichen Kerns ### M4: Migration des fachlichen Kerns
Ziel: Ziel:
Bestehende Kaffeelisten-Daten tenant-sicher uebernehmen. Bestehende Kaffeelisten-Daten tenant-sicher übernehmen.
Schritte: Schritte:
- Initialen Tenant fuer den bisherigen Bestand anlegen: erledigt in M3. - Initialen Tenant für den bisherigen Bestand anlegen: erledigt in M3.
- `kl_Mitarbeiter` nach `participants` migrieren: erledigt in M3. - `kl_Mitarbeiter` nach `participants` migrieren: erledigt in M3.
- `admin`-Informationen in `tenant_memberships` oder Admin-Rollen ueberfuehren: - `admin`-Informationen in `tenant_memberships` oder Admin-Rollen überführen:
erledigt in M3. erledigt in M3.
- `kl_config` nach `tenant_settings` migrieren: erledigt in M3. - `kl_config` nach `tenant_settings` migrieren: erledigt in M3.
- `ledger_entries` als tenant-sichere Buchungstabelle anlegen: erledigt. - `ledger_entries` als tenant-sichere Buchungstabelle anlegen: erledigt.
- `kl_Einzahlungen` und `kl_Kaffeeverbrauch` additiv nach `ledger_entries` - `kl_Einzahlungen` und `kl_Kaffeeverbrauch` additiv nach `ledger_entries`
spiegeln: erster Backfill erledigt. spiegeln: erster Backfill erledigt.
- Legacy-IDs speichern: erledigt ueber `legacy_table` und `legacy_id`. - Legacy-IDs speichern: erledigt über `legacy_table` und `legacy_id`.
- Salden gegen Golden-Master vergleichen: Kontrollskript erledigt. - Salden gegen Golden-Master vergleichen: Kontrollskript erledigt.
- Ledger-Service fuer Tenant-Summen, Teilnehmer-Summen und letzte Buchungen: - Ledger-Service für Tenant-Summen, Teilnehmer-Summen und letzte Buchungen:
erledigt. erledigt.
- Read-only Ledger-Preview fuer Browservergleich: erledigt. - Read-only Ledger-Preview für Browservergleich: erledigt.
Ergebnis: Ergebnis:
- Bestehender Datenbestand kann im neuen Modell abgebildet werden. - Bestehender Datenbestand kann im neuen Modell abgebildet werden.
- Saldenparitaet ist ueber `scripts/check-m4-ledger-migration.php` - Saldenparität ist über `scripts/check-m4-ledger-migration.php`
nachweisbar. nachweisbar.
- Erste App-Abfragen koennen ueber `app/ledger.php` tenant-sicher aus dem neuen - Erste App-Abfragen können über `app/ledger.php` tenant-sicher aus dem neuen
Modell lesen. Modell lesen.
- `ledger-preview.php` zeigt die neue Ledger-Auswertung ohne Schreibzugriffe. - `ledger-preview.php` zeigt die neue Ledger-Auswertung ohne Schreibzugriffe.
- Dokumentation: `docs/m4-data-migration.md`. - Dokumentation: `docs/m4-data-migration.md`.
Abhaengigkeiten: Abhängigkeiten:
- M1 und M3 abgeschlossen. - M1 und M3 abgeschlossen.
### M5: Geschuetzte App-Funktionen ### M5: Geschützte App-Funktionen
Ziel: Ziel:
Die operativen Kernseiten im neuen Modell bereitstellen. Die operativen Kernseiten im neuen Modell bereitstellen.
@@ -509,9 +509,9 @@ Schritte:
- Eigene Stricherfassung umsetzen. - Eigene Stricherfassung umsetzen.
- Zahlungs-/PayPal-Bereich umsetzen. - Zahlungs-/PayPal-Bereich umsetzen.
- Mitgliederverwaltung tenant- und rollenbasiert umsetzen. - Mitgliederverwaltung tenant- und rollenbasiert umsetzen.
- Gesamtuebersicht umsetzen: erster read-only Stand in `kaffeeliste.php` - Gesamtübersicht umsetzen: erster read-only Stand in `kaffeeliste.php`
erledigt. erledigt.
- Letzte Eintraege und Korrekturen als Storno statt Delete umsetzen. - Letzte Einträge und Korrekturen als Storno statt Delete umsetzen.
- Hinweise als tenant-spezifische Notices umsetzen. - Hinweise als tenant-spezifische Notices umsetzen.
Ergebnis: Ergebnis:
@@ -520,7 +520,7 @@ Ergebnis:
- App sieht weiterhin nach bestehender Kaffeeliste aus. - App sieht weiterhin nach bestehender Kaffeeliste aus.
- Erster M5-Stand ist in `docs/m5-app-kern.md` dokumentiert. - Erster M5-Stand ist in `docs/m5-app-kern.md` dokumentiert.
Abhaengigkeiten: Abhängigkeiten:
- M4 abgeschlossen. - M4 abgeschlossen.
@@ -531,8 +531,8 @@ Admin- und Treasurer-Flows produktionsreif machen.
Schritte: Schritte:
- CSV-Import mit Vorschau, Dublettenpruefung und Audit bauen. - CSV-Import mit Vorschau, Dublettenprüfung und Audit bauen.
- Uploads ausserhalb des Webroots speichern. - Uploads außerhalb des Webroots speichern.
- PDF-/Listenexport aus neuem Datenmodell bauen. - PDF-/Listenexport aus neuem Datenmodell bauen.
- Mailversand als nachvollziehbaren Versandjob mit Dry-Run und Versandlog - Mailversand als nachvollziehbaren Versandjob mit Dry-Run und Versandlog
gestalten. gestalten.
@@ -540,22 +540,22 @@ Schritte:
Ergebnis: Ergebnis:
- Kassenverwaltung ist fuer reale Betriebsablaeufe vollstaendig. - Kassenverwaltung ist für reale Betriebsabläufe vollständig.
- Import/Export/Mail sind auditierbar. - Import/Export/Mail sind auditierbar.
Abhaengigkeiten: Abhängigkeiten:
- M5 fuer die Kernansichten. - M5 für die Kernansichten.
### M7: Oeffentliche Landingpage ### M7: Öffentliche Landingpage
Ziel: Ziel:
Werbliche Einstiegseite und Registrierung verfuegbar machen, ohne die App-Optik Werbliche Einstiegseite und Registrierung verfügbar machen, ohne die App-Optik
zu verwischen. zu verwischen.
Schritte: Schritte:
- Public-Layout mit bestehender Typografie und gruenem Akzent bauen. Erster - Public-Layout mit bestehender Typografie und grünem Akzent bauen. Erster
Stand als `landing.php` und `assets/css/public.css` erledigt. Stand als `landing.php` und `assets/css/public.css` erledigt.
- Landingpage-Inhalte erstellen. Erster Stand erledigt. - Landingpage-Inhalte erstellen. Erster Stand erledigt.
- Demo-Screenshot oder Demo-Ansicht einbinden. - Demo-Screenshot oder Demo-Ansicht einbinden.
@@ -568,26 +568,26 @@ Schritte:
Ergebnis: Ergebnis:
- Interessenten verstehen das Produkt und koennen sich registrieren. - Interessenten verstehen das Produkt und können sich registrieren.
- Bestehende App bleibt optisch eigenstaendig und arbeitsorientiert. - Bestehende App bleibt optisch eigenständig und arbeitsorientiert.
Abhaengigkeiten: Abhängigkeiten:
- M3 fuer Registrierung. - M3 für Registrierung.
- M5 fuer echte App-Screens oder Demo. - M5 für echte App-Screens oder Demo.
### M8: Haertung, Datenschutz und Betrieb ### M8: Härtung, Datenschutz und Betrieb
Ziel: Ziel:
Die SaaS-App fuer mehrere Kunden sicher betreiben. Die SaaS-App für mehrere Kunden sicher betreiben.
Schritte: Schritte:
- Backups und Restore-Prozess definieren. - Backups und Restore-Prozess definieren.
- Monitoring und Fehlerlogging einrichten. - Monitoring und Fehlerlogging einrichten.
- Audit-Log fuer Admin-Aktionen pruefen. - Audit-Log für Admin-Aktionen prüfen.
- Datenexport pro Tenant. - Datenexport pro Tenant.
- Loesch-/Anonymisierungsprozess fuer Teilnehmer und Kunden. - Lösch-/Anonymisierungsprozess für Teilnehmer und Kunden.
- Rate-Limits und Security Headers. - Rate-Limits und Security Headers.
- Mandanten-Isolation testen. - Mandanten-Isolation testen.
- Rollenmatrix testen. - Rollenmatrix testen.
@@ -596,7 +596,7 @@ Ergebnis:
- SaaS ist betrieblich und datenschutzseitig belastbarer. - SaaS ist betrieblich und datenschutzseitig belastbarer.
Abhaengigkeiten: Abhängigkeiten:
- M3 bis M6. - M3 bis M6.
@@ -607,10 +607,10 @@ Produktive Umstellung ohne Datenverlust.
Schritte: Schritte:
- Legacy fuer Schreibzugriffe sperren. - Legacy für Schreibzugriffe sperren.
- Finalen Delta-Export ziehen. - Finalen Delta-Export ziehen.
- Migration ausfuehren. - Migration ausführen.
- Rowcounts, Salden, PDF-Stichproben und Importhistorie pruefen. - Rowcounts, Salden, PDF-Stichproben und Importhistorie prüfen.
- DNS oder Reverse Proxy umschalten. - DNS oder Reverse Proxy umschalten.
- Legacy 30 bis 90 Tage read-only als Archiv behalten. - Legacy 30 bis 90 Tage read-only als Archiv behalten.
- Rollback-Fenster und Vorgehen dokumentieren. - Rollback-Fenster und Vorgehen dokumentieren.
@@ -618,15 +618,15 @@ Schritte:
Ergebnis: Ergebnis:
- Neuer SaaS-Betrieb ist live. - Neuer SaaS-Betrieb ist live.
- Alter Bestand bleibt fuer Rueckfragen lesbar. - Alter Bestand bleibt für Rückfragen lesbar.
Abhaengigkeiten: Abhängigkeiten:
- M8 abgeschlossen. - M8 abgeschlossen.
## Empfohlene MVP-Reihenfolge ## Empfohlene MVP-Reihenfolge
Fuer den ersten produktnahen MVP sollten nicht alle Randmodule gleichzeitig Für den ersten produktnahen MVP sollten nicht alle Randmodule gleichzeitig
umgebaut werden. umgebaut werden.
MVP-Paket: MVP-Paket:
@@ -634,12 +634,12 @@ MVP-Paket:
1. Tenant, User, Login, Registrierung. 1. Tenant, User, Login, Registrierung.
2. Mail-Links, zentrale Mandantenauswahl und erste Landingpage. 2. Mail-Links, zentrale Mandantenauswahl und erste Landingpage.
3. Teilnehmer, Settings, Ledger. 3. Teilnehmer, Settings, Ledger.
4. Dashboard, Striche, Einzahlungen, Gesamtuebersicht. 4. Dashboard, Striche, Einzahlungen, Gesamtübersicht.
5. Mitgliederverwaltung. 5. Mitgliederverwaltung.
6. Hinweise. 6. Hinweise.
7. CSV-Import, Export. 7. CSV-Import, Export.
Spaeter: Später:
- Umfrage-Modul. - Umfrage-Modul.
- SSO/LDAP pro Kunde. - SSO/LDAP pro Kunde.
@@ -650,7 +650,7 @@ Spaeter:
## Validierung ## Validierung
Fachliche Pruefungen: Fachliche Prüfungen:
- Saldo je Teilnehmer alt gegen neu. - Saldo je Teilnehmer alt gegen neu.
- Jahreswerte alt gegen neu. - Jahreswerte alt gegen neu.
@@ -659,37 +659,37 @@ Fachliche Pruefungen:
- Export-Summen alt gegen neu. - Export-Summen alt gegen neu.
- CSV-Import: identische Treffer- und Dublettenlogik. - CSV-Import: identische Treffer- und Dublettenlogik.
Sicherheitspruefungen: Sicherheitsprüfungen:
- Fremde Tenant-IDs liefern keine Daten. - Fremde Tenant-IDs liefern keine Daten.
- Fremde Teilnehmer-IDs liefern keine Daten. - Fremde Teilnehmer-IDs liefern keine Daten.
- Admin-Funktionen sind ohne Rolle blockiert. - Admin-Funktionen sind ohne Rolle blockiert.
- Alle POST-Aktionen brauchen CSRF. - Alle POST-Aktionen brauchen CSRF.
- Uploads sind nicht direkt ausfuehrbar. - Uploads sind nicht direkt ausführbar.
- Ausgabe von Namen, Mails und Hinweisen ist escaped. - Ausgabe von Namen, Mails und Hinweisen ist escaped.
UX-Pruefungen: UX-Prüfungen:
- Kernseiten bleiben optisch wiedererkennbar. - Kernseiten bleiben optisch wiedererkennbar.
- Sidebar bleibt fuer App-Nutzer vertraut. - Sidebar bleibt für App-Nutzer vertraut.
- Landingpage zeigt keine App-Adminnavigation. - Landingpage zeigt keine App-Adminnavigation.
- Mobile Darstellung bleibt bedienbar. - Mobile Darstellung bleibt bedienbar.
- Tabellen bleiben lesbar. - Tabellen bleiben lesbar.
## Risiken und Gegenmassnahmen ## Risiken und Gegenmaßnahmen
| Risiko | Gegenmassnahme | | Risiko | Gegenmaßnahme |
| --- | --- | | --- | --- |
| Tenant-Leak durch vergessene Query-Filter | Zentrale Repository-/Query-Schicht, Tests mit zwei Tenants | | Tenant-Leak durch vergessene Query-Filter | Zentrale Repository-/Query-Schicht, Tests mit zwei Tenants |
| Falsche Salden nach Migration | Golden-Master-Vergleich vor Cutover | | Falsche Salden nach Migration | Golden-Master-Vergleich vor Cutover |
| Harte Deletes verlieren Audit-Historie | Storno-/Reversal-Modell fuer Buchungen | | Harte Deletes verlieren Audit-Historie | Storno-/Reversal-Modell für Buchungen |
| Login und Teilnehmer werden vermischt | `users` und `participants` strikt trennen | | Login und Teilnehmer werden vermischt | `users` und `participants` strikt trennen |
| Bestehendes Design driftet weg | Designreferenz und App-Komponenten definieren | | Bestehendes Design driftet weg | Designreferenz und App-Komponenten definieren |
| CSV-Upload unsicher | Upload ausserhalb Webroot, Dateityppruefung, Importvorschau | | CSV-Upload unsicher | Upload außerhalb Webroot, Dateitypprüfung, Importvorschau |
| Mailversand nicht nachvollziehbar | Versandlog, Dry-Run, Job-Status | | Mailversand nicht nachvollziehbar | Versandlog, Dry-Run, Job-Status |
| Unvollstaendige Vendor-Kopien fuer PDF/Mail | Dependencies sauber vendoren oder ersetzen, Smoke-Tests danach erweitern | | 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 spaeter optional | | AD/LDAP blockiert SaaS-Onboarding | E-Mail/Passwort als Basis, SSO später optional |
| Kein DB-Schema im Repo | Schema exportieren und Migrationen einfuehren | | Kein DB-Schema im Repo | Schema exportieren und Migrationen einführen |
## Offene Entscheidungen ## Offene Entscheidungen
@@ -703,9 +703,9 @@ UX-Pruefungen:
- Braucht der MVP schon Tarife/Billing oder erstmal nur Registrierung? - Braucht der MVP schon Tarife/Billing oder erstmal nur Registrierung?
- Sollen bestehende Kunden per Einladung oder per Self-Service onboarden? - Sollen bestehende Kunden per Einladung oder per Self-Service onboarden?
## Naechster konkreter Arbeitsschritt ## Nächster konkreter Arbeitsschritt
Als naechstes sollte M0 gestartet werden: Als nächstes sollte M0 gestartet werden:
1. Produktiv-/Testdatenbankschema exportieren. 1. Produktiv-/Testdatenbankschema exportieren.
2. Screenshot-Referenz der wichtigsten Seiten erstellen. 2. Screenshot-Referenz der wichtigsten Seiten erstellen.
+2 -2
View File
@@ -15,7 +15,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($result['ok']) { if ($result['ok']) {
if (!empty($result['already_verified'])) { if (!empty($result['already_verified'])) {
$message = 'Die E-Mail-Adresse ist bereits bestaetigt.'; $message = 'Die E-Mail-Adresse ist bereits bestätigt.';
} else { } else {
$message = 'Der Verifizierungslink wurde vorbereitet.'; $message = 'Der Verifizierungslink wurde vorbereitet.';
if (saas_should_show_auth_links() && !empty($result['token'])) { if (saas_should_show_auth_links() && !empty($result['token'])) {
@@ -61,7 +61,7 @@ include 'nav.php';
<p><?php echo saas_html($user['email']); ?></p> <p><?php echo saas_html($user['email']); ?></p>
<ul class="actions"> <ul class="actions">
<li><button type="submit">Verifizierungslink vorbereiten</button></li> <li><button type="submit">Verifizierungslink vorbereiten</button></li>
<li><a href="konto.php" class="button alt">Zurueck</a></li> <li><a href="konto.php" class="button alt">Zurück</a></li>
</ul> </ul>
</form> </form>
</div> </div>
+3 -3
View File
@@ -6,7 +6,7 @@ $pdo = app_db_pdo();
$token = trim((string)($_GET['token'] ?? '')); $token = trim((string)($_GET['token'] ?? ''));
$result = $token !== '' $result = $token !== ''
? saas_verify_email_token($pdo, $token) ? saas_verify_email_token($pdo, $token)
: ['ok' => false, 'errors' => ['Der Link ist ungueltig oder abgelaufen.']]; : ['ok' => false, 'errors' => ['Der Link ist ungültig oder abgelaufen.']];
?> ?>
@@ -32,14 +32,14 @@ $result = $token !== ''
<div class="public-auth-grid"> <div class="public-auth-grid">
<div class="public-auth-copy"> <div class="public-auth-copy">
<h1>E-Mail verifizieren</h1> <h1>E-Mail verifizieren</h1>
<p>Bestaetige deine Adresse, damit dein Kundenkonto vollstaendig eingerichtet ist.</p> <p>Bestätige deine Adresse, damit dein Kundenkonto vollständig eingerichtet ist.</p>
</div> </div>
<div class="public-panel"> <div class="public-panel">
<h2>Status</h2> <h2>Status</h2>
<?php if ($result['ok']): ?> <?php if ($result['ok']): ?>
<div class="hint-box success"><p>Die E-Mail-Adresse wurde bestaetigt.</p></div> <div class="hint-box success"><p>Die E-Mail-Adresse wurde bestätigt.</p></div>
<ul class="actions"> <ul class="actions">
<li><a href="konto.php" class="button primary">Zum Konto</a></li> <li><a href="konto.php" class="button primary">Zum Konto</a></li>
</ul> </ul>
+2 -2
View File
@@ -43,7 +43,7 @@ include 'nav.php';
<td><?php echo saas_html($user['tenant_name']); ?></td> <td><?php echo saas_html($user['tenant_name']); ?></td>
</tr> </tr>
<tr> <tr>
<th>Kundenkuerzel</th> <th>Kundenkürzel</th>
<td><?php echo saas_html($user['tenant_slug']); ?></td> <td><?php echo saas_html($user['tenant_slug']); ?></td>
</tr> </tr>
<tr> <tr>
@@ -51,7 +51,7 @@ include 'nav.php';
<td><?php echo saas_html($user['role']); ?></td> <td><?php echo saas_html($user['role']); ?></td>
</tr> </tr>
<tr> <tr>
<th>E-Mail bestaetigt</th> <th>E-Mail bestätigt</th>
<td><?php echo $user['email_verified_at'] !== null ? 'ja' : 'nein'; ?></td> <td><?php echo $user['email_verified_at'] !== null ? 'ja' : 'nein'; ?></td>
</tr> </tr>
</table> </table>
+3 -3
View File
@@ -19,7 +19,7 @@
</nav> </nav>
<div class="public-hero-content"> <div class="public-hero-content">
<h1>Kaffeeliste</h1> <h1>Kaffeeliste</h1>
<p>Die digitale Kaffeekasse fuer Teams, Bueros und Vereine: Striche, Einzahlungen und offene Betraege bleiben nachvollziehbar an einem Ort.</p> <p>Die digitale Kaffeekasse für Teams, Büros und Vereine: Striche, Einzahlungen und offene Beträge bleiben nachvollziehbar an einem Ort.</p>
<ul class="actions"> <ul class="actions">
<li><a href="register.php" class="button primary big">Kundenkonto anlegen</a></li> <li><a href="register.php" class="button primary big">Kundenkonto anlegen</a></li>
<li><a href="login.php" class="button big">Zur App</a></li> <li><a href="login.php" class="button big">Zur App</a></li>
@@ -37,12 +37,12 @@
<p>Teilnehmer und Admins behalten Verbrauch, Einzahlungen und Salden im Blick.</p> <p>Teilnehmer und Admins behalten Verbrauch, Einzahlungen und Salden im Blick.</p>
</article> </article>
<article class="public-feature"> <article class="public-feature">
<h3>Mandantenfaehig</h3> <h3>Mandantenfähig</h3>
<p>Jeder Kunde arbeitet in seinem eigenen Bereich mit eigenen Einstellungen.</p> <p>Jeder Kunde arbeitet in seinem eigenen Bereich mit eigenen Einstellungen.</p>
</article> </article>
<article class="public-feature"> <article class="public-feature">
<h3>Webspace-tauglich</h3> <h3>Webspace-tauglich</h3>
<p>Der Start erfolgt ueber eine zentrale App-Adresse mit Login und Mandantenauswahl.</p> <p>Der Start erfolgt über eine zentrale App-Adresse mit Login und Mandantenauswahl.</p>
</article> </article>
</div> </div>
</section> </section>
+1 -1
View File
@@ -71,7 +71,7 @@ include 'nav.php';
<div class="hint-box error"><p>Kein Zugriff.</p></div> <div class="hint-box error"><p>Kein Zugriff.</p></div>
<?php else: ?> <?php else: ?>
<p> <p>
Read-only Vorschau aus <code>ledger_entries</code> fuer Read-only Vorschau aus <code>ledger_entries</code> für
<?php echo saas_html($tenantName); ?> <?php echo saas_html($tenantName); ?>
(<code><?php echo saas_html($tenantSlug); ?></code>). (<code><?php echo saas_html($tenantSlug); ?></code>).
Die bestehenden App-Seiten schreiben weiterhin in die Legacy-Tabellen. Die bestehenden App-Seiten schreiben weiterhin in die Legacy-Tabellen.
+2 -2
View File
@@ -56,7 +56,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="public-auth-grid"> <div class="public-auth-grid">
<div class="public-auth-copy"> <div class="public-auth-copy">
<h1>Login</h1> <h1>Login</h1>
<p>Melde dich mit deinem Kundenkonto an. Wenn du mehreren Mandanten zugeordnet bist, waehlst du danach den passenden Bereich aus.</p> <p>Melde dich mit deinem Kundenkonto an. Wenn du mehreren Mandanten zugeordnet bist, wählst du danach den passenden Bereich aus.</p>
</div> </div>
<div class="public-panel"> <div class="public-panel">
@@ -86,7 +86,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<input type="password" name="password" id="password" required> <input type="password" name="password" id="password" required>
</div> </div>
<div class="col-12"> <div class="col-12">
<label for="tenant_slug">Kundenkuerzel</label> <label for="tenant_slug">Kundenkürzel</label>
<input type="text" name="tenant_slug" id="tenant_slug" value="<?php echo saas_html($tenantSlug); ?>" placeholder="optional"> <input type="text" name="tenant_slug" id="tenant_slug" value="<?php echo saas_html($tenantSlug); ?>" placeholder="optional">
</div> </div>
</div> </div>
+3 -3
View File
@@ -19,7 +19,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$identity = $tenantId > 0 ? saas_identity_for_user_tenant($pdo, $pendingUserId, $tenantId) : null; $identity = $tenantId > 0 ? saas_identity_for_user_tenant($pdo, $pendingUserId, $tenantId) : null;
if ($identity === null) { if ($identity === null) {
$errors[] = 'Dieser Mandant ist fuer dein Konto nicht verfuegbar.'; $errors[] = 'Dieser Mandant ist für dein Konto nicht verfügbar.';
} else { } else {
saas_session_login($identity); saas_session_login($identity);
header('Location: konto.php'); header('Location: konto.php');
@@ -34,7 +34,7 @@ include 'nav.php';
<section id="banner"> <section id="banner">
<div class="content"> <div class="content">
<h2>Mandant auswaehlen</h2> <h2>Mandant auswählen</h2>
<?php if ($errors !== []): ?> <?php if ($errors !== []): ?>
<div class="hint-box error"> <div class="hint-box error">
@@ -45,7 +45,7 @@ include 'nav.php';
<?php endif; ?> <?php endif; ?>
<?php if ($memberships === []): ?> <?php if ($memberships === []): ?>
<div class="hint-box error"><p>Fuer dieses Konto ist kein aktiver Mandant verfuegbar.</p></div> <div class="hint-box error"><p>Für dieses Konto ist kein aktiver Mandant verfügbar.</p></div>
<?php else: ?> <?php else: ?>
<form method="post" action="mandant-auswahl.php"> <form method="post" action="mandant-auswahl.php">
<?php echo app_csrf_field(); ?> <?php echo app_csrf_field(); ?>
+3 -3
View File
@@ -47,7 +47,7 @@ include 'nav.php';
<?php if (!$canManageSettings): ?> <?php if (!$canManageSettings): ?>
<?php http_response_code(403); ?> <?php http_response_code(403); ?>
<div class="hint-box error"><p>Du hast keine Berechtigung fuer diese Einstellungen.</p></div> <div class="hint-box error"><p>Du hast keine Berechtigung für diese Einstellungen.</p></div>
<?php elseif ($settings === null): ?> <?php elseif ($settings === null): ?>
<div class="hint-box error"><p>Die Einstellungen konnten nicht geladen werden.</p></div> <div class="hint-box error"><p>Die Einstellungen konnten nicht geladen werden.</p></div>
<?php else: ?> <?php else: ?>
@@ -79,7 +79,7 @@ include 'nav.php';
<input type="text" name="locale" id="locale" value="<?php echo saas_html($settings['locale']); ?>" required> <input type="text" name="locale" id="locale" value="<?php echo saas_html($settings['locale']); ?>" required>
</div> </div>
<div class="col-6 col-12-small"> <div class="col-6 col-12-small">
<label for="currency_code">Waehrung</label> <label for="currency_code">Währung</label>
<input type="text" name="currency_code" id="currency_code" value="<?php echo saas_html($settings['currency_code']); ?>" maxlength="3" required> <input type="text" name="currency_code" id="currency_code" value="<?php echo saas_html($settings['currency_code']); ?>" maxlength="3" required>
</div> </div>
<div class="col-6 col-12-small"> <div class="col-6 col-12-small">
@@ -109,7 +109,7 @@ include 'nav.php';
</div> </div>
<ul class="actions"> <ul class="actions">
<li><button type="submit">Speichern</button></li> <li><button type="submit">Speichern</button></li>
<li><a href="konto.php" class="button alt">Zurueck</a></li> <li><a href="konto.php" class="button alt">Zurück</a></li>
</ul> </ul>
</form> </form>
<?php endif; ?> <?php endif; ?>
+3 -3
View File
@@ -52,7 +52,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="public-auth-grid"> <div class="public-auth-grid">
<div class="public-auth-copy"> <div class="public-auth-copy">
<h1>Passwort vergessen</h1> <h1>Passwort vergessen</h1>
<p>Fordere einen Link an, um dein Passwort neu zu setzen. Wenn du mehrere Kundenbereiche nutzt, kannst du das Kuerzel optional angeben.</p> <p>Fordere einen Link an, um dein Passwort neu zu setzen. Wenn du mehrere Kundenbereiche nutzt, kannst du das Kürzel optional angeben.</p>
</div> </div>
<div class="public-panel"> <div class="public-panel">
@@ -62,7 +62,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="hint-box success"> <div class="hint-box success">
<p><?php echo saas_html($message); ?></p> <p><?php echo saas_html($message); ?></p>
<?php if ($resetLink !== null): ?> <?php if ($resetLink !== null): ?>
<p><a href="<?php echo saas_html($resetLink); ?>">Dev-Link zum Zuruecksetzen</a></p> <p><a href="<?php echo saas_html($resetLink); ?>">Dev-Link zum Zurücksetzen</a></p>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -83,7 +83,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<input type="email" name="email" id="email" value="<?php echo saas_html($email); ?>" required> <input type="email" name="email" id="email" value="<?php echo saas_html($email); ?>" required>
</div> </div>
<div class="col-12"> <div class="col-12">
<label for="tenant_slug">Kundenkuerzel</label> <label for="tenant_slug">Kundenkürzel</label>
<input type="text" name="tenant_slug" id="tenant_slug" value="<?php echo saas_html($tenantSlug); ?>" placeholder="optional"> <input type="text" name="tenant_slug" id="tenant_slug" value="<?php echo saas_html($tenantSlug); ?>" placeholder="optional">
</div> </div>
</div> </div>
+5 -5
View File
@@ -31,7 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="de"> <html lang="de">
<head> <head>
<title>Kaffeeliste Passwort zuruecksetzen</title> <title>Kaffeeliste Passwort zurücksetzen</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<link rel="stylesheet" href="assets/css/main.css" /> <link rel="stylesheet" href="assets/css/main.css" />
@@ -49,15 +49,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="public-auth-grid"> <div class="public-auth-grid">
<div class="public-auth-copy"> <div class="public-auth-copy">
<h1>Passwort zuruecksetzen</h1> <h1>Passwort zurücksetzen</h1>
<p>Setze ein neues Passwort fuer dein Kaffeeliste-Konto.</p> <p>Setze ein neues Passwort für dein Kaffeeliste-Konto.</p>
</div> </div>
<div class="public-panel"> <div class="public-panel">
<h2>Neues Passwort</h2> <h2>Neues Passwort</h2>
<?php if ($success): ?> <?php if ($success): ?>
<div class="hint-box success"><p>Das Passwort wurde geaendert.</p></div> <div class="hint-box success"><p>Das Passwort wurde geändert.</p></div>
<ul class="actions"> <ul class="actions">
<li><a href="login.php" class="button primary">Zum Login</a></li> <li><a href="login.php" class="button primary">Zum Login</a></li>
</ul> </ul>
@@ -69,7 +69,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php elseif (!$tokenValid): ?> <?php elseif (!$tokenValid): ?>
<div class="hint-box error"><p>Der Link ist ungueltig oder abgelaufen.</p></div> <div class="hint-box error"><p>Der Link ist ungültig oder abgelaufen.</p></div>
<?php endif; ?> <?php endif; ?>
<?php if ($tokenValid): ?> <?php if ($tokenValid): ?>
+2 -2
View File
@@ -63,7 +63,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="public-auth-grid"> <div class="public-auth-grid">
<div class="public-auth-copy"> <div class="public-auth-copy">
<h1>Registrierung</h1> <h1>Registrierung</h1>
<p>Lege einen neuen Kundenbereich an. Danach bist du als Owner angemeldet und kannst die Kaffeeliste fuer dein Team einrichten.</p> <p>Lege einen neuen Kundenbereich an. Danach bist du als Owner angemeldet und kannst die Kaffeeliste für dein Team einrichten.</p>
</div> </div>
<div class="public-panel"> <div class="public-panel">
@@ -85,7 +85,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<input type="text" name="tenant_name" id="tenant_name" value="<?php echo saas_html($values['tenant_name']); ?>" required> <input type="text" name="tenant_name" id="tenant_name" value="<?php echo saas_html($values['tenant_name']); ?>" required>
</div> </div>
<div class="col-6 col-12-small"> <div class="col-6 col-12-small">
<label for="tenant_slug">Kundenkuerzel</label> <label for="tenant_slug">Kundenkürzel</label>
<input type="text" name="tenant_slug" id="tenant_slug" value="<?php echo saas_html($values['tenant_slug']); ?>" placeholder="kaffeeliste-team" required> <input type="text" name="tenant_slug" id="tenant_slug" value="<?php echo saas_html($values['tenant_slug']); ?>" placeholder="kaffeeliste-team" required>
</div> </div>
<div class="col-6 col-12-small"> <div class="col-6 col-12-small">
+8 -8
View File
@@ -9,7 +9,7 @@ $checks = [
[ [
'label' => 'Landingpage', 'label' => 'Landingpage',
'path' => 'landing.php', 'path' => 'landing.php',
'contains' => ['Kaffeeliste', 'Kundenkonto anlegen', 'Mandantenfaehig'], 'contains' => ['Kaffeeliste', 'Kundenkonto anlegen', 'Mandantenfähig'],
], ],
[ [
'label' => 'Dashboard', 'label' => 'Dashboard',
@@ -53,7 +53,7 @@ $checks = [
'contains' => ['Mitglieder verwalten', 'PayPal-Name'], 'contains' => ['Mitglieder verwalten', 'PayPal-Name'],
], ],
[ [
'label' => 'Letzte Eintraege', 'label' => 'Letzte Einträge',
'path' => 'letzteneintraege.php', 'path' => 'letzteneintraege.php',
'contains' => ['Letzte 100 Einzahlungen'], 'contains' => ['Letzte 100 Einzahlungen'],
], ],
@@ -93,14 +93,14 @@ $checks = [
'contains' => ['Login'], 'contains' => ['Login'],
], ],
[ [
'label' => 'Passwort zuruecksetzen Invalid', 'label' => 'Passwort zurücksetzen Invalid',
'path' => 'passwort-zuruecksetzen.php', 'path' => 'passwort-zuruecksetzen.php',
'contains' => ['Passwort zuruecksetzen', 'ungueltig oder abgelaufen'], 'contains' => ['Passwort zurücksetzen', 'ungültig oder abgelaufen'],
], ],
[ [
'label' => 'E-Mail verifizieren Invalid', 'label' => 'E-Mail verifizieren Invalid',
'path' => 'email-verifizieren.php', 'path' => 'email-verifizieren.php',
'contains' => ['E-Mail verifizieren', 'ungueltig oder abgelaufen'], 'contains' => ['E-Mail verifizieren', 'ungültig oder abgelaufen'],
], ],
[ [
'label' => 'Mandant-Einstellungen Login-Schutz', 'label' => 'Mandant-Einstellungen Login-Schutz',
@@ -146,11 +146,11 @@ $knownIssueChecks = [
$skippedUnsafe = [ $skippedUnsafe = [
[ [
'path' => 'mailversenden.php', 'path' => 'mailversenden.php',
'reason' => 'GET kann bei vollstaendiger PHPMailer-Installation E-Mails versenden.', 'reason' => 'GET kann bei vollständiger PHPMailer-Installation E-Mails versenden.',
], ],
[ [
'path' => 'jahresauswertung.php', 'path' => 'jahresauswertung.php',
'reason' => 'GET kann Jahresbuchungen schreiben und E-Mails versenden; PHPMailer-Abhaengigkeit ist zudem unvollstaendig.', 'reason' => 'GET kann Jahresbuchungen schreiben und E-Mails versenden; PHPMailer-Abhängigkeit ist zudem unvollständig.',
], ],
]; ];
@@ -260,7 +260,7 @@ foreach ($knownIssueChecks as $check) {
} }
if ($response['status'] === 200 && preg_match($issuePattern, $response['body']) !== 1) { if ($response['status'] === 200 && preg_match($issuePattern, $response['body']) !== 1) {
echo "RESOLVED {$label}: bitte in die regulaeren Smoke-Checks aufnehmen.\n"; echo "RESOLVED {$label}: bitte in die regulären Smoke-Checks aufnehmen.\n";
continue; continue;
} }
+2 -2
View File
@@ -100,8 +100,8 @@ try {
$step = 'insert notices'; $step = 'insert notices';
$insertNotice = $pdo->prepare('INSERT INTO kl_hinweise (nachricht, gueltig_bis) VALUES (?, ?)'); $insertNotice = $pdo->prepare('INSERT INTO kl_hinweise (nachricht, gueltig_bis) VALUES (?, ?)');
$insertNotice->execute(['[GM] Aktiver Hinweis fuer Golden-Master-Test', $dates['notice_active']]); $insertNotice->execute(['[GM] Aktiver Hinweis für Golden-Master-Test', $dates['notice_active']]);
$insertNotice->execute(['[GM] Abgelaufener Hinweis fuer Golden-Master-Test', $dates['notice_expired']]); $insertNotice->execute(['[GM] Abgelaufener Hinweis für Golden-Master-Test', $dates['notice_expired']]);
$metadata = [ $metadata = [
'golden_master_seed' => 'seeded', 'golden_master_seed' => 'seeded',