Kuendigungsschutz bekommt einen Personenkreis, die Behinderung eigene Felder

Anforderungen 5 und 5a aus dem Workshop. Migration 20260915120000.

Bisher gab es ein Kennzeichen und ein Enddatum. Das beantwortet "darf hier
ohne Weiteres beendet werden?" — nicht aber, *warum* jemand geschuetzt ist,
und davon haengt ab, wer zustimmen muss. Nachzusehen war das nur im
Papierakt, also dort, wo unter Zeitdruck niemand nachsieht.

Zwoelf Personenkreise als CHECK auf text und nicht als Aufzaehlungstyp: die
Liste ist Rechtslage und aendert sich mit dem Gesetz, ein Typ liesse einen
zurueckgenommenen Wert fuer immer stehen. Die Oberflaeche liest dieselbe
Liste aus lib/kuendigungsschutz.ts; ein Test liest die Migration und haelt
beide gegeneinander, damit die begruendete Doppelung keine stille wird.

Die begueenstigte Behinderung bekommt Kennzeichen, Grad, Beginn und Ende —
vier Spalten und nicht eine zusammengesetzte, weil in Excel danach
gefiltert und summiert wird. Datenbankseitig sind sie *nicht* an den
Personenkreis gekettet: eine solche Bedingung scheiterte genau dann, wenn
jemand den Kreis korrigiert und der Grad noch dransteht. Die Oberflaeche
stellt den Zusammenhang her.

Dazu zwei Dinge, die auf dem Weg auffielen:

  * Der Nachtlauf wendete bei einer auf spaeter datierten Aenderung nur
    Person und Vertrag an — die ganze Gruppe "role" fiel weg. Betriebsrat,
    Dienstwagen, Kollektivvertrag, Arbeitstage, Teilzeit und
    Kuendigungsschutz wurden erfasst, in der Historie vermerkt, protokolliert
    und am Stichtag nicht geschrieben. Sichtbar wurde das nie. Die neuen
    Felder haetten den Fehler geerbt; er ist jetzt fuer alle behoben.
  * Der Mitarbeiter-Export filterte ohne Stichtag ueber die Spalte `status`,
    mit Stichtag ueber die Ableitung. Der Export nach "Ausgetreten" liess
    damit genau die Leute aus, die gerade ausgetreten sind.
This commit is contained in:
2026-09-15 22:47:22 +02:00
parent 8d0c9b4b65
commit c25c16e372
13 changed files with 1148 additions and 9 deletions

View File

@@ -7,6 +7,7 @@ import { deleteHireDraft, saveHireDraft } from "@/actions/hireDrafts";
import { Button } from "@/components/ui/Button";
import { Modal } from "@/components/ui/Modal";
import { useToast } from "@/components/ui/Toast";
import { GRUND_BEGUENSTIGT_BEHINDERT } from "@/lib/kuendigungsschutz";
import type { OpenPositionResolved } from "@/lib/positions";
import { isValidSvnr, requiresAustrianSvnr } from "@/lib/svnr";
import { StepAngehoerige } from "./StepAngehoerige";
@@ -116,6 +117,10 @@ export function HireWizard({ open, onClose, openPositions, locations, resumeDraf
async function handleSubmit() {
if (!selectedPosition || !draft.besetzung) return;
// Dieselbe Regel wie im Formular (RoleEmploymentFields): der
// Personenkreis zieht das Kennzeichen mit. Was gezeigt wurde, muss auch
// abgeschickt werden.
const istBehindert = draft.istBeguenstigtBehindert || draft.kuendigungsschutzGrund === GRUND_BEGUENSTIGT_BEHINDERT;
setSubmitting(true);
const result = await hireEmployee({
personnel_number: Number(draft.personnelNumber),
@@ -146,8 +151,15 @@ export function HireWizard({ open, onClose, openPositions, locations, resumeDraf
// sonst nicht zu.
dienstwagen_art: draft.hasDienstwagen ? draft.dienstwagenArt : null,
has_kuendigungsschutz: draft.hasKuendigungsschutz,
// Ohne Schutz kein Enddatum — der CHECK lässt es nicht anders zu.
// Ohne Schutz kein Enddatum, kein Beginn, kein Personenkreis — die
// CHECKs lassen es nicht anders zu.
kuendigungsschutz_bis: draft.hasKuendigungsschutz ? draft.kuendigungsschutzBis || null : null,
kuendigungsschutz_grund: draft.hasKuendigungsschutz ? draft.kuendigungsschutzGrund || null : null,
kuendigungsschutz_ab: draft.hasKuendigungsschutz ? draft.kuendigungsschutzAb || null : null,
ist_beguenstigt_behindert: istBehindert,
behinderung_grad: istBehindert ? draft.behinderungGrad || null : null,
behinderung_ab: istBehindert ? draft.behinderungAb || null : null,
behinderung_bis: istBehindert ? draft.behinderungBis || null : null,
emergency_contact_name: draft.emergencyContactName.trim() || undefined,
emergency_contact_phone: draft.emergencyContactPhone.trim() || undefined,
emergency_contact_relation: draft.emergencyContactRelation.trim() || undefined,

View File

@@ -59,6 +59,14 @@ export type HireDraftData = {
hasKuendigungsschutz: boolean;
/** Freiwillig — leer heisst „bis auf Weiteres". */
kuendigungsschutzBis: string;
/** Personenkreis nach dem besonderen Kündigungsschutz; leer heisst „nicht erfasst". */
kuendigungsschutzGrund: string;
kuendigungsschutzAb: string;
istBeguenstigtBehindert: boolean;
/** Grad in Prozent als Text — ein leeres Zahlenfeld wäre sonst 0. */
behinderungGrad: string;
behinderungAb: string;
behinderungBis: string;
};
export const EMPTY_HIRE_DRAFT: HireDraftData = {
@@ -95,4 +103,10 @@ export const EMPTY_HIRE_DRAFT: HireDraftData = {
isCLevel: false,
hasKuendigungsschutz: false,
kuendigungsschutzBis: "",
kuendigungsschutzGrund: "",
kuendigungsschutzAb: "",
istBeguenstigtBehindert: false,
behinderungGrad: "",
behinderungAb: "",
behinderungBis: "",
};