Class Types — Behavior
State
Class types do not have a status machine. They have a single boolean is_active:
| State | Meaning |
|---|---|
is_active=true | Surfaces in findAll, allowed as class_type_id for new sessions. |
is_active=false | Hidden from findAll. Existing class sessions still reference it — soft-deactivate, not cascade. |
DELETE /class-types/:id performs update set is_active=false, never DELETE FROM. The row stays so historical sessions, bookings, and analytics keep their referential integrity.
Invariants
- Program must belong to the same org as the caller’s membership. Create + update both verify
program.organizationId === orgIdbefore mutating (class-types.service.ts:33-42,:115-121). - Class type cannot be reassigned to another program — there is no
programIdfield onUpdateClassTypeDto. - No hard delete — deactivation only. This is load-bearing for the schedule history.
- Org isolation via the program — class types have no direct
organization_idcolumn.
Golden paths
Owner: add a new class
- Go to
/dashboard/schedule→ “Tracks” / class types tab. - Click “Add class type”, pick parent program from a dropdown, name it, set defaults (60 min, capacity 20, color), toggle
hasWorkoutif it’s a no-program class. POST /organizations/:orgId/class-typesreturns the new row + program metadata.- The class type appears in the create-session form.
Owner: deactivate
- Open class type; click “Delete”.
DELETE /organizations/:orgId/class-types/:id.- The class type disappears from
findAll. Historical sessions still resolveclassType(relational query returns the row regardless ofis_active).
Edge cases & error states
| Scenario | Behavior |
|---|---|
Create with programId belonging to a different org | BadRequestException('Program not found in this organization') |
| Member or coach tries to create/update/delete | ForbiddenException('Only owners and admins can …') |
| Get class type by id from another org | NotFoundException('Class type not found') (post-fetch org check) |
| Update with empty body | Returns the row with updated_at bumped; all field updates are conditional on !== undefined. |
| Deactivate a class type with active future sessions | Sessions remain. UI calendar still shows them. Booking still works. (TODO: confirm whether dashboard surfaces a warning.) |
Side effects
None outside the row mutation itself. No events emitted, no notifications, no cascades.
Permissions
| Action | owner | admin | coach | member |
|---|---|---|---|---|
| List | ✓ | ✓ | ✓ | ✓ |
| Get by id | ✓ | ✓ | ✓ | ✓ |
| Create | ✓ | ✓ | ✗ | ✗ |
| Update | ✓ | ✓ | ✗ | ✗ |
| Deactivate | ✓ | ✓ | ✗ | ✗ |
All endpoints require an active membership in the org; cross-org access fails at requireMembership (403) or the program.organizationId post-check (404).