-- Eine Planstelle ändern. -- -- Bisher liess sie sich nur anlegen und löschen. Ein Tippfehler in der -- Tätigkeit oder ein falsches Gültigkeitsdatum bedeutete: löschen und neu — -- mit neuer Planstellennummer. Die Nummer steht aber in Stellenausschreibungen -- und Budgets, und die Protokollspur reisst ab. -- -- Was hier bewusst **nicht** geht, steht als Sperre drin, nicht als -- Anmerkung — siehe die drei Prüfungen unten. create or replace function update_position(payload jsonb) returns void language plpgsql set search_path = public, pg_temp as $function$ declare v_id uuid := (payload->>'position_id')::uuid; v_alt om_positions%rowtype; v_alt_titel text; v_alt_einheit text; v_org_unit_id uuid; v_job_title text := nullif(trim(payload->>'job_title'), ''); v_is_chief boolean; v_valid_from date; v_valid_to date; v_job_id uuid; v_unit_name text; v_besetzt boolean; v_changes jsonb := '[]'::jsonb; begin perform require_hr_admin(); select * into v_alt from om_positions where id = v_id; if v_alt.id is null then raise exception 'Die Planstelle existiert nicht.'; end if; select title into v_alt_titel from jobs where id = v_alt.job_id; select name into v_alt_einheit from org_units where id = v_alt.org_unit_id; -- Fehlende Schlüssel heissen „unverändert", nicht „leeren". v_org_unit_id := coalesce(nullif(payload->>'org_unit_id','')::uuid, v_alt.org_unit_id); v_job_title := coalesce(v_job_title, v_alt_titel); v_is_chief := coalesce((payload->>'is_chief')::boolean, v_alt.is_chief); v_valid_from := coalesce(nullif(payload->>'valid_from','')::date, v_alt.valid_from); v_valid_to := case when payload ? 'valid_to' then nullif(payload->>'valid_to','')::date else v_alt.valid_to end; select name into v_unit_name from org_units where id = v_org_unit_id; if v_unit_name is null then raise exception 'Die Organisationseinheit existiert nicht.'; end if; if v_valid_to is not null and v_valid_to < v_valid_from then raise exception 'Das Ende der Gültigkeit liegt vor ihrem Beginn.'; end if; select exists ( select 1 from position_assignments where position_id = v_id and (valid_to is null or valid_to > current_date) ) into v_besetzt; -- (1) Die Einheit einer vergebenen Planstelle zu wechseln wäre eine -- Versetzung — mit allem, was dazugehört: Historie, Berichtslinie, -- Protokoll. Das gehört in transfer_employee und nicht hierher, sonst -- wandert jemand lautlos in eine andere Abteilung. if v_besetzt and v_org_unit_id is distinct from v_alt.org_unit_id then raise exception 'Diese Planstelle ist vergeben. Für einen Wechsel der Einheit die Versetzung benutzen.'; end if; -- (2) Ein Ende, während noch jemand darauf sitzt, hinterlässt eine -- Besetzung ohne Planstelle. if v_besetzt and v_valid_to is not null then raise exception 'Diese Planstelle ist vergeben und kann kein Ende der Gültigkeit bekommen.'; end if; -- (3) Je Einheit nur eine gültige Leitung. Der Unique-Index fängt das auch, -- aber mit einer Meldung, die in der Oberfläche nichts erklärt. if v_is_chief and exists ( select 1 from om_positions where org_unit_id = v_org_unit_id and is_chief and valid_to is null and id <> v_id ) then raise exception 'Für % besteht bereits eine Leitungsplanstelle.', v_unit_name; end if; -- Gleiche Tätigkeit, ein Katalogeintrag — dieselbe Regel wie beim Anlegen. select id into v_job_id from jobs where lower(title) = lower(v_job_title); if v_job_id is null then insert into jobs (code, title) values ('J' || lpad((select count(*) + 1 from jobs)::text, 4, '0'), v_job_title) returning id into v_job_id; end if; v_changes := app_aenderung(v_changes, 'Tätigkeit', v_alt_titel, v_job_title); v_changes := app_aenderung(v_changes, 'Organisationseinheit', v_alt_einheit, v_unit_name); v_changes := app_aenderung(v_changes, 'Leitungsplanstelle', v_alt.is_chief::text, v_is_chief::text); v_changes := app_aenderung(v_changes, 'Gültig ab', v_alt.valid_from::text, v_valid_from::text); v_changes := app_aenderung(v_changes, 'Gültig bis', v_alt.valid_to::text, v_valid_to::text); -- Nichts geändert ist ein Ergebnis, kein Erfolg. -- -- Vorher kehrte die Funktion hier stumm zurück, und die Oberfläche meldete -- „Planstelle geändert." Wer etwas eingetragen hatte, das unterwegs -- verworfen wurde — etwa das Leitungshäkchen, das bei bereits vergebener -- Leitung nicht durchkommt —, sah eine Erfolgsmeldung und eine unveränderte -- Liste. Das ist genau die Rückmeldung, die einen suchen lässt. if jsonb_array_length(v_changes) = 0 then raise exception 'Es wurde nichts geändert.'; end if; update om_positions set org_unit_id = v_org_unit_id, job_id = v_job_id, is_chief = v_is_chief, valid_from = v_valid_from, valid_to = v_valid_to where id = v_id; insert into audit_log (actor_user_id, actor_name, action, target_label, details, changes) values (app_current_user_id(), current_actor_name(), 'Planstelle geändert', v_job_title || ' (' || v_unit_name || ')', app_aenderungsfelder(v_changes), v_changes); end; $function$; do $$ declare r text; begin foreach r in array array['anon', 'authenticated', 'service_role', 'alpenwerk_app'] loop if exists (select 1 from pg_roles where rolname = r) then execute format('grant execute on function update_position(jsonb) to %I', r); end if; end loop; end; $$;