Files
Laufzettel/prisma/seed-daten/dienstleistung.ts
T
florian.wilde 9bd198a531
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
feat(seed): sieben Vorlagen, Beispielbestand und Demo-Kunden (#33)
2026-09-26 03:26:58 +00:00

190 lines
6.1 KiB
TypeScript

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 };
}