Files
FireAdministration/prisma/migrations/20260903214555_kartenmodule/migration.sql
T
Florian WildeandClaude Opus 5 1c1e141666 Kartenmodule ergaenzen
Phase 6: alles, was einen Ort hat — Loeschwasser, Objekte mit Feuerwehrplan,
die Lagekarte eines Einsatzes und die geplante Foerderstrecke.

Keine Kacheln aus dem Netz. Wo eine Wehr ausrueckt und wo ihre Hydranten
liegen, ist niemandes Geschaeft; der Kartenserver laeuft selbst
(tileserver-gl im Compose-Profil "karte"). Ohne ihn zeigt die Karte einen
leeren Grund und sagt das auch — Punkte, Entfernungen und Umkreissuche
funktionieren trotzdem. Dasselbe gilt fuer einen eingetragenen, aber nicht
erreichbaren Server.

Die Umkreissuche laeuft ueber PostGIS: ST_DWithin auf der Geografie mit
GiST-Index rechnet in Metern und nutzt den Index. Gefragt wird waehrend der
Fahrt, und bei tausend Hydranten waere alles andere ein vollstaendiger
Tabellendurchlauf.

Der Import aus GeoJSON und Stadtwerke-CSV ist nachsichtig, aber ehrlich: er
erkennt "DN 100", deutsche Kommazahlen, Datumsangaben und vertauschte Achsen
— raet aber nichts. Fehlt die Position, wird die Zeile uebersprungen; ohne
Statusangabe steht "ungeprueft", nicht "einsatzbereit".

22 taktische Zeichen nach FwDV 102 selbst gezeichnet, die Lagekarte gleicht
ueber denselben Ereignisstrom ab wie die Atemschutzueberwachung.

Die Wasserfoerderung rechnet Reibung, Hoehe und Pumpenzahl; das Ergebnis wird
nie gespeichert, sondern bei jeder Anzeige neu gerechnet. Geht eine Strecke
nicht auf, sagt die Ausgabe das.

Behoben: die Reibungsbeiwerte lagen im ersten Wurf um den Faktor zehn daneben.
Aufgefallen ist es nur, weil die Tests gegen die Tabellenwerte der Ausbildung
pruefen und nicht gegen die eigene Implementierung.

Dazu ein Abruf fuer abgelegte Dateien (/api/dateien/[id]) — bis hierher gab es
nur den Upload.

59 neue Tests (48 Unit, 11 E2E je Browser).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 01:41:24 +02:00

274 lines
11 KiB
SQL

-- Phase 6 — Kartenmodule
--
-- Von Prisma erzeugt, danach von Hand um die DropIndex-Anweisungen erleichtert:
-- die Volltext- und GiST-Indizes aus den fruehereren Phasen stehen nicht im
-- Schema, weil Prisma sie nicht abbilden kann. Rechte, Mandantentrennung,
-- Koordinaten-Trigger und Umkreisindizes stehen in der Migration daneben.
-- CreateEnum
CREATE TYPE "HydrantTyp" AS ENUM ('UNTERFLUR', 'UEBERFLUR', 'SAUGSTELLE', 'ZISTERNE', 'OFFENES_GEWAESSER', 'SONSTIGES');
-- CreateEnum
CREATE TYPE "HydrantStatus" AS ENUM ('EINSATZBEREIT', 'EINGESCHRAENKT', 'DEFEKT', 'UNBEKANNT');
-- CreateEnum
CREATE TYPE "ObjektKategorie" AS ENUM ('SCHULE', 'KINDERGARTEN', 'PFLEGEHEIM', 'KRANKENHAUS', 'VERSAMMLUNGSSTAETTE', 'HOCHHAUS', 'INDUSTRIE', 'LANDWIRTSCHAFT', 'GEFAHRSTOFFLAGER', 'SONDERBAU', 'SONSTIGES');
-- CreateEnum
CREATE TYPE "PlanArt" AS ENUM ('UEBERSICHTSPLAN', 'GESCHOSSPLAN', 'UMGEBUNGSPLAN', 'SONDERPLAN', 'ANLAGE');
-- CreateEnum
CREATE TYPE "LageelementArt" AS ENUM ('ZEICHEN', 'LINIE', 'FLAECHE', 'TEXT');
-- CreateEnum
CREATE TYPE "FoerderpunktArt" AS ENUM ('ENTNAHME', 'VERSTAERKER', 'WEGPUNKT', 'ZIEL');
-- AlterTable
ALTER TABLE "standort" ADD COLUMN "breitengrad" DECIMAL(9,6),
ADD COLUMN "laengengrad" DECIMAL(9,6);
-- CreateTable
CREATE TABLE "hydrant" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"bezeichnung" VARCHAR(120),
"typ" "HydrantTyp" NOT NULL DEFAULT 'UNTERFLUR',
"status" "HydrantStatus" NOT NULL DEFAULT 'UNBEKANNT',
"nennweite" INTEGER,
"durchfluss" INTEGER,
"strasse" VARCHAR(200),
"hausnummer" VARCHAR(20),
"ortsteil" VARCHAR(120),
"breitengrad" DECIMAL(9,6) NOT NULL,
"laengengrad" DECIMAL(9,6) NOT NULL,
"koordinaten" geography(Point, 4326),
"letztePruefung" DATE,
"naechstePruefung" DATE,
"betreiber" VARCHAR(200),
"hinweis" VARCHAR(1000),
"fotoDateiId" UUID,
"quelle" VARCHAR(40),
"externeNummer" VARCHAR(80),
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
"geloeschtAm" TIMESTAMPTZ(3),
CONSTRAINT "hydrant_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "objekt" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"name" VARCHAR(200) NOT NULL,
"kategorie" "ObjektKategorie" NOT NULL DEFAULT 'SONSTIGES',
"strasse" VARCHAR(200),
"hausnummer" VARCHAR(20),
"plz" VARCHAR(10),
"ort" VARCHAR(120),
"breitengrad" DECIMAL(9,6),
"laengengrad" DECIMAL(9,6),
"koordinaten" geography(Point, 4326),
"zufahrt" VARCHAR(2000),
"gefahren" VARCHAR(2000),
"schluesseldepot" VARCHAR(500),
"loeschwasser" VARCHAR(1000),
"bemerkung" VARCHAR(2000),
"personenzahl" INTEGER,
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
"geloeschtAm" TIMESTAMPTZ(3),
CONSTRAINT "objekt_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "objektkontakt" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"objektId" UUID NOT NULL,
"name" VARCHAR(200) NOT NULL,
"funktion" VARCHAR(120),
"telefon" VARCHAR(60),
"mobil" VARCHAR(60),
"jederzeit" BOOLEAN NOT NULL DEFAULT false,
"bemerkung" VARCHAR(500),
"reihenfolge" INTEGER NOT NULL DEFAULT 0,
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "objektkontakt_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "objektplan" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"objektId" UUID NOT NULL,
"dateiId" UUID NOT NULL,
"art" "PlanArt" NOT NULL DEFAULT 'UEBERSICHTSPLAN',
"bezeichnung" VARCHAR(200) NOT NULL,
"standAm" DATE,
"reihenfolge" INTEGER NOT NULL DEFAULT 0,
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "objektplan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "lagekarte" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"einsatzId" UUID NOT NULL,
"breitengrad" DECIMAL(9,6),
"laengengrad" DECIMAL(9,6),
"zoomstufe" INTEGER,
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "lagekarte_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "lageelement" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"lagekarteId" UUID NOT NULL,
"art" "LageelementArt" NOT NULL DEFAULT 'ZEICHEN',
"zeichen" VARCHAR(60),
"text" VARCHAR(300),
"farbe" VARCHAR(20),
"geometrie" JSONB NOT NULL,
"drehung" INTEGER NOT NULL DEFAULT 0,
"erstelltVonId" UUID,
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
"geloeschtAm" TIMESTAMPTZ(3),
CONSTRAINT "lageelement_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "wasserfoerderung" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"einsatzId" UUID,
"bezeichnung" VARCHAR(200) NOT NULL,
"durchmesser" INTEGER NOT NULL DEFAULT 75,
"foerdermenge" INTEGER NOT NULL DEFAULT 800,
"ausgangsdruck" DECIMAL(4,1) NOT NULL DEFAULT 8,
"eingangsdruck" DECIMAL(4,1) NOT NULL DEFAULT 1.5,
"bemerkung" VARCHAR(2000),
"geplantVonId" UUID,
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"geaendertAm" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "wasserfoerderung_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "foerderpunkt" (
"id" UUID NOT NULL,
"organisationId" UUID NOT NULL,
"wasserfoerderungId" UUID NOT NULL,
"reihenfolge" INTEGER NOT NULL,
"art" "FoerderpunktArt" NOT NULL DEFAULT 'WEGPUNKT',
"bezeichnung" VARCHAR(200),
"breitengrad" DECIMAL(9,6) NOT NULL,
"laengengrad" DECIMAL(9,6) NOT NULL,
"hoehe" DECIMAL(7,2),
"bemerkung" VARCHAR(500),
"erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "foerderpunkt_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "hydrant_organisationId_status_idx" ON "hydrant"("organisationId", "status");
-- CreateIndex
CREATE INDEX "hydrant_organisationId_typ_idx" ON "hydrant"("organisationId", "typ");
-- CreateIndex
CREATE UNIQUE INDEX "hydrant_organisationId_externeNummer_key" ON "hydrant"("organisationId", "externeNummer");
-- CreateIndex
CREATE INDEX "objekt_organisationId_kategorie_idx" ON "objekt"("organisationId", "kategorie");
-- CreateIndex
CREATE INDEX "objekt_organisationId_name_idx" ON "objekt"("organisationId", "name");
-- CreateIndex
CREATE INDEX "objektkontakt_organisationId_objektId_idx" ON "objektkontakt"("organisationId", "objektId");
-- CreateIndex
CREATE INDEX "objektplan_organisationId_objektId_idx" ON "objektplan"("organisationId", "objektId");
-- CreateIndex
CREATE UNIQUE INDEX "lagekarte_einsatzId_key" ON "lagekarte"("einsatzId");
-- CreateIndex
CREATE INDEX "lageelement_organisationId_lagekarteId_geloeschtAm_idx" ON "lageelement"("organisationId", "lagekarteId", "geloeschtAm");
-- CreateIndex
CREATE INDEX "wasserfoerderung_organisationId_einsatzId_idx" ON "wasserfoerderung"("organisationId", "einsatzId");
-- CreateIndex
CREATE INDEX "foerderpunkt_organisationId_wasserfoerderungId_idx" ON "foerderpunkt"("organisationId", "wasserfoerderungId");
-- CreateIndex
CREATE UNIQUE INDEX "foerderpunkt_wasserfoerderungId_reihenfolge_key" ON "foerderpunkt"("wasserfoerderungId", "reihenfolge");
-- AddForeignKey
ALTER TABLE "hydrant" ADD CONSTRAINT "hydrant_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "hydrant" ADD CONSTRAINT "hydrant_fotoDateiId_fkey" FOREIGN KEY ("fotoDateiId") REFERENCES "datei"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "objekt" ADD CONSTRAINT "objekt_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "objektkontakt" ADD CONSTRAINT "objektkontakt_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "objektkontakt" ADD CONSTRAINT "objektkontakt_objektId_fkey" FOREIGN KEY ("objektId") REFERENCES "objekt"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "objektplan" ADD CONSTRAINT "objektplan_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "objektplan" ADD CONSTRAINT "objektplan_objektId_fkey" FOREIGN KEY ("objektId") REFERENCES "objekt"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "objektplan" ADD CONSTRAINT "objektplan_dateiId_fkey" FOREIGN KEY ("dateiId") REFERENCES "datei"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lagekarte" ADD CONSTRAINT "lagekarte_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lagekarte" ADD CONSTRAINT "lagekarte_einsatzId_fkey" FOREIGN KEY ("einsatzId") REFERENCES "einsatz"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lageelement" ADD CONSTRAINT "lageelement_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lageelement" ADD CONSTRAINT "lageelement_lagekarteId_fkey" FOREIGN KEY ("lagekarteId") REFERENCES "lagekarte"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "lageelement" ADD CONSTRAINT "lageelement_erstelltVonId_fkey" FOREIGN KEY ("erstelltVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "wasserfoerderung" ADD CONSTRAINT "wasserfoerderung_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "wasserfoerderung" ADD CONSTRAINT "wasserfoerderung_einsatzId_fkey" FOREIGN KEY ("einsatzId") REFERENCES "einsatz"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "wasserfoerderung" ADD CONSTRAINT "wasserfoerderung_geplantVonId_fkey" FOREIGN KEY ("geplantVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "foerderpunkt" ADD CONSTRAINT "foerderpunkt_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "foerderpunkt" ADD CONSTRAINT "foerderpunkt_wasserfoerderungId_fkey" FOREIGN KEY ("wasserfoerderungId") REFERENCES "wasserfoerderung"("id") ON DELETE CASCADE ON UPDATE CASCADE;