feat(bausteine): Abschnitte, die mehrere Vorlagen teilen (#57)
This commit was merged in pull request #57.
This commit is contained in:
@@ -364,6 +364,89 @@ export function versionMussEntwurfSein(version: {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt die Schritte verwiesener Bausteine in den Entwurf ein (#030).
|
||||
*
|
||||
* Der Kern der Entscheidung aus dem Issue: ein Entwurf **verweist**, eine
|
||||
* veroeffentlichte Version traegt **Kopien**. Danach sieht ein Lauf weiterhin
|
||||
* konkrete Schritte, und `Schrittergebnis`, Druckansicht und Archiv muessen
|
||||
* nichts von Bausteinen wissen. Eine spaetere Baustein-Fassung kann diese
|
||||
* Version nicht mehr anfassen — genau das soll die Unveraenderlichkeit leisten.
|
||||
*
|
||||
* Der Verweis bleibt stehen. In einer veroeffentlichten Version ist er nur noch
|
||||
* Herkunft, und die will man beim Lesen sehen.
|
||||
*
|
||||
* Gibt zurueck, was eingesetzt wurde — fuer den Protokolleintrag.
|
||||
*/
|
||||
async function bausteineAufloesen(
|
||||
tx: Prisma.TransactionClient,
|
||||
versionId: string,
|
||||
): Promise<readonly { readonly name: string; readonly version: number }[]> {
|
||||
const abschnitte = await tx.vorlagenAbschnitt.findMany({
|
||||
where: { versionId, bausteinVersionId: { not: null } },
|
||||
orderBy: { position: "asc" },
|
||||
select: {
|
||||
id: true,
|
||||
titel: true,
|
||||
bausteinVersion: {
|
||||
select: {
|
||||
version: true,
|
||||
titel: true,
|
||||
baustein: { select: { name: true, archiviert: true } },
|
||||
schritte: { orderBy: { position: "asc" } },
|
||||
},
|
||||
},
|
||||
_count: { select: { schritte: true } },
|
||||
},
|
||||
});
|
||||
|
||||
const eingesetzt: { name: string; version: number }[] = [];
|
||||
for (const abschnitt of abschnitte) {
|
||||
const quelle = abschnitt.bausteinVersion;
|
||||
if (!quelle) continue;
|
||||
if (quelle.baustein.archiviert) {
|
||||
throw new VorlagenFehler(
|
||||
`Der Baustein „${quelle.baustein.name}“ ist archiviert. Erst wieder freigeben, ` +
|
||||
"dann veröffentlichen — oder den Abschnitt aus dem Entwurf nehmen.",
|
||||
);
|
||||
}
|
||||
if (quelle.schritte.length === 0) {
|
||||
throw new VorlagenFehler(
|
||||
`Der Baustein „${quelle.baustein.name}“ hat keine Schritte. Ein Abschnitt daraus ` +
|
||||
"wäre leer, und eine Vorlage mit leerem Abschnitt lässt sich nicht abarbeiten.",
|
||||
);
|
||||
}
|
||||
// Eigene Schritte hat ein Baustein-Abschnitt nicht; falls doch etwas liegen
|
||||
// geblieben ist, weg damit, sonst stuenden die Schritte doppelt.
|
||||
if (abschnitt._count.schritte > 0) {
|
||||
await tx.vorlagenSchritt.deleteMany({ where: { abschnittId: abschnitt.id } });
|
||||
}
|
||||
await tx.vorlagenSchritt.createMany({
|
||||
data: quelle.schritte.map((schritt, index) => ({
|
||||
abschnittId: abschnitt.id,
|
||||
titel: schritt.titel,
|
||||
hilfetext: schritt.hilfetext,
|
||||
typ: schritt.typ,
|
||||
pflicht: schritt.pflicht,
|
||||
konfig: schritt.konfig as Prisma.InputJsonValue,
|
||||
dokuUrl: schritt.dokuUrl,
|
||||
position: index,
|
||||
})),
|
||||
});
|
||||
// Der Titel kommt aus dem Baustein: er gehoert zum Abschnitt, den der
|
||||
// Baustein beschreibt, und soll nicht in jeder Vorlage einzeln gepflegt
|
||||
// werden.
|
||||
if (abschnitt.titel !== quelle.titel) {
|
||||
await tx.vorlagenAbschnitt.update({
|
||||
where: { id: abschnitt.id },
|
||||
data: { titel: quelle.titel },
|
||||
});
|
||||
}
|
||||
eingesetzt.push({ name: quelle.baustein.name, version: quelle.version });
|
||||
}
|
||||
return eingesetzt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ersetzt den Aufbau eines Entwurfs vollstaendig.
|
||||
*
|
||||
@@ -443,7 +526,14 @@ export async function entwurfSpeichern(
|
||||
|
||||
for (const [index, abschnitt] of struktur.abschnitte.entries()) {
|
||||
const angelegt = await tx.vorlagenAbschnitt.create({
|
||||
data: { versionId, titel: abschnitt.titel, position: index },
|
||||
data: {
|
||||
versionId,
|
||||
titel: abschnitt.titel,
|
||||
position: index,
|
||||
// Ein Trigger laesst nur veroeffentlichte Baustein-Versionen zu; der
|
||||
// Dienst prueft dasselbe mit besserer Meldung (#030).
|
||||
bausteinVersionId: abschnitt.bausteinVersionId ?? null,
|
||||
},
|
||||
});
|
||||
if (abschnitt.schritte.length > 0) {
|
||||
await tx.vorlagenSchritt.createMany({
|
||||
@@ -509,6 +599,11 @@ export async function versionVeroeffentlichen(
|
||||
);
|
||||
}
|
||||
|
||||
// **Vor** dem Umschalten: Bausteine in echte Schritte aufloesen. Danach
|
||||
// greifen die Trigger auf veroeffentlichten Versionen, und es waere zu spaet
|
||||
// (#030).
|
||||
const eingesetzt = await bausteineAufloesen(tx, versionId);
|
||||
|
||||
// Bedingtes Update: trifft es keine Zeile, hat jemand anderes in derselben
|
||||
// Sekunde veroeffentlicht. Ohne diese Bedingung entstuenden zwei
|
||||
// Protokolleintraege fuer denselben Vorgang.
|
||||
@@ -537,6 +632,10 @@ export async function versionVeroeffentlichen(
|
||||
status: VersionStatus.VEROEFFENTLICHT,
|
||||
version: zeile.version,
|
||||
aenderungsnotiz: notiz,
|
||||
// Welche Bausteine in dieser Version steckengeblieben sind, in welcher
|
||||
// Fassung. Spaeter die einzige Stelle, an der das noch nachlesbar ist:
|
||||
// der Verweis am Abschnitt nennt die Kennung, nicht den Namen.
|
||||
...(eingesetzt.length > 0 ? { bausteine: eingesetzt } : {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user