Show the Offboarding tab from the day the exit is recorded, not the day it takes effect
The tab was keyed off employee.status === "Ausgetreten", and that stays wrong for weeks: terminate_employee writes exit_date immediately no matter how far out the date is, but only flips status once the date itself arrives. A termination entered today for four weeks out left the tab invisible for the entire notice period — exactly the stretch in which IT access, hardware and deregistration actually get worked through, and exactly where the checklist was supposed to live "next to Onboarding," per the report that caught this. The rule now reads exit_date instead: not null, and not a No Show (which sets exit_date too, to the entry day, but never worked a day and gets no checklist). rehire_employee resets both exit_date and exit_reason to null, so a rehired person's tab still disappears the same way it did before — nothing about that case changed, only the signal the check reads. Verified against the real database with the exact shape from the report: a termination dated 30 days out. Status stays "Aktiv", the checklist exists immediately, and the tab's own predicate says yes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,15 +66,25 @@ export function fortschritt(staende: AufgabenStand[]): Fortschritt {
|
|||||||
/**
|
/**
|
||||||
* Ob eine Person offzuboarden ist — die Regel hinter dem Reiter.
|
* Ob eine Person offzuboarden ist — die Regel hinter dem Reiter.
|
||||||
*
|
*
|
||||||
* Nur ein echter Austritt zählt. No Show scheidet zwar mit demselben Status
|
* Geprüft wird `exit_date`, nicht `status`. Eine künftige Beendigung setzt
|
||||||
* „Ausgetreten" aus, hat aber nie einen Tag gearbeitet: keinen IT-Zugang, der
|
* beide Felder sofort — terminate_employee schreibt `exit_date` unabhängig
|
||||||
* eingerichtet wurde, keine GKK-Anmeldung, kein Dienstzettel. Ein Reiter mit
|
* davon, ob der Stichtag schon erreicht ist —, während `status` erst am
|
||||||
* einer leeren Liste wäre dort ein Etikett ohne Inhalt.
|
* Stichtag selbst auf „Ausgetreten" wechselt. Bis dahin steht die Person
|
||||||
|
* weiterhin als „Aktiv"; die vier Wochen bis zum Austritt sind aber genau die
|
||||||
|
* Zeit, in der IT-Zugänge, Hardware und Anmeldungen abgewickelt werden. Mit
|
||||||
|
* `status` als Bedingung bliebe der Reiter bis zum letzten Arbeitstag
|
||||||
|
* unsichtbar — zu spät, um dort etwas vorzubereiten.
|
||||||
*
|
*
|
||||||
* Wird die Person wiedereingestellt, wechselt der Status weg von
|
* No Show hat `exit_date` ebenfalls gesetzt (den Eintrittstag), aber nie
|
||||||
* „Ausgetreten", und die Regel verneint wieder — die Checkliste bleibt in den
|
* einen Tag gearbeitet: keinen IT-Zugang, der eingerichtet wurde, keine
|
||||||
* Daten stehen, nur ihr Reiter verschwindet, solange sie ohne Gegenstand ist.
|
* GKK-Anmeldung, kein Dienstzettel. Ein Reiter mit einer leeren Liste wäre
|
||||||
|
* dort ein Etikett ohne Inhalt — deshalb der Ausschluss über `exit_reason`.
|
||||||
|
*
|
||||||
|
* Wird die Person wiedereingestellt, setzt rehire_employee `exit_date` und
|
||||||
|
* `exit_reason` ausdrücklich auf null zurück, und die Regel verneint wieder —
|
||||||
|
* die Checkliste bleibt in den Daten stehen, nur ihr Reiter verschwindet,
|
||||||
|
* solange sie ohne Gegenstand ist.
|
||||||
*/
|
*/
|
||||||
export function gehoertOffboarding(employee: { status: string; exit_reason: string | null }): boolean {
|
export function gehoertOffboarding(employee: { exit_date: string | null; exit_reason: string | null }): boolean {
|
||||||
return employee.status === "Ausgetreten" && employee.exit_reason !== "No Show";
|
return employee.exit_date !== null && employee.exit_reason !== "No Show";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,23 +93,29 @@ describe("istErledigt", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("gehoertOffboarding", () => {
|
describe("gehoertOffboarding", () => {
|
||||||
// Die Regel hinter dem Reiter: er erscheint bei einem echten Austritt,
|
// Geprüft wird exit_date, nicht status: eine künftige Beendigung setzt
|
||||||
// nicht bei jedem, der denselben Status „Ausgetreten" trägt.
|
// exit_date sofort, aber status erst am Stichtag selbst. Bis dahin steht
|
||||||
it("bejaht bei einem echten Austritt", () => {
|
// die Person weiterhin als „Aktiv" — mit status als Bedingung bliebe der
|
||||||
expect(gehoertOffboarding({ status: "Ausgetreten", exit_reason: "Kündigung AN" })).toBe(true);
|
// Reiter bis zum letzten Arbeitstag unsichtbar, genau dann, wenn er
|
||||||
|
// gebraucht wird.
|
||||||
|
it("bejaht bei einem bereits wirksamen Austritt", () => {
|
||||||
|
expect(gehoertOffboarding({ exit_date: "2026-08-01", exit_reason: "Kündigung AN" })).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("verneint bei No Show, obwohl der Status derselbe ist", () => {
|
it("bejaht schon bei einem erst künftig wirksamen Austritt", () => {
|
||||||
|
expect(gehoertOffboarding({ exit_date: "2099-01-01", exit_reason: "Kündigung AN" })).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("verneint bei No Show, obwohl exit_date ebenso gesetzt ist", () => {
|
||||||
// Nie einen Tag gearbeitet: kein IT-Zugang, keine GKK-Anmeldung, kein
|
// Nie einen Tag gearbeitet: kein IT-Zugang, keine GKK-Anmeldung, kein
|
||||||
// Dienstzettel. Der Reiter wäre ein Etikett ohne Inhalt.
|
// Dienstzettel. Der Reiter wäre ein Etikett ohne Inhalt.
|
||||||
expect(gehoertOffboarding({ status: "Ausgetreten", exit_reason: "No Show" })).toBe(false);
|
expect(gehoertOffboarding({ exit_date: "2026-08-01", exit_reason: "No Show" })).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("verneint für eine aktive Person", () => {
|
it("verneint ohne Austritt — auch nach einer Wiedereinstellung, die exit_date zurücksetzt", () => {
|
||||||
expect(gehoertOffboarding({ status: "Aktiv", exit_reason: null })).toBe(false);
|
// Beides führt auf dieselbe Datenlage: wer nie ausgeschieden ist, und wer
|
||||||
});
|
// wiedereingestellt wurde (rehire_employee setzt exit_date und
|
||||||
|
// exit_reason ausdrücklich auf null), sehen für diese Regel gleich aus.
|
||||||
it("verneint für eine geplante Person", () => {
|
expect(gehoertOffboarding({ exit_date: null, exit_reason: null })).toBe(false);
|
||||||
expect(gehoertOffboarding({ status: "Geplant", exit_reason: null })).toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user