Phase 2: Wartungs- und Pruefmanagement
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7f866ac35c
commit
e503aafc18
+108
-1
@@ -13,7 +13,114 @@ Die aufgewendete Zeit wandert nach jedem Abschnitt in die Spalte
|
||||
| Phase 0 — Fundament | 1:00 h | 1 | 03.09.2026 |
|
||||
| Phase 1 — Stammdaten | 1:15 h | 1 | 03.09.2026 |
|
||||
| Phase 1 — Nachtrag Geräteformular | 0:50 h | 1 | 03.09.2026 |
|
||||
| **Summe** | **3:05 h** | **3** | |
|
||||
| Phase 2 — Wartung und Prüfung | 2:25 h | 1 | 03.09.2026 |
|
||||
| **Summe** | **5:30 h** | **4** | |
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-03 · Phase 2 — Wartungs- und Prüfmanagement
|
||||
|
||||
**09:55 – 12:20 · 2:25 h · Version 0.3.0**
|
||||
|
||||
### Was entstanden ist
|
||||
|
||||
Das Modul, wegen dem eine Wehr so ein Programm überhaupt einführt. Ab hier
|
||||
ersetzt FLORIAN den Ordner mit den Prüfnachweisen.
|
||||
|
||||
**Datenmodell.** Elf neue Tabellen: Wartungsvorlage und Checklistenpunkt als
|
||||
Regel, Wartungstermin als deren Anwendung auf ein einzelnes Gerät, Prüfung mit
|
||||
Einzelergebnissen und Unterschriften als Nachweis, dazu Mangel, Rückruf mit
|
||||
betroffener Bauart und je Gerät ein Bearbeitungsstand, und schließlich
|
||||
Erinnerung als Nachweis und Sperre gegen Doppelversand in einem.
|
||||
|
||||
**Fristen-Dashboard.** Vier Töpfe nach Dringlichkeit, jede Zahl gleichzeitig
|
||||
ein Filter, darunter die Verteilung nach Fahrzeug, Standort, Gerätetyp oder
|
||||
Zuständigem. Daneben die Lebensdauerüberwachung mit Aussonderungsvorschlag.
|
||||
|
||||
**Prüfwizard.** Ein Punkt je Bildschirm, Foto aus der Kamera, Zeiterfassung im
|
||||
Hintergrund, Unterschrift auf einem Zeichenfeld. Jede Antwort geht sofort zum
|
||||
Server; ein abgebrochener Durchgang wird beim nächsten Öffnen fortgesetzt.
|
||||
|
||||
**Protokoll.** Der Worker rendert über Gotenberg ein PDF mit Kopf, Checkliste,
|
||||
eingebetteten Fotos und Unterschriften und legt es in MinIO ab. Laufende
|
||||
Nummer je Wehr und Jahr.
|
||||
|
||||
**Nächtlicher Lauf.** Termine planen, Gerätestatus nachziehen, erinnern bei
|
||||
T-30, T-7, T-0 und einmal bei Überfälligkeit — als Sammelnachricht je
|
||||
Empfänger.
|
||||
|
||||
### Entscheidungen
|
||||
|
||||
**Die Unveränderbarkeit sitzt in der Datenbank, nicht im Code.** Ein Trigger
|
||||
weist jede Änderung an einer abgeschlossenen Prüfung ab, auch für den
|
||||
Eigentümer-Zugang. Zulässig bleiben drei Dinge, die das Protokoll ergänzen
|
||||
statt es zu verfälschen: der Wechsel auf „ersetzt", das Nachtragen des PDF,
|
||||
solange keines hinterlegt ist, und `geaendertAm`. Dasselbe gilt für die
|
||||
Bestandteile — sonst ließe sich ein festgeschriebenes Protokoll über den Umweg
|
||||
der Einzelergebnisse verändern.
|
||||
|
||||
Der Grund ist derselbe wie beim Audit-Log in Phase 0: ein vergessener
|
||||
Zustandscheck in einem Router reicht sonst, um den Nachweis wertlos zu machen.
|
||||
|
||||
**„Überfällig" wird gerechnet, nicht gespeichert.** Ein gespeicherter Zustand
|
||||
läuft der Uhr hinterher — ein Termin, der heute Nacht fällig geworden ist,
|
||||
stünde bis zum nächsten Lauf als „offen" da. Aus demselben Grund kennt
|
||||
`WartungsterminStatus` nur offen, erledigt und entfallen.
|
||||
|
||||
**Pflichtpunkt heißt: davon hängt die Einsatzbereitschaft ab.** Ein
|
||||
beanstandeter Pflichtpunkt bedeutet „nicht bestanden" und sperrt das Gerät, ein
|
||||
beanstandeter Kann-Punkt „bestanden mit Mängeln". Damit ist das Ergebnis eine
|
||||
fachliche Aussage und keine Zählübung. Übersteuern darf der Prüfer trotzdem —
|
||||
er trägt die Verantwortung, nicht die Software.
|
||||
|
||||
**Bestanden hebt eine bestehende Sperre nicht auf.** Wer ein Gerät gesperrt
|
||||
hat, hatte einen Grund, und der kann außerhalb dieser Prüfung liegen. Von
|
||||
selbst löst sich nur „Prüfung fällig".
|
||||
|
||||
**Melden darf jedes Mitglied.** Der Mangel fällt dem auf, der das Gerät
|
||||
benutzt. Eine Hürde beim Melden führt dazu, dass gar nicht gemeldet wird — und
|
||||
ein nicht gemeldeter Riss im Schlauch ist gefährlicher als eine Fehlmeldung,
|
||||
die jemand wieder verwirft.
|
||||
|
||||
**Die Rückrufsuche ist ein Vorschlag.** Seriennummern werden als Text
|
||||
verglichen; das stimmt bei gleicher Stellenzahl und sonst nicht immer. Eine
|
||||
automatische Sperre auf dieser Grundlage wäre gefährlicher als hilfreich.
|
||||
|
||||
### Wo es gehakt hat
|
||||
|
||||
| Problem | Ursache | Lösung |
|
||||
|---|---|---|
|
||||
| Prisma wollte die Suchindizes aus Phase 1 löschen | gin_trgm- und gist-Indizes kennt das Schema nicht | die `DROP INDEX`-Zeilen aus der erzeugten Migration entfernt, mit Kommentar für den nächsten, der sie neu erzeugt |
|
||||
| Der Seed brach beim Beispielprotokoll ab | er legte die Prüfung fertig an, und der Trigger verbot die Ergebnisse dazu | erst Entwurf, dann abschließen — genau wie im Wizard |
|
||||
| Zwei gleichzeitig abgeschlossene Prüfungen zogen dieselbe Protokollnummer | die Nummer kam aus einer Zählung ohne Sperre | Transaktionssperre je Wehr und Jahr, Nummer aus dem Höchstwert statt aus der Anzahl |
|
||||
| Der Worker startete außerhalb des Containers nicht | `.env` wurde nicht geladen, und `server-only` wirft unter Node | `dotenv/config` und `--conditions=react-server` in beiden Startbefehlen |
|
||||
| Der Wizard-Test klickte ins Leere | die Mauskoordinaten beziehen sich auf das Fenster, das Zeichenfeld lag darunter | vor dem Zeichnen ins Bild holen |
|
||||
| Zwei E2E-Projekte teilten sich denselben Prüfentwurf | Desktop und Tablet laufen parallel unter demselben Benutzer | je Projekt ein eigenes Gerät |
|
||||
|
||||
### Abweichung von der Vorgabe
|
||||
|
||||
**Keine.** Alle elf Tabellen tragen `organisationId` und stehen unter Row Level
|
||||
Security, jede schreibende Operation protokolliert, gelöscht wird weich.
|
||||
|
||||
Eine Ergänzung zur Regel „Prüfdaten werden nie hart gelöscht": ein
|
||||
**Prüfentwurf** darf verschwinden. Er ist noch kein Nachweis, sondern ein
|
||||
abgebrochener Vorgang; ihn aufzuheben würde die Liste der offenen Prüfungen
|
||||
zumüllen. Ab dem Abschluss greift die Sperre.
|
||||
|
||||
### Was aufgefallen ist
|
||||
|
||||
Der Navigationseintrag „Protokoll" (`/protokoll`) zeigt seit Phase 1 auf eine
|
||||
Seite, die es nicht gibt. Das Audit-Log ist über tRPC abrufbar, hat aber keine
|
||||
Ansicht. Gehört in ein eigenes Issue.
|
||||
|
||||
### Nachweise
|
||||
|
||||
- `npm run typecheck` — ohne Ausgabe
|
||||
- `npm run test` — 161 Tests grün, davon 18 gegen eine echte Datenbank
|
||||
- `npm run test:e2e` — 71 Tests grün, 2 begründet übersprungen (Desktop und Tablet)
|
||||
- `npm run build` — 30 Routen
|
||||
- `docker compose up -d` — Postgres, Redis, MinIO, Gotenberg gesund; Worker
|
||||
richtet die Zeitpläne ein und erzeugt die Protokoll-PDFs in gut einer Sekunde
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user