Deutsche Umlaute in UI und Dokumentation korrigieren
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
# 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 Quelle fuer Datenmigrationen in die SaaS-Version
|
||||
- als Rueckfalloption waehrend der Uebergangsphase
|
||||
- als Referenz für die bestehende Fachlogik
|
||||
- als Quelle für Datenmigrationen in die SaaS-Version
|
||||
- als Rückfalloption während der Übergangsphase
|
||||
|
||||
## Wichtige Legacy-Bereiche
|
||||
|
||||
- `index.php`: persoenliches Dashboard
|
||||
- `stricheintragen.php`: Sammelerfassung fuer Kaffee-Striche
|
||||
- `einzahlung.php`: Sammelerfassung fuer Einzahlungen
|
||||
- `kaffeeliste.php`: operative Gesamtuebersicht
|
||||
- `index.php`: persönliches Dashboard
|
||||
- `stricheintragen.php`: Sammelerfassung für Kaffee-Striche
|
||||
- `einzahlung.php`: Sammelerfassung für Einzahlungen
|
||||
- `kaffeeliste.php`: operative Gesamtübersicht
|
||||
- `mitarbeiterverwalten.php`: Mitglieder- und Rollenpflege
|
||||
- `letzteneintraege.php`: Korrektur letzter Buchungen
|
||||
- `hinweise.php`: Banner/Hinweise
|
||||
@@ -21,5 +21,5 @@ Es bleibt aus drei Gruenden im Repository:
|
||||
## Umgang Mit Dem Archiv
|
||||
|
||||
- Keine neuen Produktfunktionen mehr hier entwickeln.
|
||||
- Nur noch fuer Referenz, Datenabgleich oder Notfallbetrieb verwenden.
|
||||
- Neue Arbeit findet ausschliesslich in `../saas-app/` und `../docs/` statt.
|
||||
- Nur noch für Referenz, Datenabgleich oder Notfallbetrieb verwenden.
|
||||
- Neue Arbeit findet ausschließlich in `../saas-app/` und `../docs/` statt.
|
||||
|
||||
+13
-13
@@ -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.';
|
||||
}
|
||||
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)) {
|
||||
$errors[] = 'Die Locale muss dem Muster de-DE entsprechen.';
|
||||
}
|
||||
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) {
|
||||
$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)) {
|
||||
return [
|
||||
'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');
|
||||
if ($tokenRow === null) {
|
||||
$errors[] = 'Der Link ist ungueltig oder abgelaufen.';
|
||||
$errors[] = 'Der Link ist ungültig oder abgelaufen.';
|
||||
}
|
||||
|
||||
if ($errors !== []) {
|
||||
@@ -730,7 +730,7 @@ function saas_verify_email_token(PDO $pdo, string $token): array
|
||||
if ($tokenRow === null) {
|
||||
return [
|
||||
'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 [
|
||||
'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 === '') {
|
||||
return [
|
||||
'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 [
|
||||
'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 === []) {
|
||||
return [
|
||||
'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) {
|
||||
return [
|
||||
'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.';
|
||||
}
|
||||
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) {
|
||||
$errors[] = 'Der Name muss zwischen 2 und 255 Zeichen lang sein.';
|
||||
}
|
||||
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) {
|
||||
$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) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'errors' => ['Dieses Kundenkuerzel ist bereits vergeben.'],
|
||||
'errors' => ['Dieses Kundenkürzel ist bereits vergeben.'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -117,8 +117,8 @@ function saas_send_password_reset_mail(string $to, string $token): array
|
||||
$body = implode("\n", [
|
||||
'Hallo,',
|
||||
'',
|
||||
'fuer dein Kaffeeliste-Konto wurde ein Passwort-Reset angefordert.',
|
||||
'Oeffne diesen Link, um ein neues Passwort zu setzen:',
|
||||
'für dein Kaffeeliste-Konto wurde ein Passwort-Reset angefordert.',
|
||||
'Öffne diesen Link, um ein neues Passwort zu setzen:',
|
||||
'',
|
||||
$link,
|
||||
'',
|
||||
@@ -128,7 +128,7 @@ function saas_send_password_reset_mail(string $to, string $token): array
|
||||
'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
|
||||
@@ -137,8 +137,8 @@ function saas_send_email_verification_mail(string $to, string $token): array
|
||||
$body = implode("\n", [
|
||||
'Hallo,',
|
||||
'',
|
||||
'bitte bestaetige deine E-Mail-Adresse fuer Kaffeeliste.',
|
||||
'Oeffne dazu diesen Link:',
|
||||
'bitte bestätige deine E-Mail-Adresse für Kaffeeliste.',
|
||||
'Öffne dazu diesen Link:',
|
||||
'',
|
||||
$link,
|
||||
'',
|
||||
@@ -147,5 +147,5 @@ function saas_send_email_verification_mail(string $to, string $token): array
|
||||
'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
@@ -33,7 +33,7 @@ function csv_prepare_upload(array $file): array
|
||||
}
|
||||
|
||||
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'] ?? '');
|
||||
@@ -248,7 +248,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
}elseif($eintrag[3] == 3){
|
||||
echo "Benutzer nicht gefunden";
|
||||
}elseif($eintrag[3] == 4){
|
||||
echo "Ungueltige CSV-Zeile";
|
||||
echo "Ungültige CSV-Zeile";
|
||||
}
|
||||
|
||||
echo "</td>
|
||||
|
||||
+12
-12
@@ -1,17 +1,17 @@
|
||||
# Lokaler MySQL-Dev-Start
|
||||
|
||||
Die Legacy-App kann fuer Entwicklung gegen MySQL laufen. Dafuer gibt es eine
|
||||
Kompatibilitaetsschicht, die die verwendeten `sqlsrv_*`-Funktionen im Dev-Modus
|
||||
Die Legacy-App kann für Entwicklung gegen MySQL laufen. Dafür gibt es eine
|
||||
Kompatibilitätsschicht, die die verwendeten `sqlsrv_*`-Funktionen im Dev-Modus
|
||||
auf PDO/MySQL abbildet.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- PHP CLI mit `pdo_mysql`.
|
||||
- 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,
|
||||
weil `sudo` nicht passwordless verfuegbar war. `.local/` wird von Git ignoriert.
|
||||
weil `sudo` nicht passwordless verfügbar war. `.local/` wird von Git ignoriert.
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
@@ -26,7 +26,7 @@ export DEV_AUTH_EMAIL="admin@test.local"
|
||||
export DEV_AUTH_NAME="Test Admin"
|
||||
# Optional: Standard ist var/sessions im Repository.
|
||||
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_MAIL_TRANSPORT="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
|
||||
der Port ueber die Ports-Ansicht weitergeleitet werden.
|
||||
der Port über die Ports-Ansicht weitergeleitet werden.
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Der Dev-Login umgeht AD/LDAP und nutzt `DEV_AUTH_EMAIL`.
|
||||
- Der Dev-Mailversand nutzt standardmaessig `APP_MAIL_TRANSPORT=log` und legt
|
||||
Nachrichten unter `var/mail` ab. Fuer Produktion kann zunaechst
|
||||
- Der Dev-Mailversand nutzt standardmäßig `APP_MAIL_TRANSPORT=log` und legt
|
||||
Nachrichten unter `var/mail` ab. Für Produktion kann zunächst
|
||||
`APP_MAIL_TRANSPORT=mail` mit passendem `APP_BASE_URL` und Absender gesetzt
|
||||
werden; eine SMTP-/Provider-Anbindung bleibt ein spaeterer Betriebsausbau.
|
||||
- Die Tabellen werden ueber `database/migrations/` angelegt.
|
||||
werden; eine SMTP-/Provider-Anbindung bleibt ein späterer Betriebsausbau.
|
||||
- Die Tabellen werden über `database/migrations/` angelegt.
|
||||
- `database/mysql-dev-schema.sql` bleibt als historische Dev-Schema-Baseline
|
||||
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.
|
||||
- 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
@@ -3,7 +3,7 @@
|
||||
Stand: 2026-07-11
|
||||
|
||||
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.
|
||||
|
||||
## Status
|
||||
@@ -13,24 +13,24 @@ Design-Erhalt und fachliche Vergleichstests.
|
||||
| Code-Inventar | begonnen | Siehe `code-inventory.md` |
|
||||
| Prozesslandkarte | begonnen | In `code-inventory.md` enthalten |
|
||||
| Sicherheitsbaseline | begonnen | Siehe `security-baseline.md` |
|
||||
| DB-Schema-Export | nicht aus Alt-DB verfuegbar | Siehe `legacy-db-status.md` |
|
||||
| Golden-Master-Auswertungen | 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 verfügbar | Siehe `legacy-db-status.md` |
|
||||
| Design-/Screenshot-Referenz | teilweise geliefert | Siehe `screenshot-checklist.md` |
|
||||
|
||||
## Lokal abgeschlossen
|
||||
|
||||
- Legacy-Einstiegspunkte und Kernseiten identifiziert.
|
||||
- Fachliche Tabellen aus SQL-Strings abgeleitet.
|
||||
- Schreibende Aktionen und kritische Datenfluesse markiert.
|
||||
- Sicherheitsrisiken fuer M0 inventarisiert.
|
||||
- SQL-Vorlagen fuer Schema- und Golden-Master-Export angelegt.
|
||||
- Screenshot-Checkliste fuer Design-Erhalt angelegt.
|
||||
- Schreibende Aktionen und kritische Datenflüsse markiert.
|
||||
- Sicherheitsrisiken für M0 inventarisiert.
|
||||
- SQL-Vorlagen für Schema- und Golden-Master-Export angelegt.
|
||||
- Screenshot-Checkliste für Design-Erhalt angelegt.
|
||||
- Erste Screenshot-Referenzen unter `docs/m0/screenshots/` abgelegt.
|
||||
- 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.
|
||||
|
||||
## Lokale Einschraenkungen
|
||||
## Lokale Einschränkungen
|
||||
|
||||
- `php` ist in dieser Umgebung nicht im PATH, daher kein lokaler Syntaxcheck und
|
||||
kein lokaler App-Start.
|
||||
@@ -40,33 +40,33 @@ Design-Erhalt und fachliche Vergleichstests.
|
||||
|
||||
## 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
|
||||
Golden-Master-Werte nachtraeglich exportieren.
|
||||
1. Falls später doch noch ein MS-SQL-Dump auftaucht: Schema und
|
||||
Golden-Master-Werte nachträglich exportieren.
|
||||
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,
|
||||
Auth-Setup, Cron/Job-Ausfuehrung.
|
||||
Auth-Setup, Cron/Job-Ausführung.
|
||||
|
||||
## 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:
|
||||
|
||||
- Idealerweise: einen anonymisierten SQL-Server-Schemaexport plus Rowcounts.
|
||||
- Alternativ: Zugriffsdaten zu einer Test-/Staging-DB, nicht zu Produktion.
|
||||
- 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
|
||||
`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.
|
||||
- Info, ob hart codierte Zugangsdaten aus Legacy-Skripten bereits rotiert wurden.
|
||||
- Entscheidung, ob M1/M2 nativ in PHP oder mit Framework vorbereitet werden
|
||||
sollen.
|
||||
|
||||
Bitte keine Produktivpasswoerter oder echten personenbezogenen Exportdaten in
|
||||
Bitte keine Produktivpasswörter oder echten personenbezogenen Exportdaten in
|
||||
das Repository legen.
|
||||
|
||||
+36
-36
@@ -10,28 +10,28 @@ Das echte Datenbankschema muss noch mit `schema-export.sql` verifiziert werden.
|
||||
| Datei | Rolle | Wichtige Beobachtung |
|
||||
| --- | --- | --- |
|
||||
| `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 |
|
||||
| `header.php` | HTML-Head und Hinweisbanner | Laedt `assets/css/main.css`, liest `kl_hinweise` |
|
||||
| `footer.php` | App-Sidebar und JS | Enthaelt die sichtbare Navigation und Admin-Menues |
|
||||
| `header.php` | HTML-Head und Hinweisbanner | Lädt `assets/css/main.css`, liest `kl_hinweise` |
|
||||
| `footer.php` | App-Sidebar und JS | Enthält die sichtbare Navigation und Admin-Menüs |
|
||||
| `nav.php` | Navigation | Aktuell praktisch leer |
|
||||
| `headerline.php` | Headerfragment | Enthaelt NUL-Zeichen, fachlich unklar |
|
||||
| `assets/css/main.css` | Hauptdesign | HTML5-UP Editorial, gruener Akzent, Tabellen, Buttons, Sidebar |
|
||||
| `headerline.php` | Headerfragment | Enthält NUL-Zeichen, fachlich unklar |
|
||||
| `assets/css/main.css` | Hauptdesign | HTML5-UP Editorial, grüner Akzent, Tabellen, Buttons, Sidebar |
|
||||
| `DataTables/` | Tabellen-Assets | Wird teilweise eingebunden, aber nicht konsequent initialisiert |
|
||||
| `PHPMailer/` | Mailversand | Fuer Rundmails/Jahresauswertung genutzt |
|
||||
| `TCPDF/` | PDF-Export | Fuer Kaffeelisten-Export genutzt |
|
||||
| `PHPMailer/` | Mailversand | Für Rundmails/Jahresauswertung genutzt |
|
||||
| `TCPDF/` | PDF-Export | Für Kaffeelisten-Export genutzt |
|
||||
|
||||
## Fachliche Kernseiten
|
||||
|
||||
| Datei | Prozess | Tabellen |
|
||||
| --- | --- | --- |
|
||||
| `index.php` | Persoenliches 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` |
|
||||
| `index.php` | Persönliches Dashboard, Saldo, Jahreswerte, eigene Striche, PayPal-Links | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_config` |
|
||||
| `kaffeeliste.php` | Admin-Gesamtübersicht mit Salden | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
|
||||
| `stricheintragen.php` | Sammelerfassung von Strichen | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_config` |
|
||||
| `einzahlung.php` | Sammelerfassung von Einzahlungen | `kl_Mitarbeiter`, `kl_Einzahlungen`, `kl_Kaffeeverbrauch` |
|
||||
| `mitarbeiterverwalten.php` | Mitglieder anlegen, bearbeiten, aktivieren, deaktivieren | `kl_Mitarbeiter` |
|
||||
| `namenanpassen.php` | Anzeigenamen aendern | `kl_Mitarbeiter` |
|
||||
| `letzteneintraege.php` | Letzte Buchungen anzeigen und loeschen | `kl_Einzahlungen`, `kl_Kaffeeverbrauch`, `kl_Mitarbeiter` |
|
||||
| `namenanpassen.php` | Anzeigenamen ändern | `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` |
|
||||
| `exportKaffeeliste.php` | PDF-/Listenexport | `kl_Mitarbeiter`, `kl_Kaffeeverbrauch`, `kl_Einzahlungen`, `kl_config` |
|
||||
| `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
|
||||
|
||||
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.
|
||||
|
||||
| Tabelle | Abgeleitete Spalten |
|
||||
@@ -64,13 +64,13 @@ abgeglichen werden.
|
||||
1. Seite inkludiert `functions.php`.
|
||||
2. `functions.php` liest `$_SERVER['AUTH_USER']`.
|
||||
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:
|
||||
Login-Konto, Kaffee-Teilnehmer und Rolle sind im Legacy-Modell gekoppelt. Fuer
|
||||
SaaS muessen `users`, `participants` und `tenant_memberships` getrennt werden.
|
||||
Login-Konto, Kaffee-Teilnehmer und Rolle sind im Legacy-Modell gekoppelt. Für
|
||||
SaaS müssen `users`, `participants` und `tenant_memberships` getrennt werden.
|
||||
|
||||
### Persoenliches Dashboard
|
||||
### Persönliches Dashboard
|
||||
|
||||
Quelle: `index.php`.
|
||||
|
||||
@@ -81,7 +81,7 @@ Berechnungen:
|
||||
- Gesamtausgabe: Summe `kl_Kaffeeverbrauch.Kosten`.
|
||||
- Gesamtstriche: Summe `kl_Kaffeeverbrauch.AnzahlStriche`.
|
||||
- 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 `paypaluse`: PayPal-Links anzeigen.
|
||||
|
||||
@@ -123,19 +123,19 @@ M0-Feststellung:
|
||||
Das `admin`-Flag ist fachlich eine Rolleninformation und sollte in der SaaS-App
|
||||
in `tenant_memberships.role` wandern.
|
||||
|
||||
### Korrektur letzter Eintraege
|
||||
### Korrektur letzter Einträge
|
||||
|
||||
Quelle: `letzteneintraege.php`.
|
||||
|
||||
Aktionen:
|
||||
|
||||
- Letzte 100 Einzahlungen anzeigen.
|
||||
- Einzahlung hart loeschen.
|
||||
- Letzte 100 Strich-Eintraege anzeigen.
|
||||
- Strich-Eintrag hart loeschen.
|
||||
- Einzahlung hart löschen.
|
||||
- Letzte 100 Strich-Einträge anzeigen.
|
||||
- Strich-Eintrag hart löschen.
|
||||
|
||||
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.
|
||||
|
||||
### CSV-Import
|
||||
@@ -148,8 +148,8 @@ Ablauf:
|
||||
- CSV mit Komma-Trennung lesen.
|
||||
- Name aus Spalte 3, Betrag aus Spalte 7, Datum aus Spalte 0.
|
||||
- Mitarbeiter per Name oder `paypalname` finden.
|
||||
- Dublette anhand Mitarbeiter, Betrag und Datumstag pruefen.
|
||||
- Einzahlung einfuegen.
|
||||
- Dublette anhand Mitarbeiter, Betrag und Datumstag prüfen.
|
||||
- Einzahlung einfügen.
|
||||
- Ergebnis in Erfolg/Fehler klassifizieren.
|
||||
|
||||
### Jahresauswertung
|
||||
@@ -160,33 +160,33 @@ Ablauf:
|
||||
|
||||
- Gesamtstriche des aktuellen Jahres je aktivem Mitarbeiter berechnen.
|
||||
- Konstante Anzahl neuer Striche/Gutschrift proportional verteilen.
|
||||
- Einzahlung je Mitarbeiter einfuegen.
|
||||
- Einzahlung je Mitarbeiter einfügen.
|
||||
- Mail an Mitarbeiter senden.
|
||||
|
||||
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.
|
||||
|
||||
## Offene Schemafragen
|
||||
|
||||
- Gibt es Primary Keys, Foreign Keys und Indizes fuer die `kl_`-Tabellen?
|
||||
- Sind Datentypen fuer Geldwerte `decimal`, `money`, `float` oder etwas anderes?
|
||||
- Gibt es Primary Keys, Foreign Keys und Indizes für die `kl_`-Tabellen?
|
||||
- Sind Datentypen für Geldwerte `decimal`, `money`, `float` oder etwas anderes?
|
||||
- Gibt es historische Spalten, die im Code nicht sichtbar sind?
|
||||
- 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?
|
||||
|
||||
## Technische Legacy-Inkonsistenzen
|
||||
|
||||
- `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.
|
||||
- `einzahlung.php` prueft auf `$_GET["action"]`, die Buttons erzeugen aber
|
||||
URLs mit `aktion=...`. Der Vorderseite-/Rueckseite-Filter kann dadurch
|
||||
- `einzahlung.php` prüft auf `$_GET["action"]`, die Buttons erzeugen aber
|
||||
URLs mit `aktion=...`. Der Vorderseite-/Rückseite-Filter kann dadurch
|
||||
abweichend vom erwarteten Verhalten laufen.
|
||||
- `exportKaffeeliste.php` referenziert `tcpdf/tcpdf.php`, waehrend der Ordner im
|
||||
Repository `TCPDF/` heisst. Auf case-sensitiven Dateisystemen kann das
|
||||
- `exportKaffeeliste.php` referenziert `tcpdf/tcpdf.php`, während der Ordner im
|
||||
Repository `TCPDF/` heißt. Auf case-sensitiven Dateisystemen kann das
|
||||
brechen.
|
||||
- `headerline.php` ist fachlich leer beziehungsweise mit NUL-Zeichen gefuellt.
|
||||
Vor der neuen App-Shell sollte geklaert werden, ob die Datei entfernt werden
|
||||
- `headerline.php` ist fachlich leer beziehungsweise mit NUL-Zeichen gefüllt.
|
||||
Vor der neuen App-Shell sollte geklärt werden, ob die Datei entfernt werden
|
||||
kann.
|
||||
|
||||
@@ -3,10 +3,10 @@ M0 Golden-Master Queries
|
||||
|
||||
Ziel:
|
||||
- Fachliche Legacy-Ergebnisse exportieren, damit Migration und neue SaaS-App
|
||||
gegen denselben Stand verglichen werden koennen.
|
||||
gegen denselben Stand verglichen werden können.
|
||||
|
||||
Ausfuehrung:
|
||||
- In der Legacy-Datenbank ausfuehren.
|
||||
Ausführung:
|
||||
- In der Legacy-Datenbank ausführen.
|
||||
- Ergebnisse je Query als CSV speichern.
|
||||
- Keine personenbezogenen Exporte ins Repo committen.
|
||||
*/
|
||||
@@ -94,7 +94,7 @@ FROM kl_Einzahlungen e
|
||||
JOIN kl_Mitarbeiter m ON m.MitarbeiterID = e.MitarbeiterID
|
||||
ORDER BY e.Datum DESC, e.EinzahlungsID DESC;
|
||||
|
||||
-- 5. Letzte Strich-/Verbrauchseintraege je Teilnehmer
|
||||
-- 5. Letzte Strich-/Verbrauchseinträge je Teilnehmer
|
||||
SELECT
|
||||
v.VerbrauchID,
|
||||
v.MitarbeiterID,
|
||||
@@ -142,7 +142,7 @@ GROUP BY m.MitarbeiterID, m.Name, m.Email
|
||||
HAVING SUM(v.AnzahlStriche) >= 10
|
||||
ORDER BY m.Name;
|
||||
|
||||
-- 9. 100-Tage-Rueckseite analog Legacy-Stricherfassung
|
||||
-- 9. 100-Tage-Rückseite analog Legacy-Stricherfassung
|
||||
SELECT
|
||||
m.MitarbeiterID,
|
||||
m.Name,
|
||||
@@ -182,4 +182,3 @@ WHERE YEAR(v.Datum) = @CurrentYear
|
||||
AND m.aktiv = 1
|
||||
GROUP BY m.MitarbeiterID, m.Name, m.Email
|
||||
ORDER BY GesamtStriche DESC, m.Name;
|
||||
|
||||
|
||||
+14
-15
@@ -4,44 +4,43 @@ Stand: 2026-07-11
|
||||
|
||||
## Entscheidung
|
||||
|
||||
Ein alter MS-SQL-Datenbankstand steht aktuell nicht zur Verfuegung. Deshalb
|
||||
koennen `schema-export.sql` und `golden-master-queries.sql` derzeit nicht gegen
|
||||
eine echte Legacy-Datenbank ausgefuehrt werden.
|
||||
Ein alter MS-SQL-Datenbankstand steht aktuell nicht zur Verfügung. Deshalb
|
||||
können `schema-export.sql` und `golden-master-queries.sql` derzeit nicht gegen
|
||||
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 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.
|
||||
|
||||
## Konsequenz fuer M1
|
||||
## Konsequenz für M1
|
||||
|
||||
M1 wird nicht als Vergleich gegen echte Alt-Daten gestartet, sondern als
|
||||
rekonstruierter Golden-Master:
|
||||
|
||||
1. Fachliche Regeln aus dem Code ableiten.
|
||||
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.
|
||||
4. Neue Implementierung gegen diese erwarteten Werte testen.
|
||||
|
||||
Wenn spaeter doch noch ein MS-SQL-Dump oder Schemaexport auftaucht, werden die
|
||||
urspruenglichen M0-SQL-Dateien nachtraeglich genutzt und die Baseline ergaenzt.
|
||||
Wenn später doch noch ein MS-SQL-Dump oder Schemaexport auftaucht, werden die
|
||||
ursprünglichen M0-SQL-Dateien nachträglich genutzt und die Baseline ergänzt.
|
||||
|
||||
## Aktuelle rekonstruierte Basis
|
||||
|
||||
- MySQL-Dev-Schema: `database/mysql-dev-schema.sql`
|
||||
- Initialisierung: `scripts/init-mysql-dev.php`
|
||||
- Dev-Start: `scripts/run-dev-server.sh`
|
||||
- MySQL-Kompatibilitaet: `lib/sqlsrv_mysql_compat.php`
|
||||
- MySQL-Kompatibilität: `lib/sqlsrv_mysql_compat.php`
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
- 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
|
||||
SaaS-Neuaufbau mit uebernommener Fachlogik?
|
||||
- Welche Testfaelle muessen fachlich auf jeden Fall abgedeckt werden?
|
||||
|
||||
SaaS-Neuaufbau mit übernommener Fachlogik?
|
||||
- Welche Testfälle müssen fachlich auf jeden Fall abgedeckt werden?
|
||||
|
||||
@@ -6,7 +6,7 @@ Ziel:
|
||||
aus der Legacy-Datenbank exportieren.
|
||||
|
||||
Hinweise:
|
||||
- In der Ziel-Datenbank ausfuehren.
|
||||
- In der Ziel-Datenbank ausführen.
|
||||
- Ergebnisse als CSV/Excel speichern und nicht mit Produktiv-Secrets ins Repo
|
||||
legen.
|
||||
- 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%'
|
||||
)
|
||||
ORDER BY s.name, o.name;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Stand: 2026-07-11
|
||||
|
||||
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.
|
||||
|
||||
## Allgemeine Vorgaben
|
||||
@@ -11,24 +11,24 @@ aktuellen Umgebung geliefert werden.
|
||||
- Je Seite Desktop mit ca. 1440 px Breite aufnehmen.
|
||||
- Je Kernseite mobile Ansicht mit ca. 390 px Breite aufnehmen.
|
||||
- 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`.
|
||||
|
||||
## 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` | 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 | `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 | `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 | `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 | `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 | `mailausgebe.php` | Admin | offen | Mail-/Adressausgabe |
|
||||
| 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
|
||||
```
|
||||
|
||||
## 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.
|
||||
- Negativer Kontostand.
|
||||
@@ -62,14 +62,14 @@ Diese UI-Zustaende sollten, wenn moeglich, separat gesichert werden:
|
||||
- CSV-Import mit Erfolg, Dublette und unbekanntem Nutzer.
|
||||
- Hinweisbanner aktiv.
|
||||
- Leere Tabellen oder keine Daten.
|
||||
- Mobile Sidebar geoeffnet und geschlossen.
|
||||
- Mobile Sidebar geöffnet und geschlossen.
|
||||
|
||||
## Designmerkmale, die erhalten bleiben sollen
|
||||
|
||||
- Gruener Akzent fuer Links, Buttons und Linien.
|
||||
- Roboto-Slab-Ueberschriften.
|
||||
- Open-Sans-Fliesstext.
|
||||
- Weisse Hauptflaeche.
|
||||
- Grüner Akzent für Links, Buttons und Linien.
|
||||
- Roboto-Slab-Überschriften.
|
||||
- Open-Sans-Fließtext.
|
||||
- Weiße Hauptfläche.
|
||||
- Graue Sidebar in der App.
|
||||
- Tabellen als zentrale Datenansicht.
|
||||
- Schlichte Formularfelder und Buttons.
|
||||
@@ -84,5 +84,5 @@ docs/m0/screenshots/
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
Stand: 2026-07-11
|
||||
|
||||
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
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| Harte Deletes fuer Buchungen | `letzteneintraege.php` | Audit-Historie und Revisionsfaehigkeit 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 |
|
||||
| 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, Löschungen, Imports | CSRF für alle verbleibenden schreibenden Aktionen |
|
||||
| Harte Deletes für Buchungen | `letzteneintraege.php` | Audit-Historie und Revisionsfähigkeit gehen verloren | Storno-/Reversal-Modell statt Delete |
|
||||
| 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 |
|
||||
|
||||
## 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
## Rollen- und Zugriffsrisiken
|
||||
@@ -39,29 +39,29 @@ Aktuell gilt:
|
||||
gesteuert.
|
||||
- Einige Admin-Zielseiten haben eigene Checks.
|
||||
- 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.
|
||||
- Jede Route braucht serverseitige Auth- und Rollenpruefung.
|
||||
- Menü-Ausblendung ist keine Berechtigungsprüfung.
|
||||
- Jede Route braucht serverseitige Auth- und Rollenprüfung.
|
||||
- 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.
|
||||
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.
|
||||
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.
|
||||
6. Einheitliche Escape-/View-Helfer einfuehren.
|
||||
6. Einheitliche Escape-/View-Helfer einführen.
|
||||
7. Tenant-Isolation mit Tests gegen zwei Tenants absichern.
|
||||
|
||||
## Nicht im Repo speichern
|
||||
|
||||
- Produktivpasswoerter.
|
||||
- Produktivpasswörter.
|
||||
- Echte Nutzerlisten.
|
||||
- Bank-/PayPal-Exportdaten mit Personenbezug.
|
||||
- AD-/LDAP-Service-Account-Daten.
|
||||
- Vollstaendige Produktiv-Dumps.
|
||||
- Vollständige Produktiv-Dumps.
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
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
|
||||
kontrollierten Testdaten in der MySQL-Dev-Datenbank.
|
||||
|
||||
## Ziel
|
||||
|
||||
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.
|
||||
|
||||
## Testdaten-Gruppen
|
||||
@@ -28,7 +28,7 @@ die SaaS-Umstrukturierung.
|
||||
- Mehrere Einzahlungen an unterschiedlichen Tagen.
|
||||
- Strichbuchung mit einem Strich.
|
||||
- Strichbuchung mit zwei Strichen.
|
||||
- Sammelerfassung fuer mehrere Teilnehmer.
|
||||
- Sammelerfassung für mehrere Teilnehmer.
|
||||
- Web-Strich-Eintrag mit `Eintragsart = 2`.
|
||||
|
||||
### Salden
|
||||
@@ -36,13 +36,13 @@ die SaaS-Umstrukturierung.
|
||||
- Positiver Kontostand.
|
||||
- Negativer Kontostand.
|
||||
- Nullsaldo.
|
||||
- Jahreswerte fuer aktuelles Jahr.
|
||||
- Jahreswerte für aktuelles Jahr.
|
||||
- Altdaten aus Vorjahr, die nicht in Jahreswerte fallen.
|
||||
|
||||
### CSV-Import
|
||||
|
||||
- Treffer ueber Name.
|
||||
- Treffer ueber `paypalname`.
|
||||
- Treffer über Name.
|
||||
- Treffer über `paypalname`.
|
||||
- Dublette gleicher Teilnehmer, Betrag und Datum.
|
||||
- Unbekannter Name.
|
||||
- Negativer oder leerer Betrag.
|
||||
@@ -51,11 +51,11 @@ die SaaS-Umstrukturierung.
|
||||
|
||||
- Aktiver Hinweis.
|
||||
- Abgelaufener Hinweis.
|
||||
- Neuer Hinweis ueber Admin-Formular.
|
||||
- Neuer Hinweis über Admin-Formular.
|
||||
|
||||
## Erwartete Referenzwerte
|
||||
|
||||
Fuer jeden Testfall sollen erwartete Werte dokumentiert werden:
|
||||
Für jeden Testfall sollen erwartete Werte dokumentiert werden:
|
||||
|
||||
- Summe Einzahlungen.
|
||||
- Summe Kosten.
|
||||
@@ -72,25 +72,25 @@ Fuer jeden Testfall sollen erwartete Werte dokumentiert werden:
|
||||
Seed-Skript anlegen. Erledigt mit `scripts/seed-golden-master.php`.
|
||||
2. `scripts/init-mysql-dev.php` so erweitern, dass es die Testdaten idempotent
|
||||
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.
|
||||
Erledigt.
|
||||
4. Die wichtigsten Seiten per HTTP abrufen und auf erwartete Texte/Werte
|
||||
pruefen. Erledigt mit `scripts/http-smoke.php`.
|
||||
5. Danach M2 starten: technisches Fundament fuer SaaS-Struktur. M1 ist als
|
||||
prüfen. Erledigt mit `scripts/http-smoke.php`.
|
||||
5. Danach M2 starten: technisches Fundament für SaaS-Struktur. M1 ist als
|
||||
Sicherheitsnetz nutzbar; offene Dependency-Themen sind unten dokumentiert.
|
||||
|
||||
## 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/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.
|
||||
- `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.
|
||||
|
||||
## Ausfuehrung
|
||||
## Ausführung
|
||||
|
||||
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`,
|
||||
`DB_USER` und `DB_PASS`.
|
||||
|
||||
## Abgedeckte Testfaelle
|
||||
## Abgedeckte Testfälle
|
||||
|
||||
| Testfall | Fixture |
|
||||
| --- | --- |
|
||||
@@ -131,77 +131,77 @@ Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
|
||||
| Negativer Saldo | `gm-negative@test.local` |
|
||||
| Nullsaldo | `gm-zero@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` |
|
||||
| Vieltrinker fuer Vorderseite | `gm-heavy@test.local` |
|
||||
| Aktiver Hinweis | `[GM] Aktiver Hinweis fuer Golden-Master-Test` |
|
||||
| Abgelaufener Hinweis | `[GM] Abgelaufener Hinweis fuer Golden-Master-Test` |
|
||||
| Vieltrinker für Vorderseite | `gm-heavy@test.local` |
|
||||
| Aktiver Hinweis | `[GM] Aktiver Hinweis für Golden-Master-Test` |
|
||||
| Abgelaufener Hinweis | `[GM] Abgelaufener Hinweis für Golden-Master-Test` |
|
||||
|
||||
## 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.
|
||||
|
||||
| Seite | Erwartung |
|
||||
| --- | --- |
|
||||
| `index.php` | Dashboard laedt fuer `Test Admin` |
|
||||
| `stricheintragen.php` | Stricherfassung laedt |
|
||||
| `einzahlung.php` | Einzahlungserfassung laedt |
|
||||
| `kaffeeliste.php` | Gesamtuebersicht laedt |
|
||||
| `mitarbeiterverwalten.php` | Mitgliederverwaltung inkl. `PayPal-Name` laedt |
|
||||
| `index.php` | Dashboard lädt für `Test Admin` |
|
||||
| `stricheintragen.php` | Stricherfassung lädt |
|
||||
| `einzahlung.php` | Einzahlungserfassung lädt |
|
||||
| `kaffeeliste.php` | Gesamtübersicht lädt |
|
||||
| `mitarbeiterverwalten.php` | Mitgliederverwaltung inkl. `PayPal-Name` lädt |
|
||||
| `letzteneintraege.php` | letzte Einzahlungen und Striche laden |
|
||||
| `csvupload.php` | CSV-Upload-Formular laedt |
|
||||
| `hinweise.php` | Hinweisverwaltung laedt |
|
||||
| `faq.php` | FAQ laedt |
|
||||
| `namenanpassen.php` | Namensanpassung laedt |
|
||||
| `teilnehmerauswertung.php?user_id=1` | Teilnehmerauswertung fuer Dev-Admin laedt |
|
||||
| `mailausgebe.php` | Mailadress-Ausgabe laedt |
|
||||
| `umfrage.php` | geschlossene Umfrage laedt |
|
||||
| `umfrageergebnisse.php` | Umfrageauswertung laedt |
|
||||
| `csvupload.php` | CSV-Upload-Formular lädt |
|
||||
| `hinweise.php` | Hinweisverwaltung lädt |
|
||||
| `faq.php` | FAQ lädt |
|
||||
| `namenanpassen.php` | Namensanpassung lädt |
|
||||
| `teilnehmerauswertung.php?user_id=1` | Teilnehmerauswertung für Dev-Admin lädt |
|
||||
| `mailausgebe.php` | Mailadress-Ausgabe lädt |
|
||||
| `umfrage.php` | geschlossene Umfrage lädt |
|
||||
| `umfrageergebnisse.php` | Umfrageauswertung lädt |
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- Testdaten sind reproduzierbar.
|
||||
- Tests koennen mehrfach laufen, ohne Duplikate zu erzeugen.
|
||||
- Salden und Jahreswerte sind automatisch pruefbar.
|
||||
- CSV-Importfaelle sind fachlich abgedeckt.
|
||||
- Tests können mehrfach laufen, ohne Duplikate zu erzeugen.
|
||||
- Salden und Jahreswerte sind automatisch prüfbar.
|
||||
- CSV-Importfälle sind fachlich abgedeckt.
|
||||
- Die wichtigsten UI-Seiten liefern HTTP 200 ohne PHP-Warnings.
|
||||
- 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
|
||||
|
||||
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
|
||||
`0` behandelt.
|
||||
- `paypalname` in der Mitgliederverwaltung: eingepflegt und im Golden Master
|
||||
abgedeckt.
|
||||
- `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
|
||||
bleibt trotzdem als bekannter Dependency-Fehler offen, weil die TCPDF-Kopie im
|
||||
Repo unvollstaendig ist und `TCPDF/include/tcpdf_font_data.php` fehlt.
|
||||
- `mailversenden.php`: nicht automatisch per HTTP ausgefuehrt, weil ein GET bei
|
||||
vollstaendiger PHPMailer-Installation E-Mails versenden kann. Zusaetzlich ist
|
||||
die PHPMailer-Kopie im Repo unvollstaendig.
|
||||
- `jahresauswertung.php`: nicht automatisch per HTTP ausgefuehrt, weil ein GET
|
||||
Jahresbuchungen schreiben und E-Mails versenden kann. Zusaetzlich ist die
|
||||
PHPMailer-Kopie im Repo unvollstaendig.
|
||||
Repo unvollständig ist und `TCPDF/include/tcpdf_font_data.php` fehlt.
|
||||
- `mailversenden.php`: nicht automatisch per HTTP ausgeführt, weil ein GET bei
|
||||
vollständiger PHPMailer-Installation E-Mails versenden kann. Zusätzlich ist
|
||||
die PHPMailer-Kopie im Repo unvollständig.
|
||||
- `jahresauswertung.php`: nicht automatisch per HTTP ausgeführt, weil ein GET
|
||||
Jahresbuchungen schreiben und E-Mails versenden kann. Zusätzlich ist die
|
||||
PHPMailer-Kopie im Repo unvollständig.
|
||||
|
||||
## Aktueller Pruefstatus
|
||||
## Aktueller Prüfstatus
|
||||
|
||||
Stand 2026-07-12:
|
||||
|
||||
- `scripts/seed-golden-master.php` erfolgreich gegen die MySQL-Dev-Datenbank
|
||||
ausgefuehrt.
|
||||
- `scripts/check-golden-master.php` erfolgreich ausgefuehrt.
|
||||
- Zweiter Seed+Check-Lauf erfolgreich, Idempotenz bestaetigt.
|
||||
ausgeführt.
|
||||
- `scripts/check-golden-master.php` erfolgreich ausgeführt.
|
||||
- Zweiter Seed+Check-Lauf erfolgreich, Idempotenz bestätigt.
|
||||
- Ergebnis: 104 Assertions bestanden.
|
||||
- `scripts/http-smoke.php` erfolgreich gegen `http://127.0.0.1:8080`
|
||||
ausgefuehrt.
|
||||
ausgeführt.
|
||||
- Ergebnis: 14 sichere Seiten bestanden ohne `Deprecated`, `Warning`,
|
||||
`Fatal error`, `Parse error`, `Notice` oder `Uncaught Error`.
|
||||
- Bekannter offener Punkt: PDF-Export wegen unvollstaendiger TCPDF-Abhaengigkeit.
|
||||
- Bewusst nicht automatisch ausgefuehrt: `mailversenden.php` und
|
||||
- Bekannter offener Punkt: PDF-Export wegen unvollständiger TCPDF-Abhängigkeit.
|
||||
- Bewusst nicht automatisch ausgeführt: `mailversenden.php` und
|
||||
`jahresauswertung.php`, da GET dort Nebenwirkungen haben kann.
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
Stand: 2026-07-13
|
||||
|
||||
M2 fuehrt die technischen Grundbausteine fuer den SaaS-Umbau ein, ohne das
|
||||
Legacy-Verhalten oder das bestehende Design zu veraendern.
|
||||
M2 führt die technischen Grundbausteine für den SaaS-Umbau ein, ohne das
|
||||
Legacy-Verhalten oder das bestehende Design zu verändern.
|
||||
|
||||
Status: abgeschlossen fuer den M2-Scope. Bewusst ausgelagerte Themen sind unten
|
||||
als Grenzen und M3-/M6-Uebergabe dokumentiert.
|
||||
Status: abgeschlossen für den M2-Scope. Bewusst ausgelagerte Themen sind unten
|
||||
als Grenzen und M3-/M6-Übergabe dokumentiert.
|
||||
|
||||
## Ziel
|
||||
|
||||
- Versionierte Datenbankmigrationen statt loser Schema-Ausfuehrung.
|
||||
- Zentrales Bootstrap fuer Umgebung, Session und CSRF-Helper.
|
||||
- Versionierte Datenbankmigrationen statt loser Schema-Ausführung.
|
||||
- Zentrales Bootstrap für Umgebung, Session und CSRF-Helper.
|
||||
- Bestehende Legacy-Seiten bleiben kompatibel.
|
||||
- Layout-Trennung wird vorbereitet, aber noch nicht in bestehende Seiten
|
||||
hineingezogen.
|
||||
@@ -28,47 +28,47 @@ als Grenzen und M3-/M6-Uebergabe dokumentiert.
|
||||
- `app_csrf_field()`
|
||||
- `app_verify_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.
|
||||
- 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
|
||||
von Git ignoriert. Der Pfad kann ueber `APP_SESSION_PATH` ueberschrieben
|
||||
von Git ignoriert. Der Pfad kann über `APP_SESSION_PATH` überschrieben
|
||||
werden.
|
||||
- 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.
|
||||
|
||||
### CSRF-Rollout
|
||||
|
||||
Erste Legacy-POST-Seiten sind opt-in abgesichert:
|
||||
|
||||
- `hinweise.php`: Hinweis anlegen und Hinweis loeschen. Der bisherige
|
||||
GET-Loeschlink wurde durch ein POST-Formular mit CSRF-Token ersetzt.
|
||||
- `mitarbeiterverwalten.php`: Mitglied anlegen, Bearbeitungsformular oeffnen,
|
||||
- `hinweise.php`: Hinweis anlegen und Hinweis löschen. Der bisherige
|
||||
GET-Löschlink wurde durch ein POST-Formular mit CSRF-Token ersetzt.
|
||||
- `mitarbeiterverwalten.php`: Mitglied anlegen, Bearbeitungsformular öffnen,
|
||||
Mitglied speichern, aktivieren und deaktivieren.
|
||||
- `namenanpassen.php`: Anzeigenamen aktualisieren.
|
||||
- `index.php`: eigene Web-Striche eintragen.
|
||||
- `stricheintragen.php`: Sammelerfassung von Strichen.
|
||||
- `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.
|
||||
|
||||
Noch offen:
|
||||
|
||||
- Spezialprozesse: `mailversenden.php`, `jahresauswertung.php`.
|
||||
|
||||
### CSV-Upload-Haertung
|
||||
### CSV-Upload-Härtung
|
||||
|
||||
- `csvupload.php` nutzt jetzt CSRF.
|
||||
- Uploads werden unter `var/uploads` gespeichert und nach der Verarbeitung
|
||||
geloescht. Damit liegen importierte Dateien nicht mehr im Webroot.
|
||||
- Dateiendung, Dateigroesse und MIME-Typ werden vor der Verarbeitung geprueft.
|
||||
gelöscht. Damit liegen importierte Dateien nicht mehr im Webroot.
|
||||
- Dateiendung, Dateigröße und MIME-Typ werden vor der Verarbeitung geprüft.
|
||||
- Hochgeladene Dateien bekommen serverseitig erzeugte Zufallsnamen.
|
||||
- CSV-Auswertungswerte werden HTML-escaped ausgegeben.
|
||||
- Die PayPal-Namenssuche nutzt `Name` und `paypalname` mit expliziter
|
||||
Parameterbindung.
|
||||
|
||||
Offen fuer M6:
|
||||
Offen für M6:
|
||||
|
||||
- Importvorschau vor dem Schreiben.
|
||||
- Import-Batch/Audit-Log.
|
||||
@@ -78,19 +78,19 @@ Offen fuer M6:
|
||||
|
||||
- `database/migrations/0001_legacy_mysql_baseline.sql` bildet die bisherige
|
||||
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.
|
||||
- `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.
|
||||
|
||||
### Layout
|
||||
|
||||
- `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.
|
||||
- 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:
|
||||
|
||||
@@ -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`,
|
||||
`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`.
|
||||
|
||||
## Aktueller Pruefstatus
|
||||
## Aktueller Prüfstatus
|
||||
|
||||
- Migration `0001_legacy_mysql_baseline.sql` erfolgreich angewendet.
|
||||
- Zweiter Migrationslauf meldet: Datenbank ist aktuell.
|
||||
- PHP-Syntax fuer Bootstrap, Migrationen, Init-Skript und Config ist sauber.
|
||||
- Session-Start laeuft in der lokalen Dev-Umgebung ohne PHP-Warnings ueber
|
||||
- PHP-Syntax für Bootstrap, Migrationen, Init-Skript und Config ist sauber.
|
||||
- Session-Start läuft in der lokalen Dev-Umgebung ohne PHP-Warnings über
|
||||
`var/sessions`.
|
||||
- CSRF negative Tests: `hinweise.php`, `mitarbeiterverwalten.php` und
|
||||
`namenanpassen.php` liefern bei POST ohne Token HTTP 419.
|
||||
- CSRF positive Tests: gueltige Token funktionieren fuer Hinweis-Anlage,
|
||||
Mitglieder-Bearbeitungsformular und Namensanpassung. Der temporaere
|
||||
- CSRF positive Tests: gültige Token funktionieren für Hinweis-Anlage,
|
||||
Mitglieder-Bearbeitungsformular und Namensanpassung. Der temporäre
|
||||
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.
|
||||
- CSRF positive Tests fuer Buchungsflows: gueltige Token funktionieren fuer
|
||||
eigene Web-Striche, Sammelstriche und Sammeleinzahlungen. Die temporaeren
|
||||
- CSRF positive Tests für Buchungsflows: gültige Token funktionieren für
|
||||
eigene Web-Striche, Sammelstriche und Sammeleinzahlungen. Die temporären
|
||||
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.
|
||||
- CSRF positive Tests fuer Korrektur-/Loeschflows: gueltige Token funktionieren
|
||||
fuer das Loeschen temporaerer Einzahlungs- und Strich-Testeintraege.
|
||||
- CSRF negative Test fuer CSV-Upload: POST ohne Token liefert HTTP 419.
|
||||
- CSV-Upload positive Tests: gueltiges Token verarbeitet eine CSV-Datei, erkennt
|
||||
eine PayPal-Alias-Dublette und hinterlaesst keine Datei in `var/uploads`.
|
||||
- CSRF positive Tests für Korrektur-/Löschflows: gültige Token funktionieren
|
||||
für das Löschen temporärer Einzahlungs- und Strich-Testeinträge.
|
||||
- CSRF negative Test für CSV-Upload: POST ohne Token liefert HTTP 419.
|
||||
- CSV-Upload positive Tests: gültiges Token verarbeitet eine CSV-Datei, erkennt
|
||||
eine PayPal-Alias-Dublette und hinterlässt keine Datei in `var/uploads`.
|
||||
- CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen.
|
||||
- Golden Master weiterhin gruen mit 104 Assertions.
|
||||
- HTTP-Smoke weiterhin gruen mit 23 sicheren Seiten inklusive Landingpage, Login,
|
||||
Registrierung, Passwort-Reset, E-Mail-Verifikation und geschuetzter
|
||||
Mandant-Einstellungen, geschuetzter Mandantenauswahl sowie Ledger-Preview.
|
||||
- Golden Master weiterhin grün mit 104 Assertions.
|
||||
- HTTP-Smoke weiterhin grün mit 23 sicheren Seiten inklusive Landingpage, Login,
|
||||
Registrierung, Passwort-Reset, E-Mail-Verifikation und geschützter
|
||||
Mandant-Einstellungen, geschützter Mandantenauswahl sowie Ledger-Preview.
|
||||
|
||||
## Bewusste Grenzen
|
||||
|
||||
- Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehoeren zu M3.
|
||||
- Keine globale CSRF-Erzwingung fuer Spezialprozesse. `mailversenden.php` und
|
||||
- Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehören zu M3.
|
||||
- Keine globale CSRF-Erzwingung für Spezialprozesse. `mailversenden.php` und
|
||||
`jahresauswertung.php` werden in M6 als Jobs mit Dry-Run, Audit und
|
||||
Versandlog neu betrachtet.
|
||||
- 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
|
||||
|
||||
- Migrationsrunner ist vorhanden und idempotent.
|
||||
- Bootstrap, Session und CSRF-Helper sind zentral verfuegbar.
|
||||
- Die relevanten Legacy-Schreibseiten sind CSRF-geschuetzt.
|
||||
- Bootstrap, Session und CSRF-Helper sind zentral verfügbar.
|
||||
- Die relevanten Legacy-Schreibseiten sind CSRF-geschützt.
|
||||
- CSV-Uploads liegen nicht mehr im Webroot.
|
||||
- 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
|
||||
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.
|
||||
|
||||
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
|
||||
`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
|
||||
`legacy_mitarbeiter_id`.
|
||||
4. Admins aus `kl_Mitarbeiter.admin` als `tenant_memberships.role = 'admin'`
|
||||
beziehungsweise fuer den ersten Hauptnutzer als `owner` abbilden.
|
||||
5. Erst danach Login/Registrierung und Public-/App-Routen anschliessen.
|
||||
beziehungsweise für den ersten Hauptnutzer als `owner` abbilden.
|
||||
5. Erst danach Login/Registrierung und Public-/App-Routen anschließen.
|
||||
|
||||
@@ -2,27 +2,27 @@
|
||||
|
||||
Stand: 2026-07-13
|
||||
|
||||
M3 fuehrt Mandanten, Benutzer, Mitgliedschaften und Grundeinstellungen additiv
|
||||
ein. Die bestehende Legacy-App bleibt dabei lauffaehig und wird noch nicht auf
|
||||
M3 führt Mandanten, Benutzer, Mitgliedschaften und Grundeinstellungen additiv
|
||||
ein. Die bestehende Legacy-App bleibt dabei lauffähig und wird noch nicht auf
|
||||
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`.
|
||||
|
||||
## 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.
|
||||
- Rollen tenant-spezifisch vorbereiten.
|
||||
- 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.
|
||||
|
||||
## Nicht-Ziele fuer den ersten M3-Schritt
|
||||
## Nicht-Ziele für den ersten M3-Schritt
|
||||
|
||||
- Kein kompletter Login-Umbau in einem Schritt.
|
||||
- Keine Migration von Einzahlungen und Kaffeeverbrauch nach `ledger_entries`;
|
||||
das gehoert zu M4.
|
||||
das gehört zu M4.
|
||||
- Kein Design- oder Layout-Umbau.
|
||||
- Kein Billing und keine Tarife im ersten Schritt.
|
||||
- Kein produktiver Tenant-Wechsel in bestehenden Legacy-Seiten.
|
||||
@@ -47,7 +47,7 @@ Tabellen:
|
||||
- `participants`
|
||||
- `tenant_domains`
|
||||
|
||||
Ergaenzende Skripte:
|
||||
Ergänzende Skripte:
|
||||
|
||||
```text
|
||||
scripts/backfill-default-tenant.php
|
||||
@@ -73,7 +73,7 @@ Wichtige Regeln:
|
||||
|
||||
## Default-Tenant
|
||||
|
||||
Fuer den Bestand wird ein erster Tenant angelegt:
|
||||
Für den Bestand wird ein erster Tenant angelegt:
|
||||
|
||||
```text
|
||||
slug: default
|
||||
@@ -84,7 +84,7 @@ locale: de-DE
|
||||
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
|
||||
|
||||
@@ -98,9 +98,9 @@ Vorgang:
|
||||
|
||||
1. Default-Tenant anlegen oder laden.
|
||||
2. `tenant_settings` aus `kl_config` erzeugen.
|
||||
3. Fuer jede Zeile aus `kl_Mitarbeiter` einen `participant` anlegen oder
|
||||
3. Für jede Zeile aus `kl_Mitarbeiter` einen `participant` anlegen oder
|
||||
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.
|
||||
5. `admin = 1` als Rolle `admin` abbilden; ein konfigurierter erster Benutzer
|
||||
kann Rolle `owner` erhalten.
|
||||
@@ -108,10 +108,10 @@ Vorgang:
|
||||
aus dem Default-Tenant entfernen.
|
||||
|
||||
Der Backfill muss idempotent sein und darf keine bestehenden Legacy-Daten
|
||||
loeschen. Geloescht werden nur SaaS-Spiegelungen, deren Legacy-Mitarbeiter in
|
||||
löschen. Gelöscht werden nur SaaS-Spiegelungen, deren Legacy-Mitarbeiter in
|
||||
`kl_Mitarbeiter` nicht mehr existiert.
|
||||
|
||||
Ausgefuehrter Dev-Stand:
|
||||
Ausgeführter Dev-Stand:
|
||||
|
||||
- Default-Tenant `default` wurde angelegt.
|
||||
- 9 Legacy-Mitarbeiter wurden als `participants` gespiegelt.
|
||||
@@ -145,15 +145,15 @@ Umgesetzter Umfang:
|
||||
|
||||
- Registrierung legt Tenant, Owner-User, Default-Settings, Membership und
|
||||
Owner-Participant in einer Transaktion an.
|
||||
- Login prueft `users.password_hash`, aktive Membership und aktiven Tenant.
|
||||
- Login prüft `users.password_hash`, aktive Membership und aktiven Tenant.
|
||||
- Hat ein User genau einen aktiven Mandanten, wird dieser direkt in die Session
|
||||
gelegt.
|
||||
- Hat ein User mehrere aktive Mandanten, fuehrt der Login zur
|
||||
- Hat ein User mehrere aktive Mandanten, führt der Login zur
|
||||
Mandantenauswahl.
|
||||
- Logout beendet die neue PHP-Login-Session.
|
||||
- `konto.php` zeigt den aktuellen SaaS-Kontext fuer den angemeldeten User.
|
||||
- `functions.php` akzeptiert eine SaaS-Login-Session als erste Identitaetsquelle,
|
||||
laesst `DEV_AUTH_EMAIL` und `AUTH_USER` aber als Legacy-Fallback bestehen.
|
||||
- `konto.php` zeigt den aktuellen SaaS-Kontext für den angemeldeten User.
|
||||
- `functions.php` akzeptiert eine SaaS-Login-Session als erste Identitätsquelle,
|
||||
lässt `DEV_AUTH_EMAIL` und `AUTH_USER` aber als Legacy-Fallback bestehen.
|
||||
- `footer.php` bleibt App-Sidebar und zeigt keine Public-Login- oder
|
||||
Registrierungslinks mehr.
|
||||
- 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.
|
||||
- E-Mail-Verifikation erzeugt Single-Use-Tokens und setzt
|
||||
`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
|
||||
`mail()` umgestellt werden.
|
||||
- Dev-Links werden weiterhin nur im Dev-Modus angezeigt, damit lokale Checks
|
||||
@@ -169,7 +169,7 @@ Umgesetzter Umfang:
|
||||
|
||||
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.
|
||||
|
||||
## Public-Landingpage
|
||||
@@ -184,35 +184,35 @@ assets/css/public.css
|
||||
|
||||
Umgesetzter Umfang:
|
||||
|
||||
- Oeffentliche Landingpage ohne Legacy-DB-Zugriff.
|
||||
- Hero mit generiertem Bild-Asset, bestehender Typografie und gruenem Akzent.
|
||||
- Öffentliche Landingpage ohne Legacy-DB-Zugriff.
|
||||
- Hero mit generiertem Bild-Asset, bestehender Typografie und grünem Akzent.
|
||||
- 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.
|
||||
- Kurzabschnitt zu Stricherfassung, Mandantenfaehigkeit und Webspace-Betrieb.
|
||||
- Die geschuetzte App-Sidebar bleibt von der Public-Seite getrennt.
|
||||
- Kurzabschnitt zu Stricherfassung, Mandantenfähigkeit und Webspace-Betrieb.
|
||||
- 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.
|
||||
- Primaere App-Adresse ist eine zentrale App-Domain wie `app.kaffeeliste.de`.
|
||||
- Die Public-Seite kann ueber `kaffeeliste.de` beziehungsweise
|
||||
- Primäre App-Adresse ist eine zentrale App-Domain wie `app.kaffeeliste.de`.
|
||||
- Die Public-Seite kann über `kaffeeliste.de` beziehungsweise
|
||||
`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.
|
||||
- Bei genau einem Mandanten wird automatisch weitergeleitet.
|
||||
- Bei mehreren Mandanten nutzt der User `mandant-auswahl.php`.
|
||||
- `tenant_domains` ist vorbereitet fuer spaeter gezielt eingerichtete feste
|
||||
Domains oder Subdomains, aber nicht Voraussetzung fuer den Start.
|
||||
- `tenant_domains` ist vorbereitet für später gezielt eingerichtete feste
|
||||
Domains oder Subdomains, aber nicht Voraussetzung für den Start.
|
||||
|
||||
Noch offen:
|
||||
|
||||
- `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.
|
||||
- Produktive Domain-/Zertifikatspruefung fuer einzelne feste Domains
|
||||
- Produktive Domain-/Zertifikatsprüfung für einzelne feste Domains
|
||||
definieren.
|
||||
|
||||
## Rollen und Grundeinstellungen
|
||||
@@ -226,43 +226,43 @@ mandant-einstellungen.php
|
||||
Umgesetzter Umfang:
|
||||
|
||||
- `saas_user_has_role()`, `saas_require_role()` und
|
||||
`saas_can_manage_tenant_settings()` zentralisieren die erste Rollenpruefung.
|
||||
- Owner und Admin duerfen Tenant-Grundeinstellungen bearbeiten.
|
||||
- Die Seite bearbeitet Kundenname, Zeitzone, Locale, Waehrung, Preis pro
|
||||
`saas_can_manage_tenant_settings()` zentralisieren die erste Rollenprüfung.
|
||||
- Owner und Admin dürfen Tenant-Grundeinstellungen bearbeiten.
|
||||
- Die Seite bearbeitet Kundenname, Zeitzone, Locale, Währung, Preis pro
|
||||
Strich, Web-Striche, PayPal-Link, Listenfenster und Schulden-Warnschwelle.
|
||||
- Geldwerte werden in der UI als Euro-Werte erfasst und weiterhin als Cent-
|
||||
Integer gespeichert.
|
||||
- `konto.php` und die Sidebar verlinken die Einstellungen nur fuer passende
|
||||
- `konto.php` und die Sidebar verlinken die Einstellungen nur für passende
|
||||
Rollen.
|
||||
|
||||
## Erste Akzeptanzkriterien
|
||||
|
||||
- Migrationen laufen mehrfach ohne Fehler: erfuellt.
|
||||
- Default-Tenant existiert genau einmal: erfuellt.
|
||||
- Migrationen laufen mehrfach ohne Fehler: erfüllt.
|
||||
- Default-Tenant existiert genau einmal: erfüllt.
|
||||
- `tenant_settings` enthalten Preis pro Strich und bestehende PayPal-Optionen:
|
||||
erfuellt.
|
||||
- Jeder bestehende `kl_Mitarbeiter` hat genau einen `participant`: erfuellt.
|
||||
- `participants.legacy_mitarbeiter_id` ist gesetzt: erfuellt.
|
||||
- Admins werden als tenant-scoped Rolle abgebildet: erfuellt.
|
||||
- Registrierung und Login funktionieren fuer einen neuen Test-Tenant:
|
||||
erfuellt.
|
||||
- Owner-/Admin-Grundeinstellungen koennen aktualisiert werden: erfuellt.
|
||||
erfüllt.
|
||||
- Jeder bestehende `kl_Mitarbeiter` hat genau einen `participant`: erfüllt.
|
||||
- `participants.legacy_mitarbeiter_id` ist gesetzt: erfüllt.
|
||||
- Admins werden als tenant-scoped Rolle abgebildet: erfüllt.
|
||||
- Registrierung und Login funktionieren für einen neuen Test-Tenant:
|
||||
erfüllt.
|
||||
- Owner-/Admin-Grundeinstellungen können aktualisiert werden: erfüllt.
|
||||
- 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:
|
||||
erfuellt.
|
||||
- Mandantenauswahl funktioniert fuer User mit mehreren Mandanten: erfuellt.
|
||||
- Oeffentliche Landingpage ist per HTTP-Smoke erreichbar: erfuellt.
|
||||
- Golden-Master und HTTP-Smoke bleiben gruen: erfuellt.
|
||||
erfüllt.
|
||||
- Mandantenauswahl funktioniert für User mit mehreren Mandanten: erfüllt.
|
||||
- Öffentliche Landingpage ist per HTTP-Smoke erreichbar: erfüllt.
|
||||
- Golden-Master und HTTP-Smoke bleiben grün: erfüllt.
|
||||
|
||||
## Risiken
|
||||
|
||||
| Risiko | Gegenmassnahme |
|
||||
| Risiko | Gegenmaßnahme |
|
||||
| --- | --- |
|
||||
| Login-User und Kaffee-Teilnehmer werden vermischt | `users` und `participants` strikt getrennt halten |
|
||||
| Mehrfacher Backfill erzeugt Duplikate | Eindeutige Constraints und Upsert-Logik |
|
||||
| Legacy-App bricht durch neue Tabellen | M3 nur additiv, keine Legacy-Queries umstellen |
|
||||
| Rollen werden global statt tenant-scoped | Rollen ausschliesslich in `tenant_memberships` speichern |
|
||||
| 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 |
|
||||
|
||||
## Empfohlene Reihenfolge
|
||||
@@ -270,24 +270,24 @@ Umgesetzter Umfang:
|
||||
1. Migration `0002_saas_identity_tenants.sql` erstellen: erledigt.
|
||||
2. Migration `0003_saas_auth_account_fields.sql` erstellen: erledigt.
|
||||
3. `scripts/backfill-default-tenant.php` erstellen: erledigt.
|
||||
4. Backfill gegen die Dev-Datenbank ausfuehren: erledigt.
|
||||
5. Kontrollskript fuer Tenant/Participant/Role-Counts schreiben: erledigt.
|
||||
4. Backfill gegen die Dev-Datenbank ausführen: erledigt.
|
||||
5. Kontrollskript für Tenant/Participant/Role-Counts schreiben: erledigt.
|
||||
6. Login-/Registrierungsrouten bauen: erledigt.
|
||||
7. Auth-Flow-Kontrollskript schreiben: erledigt.
|
||||
8. Zentrale Rollenpruefung und Mandant-Einstellungen bauen: erledigt.
|
||||
8. Zentrale Rollenprüfung und Mandant-Einstellungen bauen: erledigt.
|
||||
9. Settings-Flow-Kontrollskript schreiben: erledigt.
|
||||
10. Migration `0004_saas_auth_tokens.sql` erstellen: erledigt.
|
||||
11. Passwort-Reset und E-Mail-Verifikation bauen: erledigt.
|
||||
12. Password-/E-Mail-Flow-Kontrollskript schreiben: erledigt.
|
||||
13. Migration `0005_saas_tenant_resolution.sql` erstellen: erledigt.
|
||||
14. Mandantenauswahl und zentrale Session-Aufloesung bauen: erledigt.
|
||||
14. Mandantenauswahl und zentrale Session-Auflösung bauen: 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.
|
||||
18. Public-Landingpage mit erstem Hero-Asset vorbereiten: 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
|
||||
bewusst additiv:
|
||||
|
||||
+39
-39
@@ -2,20 +2,20 @@
|
||||
|
||||
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
|
||||
weiterhin die bisherigen `kl_*`-Tabellen; das Ledger dient zunaechst als
|
||||
vergleichbare, pruefbare Zielstruktur. Erste App-Abfragen koennen nun ueber
|
||||
weiterhin die bisherigen `kl_*`-Tabellen; das Ledger dient zunächst als
|
||||
vergleichbare, prüfbare Zielstruktur. Erste App-Abfragen können nun über
|
||||
einen zentralen Ledger-Service gegen `ledger_entries` laufen.
|
||||
|
||||
## Ziel
|
||||
|
||||
- `kl_Einzahlungen` 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.
|
||||
- Summen gegen den Golden Master pruefen.
|
||||
- Keine bestehenden Legacy-Buchungen loeschen oder veraendern.
|
||||
- Summen gegen den Golden Master prüfen.
|
||||
- Keine bestehenden Legacy-Buchungen löschen oder verändern.
|
||||
|
||||
## Nicht-Ziele
|
||||
|
||||
@@ -36,7 +36,7 @@ Neue Tabelle:
|
||||
|
||||
- `ledger_entries`
|
||||
|
||||
Ergaenzende Skripte:
|
||||
Ergänzende Skripte:
|
||||
|
||||
```text
|
||||
scripts/backfill-ledger-entries.php
|
||||
@@ -44,20 +44,20 @@ scripts/check-m4-ledger-migration.php
|
||||
scripts/check-m4-ledger-service.php
|
||||
```
|
||||
|
||||
Ergaenzende App-Dateien:
|
||||
Ergänzende App-Dateien:
|
||||
|
||||
```text
|
||||
app/ledger.php
|
||||
ledger-preview.php
|
||||
```
|
||||
|
||||
Ausgefuehrter Dev-Stand:
|
||||
Ausgeführter Dev-Stand:
|
||||
|
||||
- Migration `0006_saas_ledger_entries.sql` wurde angewendet.
|
||||
- 9 Legacy-Einzahlungen wurden als `payment` gespiegelt.
|
||||
- 12 Legacy-Kaffeeverbrauch-Zeilen wurden als `consumption` gespiegelt.
|
||||
- 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.
|
||||
- `ledger-preview.php` zeigt eine read-only App-Vorschau auf Tenant-Summen,
|
||||
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
|
||||
aktuellen Saldo eines Teilnehmers.
|
||||
|
||||
## Idempotenz und Aufraeumen
|
||||
## Idempotenz und Aufräumen
|
||||
|
||||
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
|
||||
urspruengliche Legacy-Zeile nicht mehr existiert. Fachliche Daten werden dabei
|
||||
nicht aus den `kl_*`-Tabellen geloescht.
|
||||
Für den Default-Tenant werden nur solche Legacy-Spiegelungen entfernt, deren
|
||||
ursprüngliche Legacy-Zeile nicht mehr existiert. Fachliche Daten werden dabei
|
||||
nicht aus den `kl_*`-Tabellen gelöscht.
|
||||
|
||||
## Ausfuehrung
|
||||
## Ausführung
|
||||
|
||||
```bash
|
||||
php scripts/backfill-default-tenant.php
|
||||
@@ -133,33 +133,33 @@ Installation aus `.local/` verwendet werden.
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- Migration `0006_saas_ledger_entries.sql` laeuft erfolgreich: erfuellt.
|
||||
- Jede Legacy-Einzahlung hat genau eine Ledger-Zeile: erfuellt.
|
||||
- Jeder Legacy-Kaffeeverbrauch hat genau eine Ledger-Zeile: erfuellt.
|
||||
- Legacy-IDs sind je Tenant eindeutig: erfuellt.
|
||||
- Einzahlungen sind positiv, Verbrauch ist negativ: erfuellt.
|
||||
- Verbrauch behaelt Strichanzahl und Preis pro Strich: erfuellt.
|
||||
- Migration `0006_saas_ledger_entries.sql` läuft erfolgreich: erfüllt.
|
||||
- Jede Legacy-Einzahlung hat genau eine Ledger-Zeile: erfüllt.
|
||||
- Jeder Legacy-Kaffeeverbrauch hat genau eine Ledger-Zeile: erfüllt.
|
||||
- Legacy-IDs sind je Tenant eindeutig: erfüllt.
|
||||
- Einzahlungen sind positiv, Verbrauch ist negativ: erfüllt.
|
||||
- Verbrauch behält Strichanzahl und Preis pro Strich: erfüllt.
|
||||
- Web-Striche aus `Eintragsart = 2` bleiben als `source = legacy_web`
|
||||
erkennbar: erfuellt.
|
||||
- Ledger-Summen entsprechen dem Golden Master: erfuellt.
|
||||
erkennbar: erfüllt.
|
||||
- Ledger-Summen entsprechen dem Golden Master: erfüllt.
|
||||
- Zentrale Ledger-Abfragen liefern dieselben Teilnehmer-Summen wie der Golden
|
||||
Master: erfuellt.
|
||||
Master: erfüllt.
|
||||
- Tenant-Summen, aktive Teilnehmer, Einzelteilnehmer und letzte Buchungen sind
|
||||
ueber `app/ledger.php` abrufbar: erfuellt.
|
||||
- Read-only Ledger-Preview ist im Browser erreichbar: erfuellt.
|
||||
über `app/ledger.php` abrufbar: erfüllt.
|
||||
- Read-only Ledger-Preview ist im Browser erreichbar: erfüllt.
|
||||
|
||||
## Aktueller Pruefstatus
|
||||
## Aktueller Prüfstatus
|
||||
|
||||
- M4 Ledger-Migration: gruen mit 73 Assertions.
|
||||
- M4 Ledger-Service: gruen mit 110 Assertions.
|
||||
- M3 SaaS-Basis: weiterhin gruen.
|
||||
- M3 Tenant-Aufloesung: weiterhin gruen.
|
||||
- M3 Passwort/E-Mail: weiterhin gruen.
|
||||
- M3 Auth-Flow: weiterhin gruen.
|
||||
- M3 Settings-Flow: weiterhin gruen.
|
||||
- M3 Mail-Flow: weiterhin gruen.
|
||||
- Golden Master: weiterhin gruen mit 104 Assertions.
|
||||
- HTTP-Smoke: weiterhin gruen mit 23 geprueften Seiten inklusive
|
||||
- M4 Ledger-Migration: grün mit 73 Assertions.
|
||||
- M4 Ledger-Service: grün mit 110 Assertions.
|
||||
- M3 SaaS-Basis: weiterhin grün.
|
||||
- M3 Tenant-Auflösung: weiterhin grün.
|
||||
- M3 Passwort/E-Mail: weiterhin grün.
|
||||
- M3 Auth-Flow: weiterhin grün.
|
||||
- M3 Settings-Flow: weiterhin grün.
|
||||
- M3 Mail-Flow: weiterhin grün.
|
||||
- Golden Master: weiterhin grün mit 104 Assertions.
|
||||
- HTTP-Smoke: weiterhin grün mit 23 geprüften Seiten inklusive
|
||||
`ledger-preview.php`.
|
||||
|
||||
## Noch offen in M4
|
||||
@@ -167,4 +167,4 @@ Installation aus `.local/` verwendet werden.
|
||||
- Tenant-sichere Dashboard-, Strich-, Einzahlungs- und Listenqueries schrittweise
|
||||
von Legacy-Tabellen auf das Ledger umstellen.
|
||||
- Storno-/Reversal-Modell in der UI statt harter Deletes.
|
||||
- Delta-Strategie fuer den finalen Cutover.
|
||||
- Delta-Strategie für den finalen Cutover.
|
||||
|
||||
+14
-14
@@ -4,13 +4,13 @@ Stand: 2026-07-14
|
||||
|
||||
M5 stellt die operativen App-Seiten schrittweise auf das neue tenant-sichere
|
||||
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
|
||||
|
||||
- Kernseiten aus `app/ledger.php` lesen lassen.
|
||||
- Bestehendes Tabellenlayout und Bediengefuehl erhalten.
|
||||
- Schreibende Legacy-Flows erst nach stabiler Leseparitaet umstellen.
|
||||
- Bestehendes Tabellenlayout und Bediengefühl erhalten.
|
||||
- Schreibende Legacy-Flows erst nach stabiler Leseparität umstellen.
|
||||
- Tenant-Kontext serverseitig bestimmen, nicht aus Formularfeldern.
|
||||
|
||||
## Umgesetzter erster Schritt
|
||||
@@ -23,18 +23,18 @@ kaffeeliste.php
|
||||
|
||||
Umgesetzter Umfang:
|
||||
|
||||
- Die Gesamtuebersicht liest aktive Teilnehmer aus
|
||||
- Die Gesamtübersicht liest aktive Teilnehmer aus
|
||||
`ledger_fetch_participant_summaries()`.
|
||||
- Angezeigte Werte bleiben fachlich gleich: aktueller Stand, Gesamtausgabe,
|
||||
Gesamtstriche und Gesamteinzahlungen.
|
||||
- Links zur bestehenden `teilnehmerauswertung.php` bleiben ueber
|
||||
- Links zur bestehenden `teilnehmerauswertung.php` bleiben über
|
||||
`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
|
||||
Default-Tenant.
|
||||
- Die Sidebar zeigt Legacy-Schreibseiten weiterhin nur fuer Legacy-Admins;
|
||||
Ledger-Leseansichten sind fuer passende SaaS-Rollen sichtbar.
|
||||
- Export, letzte Eintraege, CSV-Upload und Info-Mail bleiben noch Legacy-Flows.
|
||||
- Die Sidebar zeigt Legacy-Schreibseiten weiterhin nur für Legacy-Admins;
|
||||
Ledger-Leseansichten sind für passende SaaS-Rollen sichtbar.
|
||||
- Export, letzte Einträge, CSV-Upload und Info-Mail bleiben noch Legacy-Flows.
|
||||
|
||||
## Noch offen
|
||||
|
||||
@@ -44,9 +44,9 @@ Umgesetzter Umfang:
|
||||
Storno-/Reversal-Strategie vorbereiten.
|
||||
- Export, Mail und Jahresprozesse bleiben M6-Themen.
|
||||
|
||||
## Aktueller Pruefstatus
|
||||
## Aktueller Prüfstatus
|
||||
|
||||
- M4 Ledger-Migration: gruen mit 73 Assertions.
|
||||
- M4 Ledger-Service: gruen mit 110 Assertions.
|
||||
- Golden Master: gruen mit 104 Assertions.
|
||||
- HTTP-Smoke: gruen mit 23 geprueften Seiten.
|
||||
- M4 Ledger-Migration: grün mit 73 Assertions.
|
||||
- M4 Ledger-Service: grün mit 110 Assertions.
|
||||
- Golden Master: grün mit 104 Assertions.
|
||||
- HTTP-Smoke: grün mit 23 geprüften Seiten.
|
||||
|
||||
+151
-151
@@ -3,28 +3,28 @@
|
||||
Stand: 2026-07-13
|
||||
|
||||
Dieses Dokument beschreibt den geplanten Umbau der bestehenden Kaffeelisten-App zu
|
||||
einer mehrkundenfaehigen SaaS-Anwendung mit oeffentlicher Landingpage,
|
||||
Kundenregistrierung und geschuetzter App. Die bestehende Bedienlogik und das
|
||||
einer mehrkundenfähigen SaaS-Anwendung mit öffentlicher Landingpage,
|
||||
Kundenregistrierung und geschützter App. Die bestehende Bedienlogik und das
|
||||
visuelle Grunddesign sollen bewusst erhalten bleiben.
|
||||
|
||||
## Zielbild
|
||||
|
||||
- Mehrere Kunden koennen eigene Kaffeelisten betreiben.
|
||||
- Mehrere Kunden können eigene Kaffeelisten betreiben.
|
||||
- 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,
|
||||
schlichte Formulare, HTML5-UP-Anmutung.
|
||||
- Authentifizierung, Rollen, Mandantenkontext und Sicherheitspruefungen werden
|
||||
- Authentifizierung, Rollen, Mandantenkontext und Sicherheitsprüfungen werden
|
||||
zentralisiert.
|
||||
- Finanznahe Vorgaenge werden nachvollziehbar und revisionsfreundlich
|
||||
- Finanznahe Vorgänge werden nachvollziehbar und revisionsfreundlich
|
||||
gespeichert.
|
||||
|
||||
## Nicht-Ziele fuer den ersten Umbau
|
||||
## Nicht-Ziele für den ersten Umbau
|
||||
|
||||
- Kein kompletter Design-Relaunch.
|
||||
- Keine verspielte Marketing-App statt der bestehenden Arbeitsoberflaeche.
|
||||
- Kein gleichzeitiger Neubau aller Randmodule, wenn diese fuer den MVP nicht
|
||||
benoetigt werden.
|
||||
- Keine verspielte Marketing-App statt der bestehenden Arbeitsoberfläche.
|
||||
- Kein gleichzeitiger Neubau aller Randmodule, wenn diese für den MVP nicht
|
||||
benötigt werden.
|
||||
- Kein Dual-Write zwischen Legacy und neuer App als Standardbetrieb.
|
||||
- Keine Mandanten-ID aus Formularfeldern oder URL-Parametern als alleinige
|
||||
Sicherheitsbasis.
|
||||
@@ -35,12 +35,12 @@ Die aktuelle App ist eine flache PHP/sqlsrv-Legacy-App im Webroot.
|
||||
|
||||
Wichtige Bereiche:
|
||||
|
||||
- `index.php`: persoenliches Dashboard.
|
||||
- `index.php`: persönliches Dashboard.
|
||||
- `stricheintragen.php`: Sammelerfassung von Kaffee-Strichen.
|
||||
- `einzahlung.php`: Sammelerfassung von Einzahlungen.
|
||||
- `kaffeeliste.php`: Gesamtuebersicht.
|
||||
- `kaffeeliste.php`: Gesamtübersicht.
|
||||
- `mitarbeiterverwalten.php`: Mitglieder- und Adminpflege.
|
||||
- `letzteneintraege.php`: Korrektur beziehungsweise Loeschung letzter Buchungen.
|
||||
- `letzteneintraege.php`: Korrektur beziehungsweise Löschung letzter Buchungen.
|
||||
- `csvupload.php`: Zahlungsimport.
|
||||
- `exportKaffeeliste.php`: PDF-/Listenexport.
|
||||
- `mailversenden.php`: Massenmail.
|
||||
@@ -49,7 +49,7 @@ Wichtige Bereiche:
|
||||
|
||||
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.
|
||||
- Rollen liegen als Boolean `admin` direkt auf `kl_Mitarbeiter`.
|
||||
- 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.
|
||||
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.
|
||||
|
||||
So bleibt die vertraute Kaffeelisten-Oberflaeche erhalten, waehrend die
|
||||
Datenbasis und Sicherheit SaaS-faehig werden.
|
||||
So bleibt die vertraute Kaffeelisten-Oberfläche erhalten, während die
|
||||
Datenbasis und Sicherheit SaaS-fähig werden.
|
||||
|
||||
## Zielarchitektur
|
||||
|
||||
### Public-Bereich
|
||||
|
||||
Der Public-Bereich ist ohne Login erreichbar und laedt keine Mandantendaten.
|
||||
Der Public-Bereich ist ohne Login erreichbar und lädt keine Mandantendaten.
|
||||
|
||||
Empfohlene Routen:
|
||||
|
||||
- `/`: Landingpage.
|
||||
- `/preise` oder spaeter `/pricing`: optional, falls Tarife eingefuehrt werden.
|
||||
- `/faq`: oeffentliche FAQ oder FAQ-Auszug.
|
||||
- `/preise` oder später `/pricing`: optional, falls Tarife eingeführt werden.
|
||||
- `/faq`: öffentliche FAQ oder FAQ-Auszug.
|
||||
- `/registrieren` beziehungsweise aktuell `register.php`: Kundenregistrierung.
|
||||
- `/login` beziehungsweise aktuell `login.php`: Login.
|
||||
- `/passwort-vergessen` beziehungsweise aktuell `passwort-vergessen.php`:
|
||||
@@ -91,16 +91,16 @@ Empfohlene Routen:
|
||||
Landingpage-Inhalte:
|
||||
|
||||
- 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,
|
||||
Hinweise.
|
||||
- App-Screenshot oder Demo-Ansicht im bestehenden Stil.
|
||||
- FAQ-Auszug.
|
||||
- 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:
|
||||
|
||||
@@ -109,8 +109,8 @@ Empfohlene Routen:
|
||||
- `/app/striche`: Eigene Striche erfassen, falls erlaubt.
|
||||
- `/app/einzahlungen`: Zahlungen und PayPal-Optionen.
|
||||
- `/app/mitglieder`: Mitgliederverwaltung.
|
||||
- `/app/liste`: Gesamtuebersicht.
|
||||
- `/app/buchungen`: Letzte Eintraege und Korrekturen.
|
||||
- `/app/liste`: Gesamtübersicht.
|
||||
- `/app/buchungen`: Letzte Einträge und Korrekturen.
|
||||
- `/app/importe`: CSV-Importe.
|
||||
- `/app/export/pdf`: PDF-/Listenexport.
|
||||
- `/app/hinweise`: Hinweise.
|
||||
@@ -118,29 +118,29 @@ Empfohlene Routen:
|
||||
|
||||
Die App sollte eine Sidebar behalten, aber besser gruppiert werden:
|
||||
|
||||
- Persoenlich: Meine Kaffeeliste, Namensanpassung, FAQ.
|
||||
- Persönlich: Meine Kaffeeliste, Namensanpassung, FAQ.
|
||||
- Erfassung: Striche, Einzahlungen.
|
||||
- Auswertung: Kaffeeliste, Buchungen, Export.
|
||||
- Administration: Mitglieder, Hinweise, Einstellungen, Importe.
|
||||
- Konto: Kundenkonto und Logout. Public-Links wie Login und Registrierung
|
||||
gehoeren nicht in die App-Sidebar.
|
||||
gehören nicht in die App-Sidebar.
|
||||
|
||||
### 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.
|
||||
- `app.kaffeeliste.de` fuer die geschuetzte App.
|
||||
- Keine Wildcard-Subdomains fuer Kunden.
|
||||
- Mandantenauswahl nach Login ueber Session und `tenant_memberships`.
|
||||
- Kundeneigene feste Domains oder Subdomains erst spaeter gezielt einrichten,
|
||||
wenn DNS und Zertifikat pro Domain sauber geprueft sind.
|
||||
- `app.kaffeeliste.de` für die geschützte App.
|
||||
- Keine Wildcard-Subdomains für Kunden.
|
||||
- Mandantenauswahl nach Login über Session und `tenant_memberships`.
|
||||
- Kundeneigene feste Domains oder Subdomains erst später gezielt einrichten,
|
||||
wenn DNS und Zertifikat pro Domain sauber geprüft sind.
|
||||
|
||||
Damit bleibt der Betrieb webspace-tauglich: Fuer den Start reichen zwei feste
|
||||
Hosts mit normalen Let's-Encrypt-Zertifikaten. Die fachliche Tenant-Aufloesung
|
||||
Damit bleibt der Betrieb webspace-tauglich: Für den Start reichen zwei feste
|
||||
Hosts mit normalen Let's-Encrypt-Zertifikaten. Die fachliche Tenant-Auflösung
|
||||
ist in M3 vorbereitet; produktive Host-Rewrites, Cookie-Domain und Zertifikats-
|
||||
Details gehoeren zur M8-Betriebshaertung.
|
||||
Details gehören zur M8-Betriebshärtung.
|
||||
|
||||
## Datenmodell
|
||||
|
||||
@@ -218,9 +218,9 @@ Wichtige Modellierungsregeln:
|
||||
- `users` sind Login-Konten.
|
||||
- `participants` sind Kaffee-Teilnehmer.
|
||||
- Ein Teilnehmer kann, muss aber nicht, ein Login-Konto haben.
|
||||
- Rollen gehoeren in `tenant_memberships`, nicht direkt auf Teilnehmer.
|
||||
- Buchungen sollten nicht hart geloescht werden.
|
||||
- Korrekturen laufen ueber Storno- oder Reversal-Eintraege.
|
||||
- Rollen gehören in `tenant_memberships`, nicht direkt auf Teilnehmer.
|
||||
- Buchungen sollten nicht hart gelöscht werden.
|
||||
- Korrekturen laufen über Storno- oder Reversal-Einträge.
|
||||
- Legacy-IDs werden bei Migration gespeichert, damit Ergebnisse vergleichbar
|
||||
bleiben.
|
||||
- Geldwerte sollten als Cent-Integer gespeichert werden, nicht als Float.
|
||||
@@ -229,7 +229,7 @@ Wichtige Modellierungsregeln:
|
||||
|
||||
Empfohlene Rollen:
|
||||
|
||||
- `owner`: Kunde verwalten, Billing, Admins, Loeschung, Grundeinstellungen.
|
||||
- `owner`: Kunde verwalten, Billing, Admins, Löschung, Grundeinstellungen.
|
||||
- `admin`: Mitglieder, Hinweise, fachliche Einstellungen, Auswertungen.
|
||||
- `treasurer`: Zahlungen, CSV-Import, Korrekturen, Exporte, Mails.
|
||||
- `member`: eigenes Dashboard, eigene Striche, eigener Anzeigename.
|
||||
@@ -246,22 +246,22 @@ SaaS-Basis:
|
||||
- E-Mail-Verifikation.
|
||||
- Passwort-Reset.
|
||||
- Sichere Session-Cookies.
|
||||
- CSRF-Schutz fuer alle schreibenden Aktionen.
|
||||
- Rate-Limits fuer Login, Registrierung und Passwort-Reset.
|
||||
- CSRF-Schutz für alle schreibenden Aktionen.
|
||||
- Rate-Limits für Login, Registrierung und Passwort-Reset.
|
||||
- Zentrale Funktionen oder Middleware: `requireLogin`, `requireTenant`,
|
||||
`requireRole`, `csrfToken`.
|
||||
|
||||
Tenant-Aufloesung:
|
||||
Tenant-Auflösung:
|
||||
|
||||
- Primaer serverseitig aus Subdomain, Custom Domain oder tenantgebundener
|
||||
- Primär serverseitig aus Subdomain, Custom Domain oder tenantgebundener
|
||||
Auswahl nach Login.
|
||||
- Nicht allein aus Hidden Inputs oder frei manipulierbaren IDs.
|
||||
- Jede fachliche Query braucht einen Tenant-Scope.
|
||||
|
||||
Spaeter moeglich:
|
||||
Später möglich:
|
||||
|
||||
- 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.
|
||||
|
||||
## Design-Leitplanken
|
||||
@@ -270,12 +270,12 @@ Das bestehende Design soll erhalten bleiben.
|
||||
|
||||
Beibehalten:
|
||||
|
||||
- Gruener Akzent `#38761d`.
|
||||
- Weiss/Grau als ruhige Grundflaeche.
|
||||
- Roboto Slab fuer Ueberschriften.
|
||||
- Open Sans fuer Fliesstext.
|
||||
- Sidebar fuer die geschuetzte App.
|
||||
- Tabellen als primaere Darstellung fuer operative Daten.
|
||||
- Grüner Akzent `#38761d`.
|
||||
- Weiß/Grau als ruhige Grundfläche.
|
||||
- Roboto Slab für Überschriften.
|
||||
- Open Sans für Fließtext.
|
||||
- Sidebar für die geschützte App.
|
||||
- Tabellen als primäre Darstellung für operative Daten.
|
||||
- Schlichte Formularfelder und Buttons.
|
||||
|
||||
Verbessern ohne Relaunch:
|
||||
@@ -283,38 +283,38 @@ Verbessern ohne Relaunch:
|
||||
- Hinweise als wiederverwendbare Komponente statt Inline-Style.
|
||||
- Einheitliche Erfolgs-, Fehler- und Warnmeldungen.
|
||||
- Dashboardwerte als kompakte Statusbereiche.
|
||||
- Aktionsleisten ueber Tabellen.
|
||||
- Aktionsleisten über Tabellen.
|
||||
- Sidebar-Gruppierung und aktive Navigation.
|
||||
- FAQ strukturieren, aber Inhalte nicht stark veraendern.
|
||||
- FAQ strukturieren, aber Inhalte nicht stark verändern.
|
||||
|
||||
Nicht tun:
|
||||
|
||||
- Kein komplett neues Farbsystem.
|
||||
- Keine dekorative Marketingoptik in der App.
|
||||
- Keine App-Tabellen in Kartenlandschaften aufloesen.
|
||||
- Keine Navigationsstruktur, die die heutigen Kernablaeufe versteckt.
|
||||
- Keine App-Tabellen in Kartenlandschaften auflösen.
|
||||
- Keine Navigationsstruktur, die die heutigen Kernabläufe versteckt.
|
||||
|
||||
## Meilensteinplan
|
||||
|
||||
Uebersicht:
|
||||
Übersicht:
|
||||
|
||||
| Meilenstein | Schwerpunkt | Hauptergebnis |
|
||||
| --- | --- | --- |
|
||||
| M0 | Baseline | Bestand, Sicherheit und Designreferenz sind dokumentiert |
|
||||
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prueft sichere Seiten |
|
||||
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prüft sichere Seiten |
|
||||
| M2 | Technisches Fundament | Abgeschlossen: Migrationen, Bootstrap, Session, CSRF-Helper und Legacy-Schreibseitenschutz stehen |
|
||||
| M3 | SaaS-Basis | Abgeschlossen: Tenants, User, Registrierung, Login, Rollen, Mail-Links und zentrale Mandantenauswahl funktionieren |
|
||||
| M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritaetscheck, Ledger-Service und Preview sind umgesetzt |
|
||||
| M5 | App-Kern | Gestartet: Gesamtuebersicht liest read-only aus dem Ledger |
|
||||
| M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritätscheck, Ledger-Service und Preview sind umgesetzt |
|
||||
| M5 | App-Kern | Gestartet: Gesamtübersicht liest read-only aus dem Ledger |
|
||||
| M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar |
|
||||
| M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spaetere Ausbaustufen folgen |
|
||||
| M8 | Haertung | Betrieb, Datenschutz, Monitoring und Isolation sind geprueft |
|
||||
| M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spätere Ausbaustufen folgen |
|
||||
| M8 | Härtung | Betrieb, Datenschutz, Monitoring und Isolation sind geprüft |
|
||||
| M9 | Cutover | Produktivumstellung ist vorbereitet und Legacy ist read-only |
|
||||
|
||||
### M0: Planungs- und Sicherheitsbaseline
|
||||
|
||||
Ziel:
|
||||
Den aktuellen Zustand verlaesslich dokumentieren, bevor umgebaut wird.
|
||||
Den aktuellen Zustand verlässlich dokumentieren, bevor umgebaut wird.
|
||||
|
||||
Arbeitsartefakte:
|
||||
|
||||
@@ -331,7 +331,7 @@ Schritte:
|
||||
- Tabellen, Spalten, Indizes und Constraints dokumentieren.
|
||||
- Bekannte Jobs/Skripte erfassen, zum Beispiel Mailversand und Jahresauswertung.
|
||||
- Secrets inventarisieren und rotieren, insbesondere hart codierte Zugangsdaten.
|
||||
- Bestehende Seiten und UI-Zustaende per Screenshots festhalten.
|
||||
- Bestehende Seiten und UI-Zustände per Screenshots festhalten.
|
||||
- Fachliche Kernregeln dokumentieren: Saldo, Jahreswerte, Preis pro Strich,
|
||||
100-Tage-Listen, CSV-Deduplizierung, PDF-Ausgabe.
|
||||
|
||||
@@ -342,7 +342,7 @@ Ergebnis:
|
||||
- Designreferenz.
|
||||
- Liste kritischer Sicherheitsrisiken.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- Zugriff auf echte oder anonymisierte Datenbank.
|
||||
- Kenntnis der Produktivumgebung.
|
||||
@@ -350,7 +350,7 @@ Abhaengigkeiten:
|
||||
### M1: Golden-Master-Validierung
|
||||
|
||||
Ziel:
|
||||
Sicherstellen, dass spaetere neue Berechnungen dieselben Ergebnisse wie der
|
||||
Sicherstellen, dass spätere neue Berechnungen dieselben Ergebnisse wie der
|
||||
Bestand liefern.
|
||||
|
||||
Schritte:
|
||||
@@ -359,42 +359,42 @@ Schritte:
|
||||
Master in der MySQL-Dev-Datenbank.
|
||||
- Pro Mitglied berechnen: Gesamteinzahlungen, Gesamtausgaben, Gesamtstriche,
|
||||
aktueller Stand, Jahreswerte. Erledigt mit `scripts/check-golden-master.php`.
|
||||
- CSV-Importfaelle sammeln: Treffer, Dubletten, unbekannte Namen. Erledigt im
|
||||
- CSV-Importfälle sammeln: Treffer, Dubletten, unbekannte Namen. Erledigt im
|
||||
Golden-Master-Check.
|
||||
- Testfaelle fuer Guthaben, Schulden, Nullsaldo und inaktive Teilnehmer
|
||||
- Testfälle für Guthaben, Schulden, Nullsaldo und inaktive Teilnehmer
|
||||
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`.
|
||||
- PDF-/Export-Summen als Referenz sichern. Offen, weil die TCPDF-Kopie im Repo
|
||||
unvollstaendig ist.
|
||||
unvollständig ist.
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- Golden-Master-Daten.
|
||||
- Vergleichsqueries oder Vergleichsskript.
|
||||
- HTTP-Smoke-Test fuer sichere UI-Seiten.
|
||||
- Akzeptanzkriterien fuer Migration und neue App.
|
||||
- Dokumentierte offene Punkte: PDF-Export/TCPDF, PHPMailer-Abhaengigkeit,
|
||||
- HTTP-Smoke-Test für sichere UI-Seiten.
|
||||
- Akzeptanzkriterien für Migration und neue App.
|
||||
- Dokumentierte offene Punkte: PDF-Export/TCPDF, PHPMailer-Abhängigkeit,
|
||||
GET-Nebenwirkungen bei Mailversand und Jahresauswertung.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M0 abgeschlossen.
|
||||
- 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
|
||||
|
||||
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:
|
||||
|
||||
- Projektstruktur festlegen: Public-Routes, App-Routes, Views/Templates,
|
||||
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`.
|
||||
- Versionierte Migrationen einfuehren. Begonnen mit
|
||||
- Versionierte Migrationen einführen. Begonnen mit
|
||||
`database/migrations/0001_legacy_mysql_baseline.sql` und
|
||||
`scripts/migrate.php`.
|
||||
- Layouts trennen: Public-Layout und App-Layout. Noch offen; Header/Footer
|
||||
@@ -405,36 +405,36 @@ Schritte:
|
||||
abgesichert: `hinweise.php`, `mitarbeiterverwalten.php`,
|
||||
`namenanpassen.php`, `index.php`, `stricheintragen.php`, `einzahlung.php`,
|
||||
`letzteneintraege.php`, `csvupload.php`.
|
||||
- CSV-Upload ausserhalb des Webroots speichern. In M2 als Legacy-Haertung
|
||||
umgesetzt: temporaer unter `var/uploads`, Dateityp-/Groessenpruefung und
|
||||
Loeschung nach Verarbeitung.
|
||||
- CSV-Upload außerhalb des Webroots speichern. In M2 als Legacy-Härtung
|
||||
umgesetzt: temporär unter `var/uploads`, Dateityp-/Größenprüfung und
|
||||
Löschung nach Verarbeitung.
|
||||
- Konfigurationswerte aus Code in Umgebung oder Settings verschieben.
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- Grundgeruest fuer neue SaaS-App.
|
||||
- Kein fachlicher Rewrite, aber klare Struktur fuer die Migration.
|
||||
- Grundgerüst für neue SaaS-App.
|
||||
- Kein fachlicher Rewrite, aber klare Struktur für die Migration.
|
||||
- Dokumentation: `docs/m2-technical-foundation.md`.
|
||||
- Status: abgeschlossen 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.
|
||||
|
||||
### M3: Mandanten, Registrierung und Login
|
||||
|
||||
Ziel:
|
||||
Mehrkundenfaehigkeit und Kunden-Onboarding technisch aktivieren.
|
||||
Mehrkundenfähigkeit und Kunden-Onboarding technisch aktivieren.
|
||||
|
||||
Schritte:
|
||||
|
||||
- Tabellen fuer `tenants`, `users`, `tenant_memberships` und
|
||||
- Tabellen für `tenants`, `users`, `tenant_memberships` und
|
||||
`tenant_settings` anlegen. Vorbereitung dokumentiert in
|
||||
`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.
|
||||
- Default-Tenant fuer den aktuellen Bestand anlegen: erledigt.
|
||||
- Default-Tenant für den aktuellen Bestand anlegen: erledigt.
|
||||
- Bestehende `kl_Mitarbeiter` idempotent in `participants` spiegeln:
|
||||
erledigt.
|
||||
- Registrierung: Tenant + Owner-User + Default-Settings erzeugen: erster
|
||||
@@ -442,10 +442,10 @@ Schritte:
|
||||
- Login und Logout bauen: erster Flow erledigt.
|
||||
- Passwort-Reset und E-Mail-Verifikation bauen: Dev-Flow mit Single-Use-Tokens
|
||||
und Mail-Transport-Abstraktion erledigt.
|
||||
- Tenant-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.
|
||||
- Rollenpruefung zentralisieren: erster Owner/Admin-Check erledigt.
|
||||
- Erste Admin-/Owner-Seite fuer Grundeinstellungen: erledigt.
|
||||
- Rollenprüfung zentralisieren: erster Owner/Admin-Check erledigt.
|
||||
- Erste Admin-/Owner-Seite für Grundeinstellungen: erledigt.
|
||||
- Erste Public-Landingpage mit CTA zu Login und Registrierung: erledigt.
|
||||
- Login, Registrierung, Passwort-Reset und E-Mail-Verifikation im Public-Stil:
|
||||
erledigt.
|
||||
@@ -454,51 +454,51 @@ Schritte:
|
||||
Ergebnis:
|
||||
|
||||
- Ein neuer Kunde kann sich registrieren und in seine eigene App gelangen.
|
||||
- Rollen und Tenant-Kontext sind serverseitig verfuegbar.
|
||||
- Reset- und Verifikationslinks koennen im Dev-Modus geloggt und produktiv ueber
|
||||
- Rollen und Tenant-Kontext sind serverseitig verfügbar.
|
||||
- Reset- und Verifikationslinks können im Dev-Modus geloggt und produktiv über
|
||||
einen konfigurierten Mail-Transport versendet werden.
|
||||
- Status: abgeschlossen fuer den M3-Scope.
|
||||
- Status: abgeschlossen für den M3-Scope.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M2 abgeschlossen.
|
||||
|
||||
### M4: Migration des fachlichen Kerns
|
||||
|
||||
Ziel:
|
||||
Bestehende Kaffeelisten-Daten tenant-sicher uebernehmen.
|
||||
Bestehende Kaffeelisten-Daten tenant-sicher übernehmen.
|
||||
|
||||
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.
|
||||
- `admin`-Informationen in `tenant_memberships` oder Admin-Rollen ueberfuehren:
|
||||
- `admin`-Informationen in `tenant_memberships` oder Admin-Rollen überführen:
|
||||
erledigt in M3.
|
||||
- `kl_config` nach `tenant_settings` migrieren: erledigt in M3.
|
||||
- `ledger_entries` als tenant-sichere Buchungstabelle anlegen: erledigt.
|
||||
- `kl_Einzahlungen` und `kl_Kaffeeverbrauch` additiv nach `ledger_entries`
|
||||
spiegeln: erster Backfill erledigt.
|
||||
- Legacy-IDs speichern: erledigt ueber `legacy_table` und `legacy_id`.
|
||||
- Legacy-IDs speichern: erledigt über `legacy_table` und `legacy_id`.
|
||||
- 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.
|
||||
- Read-only Ledger-Preview fuer Browservergleich: erledigt.
|
||||
- Read-only Ledger-Preview für Browservergleich: erledigt.
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
- `ledger-preview.php` zeigt die neue Ledger-Auswertung ohne Schreibzugriffe.
|
||||
- Dokumentation: `docs/m4-data-migration.md`.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M1 und M3 abgeschlossen.
|
||||
|
||||
### M5: Geschuetzte App-Funktionen
|
||||
### M5: Geschützte App-Funktionen
|
||||
|
||||
Ziel:
|
||||
Die operativen Kernseiten im neuen Modell bereitstellen.
|
||||
@@ -509,9 +509,9 @@ Schritte:
|
||||
- Eigene Stricherfassung umsetzen.
|
||||
- Zahlungs-/PayPal-Bereich 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.
|
||||
- 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.
|
||||
|
||||
Ergebnis:
|
||||
@@ -520,7 +520,7 @@ Ergebnis:
|
||||
- App sieht weiterhin nach bestehender Kaffeeliste aus.
|
||||
- Erster M5-Stand ist in `docs/m5-app-kern.md` dokumentiert.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M4 abgeschlossen.
|
||||
|
||||
@@ -531,8 +531,8 @@ Admin- und Treasurer-Flows produktionsreif machen.
|
||||
|
||||
Schritte:
|
||||
|
||||
- CSV-Import mit Vorschau, Dublettenpruefung und Audit bauen.
|
||||
- Uploads ausserhalb des Webroots speichern.
|
||||
- CSV-Import mit Vorschau, Dublettenprüfung und Audit bauen.
|
||||
- Uploads außerhalb des Webroots speichern.
|
||||
- PDF-/Listenexport aus neuem Datenmodell bauen.
|
||||
- Mailversand als nachvollziehbaren Versandjob mit Dry-Run und Versandlog
|
||||
gestalten.
|
||||
@@ -540,22 +540,22 @@ Schritte:
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- Kassenverwaltung ist fuer reale Betriebsablaeufe vollstaendig.
|
||||
- Kassenverwaltung ist für reale Betriebsabläufe vollständig.
|
||||
- Import/Export/Mail sind auditierbar.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M5 fuer die Kernansichten.
|
||||
- M5 für die Kernansichten.
|
||||
|
||||
### M7: Oeffentliche Landingpage
|
||||
### M7: Öffentliche Landingpage
|
||||
|
||||
Ziel:
|
||||
Werbliche Einstiegseite und Registrierung verfuegbar machen, ohne die App-Optik
|
||||
Werbliche Einstiegseite und Registrierung verfügbar machen, ohne die App-Optik
|
||||
zu verwischen.
|
||||
|
||||
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.
|
||||
- Landingpage-Inhalte erstellen. Erster Stand erledigt.
|
||||
- Demo-Screenshot oder Demo-Ansicht einbinden.
|
||||
@@ -568,26 +568,26 @@ Schritte:
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- Interessenten verstehen das Produkt und koennen sich registrieren.
|
||||
- Bestehende App bleibt optisch eigenstaendig und arbeitsorientiert.
|
||||
- Interessenten verstehen das Produkt und können sich registrieren.
|
||||
- Bestehende App bleibt optisch eigenständig und arbeitsorientiert.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M3 fuer Registrierung.
|
||||
- M5 fuer echte App-Screens oder Demo.
|
||||
- M3 für Registrierung.
|
||||
- M5 für echte App-Screens oder Demo.
|
||||
|
||||
### M8: Haertung, Datenschutz und Betrieb
|
||||
### M8: Härtung, Datenschutz und Betrieb
|
||||
|
||||
Ziel:
|
||||
Die SaaS-App fuer mehrere Kunden sicher betreiben.
|
||||
Die SaaS-App für mehrere Kunden sicher betreiben.
|
||||
|
||||
Schritte:
|
||||
|
||||
- Backups und Restore-Prozess definieren.
|
||||
- Monitoring und Fehlerlogging einrichten.
|
||||
- Audit-Log fuer Admin-Aktionen pruefen.
|
||||
- Audit-Log für Admin-Aktionen prüfen.
|
||||
- Datenexport pro Tenant.
|
||||
- Loesch-/Anonymisierungsprozess fuer Teilnehmer und Kunden.
|
||||
- Lösch-/Anonymisierungsprozess für Teilnehmer und Kunden.
|
||||
- Rate-Limits und Security Headers.
|
||||
- Mandanten-Isolation testen.
|
||||
- Rollenmatrix testen.
|
||||
@@ -596,7 +596,7 @@ Ergebnis:
|
||||
|
||||
- SaaS ist betrieblich und datenschutzseitig belastbarer.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M3 bis M6.
|
||||
|
||||
@@ -607,10 +607,10 @@ Produktive Umstellung ohne Datenverlust.
|
||||
|
||||
Schritte:
|
||||
|
||||
- Legacy fuer Schreibzugriffe sperren.
|
||||
- Legacy für Schreibzugriffe sperren.
|
||||
- Finalen Delta-Export ziehen.
|
||||
- Migration ausfuehren.
|
||||
- Rowcounts, Salden, PDF-Stichproben und Importhistorie pruefen.
|
||||
- Migration ausführen.
|
||||
- Rowcounts, Salden, PDF-Stichproben und Importhistorie prüfen.
|
||||
- DNS oder Reverse Proxy umschalten.
|
||||
- Legacy 30 bis 90 Tage read-only als Archiv behalten.
|
||||
- Rollback-Fenster und Vorgehen dokumentieren.
|
||||
@@ -618,15 +618,15 @@ Schritte:
|
||||
Ergebnis:
|
||||
|
||||
- Neuer SaaS-Betrieb ist live.
|
||||
- Alter Bestand bleibt fuer Rueckfragen lesbar.
|
||||
- Alter Bestand bleibt für Rückfragen lesbar.
|
||||
|
||||
Abhaengigkeiten:
|
||||
Abhängigkeiten:
|
||||
|
||||
- M8 abgeschlossen.
|
||||
|
||||
## 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.
|
||||
|
||||
MVP-Paket:
|
||||
@@ -634,12 +634,12 @@ MVP-Paket:
|
||||
1. Tenant, User, Login, Registrierung.
|
||||
2. Mail-Links, zentrale Mandantenauswahl und erste Landingpage.
|
||||
3. Teilnehmer, Settings, Ledger.
|
||||
4. Dashboard, Striche, Einzahlungen, Gesamtuebersicht.
|
||||
4. Dashboard, Striche, Einzahlungen, Gesamtübersicht.
|
||||
5. Mitgliederverwaltung.
|
||||
6. Hinweise.
|
||||
7. CSV-Import, Export.
|
||||
|
||||
Spaeter:
|
||||
Später:
|
||||
|
||||
- Umfrage-Modul.
|
||||
- SSO/LDAP pro Kunde.
|
||||
@@ -650,7 +650,7 @@ Spaeter:
|
||||
|
||||
## Validierung
|
||||
|
||||
Fachliche Pruefungen:
|
||||
Fachliche Prüfungen:
|
||||
|
||||
- Saldo je Teilnehmer alt gegen neu.
|
||||
- Jahreswerte alt gegen neu.
|
||||
@@ -659,37 +659,37 @@ Fachliche Pruefungen:
|
||||
- Export-Summen alt gegen neu.
|
||||
- CSV-Import: identische Treffer- und Dublettenlogik.
|
||||
|
||||
Sicherheitspruefungen:
|
||||
Sicherheitsprüfungen:
|
||||
|
||||
- Fremde Tenant-IDs liefern keine Daten.
|
||||
- Fremde Teilnehmer-IDs liefern keine Daten.
|
||||
- Admin-Funktionen sind ohne Rolle blockiert.
|
||||
- Alle POST-Aktionen brauchen CSRF.
|
||||
- Uploads sind nicht direkt ausfuehrbar.
|
||||
- Uploads sind nicht direkt ausführbar.
|
||||
- Ausgabe von Namen, Mails und Hinweisen ist escaped.
|
||||
|
||||
UX-Pruefungen:
|
||||
UX-Prüfungen:
|
||||
|
||||
- Kernseiten bleiben optisch wiedererkennbar.
|
||||
- Sidebar bleibt fuer App-Nutzer vertraut.
|
||||
- Sidebar bleibt für App-Nutzer vertraut.
|
||||
- Landingpage zeigt keine App-Adminnavigation.
|
||||
- Mobile Darstellung bleibt bedienbar.
|
||||
- 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| Unvollstaendige Vendor-Kopien fuer PDF/Mail | Dependencies sauber vendoren oder ersetzen, Smoke-Tests danach erweitern |
|
||||
| AD/LDAP blockiert SaaS-Onboarding | E-Mail/Passwort als Basis, SSO spaeter optional |
|
||||
| Kein DB-Schema im Repo | Schema exportieren und Migrationen einfuehren |
|
||||
| Unvollständige Vendor-Kopien für PDF/Mail | Dependencies sauber vendoren oder ersetzen, Smoke-Tests danach erweitern |
|
||||
| AD/LDAP blockiert SaaS-Onboarding | E-Mail/Passwort als Basis, SSO später optional |
|
||||
| Kein DB-Schema im Repo | Schema exportieren und Migrationen einführen |
|
||||
|
||||
## Offene Entscheidungen
|
||||
|
||||
@@ -703,9 +703,9 @@ UX-Pruefungen:
|
||||
- Braucht der MVP schon Tarife/Billing oder erstmal nur Registrierung?
|
||||
- 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.
|
||||
2. Screenshot-Referenz der wichtigsten Seiten erstellen.
|
||||
|
||||
@@ -15,7 +15,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if ($result['ok']) {
|
||||
if (!empty($result['already_verified'])) {
|
||||
$message = 'Die E-Mail-Adresse ist bereits bestaetigt.';
|
||||
$message = 'Die E-Mail-Adresse ist bereits bestätigt.';
|
||||
} else {
|
||||
$message = 'Der Verifizierungslink wurde vorbereitet.';
|
||||
if (saas_should_show_auth_links() && !empty($result['token'])) {
|
||||
@@ -61,7 +61,7 @@ include 'nav.php';
|
||||
<p><?php echo saas_html($user['email']); ?></p>
|
||||
<ul class="actions">
|
||||
<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>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ $pdo = app_db_pdo();
|
||||
$token = trim((string)($_GET['token'] ?? ''));
|
||||
$result = $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-copy">
|
||||
<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 class="public-panel">
|
||||
<h2>Status</h2>
|
||||
|
||||
<?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">
|
||||
<li><a href="konto.php" class="button primary">Zum Konto</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -43,7 +43,7 @@ include 'nav.php';
|
||||
<td><?php echo saas_html($user['tenant_name']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Kundenkuerzel</th>
|
||||
<th>Kundenkürzel</th>
|
||||
<td><?php echo saas_html($user['tenant_slug']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -51,7 +51,7 @@ include 'nav.php';
|
||||
<td><?php echo saas_html($user['role']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>E-Mail bestaetigt</th>
|
||||
<th>E-Mail bestätigt</th>
|
||||
<td><?php echo $user['email_verified_at'] !== null ? 'ja' : 'nein'; ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@
|
||||
</nav>
|
||||
<div class="public-hero-content">
|
||||
<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">
|
||||
<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>
|
||||
@@ -37,12 +37,12 @@
|
||||
<p>Teilnehmer und Admins behalten Verbrauch, Einzahlungen und Salden im Blick.</p>
|
||||
</article>
|
||||
<article class="public-feature">
|
||||
<h3>Mandantenfaehig</h3>
|
||||
<h3>Mandantenfähig</h3>
|
||||
<p>Jeder Kunde arbeitet in seinem eigenen Bereich mit eigenen Einstellungen.</p>
|
||||
</article>
|
||||
<article class="public-feature">
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ include 'nav.php';
|
||||
<div class="hint-box error"><p>Kein Zugriff.</p></div>
|
||||
<?php else: ?>
|
||||
<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); ?>
|
||||
(<code><?php echo saas_html($tenantSlug); ?></code>).
|
||||
Die bestehenden App-Seiten schreiben weiterhin in die Legacy-Tabellen.
|
||||
|
||||
@@ -56,7 +56,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<div class="public-auth-grid">
|
||||
<div class="public-auth-copy">
|
||||
<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 class="public-panel">
|
||||
@@ -86,7 +86,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<input type="password" name="password" id="password" required>
|
||||
</div>
|
||||
<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">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$identity = $tenantId > 0 ? saas_identity_for_user_tenant($pdo, $pendingUserId, $tenantId) : 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 {
|
||||
saas_session_login($identity);
|
||||
header('Location: konto.php');
|
||||
@@ -34,7 +34,7 @@ include 'nav.php';
|
||||
|
||||
<section id="banner">
|
||||
<div class="content">
|
||||
<h2>Mandant auswaehlen</h2>
|
||||
<h2>Mandant auswählen</h2>
|
||||
|
||||
<?php if ($errors !== []): ?>
|
||||
<div class="hint-box error">
|
||||
@@ -45,7 +45,7 @@ include 'nav.php';
|
||||
<?php endif; ?>
|
||||
|
||||
<?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: ?>
|
||||
<form method="post" action="mandant-auswahl.php">
|
||||
<?php echo app_csrf_field(); ?>
|
||||
|
||||
@@ -47,7 +47,7 @@ include 'nav.php';
|
||||
|
||||
<?php if (!$canManageSettings): ?>
|
||||
<?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): ?>
|
||||
<div class="hint-box error"><p>Die Einstellungen konnten nicht geladen werden.</p></div>
|
||||
<?php else: ?>
|
||||
@@ -79,7 +79,7 @@ include 'nav.php';
|
||||
<input type="text" name="locale" id="locale" value="<?php echo saas_html($settings['locale']); ?>" required>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
@@ -109,7 +109,7 @@ include 'nav.php';
|
||||
</div>
|
||||
<ul class="actions">
|
||||
<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>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -52,7 +52,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<div class="public-auth-grid">
|
||||
<div class="public-auth-copy">
|
||||
<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 class="public-panel">
|
||||
@@ -62,7 +62,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<div class="hint-box success">
|
||||
<p><?php echo saas_html($message); ?></p>
|
||||
<?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; ?>
|
||||
</div>
|
||||
<?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>
|
||||
</div>
|
||||
<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">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<title>Kaffeeliste Passwort zuruecksetzen</title>
|
||||
<title>Kaffeeliste Passwort zurücksetzen</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
||||
<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-copy">
|
||||
<h1>Passwort zuruecksetzen</h1>
|
||||
<p>Setze ein neues Passwort fuer dein Kaffeeliste-Konto.</p>
|
||||
<h1>Passwort zurücksetzen</h1>
|
||||
<p>Setze ein neues Passwort für dein Kaffeeliste-Konto.</p>
|
||||
</div>
|
||||
|
||||
<div class="public-panel">
|
||||
<h2>Neues Passwort</h2>
|
||||
|
||||
<?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">
|
||||
<li><a href="login.php" class="button primary">Zum Login</a></li>
|
||||
</ul>
|
||||
@@ -69,7 +69,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?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 if ($tokenValid): ?>
|
||||
|
||||
+2
-2
@@ -63,7 +63,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<div class="public-auth-grid">
|
||||
<div class="public-auth-copy">
|
||||
<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 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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
|
||||
@@ -9,7 +9,7 @@ $checks = [
|
||||
[
|
||||
'label' => 'Landingpage',
|
||||
'path' => 'landing.php',
|
||||
'contains' => ['Kaffeeliste', 'Kundenkonto anlegen', 'Mandantenfaehig'],
|
||||
'contains' => ['Kaffeeliste', 'Kundenkonto anlegen', 'Mandantenfähig'],
|
||||
],
|
||||
[
|
||||
'label' => 'Dashboard',
|
||||
@@ -53,7 +53,7 @@ $checks = [
|
||||
'contains' => ['Mitglieder verwalten', 'PayPal-Name'],
|
||||
],
|
||||
[
|
||||
'label' => 'Letzte Eintraege',
|
||||
'label' => 'Letzte Einträge',
|
||||
'path' => 'letzteneintraege.php',
|
||||
'contains' => ['Letzte 100 Einzahlungen'],
|
||||
],
|
||||
@@ -93,14 +93,14 @@ $checks = [
|
||||
'contains' => ['Login'],
|
||||
],
|
||||
[
|
||||
'label' => 'Passwort zuruecksetzen Invalid',
|
||||
'label' => 'Passwort zurücksetzen Invalid',
|
||||
'path' => 'passwort-zuruecksetzen.php',
|
||||
'contains' => ['Passwort zuruecksetzen', 'ungueltig oder abgelaufen'],
|
||||
'contains' => ['Passwort zurücksetzen', 'ungültig oder abgelaufen'],
|
||||
],
|
||||
[
|
||||
'label' => 'E-Mail verifizieren Invalid',
|
||||
'path' => 'email-verifizieren.php',
|
||||
'contains' => ['E-Mail verifizieren', 'ungueltig oder abgelaufen'],
|
||||
'contains' => ['E-Mail verifizieren', 'ungültig oder abgelaufen'],
|
||||
],
|
||||
[
|
||||
'label' => 'Mandant-Einstellungen Login-Schutz',
|
||||
@@ -146,11 +146,11 @@ $knownIssueChecks = [
|
||||
$skippedUnsafe = [
|
||||
[
|
||||
'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',
|
||||
'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) {
|
||||
echo "RESOLVED {$label}: bitte in die regulaeren Smoke-Checks aufnehmen.\n";
|
||||
echo "RESOLVED {$label}: bitte in die regulären Smoke-Checks aufnehmen.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,8 +100,8 @@ try {
|
||||
|
||||
$step = 'insert notices';
|
||||
$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] Abgelaufener Hinweis fuer Golden-Master-Test', $dates['notice_expired']]);
|
||||
$insertNotice->execute(['[GM] Aktiver Hinweis für Golden-Master-Test', $dates['notice_active']]);
|
||||
$insertNotice->execute(['[GM] Abgelaufener Hinweis für Golden-Master-Test', $dates['notice_expired']]);
|
||||
|
||||
$metadata = [
|
||||
'golden_master_seed' => 'seeded',
|
||||
|
||||
Reference in New Issue
Block a user