Files
Laufzettel/scripts/beispiel-lauf.ts
T
Florian WildeandClaude Opus 5 5c81a62cfd
Prüfung / pruefung (push) Canceled after 0s
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
feat(seed): sieben Vorlagen, Beispielbestand und Demo-Kunden
Die sieben Ablaeufe des Hauses liegen nach dem Seed als veroeffentlichte
Version 1 bereit: 36 Abschnitte, 155 Schritte, 28 Variablen, jeder
Schritt mit Hilfetext. Sie stehen als JSON im Austauschformat unter
prisma/seed-daten/vorlagen/ und werden beim Einrichten gegen dasselbe
Schema geprueft wie ein Import.

Der Seed richtet ein, er raeumt nicht auf: jede Zeile hat einen
sprechenden Schluessel, und was es schon gibt, bleibt unberuehrt. Erst
Entwurf, dann veroeffentlichen - die Trigger aus #010 lassen keinen
anderen Weg zu. Im Modus DIENSTLEISTUNG haengt der Beispielbestand an
der Musterfirma, LZ-7K3F9Q fuehrt also auf MUS-PC-001.

Dazu zwei Zusagen eingeloest, die niemand deckte: {{ou_gesperrt}} kommt
jetzt wirklich aus den Einstellungen, und Titel und Hilfetext eines
Laufs bekommen dasselbe generierte Passwort wie der Befehl daneben -
vorher stand im Befehl A und im Hilfetext B.

Closes #17

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

142 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env tsx
/**
* Beispiel-Laeufe fuer die Screenshots (#013).
*
* Eine Abarbeitungsansicht ohne Lauf zeigt nichts, und die echten Beispieldaten
* kommen erst mit #017. Bis dahin legt dieses Skript zwei Laeufe an: einen
* begonnenen mit Fortschritt, Notiz und Kommentar, und einen vorbereiteten im
* Zustand `OFFEN` — der Fall, den der Auftrag ausdruecklich verlangt (ein
* Kollege richtet ein, der Techniker scannt und arbeitet ab).
*
* Es geht durch die echten Dienste. Waeren die Regeln anders als hier
* angenommen, schlaegt es fehl, statt eine Lage herzustellen, die es in der
* Anwendung nie gibt.
*
* Idempotent.
*
* **Bleibt auch nach #017.** Der Kopf sagte frueher „Mit #017 wird das Skript
* geloescht“ — das war eine Verwechslung zweier Dinge. Der Seed richtet eine
* **Installation** ein: Vorlagen, Abteilungen, Personen, Geraete. Was
* Screenshots und Ende-zu-Ende-Abläufe brauchen, ist etwas anderes: ein offener
* Entwurf, ein halb abgearbeiteter Lauf, eine Person aus dem Verzeichnis. Das in
* den Seed zu legen hiesse, jeder frischen Installation Demo-Laeufe mitzugeben,
* die dort niemand bestellt hat. Die Zusammenfuehrung dieser Fixtures gehoert zu
* #018.
*
* pnpm beispiel:lauf
*/
import "dotenv/config";
import { SchrittZustand } from "../src/generated/prisma/enums.js";
import { prisma } from "../src/lib/db.js";
import { kommentarHinzufuegen } from "../src/server/services/kommentare.js";
import { laufAnlegen, laufZurBearbeitungOeffnen } from "../src/server/services/laeufe.js";
import { schrittergebnisSetzen } from "../src/server/services/schrittergebnisse.js";
const VORLAGE = "PC-Neuinstallation (Beispiel)";
const admin = await prisma.benutzer.findFirst({
where: { rolle: "ADMIN" },
orderBy: { erstelltAm: "asc" },
select: { id: true, rolle: true },
});
if (!admin) {
throw new Error("Es gibt keinen Administrator. Erst pnpm db:seed laufen lassen.");
}
const handelnder = { id: admin.id, rolle: admin.rolle };
const version = await prisma.vorlagenVersion.findFirst({
where: { status: "VEROEFFENTLICHT", vorlage: { name: VORLAGE } },
orderBy: { version: "desc" },
select: { id: true },
});
if (!version) {
throw new Error(`Es gibt keine veröffentlichte Version von „${VORLAGE}“. Erst pnpm beispiel:vorlage.`);
}
const geraet = await prisma.geraet.findFirst({ where: { hostname: "NB-001" }, select: { id: true } });
const person = await prisma.mitarbeiter.findFirst({ where: { sam: "mhuber" }, select: { id: true } });
if (!geraet || !person) {
throw new Error("NB-001 oder mhuber fehlen. Erst pnpm beispiel:stammdaten laufen lassen.");
}
const abteilung = await prisma.abteilung.findFirst({
where: { name: "Buchhaltung" },
select: { id: true },
});
/** Die Schritte des ersten Abschnitts, in ihrer Reihenfolge. */
const abschnitt = await prisma.vorlagenAbschnitt.findFirstOrThrow({
where: { versionId: version.id },
orderBy: { position: "asc" },
select: { schritte: { orderBy: { position: "asc" }, select: { id: true, titel: true } } },
});
const TITEL_BEGONNEN = "PC-Neuinstallation · NB-001 · Maria Huber";
const TITEL_OFFEN = "PC-Neuinstallation · PC-001 · vorbereitet";
const schonDa = await prisma.lauf.findFirst({ where: { titel: TITEL_BEGONNEN }, select: { id: true } });
if (schonDa) {
console.log("Die Beispiel-Läufe gibt es schon – nichts zu tun.");
} else {
const begonnen = await laufAnlegen(
{
vorlagenVersionId: version.id,
titel: TITEL_BEGONNEN,
geraetId: geraet.id,
mitarbeiterId: person.id,
abteilungId: abteilung?.id ?? null,
ticket: "TIK-4711",
variablen: { os_version: "Windows 11 Pro 24H2", join_typ: "Active Directory", arbeitsspeicher: 16 },
zugewiesenAnId: admin.id,
},
handelnder,
);
await laufZurBearbeitungOeffnen(begonnen.id, handelnder);
// Zwei erledigte Schritte und einer, der nicht zutrifft: so zeigt der
// Fortschrittsbalken einen echten Stand und die Begruendung steht am Schritt.
const [erster, zweiter, dritter] = abschnitt.schritte;
for (const schritt of [erster, zweiter]) {
if (!schritt) continue;
await schrittergebnisSetzen(
{ laufId: begonnen.id, vorlagenSchrittId: schritt.id, zustand: SchrittZustand.ERLEDIGT },
handelnder,
);
}
if (dritter) {
await schrittergebnisSetzen(
{
laufId: begonnen.id,
vorlagenSchrittId: dritter.id,
zustand: SchrittZustand.NICHT_ZUTREFFEND,
notiz: "Gerät ist neu ausgepackt – es gibt noch keine Altdaten.",
},
handelnder,
);
}
await kommentarHinzufuegen(
{
laufId: begonnen.id,
rumpf: "Rückfrage an die Buchhaltung: Steuer-Software wird erst nächste Woche gebraucht.",
},
handelnder,
);
const pc001 = await prisma.geraet.findFirst({ where: { hostname: "PC-001" }, select: { id: true } });
const morgen = new Date(Date.now() + 24 * 60 * 60 * 1000);
await laufAnlegen(
{
vorlagenVersionId: version.id,
titel: TITEL_OFFEN,
geraetId: pc001?.id ?? null,
variablen: { os_version: "Windows 11 Pro 24H2", join_typ: "Active Directory" },
ticket: "TIK-4712",
zugewiesenAnId: admin.id,
faelligAm: morgen,
},
handelnder,
);
console.log("Zwei Beispiel-Läufe angelegt: einer in Arbeit, einer vorbereitet.");
}
await prisma.$disconnect();