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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ae290345b7
commit
5c81a62cfd
@@ -0,0 +1,189 @@
|
||||
import { geraetSaeen, stammdatenSaeen, type StammdatenErgebnis } from "./stammdaten.js";
|
||||
import { GeraeteTyp, Herkunft } from "../../src/generated/prisma/enums.js";
|
||||
import type { PrismaClient } from "../../src/generated/prisma/client.js";
|
||||
|
||||
/**
|
||||
* Die beiden Demo-Kunden fuer `SEED_MODE=DIENSTLEISTUNG` (#017).
|
||||
*
|
||||
* Im Modus DIENSTLEISTUNG betreut das Haus fremde Betriebe. Der Seed legt zwei
|
||||
* an, damit die Kundenwahl, das Kuerzel auf dem Etikett und die Hostname-Regel
|
||||
* `{kundenkuerzel}-PC-{nnn}` ueberhaupt etwas zu zeigen haben — bei einem Kunden
|
||||
* saehe jede Auswahlliste gleich aus, und ein Mandantenfehler fiele nicht auf.
|
||||
*
|
||||
* **„Stattdessen“, nicht „zusaetzlich“.** Der Beispielbestand haengt in diesem
|
||||
* Modus an der Musterfirma und nicht an der eigenen Organisation: der Auftrag
|
||||
* verlangt, dass `LZ-7K3F9Q` hier auf `MUS-PC-001` fuehrt. Lag derselbe
|
||||
* Aufkleber an der eigenen Organisation, liefe der Hauptablauf im Modus
|
||||
* DIENSTLEISTUNG gegen die falsche Zeile (Reviewbefund zu #017).
|
||||
*
|
||||
* **Kein Verzeichnis bei Kunden.** An fremde Active Directories bindet sich diese
|
||||
* Anwendung ausdruecklich nicht (#008); im Modus DIENSTLEISTUNG gibt es das
|
||||
* Verzeichnis-Modul ueberhaupt nicht.
|
||||
*/
|
||||
|
||||
export type DemoKunde = {
|
||||
readonly name: string;
|
||||
readonly kuerzel: string;
|
||||
/**
|
||||
* Der volle Beispielbestand (drei Abteilungen, fuenf Personen, die beiden
|
||||
* Beispielgeraete mit ihren Tag-Codes, fuenf vorgedruckte Tags) — oder der
|
||||
* kleine Satz aus einer Abteilung, einer Person und einem Geraet.
|
||||
*/
|
||||
readonly vollbestand: boolean;
|
||||
readonly abteilung?: string;
|
||||
readonly person?: {
|
||||
readonly vorname: string;
|
||||
readonly nachname: string;
|
||||
readonly sam: string;
|
||||
readonly upn: string;
|
||||
readonly titel: string;
|
||||
};
|
||||
/**
|
||||
* Eigener Tag-Code: ein `tagCode` ist global eindeutig und wird nie
|
||||
* wiederverwendet. Zwei Kunden koennen sich denselben Aufkleber also nicht
|
||||
* teilen, auch wenn bei beiden ein Geraet „PC-001“ steht.
|
||||
*/
|
||||
readonly pcTag?: string;
|
||||
};
|
||||
|
||||
export const DEMO_KUNDEN: readonly DemoKunde[] = [
|
||||
{ name: "Musterfirma GmbH", kuerzel: "MUS", vollbestand: true },
|
||||
{
|
||||
name: "Bäckerei Reiter",
|
||||
kuerzel: "REI",
|
||||
vollbestand: false,
|
||||
abteilung: "Verkauf",
|
||||
person: {
|
||||
vorname: "Josef",
|
||||
nachname: "Reiter",
|
||||
sam: "jreiter",
|
||||
upn: "j.reiter@baeckerei-reiter.example",
|
||||
titel: "Inhaber",
|
||||
},
|
||||
pcTag: "LZ-B2NJ5R",
|
||||
},
|
||||
];
|
||||
|
||||
export type DemoErgebnis = {
|
||||
readonly kunde: string;
|
||||
readonly kuerzel: string;
|
||||
readonly neu: boolean;
|
||||
readonly hostname: string;
|
||||
readonly bestand: StammdatenErgebnis | null;
|
||||
};
|
||||
|
||||
export async function demoKundenSaeen(db: PrismaClient): Promise<readonly DemoErgebnis[]> {
|
||||
const ergebnisse: DemoErgebnis[] = [];
|
||||
|
||||
for (const vorgabe of DEMO_KUNDEN) {
|
||||
/*
|
||||
* Gesucht wird ueber Kuerzel **und** Name, und nur unter fremden Kunden.
|
||||
* Vorher genuegte das Kuerzel: ein echter Kunde „Muster und Soehne KG“ mit
|
||||
* dem Kuerzel MUS bekam dann eine erfundene Person, eine erfundene Ziel-OU
|
||||
* und ein Geraet — und die Ausgabe sagte „vorhanden“, es fiel also niemandem
|
||||
* auf (Reviewbefund zu #017). Einen Kunden, den der Seed nicht angelegt hat,
|
||||
* fasst er nicht an.
|
||||
*/
|
||||
const vorhanden = await db.kunde.findFirst({
|
||||
where: { kuerzel: vorgabe.kuerzel, name: vorgabe.name, istEigeneOrganisation: false },
|
||||
});
|
||||
if (!vorhanden) {
|
||||
const fremder = await db.kunde.findFirst({ where: { kuerzel: vorgabe.kuerzel } });
|
||||
if (fremder) {
|
||||
console.log(
|
||||
` Kunde ${vorgabe.kuerzel}: „${fremder.name}“ trägt dieses Kürzel bereits — ` +
|
||||
"der Demo-Kunde wird nicht angelegt",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const kunde =
|
||||
vorhanden ??
|
||||
(await db.kunde.create({
|
||||
data: { name: vorgabe.name, kuerzel: vorgabe.kuerzel, istEigeneOrganisation: false },
|
||||
}));
|
||||
|
||||
if (vorgabe.vollbestand) {
|
||||
const bestand = await stammdatenSaeen(db, kunde.id, { hostnamePraefix: vorgabe.kuerzel });
|
||||
ergebnisse.push({
|
||||
kunde: kunde.name,
|
||||
kuerzel: vorgabe.kuerzel,
|
||||
neu: vorhanden === null,
|
||||
hostname: `${vorgabe.kuerzel}-PC-001`,
|
||||
bestand,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
ergebnisse.push({
|
||||
kunde: kunde.name,
|
||||
kuerzel: vorgabe.kuerzel,
|
||||
neu: vorhanden === null,
|
||||
hostname: `${vorgabe.kuerzel}-PC-001`,
|
||||
bestand: await kleinerBestand(db, kunde.id, vorgabe),
|
||||
});
|
||||
}
|
||||
|
||||
return ergebnisse;
|
||||
}
|
||||
|
||||
/** Eine Abteilung, eine Person, ein Geraet — genug, damit der Kunde sichtbar ist. */
|
||||
async function kleinerBestand(
|
||||
db: PrismaClient,
|
||||
kundeId: string,
|
||||
vorgabe: DemoKunde,
|
||||
): Promise<StammdatenErgebnis> {
|
||||
const { abteilung, person, pcTag, kuerzel } = vorgabe;
|
||||
if (!abteilung || !person || !pcTag) {
|
||||
throw new Error(`Dem Demo-Kunden ${kuerzel} fehlen Abteilung, Person oder Tag-Code.`);
|
||||
}
|
||||
|
||||
let abteilungen = 0;
|
||||
const vorhandeneAbteilung = await db.abteilung.findUnique({
|
||||
where: { kundeId_name: { kundeId, name: abteilung } },
|
||||
});
|
||||
if (!vorhandeneAbteilung) {
|
||||
await db.abteilung.create({
|
||||
data: {
|
||||
kundeId,
|
||||
name: abteilung,
|
||||
zielOu: `OU=${abteilung},DC=${kuerzel.toLowerCase()},DC=local`,
|
||||
standardGruppen: [`G-${abteilung}`],
|
||||
},
|
||||
});
|
||||
abteilungen = 1;
|
||||
}
|
||||
|
||||
let personen = 0;
|
||||
let zeile = await db.mitarbeiter.findFirst({ where: { kundeId, upn: person.upn } });
|
||||
if (!zeile) {
|
||||
zeile = await db.mitarbeiter.create({
|
||||
data: {
|
||||
kundeId,
|
||||
vorname: person.vorname,
|
||||
nachname: person.nachname,
|
||||
anzeigename: `${person.nachname}, ${person.vorname}`,
|
||||
mail: person.upn,
|
||||
upn: person.upn,
|
||||
sam: person.sam,
|
||||
abteilung,
|
||||
titel: person.titel,
|
||||
herkunft: Herkunft.MANUELL,
|
||||
aktiv: true,
|
||||
},
|
||||
});
|
||||
personen = 1;
|
||||
}
|
||||
|
||||
const geraet = await geraetSaeen(db, pcTag, {
|
||||
kundeId,
|
||||
typ: GeraeteTyp.PC,
|
||||
hostname: `${kuerzel}-PC-001`,
|
||||
mitarbeiterId: zeile.id,
|
||||
modell: "HP ProDesk 400",
|
||||
registriert: true,
|
||||
});
|
||||
|
||||
return { abteilungen, personen, geraete: geraet ? 1 : 0, offeneTags: 0 };
|
||||
}
|
||||
Reference in New Issue
Block a user