tasq/supabase/migrations/20260307120000_leave_of_absence.sql

148 lines
4.4 KiB
PL/PgSQL

-- Leave of Absence table
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'leave_type') THEN
CREATE TYPE leave_type AS ENUM (
'emergency_leave',
'parental_leave',
'sick_leave',
'vacation_leave'
);
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'leave_status') THEN
CREATE TYPE leave_status AS ENUM (
'pending',
'approved',
'rejected',
'cancelled'
);
END IF;
END $$;
CREATE TABLE IF NOT EXISTS leave_of_absence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
leave_type leave_type NOT NULL,
justification TEXT NOT NULL,
start_time TIMESTAMPTZ NOT NULL,
end_time TIMESTAMPTZ NOT NULL,
status leave_status NOT NULL DEFAULT 'pending',
filed_by UUID NOT NULL REFERENCES auth.users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT valid_time_range CHECK (end_time > start_time)
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_leave_user_id ON leave_of_absence(user_id);
CREATE INDEX IF NOT EXISTS idx_leave_start_time ON leave_of_absence(start_time);
CREATE INDEX IF NOT EXISTS idx_leave_status ON leave_of_absence(status);
-- RLS
ALTER TABLE leave_of_absence ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "Privileged users can view all leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Users can view own leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Privileged users can file own leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Users can file own leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Admins can approve or reject leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Users can cancel own future approved leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Admins can cancel future approved leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Privileged users can update leaves" ON leave_of_absence;
DROP POLICY IF EXISTS "Users can cancel own pending leaves" ON leave_of_absence;
-- Admin/dispatcher/it_staff can see all leaves
CREATE POLICY "Privileged users can view all leaves"
ON leave_of_absence FOR SELECT
USING (
EXISTS (
SELECT 1 FROM profiles
WHERE profiles.id = auth.uid()
AND profiles.role IN ('admin', 'dispatcher', 'it_staff')
)
);
-- All users can see their own leaves
CREATE POLICY "Users can view own leaves"
ON leave_of_absence FOR SELECT
USING (user_id = auth.uid());
-- Only admin/dispatcher/it_staff can file leaves (for themselves)
CREATE POLICY "Privileged users can file own leaves"
ON leave_of_absence FOR INSERT
WITH CHECK (
user_id = auth.uid()
AND filed_by = auth.uid()
AND EXISTS (
SELECT 1 FROM profiles
WHERE profiles.id = auth.uid()
AND profiles.role IN ('admin', 'dispatcher', 'it_staff')
)
);
-- Only admins can approve/reject pending leaves
CREATE POLICY "Admins can approve or reject leaves"
ON leave_of_absence FOR UPDATE
USING (
status = 'pending'
AND EXISTS (
SELECT 1 FROM profiles
WHERE profiles.id = auth.uid()
AND profiles.role = 'admin'
)
)
WITH CHECK (
status IN ('approved', 'rejected')
);
-- Users can cancel their own future approved leaves
CREATE POLICY "Users can cancel own future approved leaves"
ON leave_of_absence FOR UPDATE
USING (
user_id = auth.uid()
AND status = 'approved'
AND start_time > now()
)
WITH CHECK (
status = 'cancelled'
);
-- Admins can cancel future approved leaves
CREATE POLICY "Admins can cancel future approved leaves"
ON leave_of_absence FOR UPDATE
USING (
status = 'approved'
AND start_time > now()
AND EXISTS (
SELECT 1 FROM profiles
WHERE profiles.id = auth.uid()
AND profiles.role = 'admin'
)
)
WITH CHECK (
status = 'cancelled'
);
-- Drop legacy reason column if it exists from an earlier migration
ALTER TABLE leave_of_absence DROP COLUMN IF EXISTS reason;
-- Updated_at trigger
CREATE OR REPLACE FUNCTION update_leave_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trigger_leave_updated_at ON leave_of_absence;
CREATE TRIGGER trigger_leave_updated_at
BEFORE UPDATE ON leave_of_absence
FOR EACH ROW
EXECUTE FUNCTION update_leave_updated_at();