# M5 App-Kern Stand: 2026-07-14 (fortgeführt) 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-Oberfläche vergleichbar bleiben. ## Ziel - Kernseiten aus `app/ledger.php` lesen lassen. - Bestehendes Tabellenlayout und Bediengefühl erhalten. - Schreibende Legacy-Flows erst nach stabiler Leseparität umstellen. - Tenant-Kontext serverseitig bestimmen, nicht aus Formularfeldern. ## Umgesetzte Schritte Umgesetzte Dateien: ```text kaffeeliste.php teilnehmerauswertung.php index.php ``` Umgesetzter Umfang `kaffeeliste.php`: - 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 über `participants.legacy_mitarbeiter_id` erhalten. - 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 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. Umgesetzter Umfang `teilnehmerauswertung.php`: - Die Detailauswertung liest Teilnehmerdaten über `ledger_fetch_participant_summary_by_legacy_id()`. - Die Route bleibt kompatibel zu bestehenden Links: `teilnehmerauswertung.php?user_id=`. - Gesamtwerte, Jahresübersicht und letzte Buchungen werden aus `ledger_entries` geladen. - PayPal-Anzeige nutzt die neuen Tenant-Settings statt `kl_config`. - Owner, Admin und Treasurer dürfen die SaaS-Ansicht lesen; der lokale Legacy-/Dev-Admin-Fallback nutzt weiterhin den Default-Tenant. Ergänzte Ledger-Helfer: - `ledger_fetch_default_tenant()` - `ledger_fetch_participant_summary_by_legacy_id()` - `ledger_mirror_legacy_consumption()` - Filter `legacy_mitarbeiter_ids` in `ledger_fetch_participant_summaries()` Umgesetzter Umfang `index.php`: - Das persönliche Dashboard liest den aktuellen Stand, Jahreswerte und letzte Buchungen aus `ledger_entries`. - Die Teilnehmerzuordnung erfolgt über den aktuellen SaaS-User oder im lokalen Legacy-/Dev-Fallback über die E-Mail-Adresse des Legacy-Mitarbeiters. - PayPal-Anzeige und Preis pro Strich nutzen die Tenant-Settings. - Eigene Web-Striche bleiben mit dem bestehenden Legacy-Datensatz kompatibel und werden direkt ins Ledger gespiegelt. ## Fortsetzung: Schreibseiten und Storno Umgesetzte Dateien: ```text stricheintragen.php einzahlung.php letzteneintraege.php app/ledger.php scripts/check-m4-ledger-migration.php ``` Umgesetzter Umfang `stricheintragen.php` und `einzahlung.php`: - Beide Sammelerfassungsseiten waren zuvor ohne jede Zugriffsprüfung erreichbar (nur CSRF-Schutz, kein `checkKaffeelisteAdmin`- oder Rollen-Check). Das ist jetzt behoben: Zugriff erfordert SaaS-Rolle `owner`, `admin` oder `treasurer`, oder im Legacy-/Dev-Fallback `checkKaffeelisteAdmin` mit dem Default-Tenant. - Jede eingetragene Legacy-Zeile (`kl_Kaffeeverbrauch` beziehungsweise `kl_Einzahlungen`) wird innerhalb derselben Transaktion sofort über `ledger_mirror_legacy_consumption()` beziehungsweise die neue `ledger_mirror_legacy_payment()` ins Ledger gespiegelt. Schlägt die Spiegelung fehl, wird die gesamte Sammelerfassung zurückgerollt statt teilweise gespeichert zu werden. - Ein vorbestehender Bug wurde nebenbei behoben: Bei einer POST-Anfrage blieb `$sqlMitarbeiter` unbelegt, was einen PHP-Warning erzeugte und die Mitarbeiterliste nach dem Speichern leer ließ. Umgesetzter Umfang `letzteneintraege.php`: - Der Seitenzugriff nutzte bisher ausschließlich den Legacy-Check `checkKaffeelisteAdmin`, wodurch neu registrierte SaaS-Mandanten (ohne Legacy-`kl_Mitarbeiter`-Zeile) die Seite nie hätten nutzen können. Jetzt gilt dieselbe Rollen-/Fallback-Logik wie in `kaffeeliste.php`. - Löschen erzeugt keinen harten Delete im Ledger mehr. Neue Funktion `ledger_void_entry_by_legacy_id()` setzt `voided_at` auf dem gespiegelten Ledger-Eintrag, bevor die Legacy-Zeile aus `kl_Einzahlungen` oder `kl_Kaffeeverbrauch` entfernt wird (in einer Transaktion). Der Ledger-Eintrag bleibt damit für die Revision erhalten; alle Lesepfade filtern bereits konsistent auf `voided_at IS NULL`. - Zwei nie aufgerufene Funktionen (`berechneGesamtausgabe`, `berechneGesamtstriche`, `berechneGesamteinzahlungen`) wurden als toten Code entfernt. Angepasstes Check-Skript: - `scripts/check-m4-ledger-migration.php` ging bisher davon aus, dass jeder gespiegelte Ledger-Eintrag eine noch existierende Legacy-Zeile hat. Das ist durch das Storno-Modell nicht mehr korrekt: Ein stornierter Eintrag hat absichtlich keine Legacy-Zeile mehr, bleibt aber im Ledger stehen. Die Prüfungen filtern jetzt zusätzlich auf `voided_at IS NULL`, sodass echte Dateninkonsistenzen weiterhin erkannt werden, stornierte Einträge aber nicht mehr als Fehler zählen. Alle drei Flows wurden gegen die Remote-Dev-Datenbank live per HTTP getestet (Sammeleintrag Striche, Sammeleinzahlung, Storno beider Buchungsarten über `letzteneintraege.php`) und anschließend wieder auf den Ausgangsstand zurückgesetzt. ## Noch offen - Eigene PayPal-/Zahlungsbereich als eigenständiger App-Screen (aktuell nur im Dashboard integriert). - Mitgliederverwaltung tenant- und rollenbasiert umsetzen (`mitarbeiterverwalten.php` ist noch nicht angefasst). - Hinweise als tenant-spezifische Notices umsetzen. - Export, Mail und Jahresprozesse bleiben M6-Themen. ## Aktueller Prüfstatus - M4 Ledger-Migration: grün mit 73 Assertions (Storno-fähig). - M4 Ledger-Service: grün mit 115 Assertions. - Golden Master: grün mit 104 Assertions. - HTTP-Smoke: grün mit 23 geprüften Seiten. - Live-Test der Schreibflows gegen die Dev-DB: Sammelstriche, Sammeleinzahlung und Storno beider Buchungsarten erfolgreich geprüft.