Skip to content

feat(events): waitlist and capacity enforcement #241

@danielhe4rt

Description

@danielhe4rt

Parent

#237

What to build

Add atomic capacity checking to the enrollment flow. When an event is full, participants are placed on a waitlist (FIFO) if the policy allows, otherwise enrollment is rejected.

Modify EnrollUserAction:

  • Inside DB::transaction, use lockForUpdate() on the enrollment policy to prevent race conditions
  • Count current confirmed + checked_in + attended enrollments for the event
  • If count < capacity (or capacity is null/unlimited): confirm as before
  • If count >= capacity AND has_waitlist = true: create enrollment with status waitlisted, assign waitlist_position (max position + 1)
  • If count >= capacity AND has_waitlist = false: throw validation exception (422 "Event is full")
  • Record appropriate transition in audit trail

Domain event:

  • Dispatch EnrollmentWaitlisted (for future notifications) implementing ShouldDispatchAfterCommit

Admin panel:

  • Waitlist column visible in enrollments RelationManager
  • Filter enrollments by status (confirmed, waitlisted, etc.)

App panel:

  • Show "You are on the waitlist (position X)" when status is waitlisted
  • Show "Event is full" message when capacity reached and no waitlist

Scopes on EventEnrollment model:

  • scopeConfirmed(), scopeWaitlisted(), scopeActive() (confirmed + checked_in + attended)

Acceptance criteria

  • Enrollment respects capacity limit — no overbooking under concurrent requests
  • Waitlisted enrollment gets correct waitlist_position (FIFO)
  • Event without waitlist returns 422 when full
  • Event with unlimited capacity (null) always confirms
  • lockForUpdate() is used inside transaction for atomicity
  • Admin panel shows waitlist status and position
  • App panel shows waitlist position to participant
  • Feature tests: capacity enforcement, waitlist assignment, concurrent enrollment simulation, unlimited capacity
  • Pint passes

Blocked by

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions