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>
190 lines
6.1 KiB
TypeScript
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 };
|
|
}
|