* Fix/employees page salary display logic and labels * feat: add salary_worked_days table and related functionality - Implemented the salary_worked_days table to track per-day worked hours for hourly employees. - Established row-level security (RLS) policies to ensure tenant isolation for salary_worked_days. - Created unique index on (employee_id, work_date) to enforce uniqueness. - Added trigger to enforce a 24-hour cap across worked and absence days for the same employee and date. - Developed tests to validate RLS, uniqueness, and 24-hour cap logic. * Fix: update hourly_salary calculation and refresh logic in salary run processing
84 lines
3.0 KiB
PL/PgSQL
84 lines
3.0 KiB
PL/PgSQL
-- Migration: 24-hour cap across worked + absence days for the same employee+date
|
|
--
|
|
-- Half-day mixing is allowed: an employee can have 4h worked + 4h sick on the
|
|
-- same date. The legal/biological constraint is that a single calendar day
|
|
-- has 24 hours — combined worked + absence hours must not exceed that.
|
|
--
|
|
-- This is enforced as a trigger (not a CHECK constraint) because CHECK cannot
|
|
-- reference another table. Two BEFORE INSERT/UPDATE triggers, one on each
|
|
-- table, both call the shared function below. The function sums the OTHER
|
|
-- table's hours for the same (employee_id, date), adds NEW.hours, and raises
|
|
-- if the total exceeds 24.
|
|
--
|
|
-- For UPDATE on the same row, we exclude the row's own previous contribution
|
|
-- from the sum (otherwise editing 8h → 6h on a worked day with 8h absence
|
|
-- would falsely report 16+8 > 24). This is handled by the WHERE id <> NEW.id
|
|
-- on the same-table side.
|
|
|
|
CREATE OR REPLACE FUNCTION public.check_salary_day_hours_cap()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_employee_id UUID;
|
|
v_date DATE;
|
|
v_other_total NUMERIC(6, 2);
|
|
v_same_total NUMERIC(6, 2);
|
|
v_total NUMERIC(6, 2);
|
|
BEGIN
|
|
-- Resolve the (employee, date) pair from whichever table fired the trigger.
|
|
IF TG_TABLE_NAME = 'salary_worked_days' THEN
|
|
v_employee_id := NEW.employee_id;
|
|
v_date := NEW.work_date;
|
|
|
|
SELECT COALESCE(SUM(hours), 0) INTO v_other_total
|
|
FROM public.salary_absence_days
|
|
WHERE employee_id = v_employee_id
|
|
AND absence_date = v_date;
|
|
|
|
SELECT COALESCE(SUM(hours), 0) INTO v_same_total
|
|
FROM public.salary_worked_days
|
|
WHERE employee_id = v_employee_id
|
|
AND work_date = v_date
|
|
AND id <> NEW.id;
|
|
|
|
ELSIF TG_TABLE_NAME = 'salary_absence_days' THEN
|
|
v_employee_id := NEW.employee_id;
|
|
v_date := NEW.absence_date;
|
|
|
|
SELECT COALESCE(SUM(hours), 0) INTO v_other_total
|
|
FROM public.salary_worked_days
|
|
WHERE employee_id = v_employee_id
|
|
AND work_date = v_date;
|
|
|
|
SELECT COALESCE(SUM(hours), 0) INTO v_same_total
|
|
FROM public.salary_absence_days
|
|
WHERE employee_id = v_employee_id
|
|
AND absence_date = v_date
|
|
AND id <> NEW.id;
|
|
|
|
ELSE
|
|
-- Defensive: should not be reachable given the trigger bindings below.
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
v_total := v_other_total + v_same_total + NEW.hours;
|
|
|
|
IF v_total > 24 THEN
|
|
RAISE EXCEPTION 'Total tid (arbete + frånvaro) för % får inte överstiga 24 timmar (försökte boka %, befintligt %)',
|
|
v_date, NEW.hours, (v_other_total + v_same_total)
|
|
USING ERRCODE = 'check_violation';
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER salary_worked_days_24h_cap
|
|
BEFORE INSERT OR UPDATE ON public.salary_worked_days
|
|
FOR EACH ROW EXECUTE FUNCTION public.check_salary_day_hours_cap();
|
|
|
|
CREATE TRIGGER salary_absence_days_24h_cap
|
|
BEFORE INSERT OR UPDATE ON public.salary_absence_days
|
|
FOR EACH ROW EXECUTE FUNCTION public.check_salary_day_hours_cap();
|
|
|
|
NOTIFY pgrst, 'reload schema';
|