-- Die Anmerkung beim Austritt wird wieder aufgeschrieben. -- -- Gefragt vom Kunden: „beim Austritt gibt es eine Anmerkung — wo sieht man -- die nachher?" Die Antwort war: nirgends. Das Formular sammelt sie ein, die -- Server Action reicht sie durch, und terminate_employee liess sie fallen. -- -- Bis 20260814100000 stand sie in der Beschreibung des Ereignisses: -- -- 'Austritt (' || exit_reason || ')' -- || case when payload->>'note' <> '' then ' — ' || note else '' end -- -- Beim Umschreiben der Funktion für den Nichtantritt ist der zweite Teil -- verlorengegangen, und bei der Austrittsart (20260917100000) wurde die -- bereits verkürzte Fassung weitergereicht. Genau davor warnt die -- Selbstprüfung jener Migration im Kommentar — „der Fehler, den ein -- `create or replace` aus einer alten Vorlage leise macht" —, und für die -- Anmerkung ist er trotzdem passiert. Deshalb steht unten eine Prüfung, die -- ihn beim nächsten Mal abfängt. -- -- Die Anmerkung geht in **beide** Einträge: in die Personalakte, wo sie -- hingehört, und ins Protokoll, wo ohne sie nicht nachvollziehbar wäre, -- was jemand beim Austritt festgehalten hat. Angehängt an die fertige -- Beschreibung und nicht in einen der Zweige: der Nichtantritt darf sie -- genauso tragen. -- -- Bestehende Einträge bleiben, wie sie sind. Der Text ist nicht mehr da; er -- wurde nie geschrieben, und es gibt nichts, woraus er sich rekonstruieren -- liesse. CREATE OR REPLACE FUNCTION public.terminate_employee(payload jsonb) RETURNS void LANGUAGE plpgsql SET search_path TO 'public', 'pg_temp' AS $function$ declare v_employee_id uuid := (payload->>'employee_id')::uuid; v_exit date := (payload->>'exit_date')::date; v_name text; -- „No Show" ist kein gewöhnlicher Austritt: die Person hat nie -- angefangen. Deshalb hängt an diesem einen Grund anderes Verhalten. v_no_show boolean := coalesce(payload->>'exit_reason', '') = 'No Show'; v_entry date; v_art text := nullif(payload->>'austrittsart', ''); v_note text := nullif(btrim(payload->>'note'), ''); begin perform require_hr_admin(); select first_name || ' ' || last_name, entry_date into v_name, v_entry from employees where id = v_employee_id; -- Wer nie angetreten ist, tritt am Tag seines Eintritts wieder aus. -- Damit gibt es keinen einzigen Tag, an dem die Person beschäftigt war: -- die Statusableitung verlangt exit_date > Stichtag, um jemanden als -- beschäftigt zu zählen, und das ist hier nie erfüllt. „Nie aktiv" ist -- damit keine zusätzliche Regel, sondern folgt aus dem Datum. if v_no_show then v_exit := v_entry; end if; update employees set -- Bei einem Nichtantritt sofort, auch wenn der Eintritt noch in der -- Zukunft lag: sonst bliebe in der Spalte auf Dauer „Geplant" stehen, -- denn es gibt keinen Lauf, der sie später nachzieht. status = case when v_no_show or v_exit <= current_date then 'Ausgetreten' else status end, exit_date = v_exit, exit_reason = payload->>'exit_reason', austrittsart = v_art where id = v_employee_id; -- Die Planstelle wird frei. Direkte Berichte müssen nicht umgehängt -- werden: die Berichtslinie wird abgeleitet und rutscht von selbst auf -- die nächste besetzte Ebene. if v_no_show then -- Die Planstelle war nie besetzt. Sie auf [Eintritt, Eintritt) zu -- schliessen ginge nicht — chk_assignment_range verlangt ein echtes -- Intervall. Die Zuordnung wird deshalb entfernt: die Stelle ist wieder -- frei, und es steht nirgends, jemand hätte sie je innegehabt. delete from position_assignments where employee_id = v_employee_id and valid_to is null; else update position_assignments set valid_to = v_exit where employee_id = v_employee_id and valid_to is null; end if; insert into employee_history (employee_id, event_date, event_type, description) values (v_employee_id, v_exit, 'Austritt', case when v_no_show then 'Kein Antritt am ' || v_entry || ' (No Show)' else 'Austritt (' || coalesce(payload->>'exit_reason', '-') || ')' || case when v_art is not null then ', ' || v_art else '' end end || case when v_note is not null then ' — ' || v_note else '' end); insert into audit_log (actor_user_id, actor_name, action, target_label, target_employee_id, details) values (app_current_user_id(), current_actor_name(), 'Austritt', v_name, v_employee_id, case when v_no_show then 'Kein Antritt am ' || v_entry || ' (No Show)' else 'Austritt am ' || v_exit || case when v_art is not null then ', ' || v_art else '' end end || case when v_note is not null then ' — ' || v_note else '' end); end; $function$; grant execute on function public.terminate_employee(payload jsonb) to alpenwerk_app; -- Selbstprüfung. do $$ declare v_term text := pg_get_functiondef('public.terminate_employee(jsonb)'::regprocedure); begin if v_term not like '%v_note%' then raise exception 'terminate_employee schreibt die Anmerkung nicht — das Feld im Formular ginge wieder ins Leere.'; end if; -- Was diese Funktion schon einmal verloren hat, darf sie nicht wieder -- verlieren. Dieselben Prüfungen wie in 20260917100000, hier um die -- Anmerkung ergänzt. if v_term not like '%austrittsart%' then raise exception 'terminate_employee schreibt die Austrittsart nicht mehr'; end if; if v_term not like '%v_no_show%' or v_term not like '%delete from position_assignments%' then raise exception 'terminate_employee kennt den Nichtantritt nicht mehr'; end if; if position('SET search_path' in v_term) = 0 then raise exception 'terminate_employee hat keinen festen search_path mehr'; end if; if v_term like '%auth.uid()%' then raise exception 'terminate_employee ruft wieder auth.uid()'; end if; end $$;