Anforderung 1 — Freiwilliger vs unfreiwilliger Austritt. Die Liste der Beendigungsarten zieht aus TerminatePanel.tsx nach lib/beendigung.ts um: der Berichtemanager braucht sie ebenso, und zwei Listen liefen auseinander. Zwei neue Arten (Beendigung in der Probezeit, je Seite). Auf wessen Betreiben beendet wurde, wird aus der Art **abgeleitet** und nicht daneben gespeichert — als zweites freies Feld liesse sich "Entlassung, freiwillig" erfassen. Das Dropdown im Formular schraenkt die Auswahl darunter ein. Drei Gruppen statt zwei: Befristungsablauf geschieht auf niemandes Betreiben, ein Nichtantritt ist kein Austritt. Beide einer Seite zuzuschlagen wuerde jede Fluktuationsquote verfaelschen. Anforderung 2 — Namensfilter in "Anstehend", ab neun Eintraegen. Anforderung 3 — die zwei Unterschriftenfelder im gedruckten Blatt sind weg; "Firmenfahrzeug" steht in beiden Checklisten. has_dienstwagen sagt, ob eines zusteht, nicht ob es uebergeben wurde. Anforderung 4 — "+794 weitere" ist ein Knopf geworden; die Namen waren vorher nur ueber den Export erreichbar. Stammdatenaenderung und Gehaltsanpassung stehen nicht mehr zur Auswahl: die eine entsteht bei jeder geaenderten Telefonnummer, die andere ist ein totes Ereignis, seit das Gehalt in Loga liegt. Neu ist der Untertyp — Beendigungsart beim Austritt, Art der Abwesenheit bei der Langzeitabwesenheit, im Bericht und im Export. Anforderung 10 — zwei Kacheln. "Aktives Dienstverhaeltnis" ist nicht dasselbe wie "Aktive Mitarbeiter:innen": dort steht, wer heute arbeitet, hier, mit wem ein Vertrag laeuft. Sichtbar waren 806 und 10, addieren musste man selbst.
87 lines
4.2 KiB
TypeScript
87 lines
4.2 KiB
TypeScript
// Wie ein Dienstverhältnis endet — und auf wessen Betreiben.
|
|
//
|
|
// Die Liste stand bisher in components/employees/panels/TerminatePanel.tsx und
|
|
// damit an genau einer Stelle: dem Formular. Der Berichtemanager konnte
|
|
// deshalb nach dem Ereignis „Austritt" fragen, aber nicht danach, *warum* —
|
|
// und die Frage, die im Personalwesen zuerst kommt, ist die nach der
|
|
// Fluktuation: wie viele sind gegangen, wie vielen wurde gekündigt.
|
|
//
|
|
// ── Warum die Zuordnung abgeleitet und nicht gespeichert wird ────────
|
|
//
|
|
// Aus dem Workshop kam „ein weiteres Dropdown: freiwilliger / unfreiwilliger
|
|
// Austritt". Als zweites, frei wählbares Feld liesse es sich dem Grund
|
|
// widersprechen — „Entlassung, freiwillig" wäre eine erfassbare Eingabe, und
|
|
// zwei Auswertungen über dieselbe Person kämen zu verschiedenen Ergebnissen.
|
|
// Auf wessen Betreiben beendet wird, steckt bereits in der Beendigungsart;
|
|
// das Dropdown schränkt deshalb die Auswahl ein, statt eine zweite Wahrheit
|
|
// daneben zu stellen. Gespeichert wird weiterhin nur `exit_reason`.
|
|
//
|
|
// ── Warum es drei Gruppen sind und nicht zwei ────────────────────────
|
|
//
|
|
// Zwei Gruppen decken die Liste nicht ab. Ein Befristungsablauf geschieht auf
|
|
// niemandes Betreiben — er war von Anfang an vereinbart. Ein Nichtantritt ist
|
|
// kein Austritt, sondern ein Verhältnis, das nie begonnen hat. Beide einer der
|
|
// beiden Seiten zuzuschlagen würde jede Fluktuationsquote verfälschen, und
|
|
// zwar in eine Richtung, die niemand mehr nachvollziehen kann. Sie stehen
|
|
// deshalb als „Sonstige" für sich.
|
|
//
|
|
// Offen und im Begleitschreiben an den Kunden gestellt: ob die
|
|
// einvernehmliche Auflösung — hier als freiwillig geführt, weil sie die
|
|
// Zustimmung der Person voraussetzt — dort richtig steht. In der Praxis geht
|
|
// ihr oft ein Wunsch des Dienstgebers voraus.
|
|
|
|
export const BEENDIGUNG_NO_SHOW = "No Show";
|
|
|
|
export type Austrittsart = "freiwillig" | "unfreiwillig" | "sonstige";
|
|
|
|
export const AUSTRITTSART_LABELS: Record<Austrittsart, string> = {
|
|
freiwillig: "Freiwilliger Austritt",
|
|
unfreiwillig: "Unfreiwilliger Austritt",
|
|
sonstige: "Sonstige Beendigung",
|
|
};
|
|
|
|
export type Beendigungsart = {
|
|
/** Der gespeicherte Wert in `employees.exit_reason`. */
|
|
wert: string;
|
|
art: Austrittsart;
|
|
};
|
|
|
|
export const BEENDIGUNGSARTEN: readonly Beendigungsart[] = [
|
|
{ wert: "Einvernehmliche Auflösung", art: "freiwillig" },
|
|
{ wert: "Kündigung AN", art: "freiwillig" },
|
|
{ wert: "Pensionierung", art: "freiwillig" },
|
|
// Aus dem Workshop nachgetragen (Anforderung 1). In der Probezeit kann jede
|
|
// Seite ohne Frist und ohne Grund beenden; welche es war, ist genau die
|
|
// Unterscheidung, die sonst verlorenginge.
|
|
{ wert: "Dienstnehmerseitige Beendigung in Probezeit", art: "freiwillig" },
|
|
{ wert: "Kündigung AG", art: "unfreiwillig" },
|
|
{ wert: "Entlassung", art: "unfreiwillig" },
|
|
{ wert: "Dienstgeberseitige Beendigung in Probezeit", art: "unfreiwillig" },
|
|
{ wert: "Befristungsablauf", art: "sonstige" },
|
|
// „No Show" steht am Ende und getrennt: es ist kein Austritt im gewohnten
|
|
// Sinn, sondern der Fall, dass jemand nie angetreten ist.
|
|
{ wert: BEENDIGUNG_NO_SHOW, art: "sonstige" },
|
|
] as const;
|
|
|
|
export const BEENDIGUNGSART_WERTE: readonly string[] = BEENDIGUNGSARTEN.map((b) => b.wert);
|
|
|
|
const ART_ZU_WERT = new Map(BEENDIGUNGSARTEN.map((b) => [b.wert, b.art] as const));
|
|
|
|
/**
|
|
* Auf wessen Betreiben beendet wurde — oder null, wenn der gespeicherte Grund
|
|
* in keiner der Gruppen steht.
|
|
*
|
|
* Null ist kein Sonderfall, sondern der Normalfall für Altdaten: `exit_reason`
|
|
* ist eine Textspalte ohne Prüfung, und der Massenimport schreibt hinein, was
|
|
* in der Datei stand. Wer hier auf eine Gruppe rät, ordnet fremde Schreibweisen
|
|
* still der falschen zu.
|
|
*/
|
|
export function austrittsartVon(grund: string | null | undefined): Austrittsart | null {
|
|
return grund ? (ART_ZU_WERT.get(grund) ?? null) : null;
|
|
}
|
|
|
|
/** Die Beendigungsarten einer Gruppe — für die abhängige Auswahl im Formular. */
|
|
export function beendigungsartenZu(art: Austrittsart | null): readonly string[] {
|
|
return art ? BEENDIGUNGSART_WERTE.filter((w) => ART_ZU_WERT.get(w) === art) : BEENDIGUNGSART_WERTE;
|
|
}
|