Bugfixes aus dem Code-Review:
- XSS: unescaptes $_SERVER['PHP_SELF'] in csvupload.php und
letzteneintraege.php durch feste Seitennamen ersetzt.
- Stripe-Webhook: Event-Deduplizierung (neue Tabelle stripe_webhook_events)
gegen doppelte Verarbeitung/Dolibarr-Rechnungen bei Retry-Zustellung.
- Stripe-Webhook: Tarifwechsel aus dem Kundenportal wird lokal nachgezogen
(plan_code aus dem Preis-lookup_key bei subscription.updated).
- Post-Redirect-Get fuer jahresauswertung, mailversenden und die
Selbst-Stricheintragung - verhindert Doppelbuchung/Doppelversand per
Browser-Refresh.
- Jahresbonus: Mails erst nach erfolgreichem Commit; Restcent-Ausgleich
beim letzten Empfaenger, damit die Summe exakt stimmt.
- Verschachtelte HTML-Dokumente in csvupload/einzahlung/stricheintragen
entfernt (Layout kommt aus header.php).
- Rate-Limit fuer den Versand von E-Mail-Verifizierungslinks.
Neue Funktionen:
- Mitglieder koennen ihren zuletzt selbst eingetragenen Strich wieder
stornieren (nur eigene Web-Eintraege, Kassenwart-Eintraege bleiben).
- Monatsuebersicht des eigenen Verbrauchs im Mitglieder-Dashboard.
- Automatische Zahlungserinnerung: opt-in pro Mandant ab der
Warnschwelle, mit Intervall; Cron-Skript scripts/send-payment-reminders.php.
- CSV-Import fuer Mitgliederlisten inkl. herunterladbarer Vorlage
(mitglieder-vorlage.php), Semikolon-/Komma- und BOM-Erkennung.
- Logo als PDF-Wasserzeichen pro Mandant (Upload in den Mandant-
Einstellungen, geschuetzt unter var/tenant_logos/, ersetzt den
Text-Wasserzeichen im Ausdruck).
Robusterer Teilnehmer-Lookup im Dashboard ueber user_id (Fallback E-Mail).
jahresauswertung.php verband sich bisher mit fest codierten (kaputten)
Zugangsdaten selbst zur Datenbank statt ueber config.php, hatte keine
Zugriffskontrolle und kein CSRF, und verteilte bei jedem Aufruf sofort
einen hart codierten Bonus-Topf (490 Striche a 0,20 Euro) per PHPMailer
(dessen Quelldateien im Repo fehlen) mit AOK-spezifischem Mailtext.
Nach Abstimmung mit dem Kunden als generisches, mandantenfaehiges Feature
neu gebaut statt nur deaktiviert oder rein lesend umgesetzt:
- Admin gibt einen frei waehlbaren Gesamtbetrag ein, das System verteilt
ihn proportional zu den Jahresstrichen auf alle aktiven Mitglieder.
- Standardmaessig aktive Dry-Run-Checkbox zeigt die Verteilung, ohne zu
buchen oder Mails zu verschicken.
- Bestaetigter Lauf bucht ueber dasselbe Zweig-Muster wie ueberall
(Default-Mandant Dual-Write, andere Mandanten ledger_record_payment)
und verschickt personalisierte Mails ueber saas_send_mail(), protokolliert
im outbound_emails-Versandlog.
- Zugriffskontrolle ergaenzt (owner/admin/treasurer + Legacy-Fallback).
- http-smoke.php: jahresauswertung.php jetzt regulaerer Check statt
uebersprungenem unsicherem Aufruf; damit sind keine Seiten mehr
uebersprungen oder als bekannter offener Punkt markiert (26/26 gruen).
Live getestet: Dry-Run mit korrekter proportionaler Verteilung (Summe
ergibt exakt den Gesamtbetrag), Live-Lauf bucht und versendet korrekt,
Testdaten anschliessend vollstaendig entfernt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mailversenden.php hatte keine Zugriffskontrolle, versendete bei jedem
GET-Request sofort echte Mails und nutzte PHPMailer, dessen Quelldateien
im Repo gar nicht vorhanden waren (nur composer.json/Lizenz) - der Aufruf
waere also ohnehin mit Fatal Error abgebrochen. Zusaetzlich waren SMTP-
Host, Absender, PayPal-Link und FAQ-URL fest auf einen Alt-Kunden (AOK)
codiert.
- Ersetzt PHPMailer durch die bestehende saas_send_mail()-Abstraktion aus
M3 (Transport log/mail je nach APP_MAIL_TRANSPORT) statt eine fehlende
Abhaengigkeit nachzuvendoren.
- Neue Tabelle outbound_emails protokolliert jeden Versandversuch:
Mandant, Mitglied, Vorlage, Betreff, Status, Fehler - das Versandlog.
- Formular hat eine standardmaessig aktive Dry-Run-Checkbox; im Dry-Run
wird nur geloggt, saas_send_mail() nicht aufgerufen.
- Mailtext ist jetzt tenant-generisch (Saldo, optionaler PayPal-Link nur
wenn der Mandant PayPal aktiviert hat, eigener Dashboard-Link) statt
hartcodierter Alt-Kunden-Inhalte.
- Zugriffskontrolle ergaenzt (owner/admin/treasurer + Legacy-Fallback).
- http-smoke.php: mailversenden.php ist jetzt reguel</EOF>
Hinweise:
- Neue Tabelle notices (tenant-scoped, Soft-Delete via deleted_at) loest
die global unscoped kl_hinweise als aktive Datenquelle ab; Migration
uebernimmt einmalig aktuell gueltige kl_hinweise-Eintraege fuer den
Default-Mandanten. kl_hinweise bleibt als Golden-Master-Referenz stehen.
- hinweise.php und der Banner in header.php sind tenant-scoped umgestellt.
Mitgliederverwaltung:
- mitarbeiterverwalten.php verwaltet jetzt participants (tenant-scoped)
statt der global unscoped kl_Mitarbeiter-Tabelle als primaere Quelle.
Das behebt nebenbei ein Mandanten-Datenleck: jeder SaaS-Mandant mit
Owner/Admin-Rolle haette zuvor die komplette Default-Mandanten-
Mitgliederliste sehen und bearbeiten koennen.
- Fuer den Default-Mandanten bleibt Dual-Write nach kl_Mitarbeiter
bestehen, damit stricheintragen.php/einzahlung.php weiter funktionieren;
andere Mandanten werden rein participant-nativ verwaltet.
- Die Legacy-Administrator-Checkbox ist raus. Stattdessen kann ein Admin
je Mitglied unabhaengig von Name/E-Mail einen Login-Zugang mit Rolle
(member/treasurer/admin) gewaehren oder entziehen
(saas_grant_participant_access / saas_revoke_participant_access).
Einladung laeuft ueber den bestehenden Passwort-Reset-Mechanismus,
Entzug setzt die Mitgliedschaft auf revoked statt sie zu loeschen.
- Kompletter Flow live getestet: anlegen, Zugang gewaehren, Einladungsmail,
Passwort setzen, Login, Rollenschutz, Zugang entziehen, Login-Sperre.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Mail-Transport fuer Reset- und Verifizierungslinks ergaenzen
- Public-Landingpage mit Hero-Asset und App-CTAs bauen
- Mail-Flow-Test, Smoke-Abdeckung und SaaS-Doku aktualisieren