refactor: remove obsolete member management API module
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
-- =====================================================
|
||||
-- Atomic Event Registration
|
||||
--
|
||||
-- Problem: Registering for an event requires multiple
|
||||
-- queries (check capacity, validate age, count registrations,
|
||||
-- insert). Race conditions can over-register an event.
|
||||
--
|
||||
-- Fix:
|
||||
-- A) Ensure member_id FK column exists on event_registrations
|
||||
-- (idempotent — may already exist from 20260416000006).
|
||||
-- B) Single transactional PG function that locks the event
|
||||
-- row, validates capacity/age, and inserts with the
|
||||
-- correct status (confirmed vs waitlisted).
|
||||
-- =====================================================
|
||||
|
||||
-- A) Add member_id column if not already present
|
||||
ALTER TABLE public.event_registrations
|
||||
ADD COLUMN IF NOT EXISTS member_id uuid
|
||||
REFERENCES public.members(id) ON DELETE SET NULL;
|
||||
|
||||
-- Ensure index exists (idempotent)
|
||||
CREATE INDEX IF NOT EXISTS ix_event_registrations_member
|
||||
ON public.event_registrations(member_id)
|
||||
WHERE member_id IS NOT NULL;
|
||||
|
||||
-- The status CHECK constraint already includes 'waitlisted' in the
|
||||
-- original schema: check (status in ('pending','confirmed','waitlisted','cancelled'))
|
||||
-- No constraint modification needed.
|
||||
|
||||
-- B) Atomic registration function
|
||||
CREATE OR REPLACE FUNCTION public.register_for_event(
|
||||
p_event_id uuid,
|
||||
p_member_id uuid DEFAULT NULL,
|
||||
p_first_name text DEFAULT NULL,
|
||||
p_last_name text DEFAULT NULL,
|
||||
p_email text DEFAULT NULL,
|
||||
p_phone text DEFAULT NULL,
|
||||
p_date_of_birth date DEFAULT NULL,
|
||||
p_parent_name text DEFAULT NULL,
|
||||
p_parent_phone text DEFAULT NULL
|
||||
)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
DECLARE
|
||||
v_event record;
|
||||
v_reg_count bigint;
|
||||
v_status text;
|
||||
v_age integer;
|
||||
v_registration_id uuid;
|
||||
BEGIN
|
||||
-- 1. Lock the event row to prevent concurrent registration races
|
||||
SELECT * INTO v_event
|
||||
FROM public.events
|
||||
WHERE id = p_event_id
|
||||
FOR UPDATE;
|
||||
|
||||
IF v_event IS NULL THEN
|
||||
RAISE EXCEPTION 'Event % not found', p_event_id
|
||||
USING ERRCODE = 'P0002';
|
||||
END IF;
|
||||
|
||||
-- 2. Validate event status is open for registration
|
||||
IF v_event.status != 'open' THEN
|
||||
RAISE EXCEPTION 'Event is not open for registration (current status: %)', v_event.status
|
||||
USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
-- 3. Check registration deadline hasn't passed
|
||||
IF v_event.registration_deadline IS NOT NULL AND v_event.registration_deadline < current_date THEN
|
||||
RAISE EXCEPTION 'Registration deadline (%) has passed', v_event.registration_deadline
|
||||
USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
-- 4. Age validation: calculate age at event_date if date_of_birth provided
|
||||
IF p_date_of_birth IS NOT NULL THEN
|
||||
v_age := extract(year FROM age(v_event.event_date, p_date_of_birth))::integer;
|
||||
|
||||
IF v_event.min_age IS NOT NULL AND v_age < v_event.min_age THEN
|
||||
RAISE EXCEPTION 'Participant age (%) is below the minimum age (%) for this event', v_age, v_event.min_age
|
||||
USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
|
||||
IF v_event.max_age IS NOT NULL AND v_age > v_event.max_age THEN
|
||||
RAISE EXCEPTION 'Participant age (%) exceeds the maximum age (%) for this event', v_age, v_event.max_age
|
||||
USING ERRCODE = 'P0001';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
-- 5. Count confirmed + pending registrations
|
||||
SELECT count(*) INTO v_reg_count
|
||||
FROM public.event_registrations
|
||||
WHERE event_id = p_event_id
|
||||
AND status IN ('confirmed', 'pending');
|
||||
|
||||
-- 6. Determine status based on capacity
|
||||
IF v_event.capacity IS NOT NULL AND v_reg_count >= v_event.capacity THEN
|
||||
v_status := 'waitlisted';
|
||||
ELSE
|
||||
v_status := 'confirmed';
|
||||
END IF;
|
||||
|
||||
-- 7. Insert the registration
|
||||
INSERT INTO public.event_registrations (
|
||||
event_id,
|
||||
member_id,
|
||||
first_name,
|
||||
last_name,
|
||||
email,
|
||||
phone,
|
||||
date_of_birth,
|
||||
parent_name,
|
||||
parent_phone,
|
||||
status
|
||||
) VALUES (
|
||||
p_event_id,
|
||||
p_member_id,
|
||||
p_first_name,
|
||||
p_last_name,
|
||||
p_email,
|
||||
p_phone,
|
||||
p_date_of_birth,
|
||||
p_parent_name,
|
||||
p_parent_phone,
|
||||
v_status
|
||||
)
|
||||
RETURNING id INTO v_registration_id;
|
||||
|
||||
-- 8. Return result
|
||||
RETURN jsonb_build_object(
|
||||
'registration_id', v_registration_id,
|
||||
'status', v_status
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.register_for_event(uuid, uuid, text, text, text, text, date, text, text) TO authenticated;
|
||||
GRANT EXECUTE ON FUNCTION public.register_for_event(uuid, uuid, text, text, text, text, date, text, text) TO service_role;
|
||||
Reference in New Issue
Block a user