Last step moved the four part-time arrangements out of the absence list and recorded the reason in the history text. That answered "what happened" but not "who is in one right now", and the profile showed nothing at all. So it becomes a real field: teilzeit_art, with an optional end date. The objection I raised then still holds — a state goes stale, because nobody goes back to note when a Bildungsteilzeit ended. teilzeit_bis is the answer to it: with an end date a report decides for itself what is still running instead of trusting that someone maintained the row. Left empty it means "open end", which is an honest thing to say. It runs through the ordinary change machinery rather than beside it. It sits in app_feld_karte, so it shows up in the history as a field with before and after, and can be corrected there like any other. The description suffix from last step is gone — writing the same thing twice is how two versions start disagreeing. Reporting: filter by variant, by "in one at all", and by when it ends; group headcount by variant, where the absence of one reads "Keine" rather than a dash, because in a report that is an answer and not a gap. Plus columns in the export and the import. One gap found while rehearsing, and only because the probe happened to pick a return date in the future: a scheduled return carries its payload through pending_org_changes, and that payload did not include the variant. Someone would have come back on reduced hours in April with the reason gone. The daily run now carries it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
120 lines
5.6 KiB
PL/PgSQL
120 lines
5.6 KiB
PL/PgSQL
-- Die Teilzeitvariante übersteht auch eine geplante Rückkehr.
|
|
--
|
|
-- record_karenz_return legt für ein Rückkehrdatum in der Zukunft eine Zeile
|
|
-- in pending_org_changes an; der Tageslauf wendet sie an. Diese Zeile trug
|
|
-- bisher nur Beschäftigungsausmaß und Stunden — die Variante wäre am Stichtag
|
|
-- verlorengegangen, und jemand käme in Elternteilzeit zurück, ohne dass es
|
|
-- irgendwo stünde ausser im Beschreibungstext der Historie.
|
|
--
|
|
-- Aufgefallen beim Proben: die Probe hatte ein Rückkehrdatum in der Zukunft
|
|
-- gewählt und lief deshalb in genau diesen Zweig.
|
|
|
|
CREATE OR REPLACE FUNCTION public.apply_due_pending_changes()
|
|
RETURNS integer
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path TO 'public', 'pg_temp'
|
|
AS $function$
|
|
declare
|
|
v_rec record;
|
|
v_count int := 0;
|
|
begin
|
|
for v_rec in
|
|
select * from pending_org_changes
|
|
where status = 'pending' and effective_date <= current_date
|
|
order by effective_date, created_at
|
|
loop
|
|
if v_rec.change_type = 'transfer' then
|
|
update position_assignments set valid_to = v_rec.effective_date
|
|
where employee_id = v_rec.employee_id and valid_to is null;
|
|
insert into position_assignments (position_id, employee_id, valid_from)
|
|
values ((v_rec.payload->>'target_position_id')::uuid, v_rec.employee_id, v_rec.effective_date);
|
|
update employees set job_title = (
|
|
select j.title from om_positions p join jobs j on j.id = p.job_id
|
|
where p.id = (v_rec.payload->>'target_position_id')::uuid
|
|
) where id = v_rec.employee_id;
|
|
|
|
elsif v_rec.change_type = 'promotion' then
|
|
update employees set
|
|
job_title = coalesce(v_rec.payload->>'new_title', job_title),
|
|
paygrade = coalesce((v_rec.payload->>'new_paygrade')::paygrade_type, paygrade)
|
|
where id = v_rec.employee_id;
|
|
|
|
elsif v_rec.change_type = 'karenz_start' then
|
|
update employees set
|
|
status = 'Karenz',
|
|
karenz_return_date = (v_rec.payload->>'planned_return_date')::date,
|
|
absence_type = coalesce(nullif(v_rec.payload->>'absence_type', ''), absence_type)
|
|
where id = v_rec.employee_id;
|
|
|
|
elsif v_rec.change_type = 'karenz_return' then
|
|
update employees set
|
|
status = 'Aktiv',
|
|
karenz_return_date = null,
|
|
karenz_start_date = null,
|
|
absence_type = null,
|
|
employment_type = coalesce((v_rec.payload->>'employment_type')::employment_type, employment_type),
|
|
weekly_hours = coalesce((v_rec.payload->>'weekly_hours')::numeric, weekly_hours),
|
|
-- Auch bei einer *geplanten* Rückkehr: sonst käme jemand am
|
|
-- Stichtag mit reduzierten Stunden zurück, und der Grund dafür
|
|
-- wäre verschwunden. Der Nachweis stünde nur in der Historie,
|
|
-- auswerten liesse sich nichts.
|
|
teilzeit_art = case when v_rec.payload ? 'teilzeit_art'
|
|
then nullif(v_rec.payload->>'teilzeit_art', '') else teilzeit_art end,
|
|
teilzeit_bis = case when v_rec.payload ? 'teilzeit_art'
|
|
then nullif(v_rec.payload->>'teilzeit_bis', '')::date else teilzeit_bis end
|
|
where id = v_rec.employee_id;
|
|
|
|
elsif v_rec.change_type = 'contract_change' then
|
|
update employees set
|
|
first_name = coalesce(v_rec.payload->'person'->>'first_name', first_name),
|
|
last_name = coalesce(v_rec.payload->'person'->>'last_name', last_name),
|
|
gender = coalesce((v_rec.payload->'person'->>'gender')::gender_type, gender),
|
|
birth_date = coalesce((v_rec.payload->'person'->>'birth_date')::date, birth_date),
|
|
sv_nummer = coalesce(v_rec.payload->'person'->>'sv_nummer', sv_nummer),
|
|
nationality = coalesce(v_rec.payload->'person'->>'nationality', nationality),
|
|
address = coalesce(v_rec.payload->'person'->>'address', address),
|
|
postal_code = coalesce(v_rec.payload->'person'->>'postal_code', postal_code),
|
|
city = coalesce(v_rec.payload->'person'->>'city', city),
|
|
address_country = coalesce(v_rec.payload->'person'->>'address_country', address_country),
|
|
email = coalesce(v_rec.payload->'person'->>'email', email),
|
|
phone = coalesce(v_rec.payload->'person'->>'phone', phone),
|
|
employment_type = coalesce((v_rec.payload->'contract'->>'employment_type')::employment_type, employment_type),
|
|
weekly_hours = coalesce((v_rec.payload->'contract'->>'weekly_hours')::numeric, weekly_hours),
|
|
contract_type = coalesce((v_rec.payload->'contract'->>'contract_type')::contract_type, contract_type)
|
|
where id = v_rec.employee_id;
|
|
|
|
elsif v_rec.change_type = 'dependent_add' then
|
|
insert into employee_dependents (employee_id, first_name, last_name, relationship, sv_nummer, birth_date)
|
|
values (v_rec.employee_id, v_rec.payload->>'first_name', v_rec.payload->>'last_name',
|
|
(v_rec.payload->>'relationship')::text,
|
|
nullif(v_rec.payload->>'sv_nummer', ''), (v_rec.payload->>'birth_date')::date);
|
|
|
|
elsif v_rec.change_type = 'dependent_remove' then
|
|
delete from employee_dependents where id = (v_rec.payload->>'dependent_id')::uuid;
|
|
end if;
|
|
|
|
update pending_org_changes set status = 'applied', applied_at = now() where id = v_rec.id;
|
|
v_count := v_count + 1;
|
|
end loop;
|
|
|
|
return v_count;
|
|
end;
|
|
$function$;
|
|
|
|
|
|
-- Selbstprüfung.
|
|
do $$
|
|
declare
|
|
v_apply text := pg_get_functiondef('public.apply_due_pending_changes()'::regprocedure);
|
|
v_ret text := pg_get_functiondef('public.record_karenz_return(jsonb)'::regprocedure);
|
|
begin
|
|
if v_apply not like '%teilzeit_art%' then
|
|
raise exception 'Der Tageslauf überträgt die Teilzeitvariante nicht';
|
|
end if;
|
|
if v_ret not like '%teilzeit_art%' then
|
|
raise exception 'record_karenz_return legt die Variante nicht in den Vorgang';
|
|
end if;
|
|
end
|
|
$$;
|