tasq/supabase/migrations/20260308090000_add_it_service_requests.sql

261 lines
10 KiB
PL/PgSQL

-- =============================================================================
-- IT Service Request Module
-- =============================================================================
-- 1. Main table
CREATE TABLE IF NOT EXISTS it_service_requests (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
request_number text UNIQUE,
-- Services (checkboxes from the form)
services text[] NOT NULL DEFAULT '{}',
services_other text,
-- Event/Activity Details (Quill Delta JSON)
event_name text NOT NULL,
event_details text, -- Quill delta JSON
-- Scheduling
event_date timestamptz,
event_end_date timestamptz,
dry_run_date timestamptz,
dry_run_end_date timestamptz,
-- Contact
contact_person text,
contact_number text,
-- Remarks (Quill Delta JSON)
remarks text,
-- Office (Department in the form)
office_id uuid REFERENCES offices(id) ON DELETE SET NULL,
-- Signatories
requested_by text, -- Name of requester (default: standard user who created)
requested_by_user_id uuid REFERENCES auth.users(id) ON DELETE SET NULL,
approved_by text, -- Name of approver (admin only)
approved_by_user_id uuid REFERENCES auth.users(id) ON DELETE SET NULL,
approved_at timestamptz,
-- Status lifecycle: draft → pending_approval → scheduled → in_progress_dry_run → in_progress → completed → cancelled
status text NOT NULL DEFAULT 'draft' CHECK (
status IN ('draft', 'pending_approval', 'scheduled', 'in_progress_dry_run', 'in_progress', 'completed', 'cancelled')
),
-- Geofence override: allow IT staff to check in/out outside CRMC premise
outside_premise_allowed boolean NOT NULL DEFAULT false,
-- Cancellation
cancellation_reason text,
cancelled_at timestamptz,
-- Metadata
creator_id uuid REFERENCES auth.users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
completed_at timestamptz,
-- Date/Time Received/Checked from the form
date_time_received timestamptz,
date_time_checked timestamptz
);
-- Auto-update updated_at
CREATE OR REPLACE FUNCTION update_it_service_requests_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER it_service_requests_updated_at_trigger
BEFORE UPDATE ON it_service_requests
FOR EACH ROW
EXECUTE FUNCTION update_it_service_requests_updated_at();
-- 2. IT Service Request Assignments (IT Staff assigned to the request)
CREATE TABLE IF NOT EXISTS it_service_request_assignments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
request_id uuid NOT NULL REFERENCES it_service_requests(id) ON DELETE CASCADE,
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(request_id, user_id)
);
-- 3. Activity Logs for audit trail
CREATE TABLE IF NOT EXISTS it_service_request_activity_logs (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
request_id uuid NOT NULL REFERENCES it_service_requests(id) ON DELETE CASCADE,
actor_id uuid REFERENCES auth.users(id) ON DELETE SET NULL,
action_type text NOT NULL,
meta jsonb,
created_at timestamptz NOT NULL DEFAULT now()
);
-- 4. Action Taken (separate table with evidence attachments)
CREATE TABLE IF NOT EXISTS it_service_request_actions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
request_id uuid NOT NULL REFERENCES it_service_requests(id) ON DELETE CASCADE,
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
action_taken text, -- Quill Delta JSON
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
-- Auto-update updated_at for actions
CREATE TRIGGER it_service_request_actions_updated_at_trigger
BEFORE UPDATE ON it_service_request_actions
FOR EACH ROW
EXECUTE FUNCTION update_it_service_requests_updated_at();
-- 5. Evidence Attachments (images only, for action taken)
CREATE TABLE IF NOT EXISTS it_service_request_evidence (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
request_id uuid NOT NULL REFERENCES it_service_requests(id) ON DELETE CASCADE,
action_id uuid REFERENCES it_service_request_actions(id) ON DELETE CASCADE,
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
file_path text NOT NULL,
file_name text NOT NULL,
taken_at timestamptz, -- Extracted from image EXIF metadata
created_at timestamptz NOT NULL DEFAULT now()
);
-- 6. Request Number Generation RPC
CREATE OR REPLACE FUNCTION insert_it_service_request_with_number(
p_event_name text,
p_services text[],
p_creator_id uuid,
p_office_id uuid DEFAULT NULL,
p_requested_by text DEFAULT NULL,
p_requested_by_user_id uuid DEFAULT NULL,
p_status text DEFAULT 'draft'
)
RETURNS TABLE(id uuid, request_number text) AS $$
DECLARE
v_seq int;
v_id uuid;
v_number text;
BEGIN
-- Get next sequence number for the current year
SELECT COALESCE(MAX(
CAST(NULLIF(regexp_replace(r.request_number, '^ISR-\d{4}-', ''), '') AS int)
), 0) + 1
INTO v_seq
FROM it_service_requests r
WHERE r.request_number LIKE 'ISR-' || EXTRACT(YEAR FROM now())::text || '-%';
v_number := 'ISR-' || EXTRACT(YEAR FROM now())::text || '-' || LPAD(v_seq::text, 4, '0');
v_id := gen_random_uuid();
INSERT INTO it_service_requests (id, request_number, event_name, services, creator_id, office_id, requested_by, requested_by_user_id, status)
VALUES (v_id, v_number, p_event_name, p_services, p_creator_id, p_office_id, p_requested_by, p_requested_by_user_id, p_status);
RETURN QUERY SELECT v_id, v_number;
END;
$$ LANGUAGE plpgsql;
-- 7. Indexes
CREATE INDEX idx_it_service_requests_status ON it_service_requests(status);
CREATE INDEX idx_it_service_requests_office_id ON it_service_requests(office_id);
CREATE INDEX idx_it_service_requests_creator_id ON it_service_requests(creator_id);
CREATE INDEX idx_it_service_requests_event_date ON it_service_requests(event_date);
CREATE INDEX idx_it_service_request_assignments_request_id ON it_service_request_assignments(request_id);
CREATE INDEX idx_it_service_request_assignments_user_id ON it_service_request_assignments(user_id);
CREATE INDEX idx_it_service_request_activity_logs_request_id ON it_service_request_activity_logs(request_id);
CREATE INDEX idx_it_service_request_actions_request_id ON it_service_request_actions(request_id);
CREATE INDEX idx_it_service_request_evidence_request_id ON it_service_request_evidence(request_id);
-- 8. Enable RLS
ALTER TABLE it_service_requests ENABLE ROW LEVEL SECURITY;
ALTER TABLE it_service_request_assignments ENABLE ROW LEVEL SECURITY;
ALTER TABLE it_service_request_activity_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE it_service_request_actions ENABLE ROW LEVEL SECURITY;
ALTER TABLE it_service_request_evidence ENABLE ROW LEVEL SECURITY;
-- 9. RLS Policies
-- IT Service Requests: authenticated can read all, insert own, update depending on role
CREATE POLICY "Authenticated users can read it_service_requests"
ON it_service_requests FOR SELECT TO authenticated USING (true);
CREATE POLICY "Authenticated users can insert it_service_requests"
ON it_service_requests FOR INSERT TO authenticated
WITH CHECK (auth.uid() = creator_id);
CREATE POLICY "Authenticated users can update it_service_requests"
ON it_service_requests FOR UPDATE TO authenticated
USING (true);
-- Assignments
CREATE POLICY "Authenticated users can read it_service_request_assignments"
ON it_service_request_assignments FOR SELECT TO authenticated USING (true);
CREATE POLICY "Authenticated users can insert it_service_request_assignments"
ON it_service_request_assignments FOR INSERT TO authenticated
WITH CHECK (true);
CREATE POLICY "Authenticated users can delete it_service_request_assignments"
ON it_service_request_assignments FOR DELETE TO authenticated
USING (true);
-- Activity Logs
CREATE POLICY "Authenticated users can read it_service_request_activity_logs"
ON it_service_request_activity_logs FOR SELECT TO authenticated USING (true);
CREATE POLICY "Authenticated users can insert it_service_request_activity_logs"
ON it_service_request_activity_logs FOR INSERT TO authenticated
WITH CHECK (true);
-- Actions
CREATE POLICY "Authenticated users can read it_service_request_actions"
ON it_service_request_actions FOR SELECT TO authenticated USING (true);
CREATE POLICY "Authenticated users can insert it_service_request_actions"
ON it_service_request_actions FOR INSERT TO authenticated
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Authenticated users can update it_service_request_actions"
ON it_service_request_actions FOR UPDATE TO authenticated
USING (auth.uid() = user_id);
-- Evidence
CREATE POLICY "Authenticated users can read it_service_request_evidence"
ON it_service_request_evidence FOR SELECT TO authenticated USING (true);
CREATE POLICY "Authenticated users can insert it_service_request_evidence"
ON it_service_request_evidence FOR INSERT TO authenticated
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Authenticated users can delete it_service_request_evidence"
ON it_service_request_evidence FOR DELETE TO authenticated
USING (auth.uid() = user_id);
-- 10. Add it_service_request_id to notifications table
ALTER TABLE notifications ADD COLUMN IF NOT EXISTS it_service_request_id uuid REFERENCES it_service_requests(id) ON DELETE CASCADE;
-- 11. Storage bucket for IT service request files
INSERT INTO storage.buckets (id, name, public, file_size_limit)
VALUES ('it_service_attachments', 'it_service_attachments', true, 26214400)
ON CONFLICT (id) DO NOTHING;
-- Storage policies
CREATE POLICY "Authenticated users can upload it_service_attachments"
ON storage.objects FOR INSERT TO authenticated
WITH CHECK (bucket_id = 'it_service_attachments');
CREATE POLICY "Authenticated users can read it_service_attachments"
ON storage.objects FOR SELECT TO authenticated
USING (bucket_id = 'it_service_attachments');
CREATE POLICY "Authenticated users can delete it_service_attachments"
ON storage.objects FOR DELETE TO authenticated
USING (bucket_id = 'it_service_attachments');
-- 12. Realtime publication
ALTER PUBLICATION supabase_realtime ADD TABLE it_service_requests;
ALTER PUBLICATION supabase_realtime ADD TABLE it_service_request_assignments;
ALTER PUBLICATION supabase_realtime ADD TABLE it_service_request_activity_logs;
ALTER PUBLICATION supabase_realtime ADD TABLE it_service_request_actions;