Locations — Behavior
State
Single boolean is_active:
| State | Meaning |
|---|---|
is_active=true | Listed in findAll, selectable at session-create. Counts against maxLocations tier limit. |
is_active=false | Hidden from findAll. Existing sessions referencing it remain valid. |
DELETE /locations/:id performs update set is_active=false, never DELETE FROM.
Invariants
- Org-direct ownership —
locations.organization_idis the boundary. Every read/write filters on it (and(eq(locations.id, id), eq(locations.organizationId, orgId))). - No hard delete — deactivation only. Historical sessions retain a valid FK.
- Capacity does not gate bookings —
location.capacityis independent ofclass_session.capacity. Booking checks only the session’s capacity. - Tier limit checked at create only — moving from a higher tier down does not auto-deactivate excess locations. (TODO: verify behavior at tier-downgrade — there’s no explicit guard found.)
- Coords are an all-or-nothing — service does not reject saving only
latitudewithoutlongitude; both are nullable. GPS check-in is what enforces both must be present.
Golden paths
Owner: add a branch
/dashboard/settings/locations(or onboarding flow).- Click “Add a location”. Form: name, address (autocomplete fills city/country/lat/lng), capacity.
- POST
/organizations/:orgId/locations. Tier check runs first. - Row inserted; appears in selector when creating sessions.
Owner: deactivate
- Click delete. DELETE
/organizations/:orgId/locations/:id. is_active=false. Removed from selector.
Edge cases & error states
| Scenario | Behavior |
|---|---|
| Create when tier limit reached | 403 ‘Location limit reached (N/M). Upgrade your plan to add more.‘ |
| Coach/member tries to create/update/delete | 403 ‘Only owners and admins can …’ |
| Get/update/delete by id from another org | 404 ‘Location not found’ (WHERE includes organization_id=$orgId) |
| Member tries to GPS check-in to a session whose location has null coords | 400 ‘Session location coordinates are not configured’ (from ClassSessionsService.selfCheckin) |
Member tries GPS check-in but the session has location_id=NULL | 400 ‘Session location coordinates are not configured’ (same path; session.location is null) |
| Update with only some fields | Conditional spreads — only !== undefined fields apply |
Side effects
None outside the row mutation. No events, no notifications.
Permissions
| Action | owner | admin | coach | member |
|---|---|---|---|---|
| List | ✓ | ✓ | ✓ | ✓ |
| Get | ✓ | ✓ | ✓ | ✓ |
| Create | ✓ | ✓ | ✗ | ✗ |
| Update | ✓ | ✓ | ✗ | ✗ |
| Deactivate | ✓ | ✓ | ✗ | ✗ |
Active org membership required for all (requireMembership).