-- Eine Organisationseinheit wieder entfernen -- -- Das Gegenstück zu create_org_unit und mit derselben Einschränkung: es geht -- nur dort, wo nichts umgeschrieben wird. -- -- ═══ Warum gelöscht und nicht geschlossen ═════════════════════════ -- -- delete_position macht es seit dem OM-Umbau vor: besetzt → Abweisung, einmal -- besetzt gewesen → `valid_to` statt Löschen (sonst verschwände mit der Stelle -- die Besetzungshistorie), nie besetzt gewesen → weg. Der Unterschied ist -- keine Vorsicht, sondern eine Aussage: was nie getragen hat, hat es auch nie -- gegeben. -- -- Für Einheiten lässt sich davon heute nur die letzte Stufe umsetzen. Ein -- `valid_to` an einer Einheit wäre zwar eingetragen, aber **nirgends -- gelesen**: weder das Organigramm noch orgMapsAbfragen, die Berichte, der -- Druck oder die Auswahl beim Anlegen einer Planstelle schränken org_units auf -- den Stichtag ein. Die geschlossene Einheit stünde überall weiter da, nur mit -- einem Datum, das niemand sieht — das wäre schlechter als die klare -- Abweisung unten, weil es wie Erledigt aussieht. -- -- Deshalb hier nur: eine Einheit, unter der nie etwas hing, verschwindet -- ganz. Alles andere wird abgewiesen, mit dem Grund. Das Schliessen zum -- Stichtag kommt, wenn org_units überall gegen den Stichtag gelesen wird — -- dieselbe Arbeit, die auch das Verschieben braucht. create or replace function delete_org_unit(payload jsonb) returns void language plpgsql set search_path = public, pg_temp as $$ declare v_id uuid := (payload->>'org_unit_id')::uuid; v_unit record; v_einheiten int; v_stellen int; begin perform require_hr_admin(); select id, org_number, name, parent_id into v_unit from org_units where id = v_id; if v_unit.id is null then raise exception 'Die Organisationseinheit existiert nicht.'; end if; if v_unit.parent_id is null then raise exception 'Die Gesellschaft ist die Wurzel und kann nicht entfernt werden.'; end if; select count(*) into v_einheiten from org_units where parent_id = v_id; if v_einheiten > 0 then raise exception 'Unter dieser Einheit hängen noch % Einheiten. Sie müssen zuerst woanders hin.', v_einheiten; end if; -- Geschlossene Planstellen zählen mit. Sie tragen weiterhin diese Einheit, -- und mit ihr verschwände die Angabe, wo jemand einmal gesessen hat. select count(*) into v_stellen from om_positions where org_unit_id = v_id; if v_stellen > 0 then raise exception 'In dieser Einheit bestehen noch % Planstellen (geschlossene zählen mit). Sie müssen zuerst entfernt oder verschoben werden.', v_stellen; end if; -- Die Kostenstellen tragen die Einheit nur als Hinweis (siehe Migration -- 20260816100000), nicht als Zuordnung. Sie stünden nach dem Löschen auf -- einer Einheit, die es nicht mehr gibt — der Fremdschlüssel würde das -- abweisen, also vorher und mit Grund. if exists (select 1 from cost_centers where org_unit_id = v_id) then raise exception 'Auf diese Einheit verweisen noch Kostenstellen.'; end if; delete from org_units where id = v_id; insert into audit_log (actor_user_id, actor_name, action, target_label, details) values (app_current_user_id(), current_actor_name(), 'Organisationseinheit gelöscht', v_unit.org_number || ' · ' || v_unit.name, 'Ohne untergeordnete Einheiten und ohne Planstellen'); end; $$; comment on function delete_org_unit(jsonb) is 'Entfernt eine Organisationseinheit, unter der nichts hängt und nie etwas hing. Einheiten mit Planstellen — auch geschlossenen — oder mit untergeordneten Einheiten werden abgewiesen; ein Schliessen zum Stichtag gibt es noch nicht, weil org_units nirgends gegen den Stichtag gelesen wird.'; grant execute on function delete_org_unit(jsonb) to alpenwerk_app; -- Selbstprüfung. do $$ declare v_def text; begin select pg_get_functiondef(p.oid) into v_def from pg_proc p join pg_namespace n on n.oid = p.pronamespace where n.nspname = 'public' and p.proname = 'delete_org_unit' and p.prokind = 'f'; if v_def is null then raise exception 'delete_org_unit wurde nicht angelegt'; end if; if v_def not like '%require_hr_admin()%' then raise exception 'delete_org_unit prüft die Rechte nicht'; end if; if v_def not like '%search_path%' then raise exception 'delete_org_unit hat keinen festen search_path'; end if; if v_def not like '%audit_log%' then raise exception 'delete_org_unit schreibt nichts ins Protokoll'; end if; -- Die drei Abweisungen. Fällt eine bei einer Neuerzeugung weg, löscht die -- Funktion weiter — nur mehr, als sie darf. if v_def not like '%om_positions%' then raise exception 'delete_org_unit prüft die Planstellen nicht'; end if; if v_def not like '%parent_id = v_id%' then raise exception 'delete_org_unit prüft die untergeordneten Einheiten nicht'; end if; if v_def not like '%v_unit.parent_id is null%' then raise exception 'delete_org_unit schützt die Wurzel nicht'; end if; -- Das Gegenstück muss weiterhin dastehen: ein Löschen ohne Anlegen wäre -- eine Einbahnstrasse. if not exists ( select 1 from pg_proc p join pg_namespace n on n.oid = p.pronamespace where n.nspname = 'public' and p.proname = 'create_org_unit' ) then raise exception 'create_org_unit fehlt'; end if; end $$;