Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesLocationsLocations — Data Model

Locations — Data Model

Table: locations in libs/db/src/lib/schema/scheduling.ts.

Columns

ColumnTypeNotes
iduuid PKdefaultRandom()
organization_iduuid NOT NULLFK → organizations.id. Direct org boundary.
namevarchar(255) NOT NULLDisplay name.
addresstextFree text; not normalized.
cityvarchar(255)
countryvarchar(100)
latitudedouble precisionDecimal degrees. Nullable.
longitudedouble precisionDecimal degrees. Nullable.
capacityintInformational; not used in booking logic.
is_activebool NOT NULL default trueSoft-deactivate flag.
created_at / updated_attimestamptz
deleted_attimestamptzColumn exists; not written by the service — soft-delete is via is_active=false.

Indexes / constraints

Only the PK. No UNIQUE; multiple locations can share a name within an org.

Relations

  • organization — one mandatory.
  • classSessions — one-to-many (nullable on the session side).

Lifecycle

  1. CreateINSERT with is_active=true. Tier check runs against count(active locations in org). Address fields and coords are optional.
  2. Update — partial PATCH; all fields conditional on !== undefined. Cannot change organization_id.
  3. DeactivateUPDATE … SET is_active=false. Historical sessions keep their FK.

Multi-org isolation pattern

locations has a direct organization_id FK. Every read/write filters on eq(locations.organizationId, orgId). No FK indirection like class_types.

Soft-delete vs hard-delete

Soft only, via is_active. The deleted_at column is unused but present for schema parity.

GPS check-in usage

ClassSessionsService.selfCheckin reads:

session.location?.latitude session.location?.longitude

Both must be non-null. If either is missing or the session has no location, selfCheckin with method='gps' returns 400 'Session location coordinates are not configured'. The distance check is Haversine (haversineMeters) compared against GPS_MAX_METERS (see class-sessions/utils/checkin.utils.ts).