F.24 -- eine Planstelle, auf die eine Versetzung vorgemerkt ist, galt als frei. Die Vormerkung steht in pending_org_changes, "besetzt?" wurde allein an position_assignments gefragt. Auffallen wuerde es erst in der Nacht des Wirksamkeitstages: dort legt der Nachtlauf die zweite Besetzung an und laeuft in den Teilindex, der genau eine laufende Besetzung je Planstelle zulaesst. Er arbeitet in einer Transaktion ueber alle faelligen Vorgaenge -- eine einzige solche Buchung haette ihn vollstaendig zum Stehen gebracht, auch fuer alle anderen. Neue Auskunft planstelle_vorgemerkt, abgefragt in hire_employee, rehire_employee, transfer_employee und promote_employee, mit Datum in der Meldung. Die eigene Vormerkung zaehlt nicht als Hindernis. D.06 -- die Struktursicht zeigte Einheiten, die es am gewaehlten Stichtag noch nicht gab: org_units wurde ungefiltert gelesen, waehrend Planstellen und Besetzungen daneben auf den Stichtag eingeschraenkt waren. Die Druckansicht filterte schon immer richtig; jetzt tun es alle drei Stellen. H.09 -- der Kuendigungsschutz stand in der Akte nur als "bis TT.MM.JJJJ", obwohl Personenkreis, Beginn, Ende und die ganze Begunstigung erfasst werden. Der Personenkreis ist die eigentliche Auskunft. Die Behinderung bekommt eine eigene Zeile: sie fuehrt oft zu Kuendigungsschutz, ist aber ein eigener Bescheid. Elternteilzeit und Wiedereingliederungsteilzeit sind jetzt auch bei der Stundenaenderung waehlbar. Sie standen nur bei der Rueckkehr aus einer Abwesenheit, mit der Begruendung, dass sie typischerweise dann beginnen -- typischerweise ist aber nicht immer. Die Ueberschneidung der beiden Listen ist damit gewollt; der Test prueft nicht mehr auf Partition, sondern darauf, dass keine Teilzeit an keinem der beiden Wege haengt.
92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
import type { EmploymentStatus } from "./types";
|
|
|
|
// Langzeitabwesenheit.
|
|
//
|
|
// The database still stores the employment status as 'Karenz' — renaming an
|
|
// enum value would mean rewriting every stored function that spells it, for
|
|
// a label change (see db/migrations/*_absence_type.sql). So the value
|
|
// is mapped to its display name here, in the one place the UI reads it from.
|
|
|
|
// Nur echte Abwesenheiten. Eine Teilzeit ist keine: wer in Bildungs-,
|
|
// Pflege-, Eltern- oder Wiedereingliederungsteilzeit ist, arbeitet — nur
|
|
// weniger. Als „Langzeitabwesenheit" geführt, verschwand die Person aus dem
|
|
// Bestand, verlor ihre Berichtslinie an eine Vertretung und zählte in
|
|
// Auswertungen nicht mehr mit, obwohl sie jede Woche im Haus war.
|
|
//
|
|
// Die vier sind deshalb hierher gewandert, wo sie hingehören: zur
|
|
// Stundenänderung (siehe STUNDEN_GRUENDE) und zur Rückkehr aus einer
|
|
// Abwesenheit (RUECKKEHR_GRUENDE).
|
|
export const ABSENCE_TYPES = [
|
|
"Wochenhilfe (Mutterschutz)",
|
|
"Elternkarenz (inkl. Väterkarenz)",
|
|
"Papamonat",
|
|
"Bildungskarenz",
|
|
"Präsenzdienst",
|
|
"Zivildienst",
|
|
"Langer Krankenstand",
|
|
"Pflegekarenz",
|
|
"Familienhospizkarenz",
|
|
"Sabbatical",
|
|
] as const;
|
|
|
|
export type AbsenceType = (typeof ABSENCE_TYPES)[number];
|
|
|
|
export function isAbsenceType(value: string | null | undefined): value is AbsenceType {
|
|
return ABSENCE_TYPES.includes(value as AbsenceType);
|
|
}
|
|
|
|
/**
|
|
* Warum jemand nach einer Abwesenheit mit weniger Stunden zurückkommt.
|
|
*
|
|
* Beides sind Ansprüche, keine freien Vereinbarungen — und beide beginnen
|
|
* typischerweise genau dann, wenn die Abwesenheit endet.
|
|
*/
|
|
export const RUECKKEHR_GRUENDE = ["Wiedereingliederungsteilzeit", "Elternteilzeit"] as const;
|
|
|
|
/**
|
|
* Warum sich die Wochenstunden ändern.
|
|
*
|
|
* Der erste Wert ist der Normalfall; die beiden anderen sind gesetzlich
|
|
* geregelte Teilzeiten, die man später wiederfinden können muss — im
|
|
* Zweifel Jahre danach, wenn jemand fragt, warum die Stunden damals sanken.
|
|
*/
|
|
export const STUNDEN_GRUENDE = [
|
|
"Vertragliche Stundenänderung",
|
|
"Bildungsteilzeit",
|
|
"Pflegeteilzeit",
|
|
// Die beiden standen zuerst nur in RUECKKEHR_GRUENDE, weil sie typischerweise
|
|
// dann beginnen, wenn eine Abwesenheit endet. Typischerweise ist aber nicht
|
|
// immer: eine Elternteilzeit kann auch vereinbart werden, ohne dass in
|
|
// diesem System je eine Karenz stand — etwa beim zweiten Elternteil oder
|
|
// wenn die Karenz vor der Einführung lag. Gemeldet von Lara am 29.09.:
|
|
// „Teilzeitvariante Elternteilzeit fehlt". Der Bericht konnte schon immer
|
|
// nach allen vier filtern; erfassen liessen sich nur zwei.
|
|
"Elternteilzeit",
|
|
"Wiedereingliederungsteilzeit",
|
|
] as const;
|
|
|
|
export type StundenGrund = (typeof STUNDEN_GRUENDE)[number];
|
|
|
|
/** Display names for the stored status values. */
|
|
const STATUS_LABELS: Record<EmploymentStatus, string> = {
|
|
Aktiv: "Aktiv",
|
|
Karenz: "Langzeitabwesenheit",
|
|
Geplant: "Geplant",
|
|
Ausgetreten: "Ausgetreten",
|
|
};
|
|
|
|
export function statusLabel(status: EmploymentStatus): string {
|
|
return STATUS_LABELS[status] ?? status;
|
|
}
|
|
|
|
/**
|
|
* The chip and the detail header show the specific kind when it is known —
|
|
* "Bildungskarenz" says more than "Langzeitabwesenheit". Absences recorded
|
|
* before the type existed have none, and fall back to the generic name
|
|
* rather than to a guess.
|
|
*/
|
|
export function absenceLabel(status: EmploymentStatus, absenceType: string | null | undefined): string {
|
|
if (status !== "Karenz") return statusLabel(status);
|
|
return isAbsenceType(absenceType) ? absenceType : STATUS_LABELS.Karenz;
|
|
}
|