-- Die Personalakte, wie die Abteilung sie wirklich führt. -- -- Drei Änderungen: die Dienstnummer entfällt (es gibt sie nicht), der Status -- wird zu mehreren Etiketten statt eines Wertes, und AfiS-Mitglieder lassen -- sich an eine Person hängen. -- =========================================================================== -- 1. Dienstnummer entfällt -- -- Sie steckt im Suchvektor und in einem Trigram-Index, beide müssen vor -- dem Löschen der Spalte weg. Der Trigger wird danach ohne sie neu gesetzt. -- =========================================================================== DROP TRIGGER IF EXISTS personnel_search_trg ON "PersonnelFile"; DROP INDEX IF EXISTS "PersonnelFile_serviceNumber_key"; ALTER TABLE "PersonnelFile" DROP COLUMN "serviceNumber"; -- =========================================================================== -- 2. Status als Etiketten -- -- "Gekündigt" und "Verhaftet" schließen einander nicht aus, ein einzelnes -- Feld zwingt aber zur Wahl. Der bisherige Wert wandert als Freitext in die -- Anmerkung, damit nichts verloren geht: er lässt sich nicht verlässlich -- auf die neuen Schlüssel abbilden. -- =========================================================================== ALTER TABLE "PersonnelFile" ADD COLUMN "statusTags" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[]; UPDATE "PersonnelFile" SET "note" = COALESCE(NULLIF("note", '') || E'\n', '') || 'Früherer Status: ' || "status" WHERE "status" IS NOT NULL AND "status" <> ''; ALTER TABLE "PersonnelFile" DROP COLUMN "status"; CREATE INDEX "PersonnelFile_statusTags_idx" ON "PersonnelFile" USING GIN ("statusTags"); CREATE TABLE "PersonnelStatusTag" ( "key" TEXT NOT NULL, "name" TEXT NOT NULL, "colorToken" TEXT NOT NULL DEFAULT 'stamp-0', "sortOrder" INTEGER NOT NULL DEFAULT 0, "isActive" BOOLEAN NOT NULL DEFAULT true, CONSTRAINT "PersonnelStatusTag_pkey" PRIMARY KEY ("key") ); CREATE INDEX "PersonnelStatusTag_isActive_sortOrder_idx" ON "PersonnelStatusTag"("isActive", "sortOrder"); -- =========================================================================== -- 3. Suchvektor ohne Dienstnummer, dafür mit den Etiketten -- =========================================================================== CREATE OR REPLACE FUNCTION personnel_refresh_search_vector() RETURNS trigger AS $$ BEGIN NEW."searchVector" := setweight(to_tsvector('german', coalesce(NEW."displayName", '')), 'A') || setweight(to_tsvector('german', coalesce(NEW."rank", '') || ' ' || coalesce(NEW."department", '') || ' ' || coalesce(array_to_string(NEW."statusTags", ' '), '')), 'B'); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER personnel_search_trg BEFORE INSERT OR UPDATE OF "displayName", "rank", "department", "statusTags" ON "PersonnelFile" FOR EACH ROW EXECUTE FUNCTION personnel_refresh_search_vector(); -- Bestehende Zeilen einmal nachziehen, sonst tragen sie den alten Vektor. UPDATE "PersonnelFile" SET "displayName" = "displayName"; -- =========================================================================== -- 4. AfiS-Mitglieder an einer Personalakte -- -- Wie CaseParticipant bei der Fallakte zugleich der Weg zum ausnahmsweisen -- Zugang: wer hier steht, liest die Akte unabhängig von seiner Stufe. -- =========================================================================== CREATE TYPE "AssignmentRole" AS ENUM ('HANDLER', 'INVESTIGATOR', 'CONTACT'); CREATE TABLE "PersonnelAssignment" ( "id" TEXT NOT NULL, "personnelFileId" TEXT NOT NULL, "userId" TEXT NOT NULL, "role" "AssignmentRole" NOT NULL, "addedById" TEXT, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "PersonnelAssignment_pkey" PRIMARY KEY ("id") ); CREATE UNIQUE INDEX "PersonnelAssignment_personnelFileId_userId_role_key" ON "PersonnelAssignment"("personnelFileId", "userId", "role"); CREATE INDEX "PersonnelAssignment_userId_idx" ON "PersonnelAssignment"("userId"); ALTER TABLE "PersonnelAssignment" ADD CONSTRAINT "PersonnelAssignment_personnelFileId_fkey" FOREIGN KEY ("personnelFileId") REFERENCES "PersonnelFile"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "PersonnelAssignment" ADD CONSTRAINT "PersonnelAssignment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- =========================================================================== -- 5. Freigabestufen der Foundation -- -- Stehen im Kopf eines Dokuments und steuern **keinen** Zugriff. Getrennte -- Tabelle, weil sie im Adminbereich pflegbar sein sollen wie die internen. -- =========================================================================== CREATE TABLE "FoundationClearance" ( "level" INTEGER NOT NULL, "name" TEXT NOT NULL, "colorHex" TEXT NOT NULL, "sortOrder" INTEGER NOT NULL DEFAULT 0, CONSTRAINT "FoundationClearance_pkey" PRIMARY KEY ("level") ); -- =========================================================================== -- 6. Kopfzeilen des Aktenblatts an der Vorlage -- =========================================================================== ALTER TYPE "DocumentLayout" ADD VALUE 'DOSSIER'; ALTER TABLE "DocumentTemplate" ADD COLUMN "sheetTitle" TEXT, ADD COLUMN "sheetSubtitle" TEXT;