-- ============================================================================= -- 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;