Files
alpenwerk-hr/components/hire/StepPerson.tsx
Andrei Laas c3e19606e2
All checks were successful
CI / Lint, Typen, Tests, Build (push) Successful in 11m58s
CI / Migrationen auf leerer Datenbank (push) Successful in 10m24s
Die Cornerstone-ID als eigenes, freiwilliges Feld
Cornerstone fuehrt jede Person unter einer eigenen Kennung. Der Export
dorthin trug in "User ID" und "Username" bisher die Alpenwerk-UUID --
richtig, solange es nichts Besseres gab, aber nicht die Kennung, unter
der Cornerstone die Person kennt. Jetzt steht dort diese Spalte.

Freiwillig, weil die Zuordnungstabelle des Kunden fuer 434 der 784
Personen keine Kennung liefert. Eindeutig, weil eine Kennung genau einer
Person gehoert. Als Text, weil fuehrende Nullen in einer numerischen
Spalte verlorengingen.

Ohne hinterlegte Kennung bleiben User ID und Username **leer**. Ein
Rueckfall auf die UUID braechte zwei Kennungsarten in eine Datei, ohne
dass es auffiele, und legte in Cornerstone eine zweite Person neben der
bestehenden an. Eine fehlende Angabe soll fehlen; dafuer gibt es einen
eigenen Test.

Fuenf Funktionen mussten mit -- dieselbe Liste und derselbe Grund wie bei
der Firmen-E-Mail: hire_employee und rehire_employee teilen sich den
Schritt "Person", change_employee_data macht das Feld aenderbar,
apply_due_pending_changes sorgt dafuer, dass eine datierte Aenderung
nicht verfaellt, app_feld_karte haelt den Eintrag in der Historie
richtigstellbar. Die Migration ist wieder erzeugt, nicht abgeschrieben,
und prueft jede der fuenf einzeln.

Erfasst wird das Feld in der Akte, in "Daten aendern", bei Einstellung
und Wiedereintritt sowie ueber den Massenimport; es steht im
Mitarbeiterexport und fuellt im Cornerstone-Export User ID und Username.
2026-09-28 10:13:52 +02:00

96 lines
4.0 KiB
TypeScript

import { SvNummerField } from "@/components/employees/SvNummerField";
import { TitleFields } from "@/components/employees/TitleFields";
import { SelectField, TextField } from "@/components/ui/Field";
import { PersonalnummerField } from "./PersonalnummerField";
import type { HireDraftData } from "./types";
type StepPersonProps = {
draft: HireDraftData;
update: (patch: Partial<HireDraftData>) => void;
locations: { id: string; name: string; country: string }[];
/** Meldet die geprüfte Personalnummer und ihr Ergebnis — steuert „Weiter". */
onNummerBefund?: (befund: { nummer: number; vergeben: boolean }) => void;
/**
* Beim Wiedereintritt: die Nummer steht fest und wird nur gezeigt.
*
* Zwei Gründe. Es ist dieselbe Person und sie behält ihre Nummer — und die
* Prüfung auf Dubletten fände hier zwangsläufig einen Treffer, nämlich sie
* selbst, und sperrte das Formular mit einer Meldung, die stimmt und
* trotzdem in die Irre führt.
*/
nummerGesperrt?: boolean;
};
export function StepPerson({ draft, update, locations, onNummerBefund, nummerGesperrt }: StepPersonProps) {
return (
<div className="flex flex-col gap-4">
{nummerGesperrt ? (
<TextField
label="Personalnummer"
value={draft.personnelNumber}
onChange={() => {}}
disabled
hint="Bleibt bei einem Wiedereintritt dieselbe."
/>
) : (
<PersonalnummerField
wert={draft.personnelNumber}
onChange={(personnelNumber) => update({ personnelNumber })}
onBefund={onNummerBefund ?? (() => {})}
/>
)}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<TextField label="Vorname" required value={draft.firstName} onChange={(firstName) => update({ firstName })} />
<TextField label="Nachname" required value={draft.lastName} onChange={(lastName) => update({ lastName })} />
</div>
<TitleFields value={draft} onChange={update} />
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<SelectField
label="Geschlecht"
required
value={draft.gender}
onChange={(gender) => update({ gender: gender as HireDraftData["gender"] })}
options={[
{ value: "m", label: "männlich" },
{ value: "w", label: "weiblich" },
]}
/>
<TextField label="Geburtsdatum" required type="date" value={draft.birthDate} onChange={(birthDate) => update({ birthDate })} />
</div>
<SvNummerField
value={draft.svNummer}
onChange={(svNummer) => update({ svNummer })}
locationCountry={locations.find((l) => l.id === draft.locationId)?.country}
birthDate={draft.birthDate || null}
/>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
{/* Die private Adresse, nicht die Firmenadresse — die entsteht erst
mit dem Eintritt. Freiwillig: wer keine hat oder keine angeben
will, soll nicht gezwungen sein, eine zu erfinden. */}
<TextField label="Private E-Mail" type="email" value={draft.email} onChange={(email) => update({ email })} />
<TextField
label="Firmen-E-Mail"
type="email"
value={draft.companyEmail}
onChange={(companyEmail) => update({ companyEmail })}
/>
<TextField
label="Cornerstone-ID"
value={draft.cornerstoneId}
onChange={(cornerstoneId) => update({ cornerstoneId })}
hint="Freiwillig. Füllt im Cornerstone-Export „User ID“ und „Username“."
/>
<TextField label="Private Telefonnummer" type="tel" value={draft.phone} onChange={(phone) => update({ phone })} />
</div>
<SelectField
label="Standort"
required
value={draft.locationId}
onChange={(locationId) => update({ locationId })}
placeholder="Bitte wählen…"
options={locations.map((l) => ({ value: l.id, label: `${l.name} (${l.country})` }))}
/>
</div>
);
}