-- ===================================================== -- Waitlist Management -- -- A) Course cancellation with automatic waitlist promotion -- B) Event cancellation with automatic waitlist promotion -- -- When an enrolled/confirmed participant is cancelled, -- the oldest waitlisted entry is atomically promoted. -- ===================================================== -- ------------------------------------------------------- -- A) Course waitlist promotion -- ------------------------------------------------------- CREATE OR REPLACE FUNCTION public.cancel_course_enrollment(p_participant_id uuid) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$ DECLARE v_participant record; v_course record; v_promoted_id uuid; v_promoted_name text; BEGIN -- Lock participant SELECT * INTO v_participant FROM public.course_participants WHERE id = p_participant_id FOR UPDATE; IF v_participant IS NULL THEN RAISE EXCEPTION 'Teilnehmer nicht gefunden' USING ERRCODE = 'P0002'; END IF; -- Lock course SELECT * INTO v_course FROM public.courses WHERE id = v_participant.course_id FOR UPDATE; -- Cancel UPDATE public.course_participants SET status = 'cancelled'::public.enrollment_status, cancelled_at = now() WHERE id = p_participant_id; -- If was enrolled (not already waitlisted/cancelled), promote oldest waitlisted IF v_participant.status = 'enrolled' THEN UPDATE public.course_participants SET status = 'enrolled'::public.enrollment_status WHERE id = ( SELECT id FROM public.course_participants WHERE course_id = v_participant.course_id AND status = 'waitlisted' ORDER BY enrolled_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED ) RETURNING id, first_name || ' ' || last_name INTO v_promoted_id, v_promoted_name; END IF; RETURN jsonb_build_object( 'cancelled_id', p_participant_id, 'promoted_id', v_promoted_id, 'promoted_name', v_promoted_name ); END; $$; GRANT EXECUTE ON FUNCTION public.cancel_course_enrollment(uuid) TO authenticated; GRANT EXECUTE ON FUNCTION public.cancel_course_enrollment(uuid) TO service_role; -- ------------------------------------------------------- -- B) Event registration cancellation + waitlist promotion -- ------------------------------------------------------- -- Add updated_at column if not present ALTER TABLE public.event_registrations ADD COLUMN IF NOT EXISTS updated_at timestamptz DEFAULT now(); CREATE OR REPLACE FUNCTION public.cancel_event_registration(p_registration_id uuid) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path = '' AS $$ DECLARE v_registration record; v_event record; v_promoted_id uuid; v_promoted_name text; BEGIN -- Lock registration SELECT * INTO v_registration FROM public.event_registrations WHERE id = p_registration_id FOR UPDATE; IF v_registration IS NULL THEN RAISE EXCEPTION 'Anmeldung nicht gefunden' USING ERRCODE = 'P0002'; END IF; -- Lock event SELECT * INTO v_event FROM public.events WHERE id = v_registration.event_id FOR UPDATE; -- Cancel UPDATE public.event_registrations SET status = 'cancelled', updated_at = now() WHERE id = p_registration_id; -- If was confirmed or pending, promote oldest waitlisted IF v_registration.status IN ('confirmed', 'pending') THEN UPDATE public.event_registrations SET status = 'confirmed', updated_at = now() WHERE id = ( SELECT id FROM public.event_registrations WHERE event_id = v_registration.event_id AND status = 'waitlisted' ORDER BY created_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED ) RETURNING id, first_name || ' ' || last_name INTO v_promoted_id, v_promoted_name; END IF; RETURN jsonb_build_object( 'cancelled_id', p_registration_id, 'promoted_id', v_promoted_id, 'promoted_name', v_promoted_name ); END; $$; GRANT EXECUTE ON FUNCTION public.cancel_event_registration(uuid) TO authenticated; GRANT EXECUTE ON FUNCTION public.cancel_event_registration(uuid) TO service_role;