feat(mitarbeiter): Vorgesetzter auch fuer Personen ohne Verzeichnis
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

Die Zuordnung lief allein ueber vorgesetzterExterneId, und die ist nur
innerhalb einer Verzeichnisquelle eindeutig: eine von Hand angelegte Person
konnte keinen Vorgesetzten haben, und {{vorgesetzter_sam}} blieb im
Onboarding-Befehl leer -- genau dort, wo er gebraucht wird.

Entschieden fuer eine eigene Selbstbeziehung vorgesetzterId. Die befuerchteten
"zwei Wege zur selben Auskunft" entstehen nicht, weil der Abgleich sie
mitschreibt: vorgesetzterExterneId ist jetzt wie abteilung nur der rohe Wert
des Verzeichnisses, gelesen wird allein vorgesetzterId. Die Alternative haette
manuellen Personen eine erfundene externeId geben muessen, und externeId heisst
"objectGUID aus dem Verzeichnis".

Liefert das Verzeichnis keinen manager, bleibt eine von Hand gesetzte Zuordnung
stehen -- geleert wird nur, was das Verzeichnis selbst gesetzt hatte.

Die Migration fuehrt bestehende Daten nach. Ohne das waere die Auskunft nach
dem Einspielen bei jeder Verzeichnis-Person still leer gewesen, bis der
naechste Abgleich laeuft. An einem nachgebauten Vor-Zustand gefahren.

Drei Grenzen, drei Stellen: der zusammengesetzte Fremdschluessel haelt die
Mandantengrenze, ein CHECK die kuerzeste Schleife, und der Dienst laeuft die
Kette hinauf fuer laengere.

Im gebauten Stand durchgeklickt: speichern, neu laden, Wert steht, entfernen,
390 px ohne horizontales Scrollen. Gegenprobe am Abgleich gefahren.

Closes #31
This commit is contained in:
Florian Wilde
2026-10-04 17:09:54 +02:00
parent 0fa41b9e08
commit 02ca74e661
19 changed files with 683 additions and 49 deletions
+49
View File
@@ -70,7 +70,10 @@ async function aufbau(modus: Betriebsmodus = Betriebsmodus.INTERN) {
abteilung: "Buchhaltung",
titel: "Sachbearbeiterin",
dn: DN,
// Beides, so wie der Abgleich es schreibt: der rohe Wert des
// Verzeichnisses und die gepflegte Zuordnung, aus der gelesen wird (#020).
vorgesetzterExterneId: "guid-chefin",
vorgesetzterId: chefin.id,
herkunft: Herkunft.ABGLEICH,
},
});
@@ -170,6 +173,52 @@ describe.runIf(datenbankVerfuegbar)("Werte eines Laufs", () => {
});
});
it("löst den Vorgesetzten auch für eine von Hand angelegte Person auf", async () => {
// Der Fall aus #020: ohne Verzeichnisquelle gab es vorher kein Feld fuer die
// Zuordnung, und {{vorgesetzter_sam}} blieb im Onboarding-Befehl leer -
// genau dort, wo er gebraucht wird.
const { eigene, versionId } = await aufbau();
const chefin = await testPrisma.mitarbeiter.create({
data: {
kundeId: eigene.id,
vorname: "Petra",
nachname: "Klein",
anzeigename: "Petra Klein",
sam: "pklein",
herkunft: Herkunft.MANUELL,
},
});
const vonHand = await testPrisma.mitarbeiter.create({
data: {
kundeId: eigene.id,
vorname: "Neu",
nachname: "Kollege",
anzeigename: "Neu Kollege",
sam: "nkollege",
herkunft: Herkunft.MANUELL,
vorgesetzterId: chefin.id,
},
});
// Keine Quelle, keine externe Kennung - der alte Leseweg haette hier
// ausgestiegen.
expect(vonHand.quelleId).toBeNull();
expect(vonHand.vorgesetzterExterneId).toBeNull();
const laufId = await laufAnlegen({
vorlagenVersionId: versionId,
titel: "Onboarding Neu Kollege",
kundeId: eigene.id,
mitarbeiterId: vonHand.id,
});
const werte = await platzhalterWerteDesLaufs({
laufId,
technikerName: "Ali Demir",
jetzt: new Date("2026-10-04T10:00:00Z"),
});
expect(werte).toMatchObject({ vorgesetzter: "Petra Klein", vorgesetzter_sam: "pklein" });
});
it("nimmt für die OU den Eltern-Container des dn, nicht die Ziel-OU", async () => {
// Der dn sagt, wo das Konto **wirklich** liegt; die Ziel-OU der Abteilung
// ist die Absicht fuer ein Konto, das es noch nicht gibt.