feat(bausteine): Abschnitte, die mehrere Vorlagen teilen (#57)
This commit was merged in pull request #57.
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
import { VersionStatus } from "@/generated/prisma/enums";
|
||||
import { prisma } from "@/lib/db";
|
||||
import type { SchrittTyp } from "@/generated/prisma/enums";
|
||||
|
||||
/**
|
||||
* Lesemodelle der Bausteine (#030).
|
||||
*
|
||||
* Ohne Mandantensicht, und das mit Absicht: ein Baustein gehoert keinem Kunden.
|
||||
* Er ist eine Quelle fuer Abschnitte, die in Vorlagen wandern — und Vorlagen
|
||||
* ohne `kundeId` gelten fuer alle. Das Recht dafuer ist `vorlagenBearbeiten`:
|
||||
* wer Vorlagen pflegt, pflegt Bausteine.
|
||||
*/
|
||||
|
||||
export type BausteinSchrittZeile = {
|
||||
readonly titel: string;
|
||||
readonly hilfetext: string | null;
|
||||
readonly typ: SchrittTyp;
|
||||
readonly pflicht: boolean;
|
||||
readonly konfig: unknown;
|
||||
readonly dokuUrl: string | null;
|
||||
};
|
||||
|
||||
export type BausteinZeile = {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly beschreibung: string | null;
|
||||
readonly archiviert: boolean;
|
||||
/** Die hoechste veroeffentlichte Version, oder `null`, wenn es noch keine gibt. */
|
||||
readonly veroeffentlichteVersion: number | null;
|
||||
readonly hatEntwurf: boolean;
|
||||
readonly anzahlSchritte: number;
|
||||
/** In wie vielen **veroeffentlichten** Vorlagenversionen er steckt. */
|
||||
readonly verwendungen: number;
|
||||
};
|
||||
|
||||
export async function bausteinListe(
|
||||
optionen: { readonly mitArchivierten?: boolean } = {},
|
||||
): Promise<readonly BausteinZeile[]> {
|
||||
const zeilen = await prisma.baustein.findMany({
|
||||
where: optionen.mitArchivierten ? {} : { archiviert: false },
|
||||
orderBy: [{ archiviert: "asc" }, { name: "asc" }],
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
beschreibung: true,
|
||||
archiviert: true,
|
||||
versionen: {
|
||||
orderBy: { version: "desc" },
|
||||
select: {
|
||||
version: true,
|
||||
status: true,
|
||||
_count: { select: { schritte: true, verwendungen: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return zeilen.map((zeile) => {
|
||||
const veroeffentlicht = zeile.versionen.find(
|
||||
(version) => version.status === VersionStatus.VEROEFFENTLICHT,
|
||||
);
|
||||
const entwurf = zeile.versionen.find((version) => version.status === VersionStatus.ENTWURF);
|
||||
return {
|
||||
id: zeile.id,
|
||||
name: zeile.name,
|
||||
beschreibung: zeile.beschreibung,
|
||||
archiviert: zeile.archiviert,
|
||||
veroeffentlichteVersion: veroeffentlicht?.version ?? null,
|
||||
hatEntwurf: entwurf !== undefined,
|
||||
// Die Schritte der Fassung, die zählt: der Entwurf, wenn es einen gibt,
|
||||
// sonst die veröffentlichte.
|
||||
anzahlSchritte: (entwurf ?? veroeffentlicht)?._count.schritte ?? 0,
|
||||
verwendungen: zeile.versionen.reduce((summe, version) => summe + version._count.verwendungen, 0),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export type BausteinAnsicht = {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly beschreibung: string | null;
|
||||
readonly archiviert: boolean;
|
||||
readonly versionen: readonly {
|
||||
readonly id: string;
|
||||
readonly version: number;
|
||||
readonly status: VersionStatus;
|
||||
readonly titel: string;
|
||||
readonly aenderungsnotiz: string | null;
|
||||
readonly veroeffentlichtAm: Date | null;
|
||||
readonly schritte: readonly BausteinSchrittZeile[];
|
||||
}[];
|
||||
};
|
||||
|
||||
export async function bausteinHolen(bausteinId: string): Promise<BausteinAnsicht | undefined> {
|
||||
const zeile = await prisma.baustein.findUnique({
|
||||
where: { id: bausteinId },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
beschreibung: true,
|
||||
archiviert: true,
|
||||
versionen: {
|
||||
orderBy: { version: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
version: true,
|
||||
status: true,
|
||||
titel: true,
|
||||
aenderungsnotiz: true,
|
||||
veroeffentlichtAm: true,
|
||||
schritte: {
|
||||
orderBy: { position: "asc" },
|
||||
select: {
|
||||
titel: true,
|
||||
hilfetext: true,
|
||||
typ: true,
|
||||
pflicht: true,
|
||||
konfig: true,
|
||||
dokuUrl: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return zeile ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wo steckt dieser Baustein (#030)?
|
||||
*
|
||||
* Vor dem Aendern will man das wissen. Genannt wird die Vorlage, die Version und
|
||||
* **welche Fassung** des Bausteins dort eingesetzt ist — eine Vorlage von vor
|
||||
* zwei Jahren traegt eine andere als die von letzter Woche.
|
||||
*/
|
||||
export async function bausteinVerwendungen(bausteinId: string): Promise<
|
||||
readonly {
|
||||
readonly vorlageId: string;
|
||||
readonly vorlageName: string;
|
||||
readonly vorlagenVersion: number;
|
||||
readonly status: VersionStatus;
|
||||
readonly bausteinVersion: number;
|
||||
readonly archiviert: boolean;
|
||||
}[]
|
||||
> {
|
||||
const abschnitte = await prisma.vorlagenAbschnitt.findMany({
|
||||
where: { bausteinVersion: { bausteinId } },
|
||||
select: {
|
||||
bausteinVersion: { select: { version: true } },
|
||||
version: {
|
||||
select: {
|
||||
version: true,
|
||||
status: true,
|
||||
vorlage: { select: { id: true, name: true, archiviert: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return abschnitte
|
||||
.flatMap((abschnitt) =>
|
||||
abschnitt.bausteinVersion === null
|
||||
? []
|
||||
: [
|
||||
{
|
||||
vorlageId: abschnitt.version.vorlage.id,
|
||||
vorlageName: abschnitt.version.vorlage.name,
|
||||
vorlagenVersion: abschnitt.version.version,
|
||||
status: abschnitt.version.status,
|
||||
bausteinVersion: abschnitt.bausteinVersion.version,
|
||||
archiviert: abschnitt.version.vorlage.archiviert,
|
||||
},
|
||||
],
|
||||
)
|
||||
.sort(
|
||||
(links, rechts) =>
|
||||
links.vorlageName.localeCompare(rechts.vorlageName, "de") ||
|
||||
rechts.vorlagenVersion - links.vorlagenVersion,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Bausteine, die sich in einen Entwurf einsetzen lassen.
|
||||
*
|
||||
* Nur **veroeffentlichte** Fassungen nicht archivierter Bausteine, je Baustein
|
||||
* die hoechste. Einen Entwurf anzubieten hiesse, beim Veroeffentlichen etwas zu
|
||||
* kopieren, das sich danach noch aendert — ein Trigger weist das ohnehin ab.
|
||||
*/
|
||||
export async function einsetzbareBausteine(): Promise<
|
||||
readonly {
|
||||
readonly bausteinVersionId: string;
|
||||
readonly name: string;
|
||||
readonly titel: string;
|
||||
readonly version: number;
|
||||
readonly anzahlSchritte: number;
|
||||
}[]
|
||||
> {
|
||||
const zeilen = await prisma.baustein.findMany({
|
||||
where: { archiviert: false, versionen: { some: { status: VersionStatus.VEROEFFENTLICHT } } },
|
||||
orderBy: { name: "asc" },
|
||||
select: {
|
||||
name: true,
|
||||
versionen: {
|
||||
where: { status: VersionStatus.VEROEFFENTLICHT },
|
||||
orderBy: { version: "desc" },
|
||||
take: 1,
|
||||
select: { id: true, version: true, titel: true, _count: { select: { schritte: true } } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return zeilen.flatMap((zeile) => {
|
||||
const version = zeile.versionen[0];
|
||||
return version === undefined
|
||||
? []
|
||||
: [
|
||||
{
|
||||
bausteinVersionId: version.id,
|
||||
name: zeile.name,
|
||||
titel: version.titel,
|
||||
version: version.version,
|
||||
anzahlSchritte: version._count.schritte,
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user