feat(events): waitlist and capacity enforcement#294
Open
BrunaDomingues wants to merge 5 commits into
Open
Conversation
Treat confirmed, checked-in, and attended enrollments as occupying seats when resolving RSVP capacity and waitlist placement.
Emit after-commit event when RSVP enrollment is placed on the waitlist, for future notifications.
Display waitlist position after RSVP and when the event has no remaining seats without a waitlist.
Assert 422 on full events, fifo waitlist positions, and that active seats never exceed capacity under rapid enrollments.
YuriSouzaDev
approved these changes
May 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes atomic capacity enforcement and waitlist (FIFO) on top of the RSVP flow (#275): when an event is full, participants are waitlisted if the policy allows, or rejected with
422otherwise. Occupied seats countconfirmed,checked_in, andattendedenrollments; audit trail and domain events are emitted on enroll.Capacity & waitlist (
EnrollUserAction)scopeActive()onEnrollment:confirmed+checked_in+attendedoccupy capacity (replaces counting onlyconfirmedin capacity resolution)DB::transaction,lockForUpdate()on event and enrollment policy (unchanged from feat(events): rsvp enrollment end-to-end #275; ensures fresh state under concurrent enrollments)capacityisnull→ alwaysconfirmedactivecount< capacity→confirmed+EnrollmentConfirmedactivecount>= capacityandhas_waitlist=true→waitlistedwithwaitlist_position = max(position) + 1+EnrollmentWaitlisted(noEnrollmentConfirmed)activecount>= capacityandhas_waitlist=false→EnrollmentException::eventFull()(HTTP 422)EnrollmentTransitionrecorded for every enroll (from_status=null,to_status=<initial>,triggered_by=user)Domain event
EnrollmentWaitlisted: implementsShouldDispatchAfterCommit(payload:enrollment_id,event_id,user_id,waitlist_position) — no listener in this sliceApp panel (participant)
waitlistedcanConfirmPresence/isEventFulluseactive()scope (aligned with backend)Admin
waitlist_positioncolumn (toggleable)Scopes (
Enrollmentmodel)scopeConfirmed(),scopeWaitlisted(),scopeActive()—active= capacity-occupying statuses onlyArchitecture
Events module └── Enrollment domain ├── EnrollUserAction (capacity via active() + lockForUpdate) ├── EnrollmentWaitlisted (domain event) └── Enrollment ├── scopeConfirmed / scopeWaitlisted / scopeActive
App panel └── EventDetail (+ waitlist copy, event full state)
Admin panel └── EnrollmentsRelationManager (+ waitlist_position column)
Files (high level)
EnrollUserAction(active count, dispatchEnrollmentWaitlisted)EnrollmentWaitlistedEnrollment(scopeActivefix)EnrollmentStatus::getResponseMessage(?waitlistPosition)en/pt_BRpages(waitlist position, event full)EventDetail,event-detail.blade.phpEnrollmentsRelationManagerEnrollmentScopeTest,EnrollUserActionTest(+ capacity/FIFO/422/unlimited),RsvpEnrollmentTest(+ UI)Out of scope (future slices)
EnrollmentWaitlistedEnrollmentConfirmedCloses #241
Parent: #237
Blocked by / builds on: #240, #275
Test plan
php artisan test --filter=EnrollmentScopeTestphp artisan test --filter=EnrollUserActionTestphp artisan test --filter=RsvpEnrollmentTest./vendor/bin/pint --testAdmin
capacityandhas_waitlistwaitlistedApp — capacity / waitlist
checked_inoccupies last seat → new user → waitlisted (validatesactive()scope)App — API / action edge cases
EnrollmentException/ 422 (event_full)