Phase 3: Einsatz- und Uebungserfassung

Schnellerfassung im Alarmfall, sechs Zeitstempel mit Ausrueckzeit, Anfahrt
und Hilfsfrist, Staerke nach FwDV 3, Personalvorschlag mit
Qualifikationsstand, Einsatzbericht als PDF, Jahresexport fuer die Gemeinde,
Statistik und Uebungen mit Anwesenheitsliste.

Dazu das Einsatzportal fuer Fremdwehren: ein signierter Link ohne Anmeldung,
zeitlich und mengenbegrenzt, der Stichwort, Ort, Zeiten und Lagemeldungen
zeigt und Meldungen mit Foto annimmt. Es sind die ersten oeffentlich
erreichbaren Endpunkte in FLORIAN.

Acht neue Tabellen, alle mit organisationId und Row Level Security. Ein
freigegebener Einsatzbericht ist unveraenderlich; ein Datenbanktrigger setzt
das durch. Koordinaten liegen doppelt — als Dezimalgrad und als
PostGIS-Punkt, zusammengehalten von einem Trigger.

57 neue Tests (32 Unit, 14 gegen eine echte Datenbank, 11 E2E).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-03 13:50:58 +02:00
co-authored by Claude Opus 5
parent bc62a9c1e8
commit 91954c402b
100 changed files with 9667 additions and 38 deletions
+8 -15
View File
@@ -4,6 +4,7 @@ import {
tagesabstand,
utcTag,
} from "@/lib/stammdaten/fristen";
import { nummerBilden, nummerLesen } from "@/lib/nummernkreis";
/**
* Fristenlogik der Wartung.
@@ -148,19 +149,11 @@ export function innerhalbPlanungshorizont(
* Laufende Protokollnummer im Format "JJJJ-NNNN", z. B. "2026-0042".
*
* Die Nummer ist je Wehr und Jahr fortlaufend. Sie steht auf dem PDF und ist
* das, was in einer Rückfrage der Unfallkasse genannt wird — deshalb kurz,
* sprechbar und ohne Bindestrichwüste.
* das, was in einer Rückfrage der Unfallkasse genannt wird.
*
* Ab Phase 3 teilen sich Prüfprotokolle, Einsätze und Übungen denselben
* Nummernkreis-Baustein — die Regel ist dieselbe, und zwei Umsetzungen davon
* würden über kurz oder lang verschieden aussehen.
*/
export function protokollNummer(jahr: number, laufend: number): string {
return `${jahr}-${String(laufend).padStart(4, "0")}`;
}
/** Zerlegt eine Protokollnummer wieder. Gibt null zurück, wenn sie nicht passt. */
export function protokollNummerLesen(
nummer: string | null | undefined,
): { jahr: number; laufend: number } | null {
if (!nummer) return null;
const treffer = /^(\d{4})-(\d{1,6})$/.exec(nummer.trim());
if (!treffer) return null;
return { jahr: Number(treffer[1]), laufend: Number(treffer[2]) };
}
export const protokollNummer = nummerBilden;
export const protokollNummerLesen = nummerLesen;