Wartungsvorlagen mit Checklisten, Prueftermine aus einem naechtlichen Lauf, Prueffristen-Dashboard, Pruefwizard mit Foto und Unterschrift, unveraenderliche Protokolle als PDF, Maengel-Workflow, Rueckrufe und Lebensdauerueberwachung. Elf neue Tabellen, alle mit organisationId und Row Level Security. Die Unveraenderbarkeit abgeschlossener Protokolle setzt ein Datenbanktrigger durch, nicht der Anwendungscode: eine Korrektur erzeugt eine neue Pruefung mit Verweis auf die alte, beide bleiben lesbar. 79 neue Tests (47 Unit, 10 gegen eine echte Datenbank, 22 E2E). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
320 lines
15 KiB
Markdown
320 lines
15 KiB
Markdown
# Changelog
|
||
|
||
Alle nennenswerten Änderungen an FLORIAN. Format nach
|
||
[Keep a Changelog](https://keepachangelog.com/de/1.1.0/), Versionierung nach
|
||
[Semantic Versioning](https://semver.org/lang/de/).
|
||
|
||
Die Regeln dazu stehen in [docs/VERSIONIERUNG.md](docs/VERSIONIERUNG.md).
|
||
|
||
## [Unveröffentlicht]
|
||
|
||
Nächster Schritt ist Phase 3 (Einsätze und Übungen).
|
||
|
||
---
|
||
|
||
## [0.3.0] — 2026-09-03
|
||
|
||
Phase 2: Wartungs- und Prüfmanagement. Das Modul, wegen dem eine Wehr so ein
|
||
Programm einführt — Fristen im Blick behalten und im Streitfall nachweisen
|
||
können, dass geprüft wurde.
|
||
|
||
### Hinzugefügt
|
||
|
||
**Datenmodell**
|
||
|
||
- `Wartungsvorlage` — was an einem Gerätetyp in welchem Takt geprüft wird, mit
|
||
Intervall, Vorwarnzeit, Prüfvorschrift und Erfahrungswert für die Dauer.
|
||
- `Checklistenpunkt` in vier Ausprägungen: Ja/Nein, Messwert (mit zulässigem
|
||
Bereich und Einheit), Text und Foto. Pflichtpunkte sind die, an denen die
|
||
Einsatzbereitschaft hängt.
|
||
- `Wartungstermin` — ein fälliger Termin an einem konkreten Gerät. Eindeutig
|
||
über Gerät, Vorlage und Fälligkeitstag, damit ein zweiter nächtlicher Lauf
|
||
nichts doppelt anlegt.
|
||
- `Pruefung`, `Pruefergebnis` und `Signatur` — die durchgeführte Prüfung mit
|
||
Antworten je Punkt und Unterschrift.
|
||
- `Mangel` mit dem Weg melden → zugewiesen → in Arbeit → behoben.
|
||
- `Rueckruf`, `RueckrufGeraetetyp` und `RueckrufBetroffenheit` — eine
|
||
Herstellerwarnung, die betroffene Bauart und der Stand je Gerät.
|
||
- `Erinnerung` als Nachweis und gleichzeitig als Sperre gegen Doppelversand.
|
||
|
||
**Unveränderbarkeit der Protokolle**
|
||
|
||
- Ein Trigger in der Datenbank weist jede Änderung an einer abgeschlossenen
|
||
Prüfung ab. Zulässig bleiben genau drei Dinge: der Wechsel auf „ersetzt", das
|
||
Nachtragen des PDF, solange keines hinterlegt ist, und `geaendertAm`.
|
||
- Dieselbe Sperre gilt für die Bestandteile: an einem abgeschlossenen Protokoll
|
||
lassen sich weder Einzelergebnisse ändern noch Unterschriften nachtragen.
|
||
- Korrekturen erzeugen eine **neue** Prüfung mit Verweis auf die alte. Beide
|
||
bleiben lesbar.
|
||
- Gelöscht wird ein Protokoll nur im gekennzeichneten Aufbewahrungslauf —
|
||
derselbe Schalter wie beim Audit-Log.
|
||
|
||
**Fristen-Dashboard** (`/pruefungen`)
|
||
|
||
- Vier Töpfe nach Dringlichkeit: überfällig, diese Woche, diesen Monat, nächste
|
||
90 Tage. Jede Zahl ist gleichzeitig ein Filter.
|
||
- Bündelung nach Fahrzeug, Standort, Gerätetyp oder Zuständigem — wer einen
|
||
Prüftag ansetzt, arbeitet ein Fahrzeug ab, keine alphabetische Liste.
|
||
- Sammelzuweisung mehrerer Termine an eine Person.
|
||
- Daneben die Lebensdauerüberwachung (`/pruefungen/lebensdauer`) mit Vorschlag
|
||
zur Aussonderung.
|
||
|
||
**Prüfwizard** (`/pruefungen/neu`)
|
||
|
||
- Ein Prüfpunkt je Bildschirm, große Schaltflächen, Foto direkt aus der Kamera.
|
||
- Zeiterfassung läuft mit und landet im Protokoll.
|
||
- Unterschrift auf einem Zeichenfeld, das Finger, Stift und Maus versteht; das
|
||
PNG landet in MinIO.
|
||
- Jede Antwort geht sofort zum Server — bricht das WLAN weg, ist höchstens die
|
||
letzte verloren, und der Entwurf wird beim nächsten Öffnen fortgesetzt.
|
||
- Mängel werden aus den beanstandeten Punkten vorgeschlagen.
|
||
- Externe Wartung: Fachfirma, Kosten und das Fremdprotokoll als PDF.
|
||
|
||
**Prüfprotokoll als PDF**
|
||
|
||
- Erzeugt der Hintergrundprozess über Gotenberg und legt es in MinIO ab: Kopf
|
||
mit Wehr, Gerät und Prüfer-Kennung, Checkliste mit Soll, Ist und Befund,
|
||
eingebettete Fotos und Unterschriften, Fußzeile mit Prüfvorschrift, Prüfdatum
|
||
und Seitenzahl.
|
||
- Laufende Protokollnummer `JJJJ-NNNN` je Wehr und Jahr.
|
||
|
||
**Nächtlicher Lauf**
|
||
|
||
- `fristen.pruefen` (3:15 Uhr) plant Termine aus den Vorlagen und zieht Frist
|
||
und Status an jedem Gerät nach.
|
||
- `erinnerung.senden` (3:45 Uhr) verschickt Vorwarnungen bei T-30, T-7, T-0 und
|
||
einmal bei Überfälligkeit — als Sammelnachricht je Empfänger.
|
||
- Beide Läufe sind wiederholbar und tragen sich beim Start des Workers je Wehr
|
||
selbst ein.
|
||
|
||
**Mängel und Rückrufe**
|
||
|
||
- Melden darf jedes Mitglied; ein kritischer Mangel sperrt das Gerät sofort.
|
||
- Beim Beheben wird die Sperre aufgehoben — aber nur, wenn kein anderer offener
|
||
Mangel dasselbe Gerät sperrt.
|
||
- Rückrufe suchen betroffene Geräte über Bauart, Modell, Seriennummernbereich
|
||
und Herstellungszeitraum. Das Ergebnis ist ein Vorschlag, keine automatische
|
||
Sperre.
|
||
|
||
**Sonstiges**
|
||
|
||
- Datei-Upload unter `/api/dateien` für Fotos, Unterschriften und
|
||
Fremdprotokolle — als Multipart, nicht Base64 durch eine JSON-Antwort.
|
||
- Der Seed legt 5 Wartungsvorlagen mit echten Prüfvorgaben an (DGUV 112-190,
|
||
DIN 14811, DGUV Vorschrift 3, DIN 14920), dazu die fälligen Termine, ein
|
||
abgeschlossenes Protokoll samt Mangel und einen Beispielrückruf.
|
||
- 79 neue Tests: 47 Unit-Tests zu Fristen, Bewertung und Planung, 10 gegen eine
|
||
echte Datenbank zur Unveränderbarkeit und Mandantentrennung, 22 Playwright-
|
||
Durchläufe über Dashboard, Vorlagen, Wizard, Mängel und Rückrufe.
|
||
|
||
**Nachzügler aus Phase 1**
|
||
|
||
Mit dieser Version wird auch das Geräteformular ausgeliefert, das nach der
|
||
0.2.0 entstanden ist:
|
||
|
||
- **Formular zum Anlegen und Bearbeiten eines Geräts** (`/geraete/neu` und
|
||
`/geraete/<id>/bearbeiten`). Damit ist die letzte offene Lücke aus Phase 1
|
||
geschlossen: Ein Neuzugang lässt sich einzeln erfassen, ohne den Umweg über
|
||
eine Importdatei.
|
||
- Die Lagerung wird als Umschalter geführt statt als zwei Auswahlfelder, die
|
||
man versehentlich beide füllen kann.
|
||
- Prüftermin und Lebensdauerende werden schon beim Tippen ausgerechnet und
|
||
angezeigt — mit denselben Funktionen, mit denen der Server anschließend
|
||
rechnet.
|
||
- Ist nur ein Standort angelegt, wird er vorbelegt.
|
||
- 7 weitere Playwright-Tests zum Formular (nun 58 statt 44, zwei davon unter
|
||
Touch-Emulation begründet übersprungen).
|
||
|
||
Dabei behoben:
|
||
|
||
- **Das Formular stürzte beim ersten Rechnen ab.** Die Vorgabewerte lagen in
|
||
einem `"use client"`-Modul und wurden von einer Server-Komponente importiert;
|
||
dort kommt nur eine Client-Referenz an, kein Objekt. Das Spreaden davon ergab
|
||
lauter `undefined`. Die Werte liegen jetzt in einem eigenen Modul ohne
|
||
`"use client"`.
|
||
- **Das Sternchen an Pflichtfeldern wurde vorgelesen.** Eine Sprachausgabe
|
||
nannte das Feld „Bezeichnung Stern". Das Sternchen trägt jetzt `aria-hidden`;
|
||
dass ein Feld ausgefüllt werden muss, sagt das `required`-Attribut.
|
||
- Fehlermeldungen in Formularen sind eindeutig ansteuerbar (`data-testid`) —
|
||
Next.js hängt einen eigenen Routen-Ansager mit derselben Rolle in jede Seite.
|
||
|
||
### Geändert
|
||
|
||
- Die Datumsarithmetik aus Phase 1 (`utcTag`, `monateAddieren`, `heutigerTag`,
|
||
`tagesabstand`) ist jetzt exportiert. Zwei Umsetzungen derselben Rechnung
|
||
wären früher oder später um einen Tag auseinandergelaufen.
|
||
- Die Seitenleiste zeigt Prüfungen, Mängel und Rückrufe als verfügbar.
|
||
- Die Gerätedetailseite zeigt Prüfhistorie, offene Mängel und anstehende
|
||
Termine — und startet die Prüfung.
|
||
|
||
### Behoben
|
||
|
||
- **Zwei gleichzeitig abgeschlossene Prüfungen zogen dieselbe Protokollnummer.**
|
||
Die eindeutige Bedingung in der Datenbank fing das ab, aber der zweite Prüfer
|
||
bekam einen Datenbankfehler statt eines Protokolls. Die Nummer wird jetzt
|
||
unter einer Transaktionssperre je Wehr und Jahr vergeben — gefunden von den
|
||
E2E-Tests, weil Desktop- und Tablet-Durchlauf parallel prüften.
|
||
- **Der Worker ließ sich außerhalb des Containers nicht starten.** Ihm fehlte
|
||
das Laden der `.env`, und `server-only` warf beim Import unter Node. Beide
|
||
Startbefehle laufen jetzt mit `--conditions=react-server`, womit der leere
|
||
Export dieses Pakets greift.
|
||
|
||
---
|
||
|
||
## [0.2.0] — 2026-09-03
|
||
|
||
Phase 1: Stammdaten. Ab hier führt die Wehr ihren Bestand in FLORIAN — mit
|
||
Standorten, Fahrzeugen samt Beladeordnung, Gerätetypen als Vorgabe und
|
||
Geräten, die per QR-Code am Regal auffindbar sind.
|
||
|
||
### Hinzugefügt
|
||
|
||
**Datenmodell**
|
||
|
||
- `Standort` (Gerätehaus, Lager, Außenstelle, Werkstatt) mit PostGIS-Punkt für
|
||
die Karte ab Phase 6.
|
||
- `Geraeteraum` — die Fächer am Fahrzeug (G1 bis G6, Heck, Dach,
|
||
Mannschaftsraum), in Beladereihenfolge sortierbar.
|
||
- `Geraetetyp` als Vorgabe für Prüfintervall, Prüfvorschrift und Lebensdauer.
|
||
Ein einzelnes Gerät darf davon abweichen; sonst gilt der Typ.
|
||
- `Qualifikationstyp` und `Qualifikation` mit Gültigkeitsdauer und eigener
|
||
Vorwarnzeit je Typ.
|
||
- `Fahrzeug` erweitert um Fahrgestellnummer, DIN-Typkurzzeichen, Erstzulassung,
|
||
Sitzplätze, Tankinhalt, Gesamtmasse und die UVV-Frist.
|
||
- `Geraet` erweitert um Gerätetyp, Lebensdauerkategorie, errechnetes
|
||
Lebensdauerende, Anschaffungspreis und die Lagerung.
|
||
|
||
**Regeln in der Datenbank**
|
||
|
||
- Ein Gerät steht entweder an einem Standort oder ist auf einem Fahrzeug
|
||
verlastet — eine Prüfbedingung setzt das durch, ein Trigger stellt zusätzlich
|
||
sicher, dass ein angegebener Geräteraum auch zum angegebenen Fahrzeug gehört.
|
||
- Row Level Security auf allen neuen Tabellen.
|
||
- Trigram-Indizes für die Volltextsuche über Geräte, Fahrzeuge und Personen.
|
||
|
||
**Funktionen**
|
||
|
||
- Geräteliste mit Volltextsuche, Statusfilter, serverseitiger Sortierung,
|
||
Blättern und Spaltenauswahl. Die Spaltenauswahl bleibt je Browser gespeichert.
|
||
- Massenbearbeitung: Status mehrerer Geräte auf einmal setzen, Geräte umlagern.
|
||
Jede Änderung erzeugt einen eigenen Protokolleintrag am jeweiligen Gerät.
|
||
- QR-Code je Gerät. Der Code zeigt auf `/g/<inventarnummer>` und führt nach der
|
||
Anmeldung direkt zur Gerätedetailseite.
|
||
- Etikettenbogen als PDF über Gotenberg: DIN A4 mit 65 Etiketten
|
||
(38,1 × 21,2 mm). Der Druck kann an einer beliebigen Position beginnen, damit
|
||
angebrochene Bögen weiterverwendet werden können.
|
||
- QR-Scan über die Kamera im Browser, mit Eingabefeld als Rückfallweg.
|
||
- Import aus CSV und Excel: Spalten werden nach den Überschriften automatisch
|
||
zugeordnet, die Vorschau zeigt Zeile für Zeile, was übernommen würde und was
|
||
daran nicht stimmt. Geschrieben wird erst nach Bestätigung.
|
||
- Gerätehistorie und Fahrzeughistorie, gerendert aus dem Audit-Log.
|
||
- Qualifikationen mit Ablaufwarnung, gerechnet mit der Vorwarnzeit des
|
||
jeweiligen Typs.
|
||
- Fahrzeugdetail zeigt die Beladung nach Geräteräumen statt alphabetisch.
|
||
|
||
**Daten und Tests**
|
||
|
||
- Der Seed legt jetzt zusätzlich 3 Standorte, 20 Geräteräume, 14 Gerätetypen
|
||
mit echten Prüfvorgaben (DGUV, DIN, vfdb), 49 Geräte und 107 Qualifikationen
|
||
an. Fristen sind bewusst gestreut, damit die Ampeln etwas zeigen.
|
||
- 104 Vitest-Tests (vorher 32), davon 40 zum Import und 32 zu QR und Fristen.
|
||
- 44 Playwright-Tests (vorher 10) über Desktop und Tablet.
|
||
|
||
### Geändert
|
||
|
||
- **Fristenrechnung auf UTC-Kalenderarithmetik umgestellt.** Vorher wurde über
|
||
`startOfDay` aus date-fns gerechnet, das die Zeitzone des Servers verwendet.
|
||
Ein Prüfdatum kommt aus der Datenbank als UTC-Mitternacht und wurde dabei auf
|
||
den Vortag zurückgeschoben — Fristen wären einen Tag zu früh fällig geworden.
|
||
- `CardTitle` rendert eine echte Überschrift statt eines `div`. Wer mit einer
|
||
Sprachausgabe arbeitet, springt von Überschrift zu Überschrift; Karten
|
||
tauchten in dieser Navigation vorher gar nicht auf.
|
||
- Die Entwicklungsanzeige von Next.js ist abgeschaltet, damit sie nicht in den
|
||
Bildern der Anleitung mitläuft.
|
||
|
||
### Sicherheit
|
||
|
||
- Der Etikettendruck wird protokolliert (`EXPORTIERT`) — damit ist
|
||
nachvollziehbar, wann welche Aufkleber erzeugt wurden.
|
||
- Der Import übernimmt keine Werte, die der Browser geschickt hat: die Datei
|
||
wird beim Übernehmen erneut serverseitig eingelesen und geprüft. Sonst ließe
|
||
sich die Vorschau umgehen.
|
||
|
||
## [0.1.0] — 2026-09-03
|
||
|
||
Phase 0: das Fundament. Ab hier steht ein lauffähiger Stack mit
|
||
Mandantentrennung, Anmeldung, Dateiablage und lückenloser Protokollierung.
|
||
|
||
### Hinzugefügt
|
||
|
||
**Betrieb**
|
||
|
||
- `docker-compose.yml` mit Traefik, App, Worker, PostgreSQL 17 + PostGIS,
|
||
Redis, MinIO und Gotenberg.
|
||
- `docker-compose.override.yml` für die Entwicklung: ohne Traefik, mit
|
||
Mailpit als SMTP-Auffangkorb und Adminer für den Blick in die Datenbank.
|
||
Host-Ports bewusst abseits der Standardwerte (3100, 5442, 6389, 9010/9011),
|
||
damit FLORIAN neben anderen lokalen Stacks laufen kann.
|
||
- Mehrstufiges `docker/Dockerfile`: eigener Benutzer `florian` (UID 1001,
|
||
kein root), `tini` als PID 1, Healthcheck gegen `/api/health`,
|
||
Standalone-Build von Next.js.
|
||
- `docker/entrypoint.sh` wendet ausstehende Migrationen an — nur im
|
||
App-Container, nicht im Worker.
|
||
- `docker/postgres/10-init-app-role.sh` legt beim ersten Start die Rolle
|
||
`florian_app` (`NOBYPASSRLS`) samt Vorgabeprivilegien an.
|
||
- `.env.example` mit einem Kommentar zu jeder einzelnen Variable.
|
||
|
||
**Datenmodell**
|
||
|
||
- Prisma-Schema mit `Organisation`, `Person`, `Rolle`, `Berechtigung`,
|
||
`RolleBerechtigung`, `PersonRolle`, `AusweisToken`, `AuditLog`, `Datei`.
|
||
- `Fahrzeug` und `Geraet` bereits schmal angelegt, damit der Seed eine
|
||
realistische Wehr aufbauen kann. Phase 1 erweitert beide.
|
||
- Migration `20260902000100_rls_policies`: Row Level Security auf allen
|
||
mandantenpflichtigen Tabellen, Funktion `aktuelle_organisation()`,
|
||
Append-only-Trigger auf `audit_log`.
|
||
- Migration `20260902000200_audit_bereinigung`: `UPDATE` auf `audit_log`
|
||
bleibt dauerhaft gesperrt, `DELETE` ist nur im gekennzeichneten
|
||
Aufbewahrungslauf möglich.
|
||
|
||
**Anwendungskern**
|
||
|
||
- `lib/env.ts` — Zod-Validierung der gesamten Umgebung beim Start.
|
||
- `lib/db.ts` — getrennte Verbindungen für Anwendung (RLS aktiv) und System
|
||
(RLS-frei), `fuerOrganisation()` als einziger Weg zu Fachdaten.
|
||
- `lib/authz.ts` — 9 Rollen, 38 Einzelrechte, zentrale Prüffunktionen.
|
||
- `lib/storage.ts` — MinIO, presignte Up- und Downloads, SHA-256-Prüfsumme.
|
||
- `lib/audit.ts` — Protokolleinträge mit Vorher/Nachher-Vergleich, entfernt
|
||
Geheimnisse vor dem Schreiben.
|
||
- `lib/auth.ts` — Auth.js mit OIDC gegen authentik, lokalem Passwort-Login
|
||
und Terminal-Anmeldung über PIN oder RFID.
|
||
- `lib/warteschlange.ts` und `src/worker/` — BullMQ-Gerüst mit den
|
||
Auftragsarten für Fristen, Erinnerungen, PDF und Aufbewahrung.
|
||
- `lib/integrations/` — Mail, Telegram und LLM hinter abschaltbaren
|
||
Schnittstellen.
|
||
- tRPC v11 mit den Prozedurarten `oeffentlich`, `angemeldet` und
|
||
`mitRecht(...)`.
|
||
|
||
**Oberfläche**
|
||
|
||
- Basis-Layout mit Seitenleiste, heller und dunkler Darstellung
|
||
(Systemvorgabe als Standard) und durchgängig deutschen Texten.
|
||
- Anmeldeseite, die genau die eingerichteten Anmeldewege zeigt.
|
||
- Übersicht mit Kennzahlen und dem Sicherheitshinweis zur analogen
|
||
Atemschutzüberwachung.
|
||
- Farbwelt in OKLCH mit festen Statusfarben für Gerätezustände.
|
||
|
||
**Daten und Tests**
|
||
|
||
- Seed mit einer vollständigen Beispielwehr: 15 Personen mit Rollen und
|
||
Terminal-Ausweisen, 3 Fahrzeuge, 40 Geräte mit gemischten Prüffristen.
|
||
- Vitest: 32 Tests, darunter acht, die die Mandantentrennung gegen eine echte
|
||
Datenbank prüfen.
|
||
- Playwright: 10 Tests über Desktop und Tablet.
|
||
- `npm run screenshots` erzeugt die Bilder für die Anleitung.
|
||
|
||
**Dokumentation**
|
||
|
||
- README mit Schnellstart in sechs Zeilen.
|
||
- Anleitung, Roadmap, Aufwandsschätzung, Devlog und Versionierungsregeln.
|