feat(bausteine): Abschnitte, die mehrere Vorlagen teilen
"Datensicherung pruefen" steht in vier der sieben mitgelieferten Vorlagen, wortgleich. Wer den Ablauf aendert, aendert ihn viermal -- oder dreimal und vergisst den vierten. Entschieden wie im Issue vorgeschlagen: ein Entwurf **verweist** auf eine veroeffentlichte Baustein-Fassung, und beim Veroeffentlichen der Vorlage setzt der Dienst deren Schritte als tiefe Kopie ein. Danach sieht ein Lauf weiterhin konkrete Schritte, und Schrittergebnis, Druckansicht und Archiv muessen nichts von Bausteinen wissen. Die Alternative -- Laeufe verweisen zur Laufzeit -- haette die Unveraenderlichkeit an einer zweiten Stelle durchsetzen muessen. Eine neue Fassung aendert damit keine veroeffentlichte Vorlagenversion und keinen laufenden Lauf. Sie wirkt in der naechsten veroeffentlichten Version; die Oberflaeche sagt das an zwei Stellen, weil man sonst Durchschlag erwartet. Vier Garantien liegen in der Datenbank, nicht nur im Dienst: hoechstens ein Entwurf je Baustein, eine veroeffentlichte Fassung ist unveraenderlich, der Weg zurueck zum Entwurf ist versperrt, und ein Abschnitt kann nur auf eine veroeffentlichte Fassung verweisen. Der Titel kommt aus dem Baustein, nicht aus dem Abschnitt der Vorlage: sonst muesste er in jeder Vorlage einzeln gepflegt werden, und zwei Vorlagen zeigten denselben Baustein unter verschiedenen Namen. Ein Export traegt die aufgeloesten Schritte, keinen Verweis -- eine Kennung aus dieser Installation bedeutet in einer anderen nichts. Das Durchklicken des gebauten Stands fand einen Fehler, den die Suite nicht gesehen haette: die Platzhalter-Schritte eines Baustein-Abschnitts haben keinen Titel, wurden aber mitgeschickt, und SchrittSchema wies den ganzen Entwurf ab. Behoben, mit Test und Gegenprobe. Der ganze Weg danach am gebauten Stand nachgemessen: Baustein anlegen, veroeffentlichen, einsetzen, Vorlage veroeffentlichen -- die Schritte stehen drin, mit Herkunft und Protokolleintrag. Closes #50
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- HANDGESCHRIEBEN. Prisma erzeugt hier zusaetzlich Loeschbefehle fuer die
|
||||
-- zusammengesetzten Fremdschluessel (Mandantengrenze), die Trigramm-Indizes
|
||||
-- (Archivsuche), die partiellen eindeutigen Indizes und die Trigger auf
|
||||
-- veroeffentlichten Versionen: es kennt sie nicht, weil sie sich im Schema nicht
|
||||
-- ausdruecken lassen. Diese Loeschbefehle sind bewusst gestrichen.
|
||||
--
|
||||
-- tests/datenbankregeln.test.ts prueft, dass alle diese Objekte vorhanden sind –
|
||||
-- ein versehentlich stehengelassener Loeschbefehl faellt dort sofort auf.
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Wiederverwendbare Bausteine (#030).
|
||||
|
||||
CREATE TABLE "Baustein" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"beschreibung" TEXT,
|
||||
"archiviert" BOOLEAN NOT NULL DEFAULT false,
|
||||
"erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"geaendertAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "Baustein_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "Baustein_name_key" ON "Baustein"("name");
|
||||
CREATE INDEX "Baustein_archiviert_idx" ON "Baustein"("archiviert");
|
||||
|
||||
CREATE TABLE "BausteinVersion" (
|
||||
"id" TEXT NOT NULL,
|
||||
"bausteinId" TEXT NOT NULL,
|
||||
"version" INTEGER NOT NULL,
|
||||
"status" "VersionStatus" NOT NULL DEFAULT 'ENTWURF',
|
||||
"titel" TEXT NOT NULL,
|
||||
"aenderungsnotiz" TEXT,
|
||||
"veroeffentlichtAm" TIMESTAMP(3),
|
||||
"erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"geaendertAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "BausteinVersion_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "BausteinVersion_bausteinId_version_key"
|
||||
ON "BausteinVersion"("bausteinId", "version");
|
||||
CREATE INDEX "BausteinVersion_bausteinId_status_idx"
|
||||
ON "BausteinVersion"("bausteinId", "status");
|
||||
|
||||
CREATE TABLE "BausteinSchritt" (
|
||||
"id" TEXT NOT NULL,
|
||||
"versionId" TEXT NOT NULL,
|
||||
"position" INTEGER NOT NULL DEFAULT 0,
|
||||
"titel" TEXT NOT NULL,
|
||||
"hilfetext" TEXT,
|
||||
"typ" "SchrittTyp" NOT NULL DEFAULT 'HAKEN',
|
||||
"pflicht" BOOLEAN NOT NULL DEFAULT false,
|
||||
"konfig" JSONB NOT NULL DEFAULT '{}',
|
||||
"dokuUrl" TEXT,
|
||||
CONSTRAINT "BausteinSchritt_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX "BausteinSchritt_versionId_position_idx"
|
||||
ON "BausteinSchritt"("versionId", "position");
|
||||
|
||||
ALTER TABLE "BausteinVersion"
|
||||
ADD CONSTRAINT "BausteinVersion_bausteinId_fkey"
|
||||
FOREIGN KEY ("bausteinId") REFERENCES "Baustein"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "BausteinSchritt"
|
||||
ADD CONSTRAINT "BausteinSchritt_versionId_fkey"
|
||||
FOREIGN KEY ("versionId") REFERENCES "BausteinVersion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- Der Verweis am Abschnitt. RESTRICT, nicht CASCADE: eine Baustein-Version, auf
|
||||
-- die eine Vorlage zeigt, darf nicht unter ihr wegbrechen.
|
||||
ALTER TABLE "VorlagenAbschnitt"
|
||||
ADD COLUMN "bausteinVersionId" TEXT;
|
||||
|
||||
CREATE INDEX "VorlagenAbschnitt_bausteinVersionId_idx"
|
||||
ON "VorlagenAbschnitt"("bausteinVersionId");
|
||||
|
||||
ALTER TABLE "VorlagenAbschnitt"
|
||||
ADD CONSTRAINT "VorlagenAbschnitt_bausteinVersionId_fkey"
|
||||
FOREIGN KEY ("bausteinVersionId") REFERENCES "BausteinVersion"("id")
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Dieselben Garantien wie bei Vorlagen, und zwar in der Datenbank.
|
||||
--
|
||||
-- Ein Baustein ist eine Quelle fuer Schritte, die in Laeufe wandern. Stuende
|
||||
-- seine Unveraenderlichkeit nur im Dienst, liesse sich die Nachvollziehbarkeit
|
||||
-- des Archivs ueber eine Baustein-Aenderung aushebeln.
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Je Baustein hoechstens ein Entwurf.
|
||||
CREATE UNIQUE INDEX "BausteinVersion_ein_entwurf"
|
||||
ON "BausteinVersion" ("bausteinId")
|
||||
WHERE "status" = 'ENTWURF';
|
||||
|
||||
-- Eine veroeffentlichte Baustein-Version ist unveraenderlich.
|
||||
CREATE OR REPLACE FUNCTION laufzettel_baustein_unveraenderlich() RETURNS trigger AS $$
|
||||
DECLARE
|
||||
betroffene_version TEXT;
|
||||
aktueller_status TEXT;
|
||||
BEGIN
|
||||
betroffene_version := COALESCE(NEW."versionId", OLD."versionId");
|
||||
SELECT status::TEXT INTO aktueller_status
|
||||
FROM "BausteinVersion" WHERE id = betroffene_version;
|
||||
|
||||
IF aktueller_status = 'VEROEFFENTLICHT' THEN
|
||||
RAISE EXCEPTION
|
||||
'Baustein-Version % ist veroeffentlicht und unveraenderlich (%).',
|
||||
betroffene_version, TG_TABLE_NAME
|
||||
USING ERRCODE = 'integrity_constraint_violation';
|
||||
END IF;
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER "BausteinSchritt_unveraenderlich"
|
||||
BEFORE INSERT OR UPDATE OR DELETE ON "BausteinSchritt"
|
||||
FOR EACH ROW EXECUTE FUNCTION laufzettel_baustein_unveraenderlich();
|
||||
|
||||
-- Der Weg zurueck waere die Hintertuer, durch die sich das Obige aushebeln
|
||||
-- liesse. Dieselbe Regel wie bei VorlagenVersion.
|
||||
CREATE OR REPLACE FUNCTION laufzettel_baustein_status_einbahn() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF OLD.status = 'VEROEFFENTLICHT' AND NEW.status <> 'VEROEFFENTLICHT' THEN
|
||||
RAISE EXCEPTION
|
||||
'Eine veroeffentlichte Baustein-Version laesst sich nicht wieder zum Entwurf machen.'
|
||||
USING ERRCODE = 'integrity_constraint_violation';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER "BausteinVersion_status_einbahn"
|
||||
BEFORE UPDATE ON "BausteinVersion"
|
||||
FOR EACH ROW EXECUTE FUNCTION laufzettel_baustein_status_einbahn();
|
||||
|
||||
-- Ein Verweis zeigt nur auf eine **veroeffentlichte** Baustein-Version. Einen
|
||||
-- Entwurf einzusetzen hiesse, beim Veroeffentlichen etwas zu kopieren, das sich
|
||||
-- danach noch aendert.
|
||||
CREATE OR REPLACE FUNCTION laufzettel_baustein_verweis_veroeffentlicht() RETURNS trigger AS $$
|
||||
DECLARE
|
||||
verwiesener_status TEXT;
|
||||
BEGIN
|
||||
IF NEW."bausteinVersionId" IS NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
SELECT status::TEXT INTO verwiesener_status
|
||||
FROM "BausteinVersion" WHERE id = NEW."bausteinVersionId";
|
||||
|
||||
IF verwiesener_status <> 'VEROEFFENTLICHT' THEN
|
||||
RAISE EXCEPTION
|
||||
'Ein Abschnitt kann nur auf eine veroeffentlichte Baustein-Version verweisen.'
|
||||
USING ERRCODE = 'integrity_constraint_violation';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER "VorlagenAbschnitt_baustein_veroeffentlicht"
|
||||
BEFORE INSERT OR UPDATE ON "VorlagenAbschnitt"
|
||||
FOR EACH ROW EXECUTE FUNCTION laufzettel_baustein_verweis_veroeffentlicht();
|
||||
Reference in New Issue
Block a user