feat: pro Mandant editierbare E-Mail-Vorlagen

Zweistufig wie der Werkzeugkatalog: Betreiber pflegt den plattformweiten
Standard (/betreiber/email-vorlagen), jeder Mandant kann ihn für sich
übersteuern (/verwaltung/email-vorlagen) - ResolveEmailVorlage nutzt die
eigene Vorlage falls vorhanden, sonst fällt sie auf den Plattform-
Standard zurück. Passwort-Zurücksetzen ist die einzige aktuell
existierende E-Mail und nutzt jetzt diese Vorlage statt Hardcoding.

Bug beim Live-Verifizieren gefunden: UNIQUE(account_id, typ) verhindert
bei NULLABLE account_id keine Duplikate (NULL != NULL in SQL) - jedes
Speichern des Plattform-Standards erzeugte eine neue Zeile statt sie zu
aktualisieren. Fix: zwei partielle Unique-Indizes statt eines
gemeinsamen Constraints, mit Regressionstest abgesichert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
noroot
2026-09-01 10:06:09 +02:00
parent b0d6b00045
commit d3b171f721
13 changed files with 749 additions and 3 deletions

View File

@@ -633,6 +633,46 @@ und live per curl gegen echten Server + Postgres verifiziert.
---
## E-Mail-Vorlagen (2026-09-01, Migration 0022)
Auf Nutzerwunsch: alle vom System versendeten E-Mails sollen editierbar
sein — für E-Mails, die an die eigenen Mitarbeiter einer Firma gehen,
durch den Mandanten selbst; für E-Mails, die die Plattform an
Mandanten-Admins schickt, durch den Betreiber. Zweistufig wie der
Werkzeugkatalog: `email_vorlage.account_id` NULL = plattformweiter
Standard (`GET/POST /betreiber/email-vorlagen`, nur Betreiber), gesetzt
= mandantenspezifische Übersteuerung (`GET/POST /verwaltung/email-
vorlagen`, nur Admin). `ResolveEmailVorlage` liefert die eigene
Übersteuerung, falls vorhanden, sonst den Plattform-Standard — ein
Mandant, der nie etwas anpasst, bekommt automatisch jede künftige
Änderung des Plattform-Standards. `emailVorlageTypen` in
`internal/web/email_vorlage_handlers.go` ist die feste, bekannte Liste
der vom System versendeten E-Mails — aktuell nur
`passwort_zuruecksetzen` (die einzige E-Mail, die es bisher gibt),
bewusst keine generische "beliebige E-Mail anlegen"-UI, da jeder Typ an
eine echte Code-Stelle gebunden ist, die ihn tatsächlich versendet
(Platzhalter wie `{{link}}` sind pro Typ verschieden und müssten sonst
geraten werden).
**Bug beim Live-Verifizieren gefunden und behoben:** `UNIQUE
(account_id, typ)` als einzelner Tabellen-Constraint reicht bei
NULLABLE `account_id` NICHT — SQL behandelt NULL nie als gleich zu
NULL, ein Mandant/Betreiber hätte also bei jedem Speichern eine neue
Plattform-Standard-Zeile statt eines Updates bekommen (genau das ist
beim ersten Testlauf passiert: zwei "Version 1"/"Version 2"-Zeilen
gleichzeitig, `ResolveEmailVorlage` griff die falsche). Fix: zwei
partielle Unique-Indizes (`... WHERE account_id IS NULL` /
`... WHERE account_id IS NOT NULL`) statt eines gemeinsamen Constraints
`UpsertEmailVorlage` braucht dafür zwei unterschiedliche
`ON CONFLICT`-Ziele (SQL erlaubt nur ein Ziel je INSERT-Anweisung), mit
dediziertem Regressionstest (`TestUpsertPlattformStandardAktualisiertStattZuDuplizieren`)
abgesichert. Live per curl mit echtem SMTP-Test-Server erneut
verifiziert: zweimaliges Speichern des Plattform-Standards ergibt eine
Zeile mit dem aktuellen Stand, eine neu registrierte Firma ohne eigene
Übersteuerung bekommt automatisch den zuletzt gesetzten Plattform-Text.
---
## Row-Level-Security (2026-09-01, Migration 0021)
**Ausgangslage:** RLS-Policies wirken nie bei Postgres-Superusern, und