PersonLocation
PersonLocation is a pivot (bridge) entity that links a People record to a Location record, recording whether that address is the person's primary address and whether it serves a legal purpose. It mirrors the company_locations pattern applied to individuals. The entity is workspace-scoped indirectly through its owning People record, carries soft-delete semantics via deleted_at, and enforces a partial-unique constraint ensuring at most one primary address per person among non-deleted rows.
| Naming | Value |
|---|---|
| Object | PersonLocation |
Resource type (JSON:API type) | person_location |
| Collection / records root | โ (not a records root) |
| REST base | /v1/person-locations |
| Entity class | PersonLocation |
Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.
API operations
| Operation | Method & path | Status |
|---|---|---|
| List | GET /v1/person-locations | ๐ก Planned |
| Retrieve | GET /v1/person-locations/{id} | ๐ก Planned |
| Create | POST /v1/person-locations | ๐ก Planned |
| Update | PATCH /v1/person-locations/{id} | ๐ก Planned |
| Delete | DELETE /v1/person-locations/{id} | ๐ก Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|---|---|---|---|---|
| is_primary | boolean | โ Yes | Partial unique index: only one row per person_pk may have is_primary = TRUE where deleted_at IS NULL (index uniq_person_locations_primary_person) | true | false | Marks this address as the person's primary (canonical) address. At most one non-deleted PersonLocation per person may be primary. |
| is_legal | boolean | โ Yes | Not null | true | false | Indicates whether this address is the person's legal domicile (registered address for official/legal purposes). |
| label | string | โ Yes | varchar(255), not null | โ | Human-readable label categorising the address (e.g. 'Home', 'Work', 'Billing'). Free-form text up to 255 characters. |
| created_at | ๐ system โ Date | โ Yes | timestamptz, set on insert via onCreate hook, never updated | โ | Timestamp when this pivot row was created. System-set on insert; not writable by the user. |
| deleted_at | Date | null | โช No | timestamptz, nullable; NULL means active; partial indexes filter WHERE deleted_at IS NULL | โ | Soft-delete timestamp. When set, the row is logically deleted and excluded from all active queries. System-managed; not directly patchable. |
Relationships
| Name | Type | Required | Description |
|---|---|---|---|
| person | to-one (ManyToOne) | โ Yes | The People record this address is attached to. Foreign key person_pk โ core_api.people.pk. Indexed via partial index idx_person_locations_person (WHERE deleted_at IS NULL). |
| location | to-one (ManyToOne) | โ Yes | The Location record (address details) associated with this pivot. Foreign key location_pk โ core_api.locations.pk. Indexed via idx_person_locations_location. |
System-computed
- pk โ auto-increment serial integer, internal join key only; never exposed on the public API.
- created_at โ set to new Date() via @Property({ onCreate }) on insert; no subsequent updates (no updated_at column on this entity).
- deleted_at โ soft-delete sentinel; null on creation; set by the application soft-delete service, never by user PATCH.
- Partial unique index uniq_person_locations_primary_person enforces at most one is_primary = TRUE row per person_pk among non-deleted rows โ maintained by the database, not application code.
- The entity has no updated_at column; mutations that change is_primary or label do not produce a timestamp trail beyond created_at.
- No UUID public id column โ PersonLocation does not carry a *_id UUID field; it is identified externally via the combination of person relationship + location relationship.
- No workspace_pk column โ tenant isolation is inherited transitively through the person โ workspace path; Hasura RLS walks this relationship chain.
Example
{
"data": {
"type": "person_location",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"attributes": {
"is_primary": true,
"is_legal": false,
"label": "Home",
"created_at": "2025-09-14T10:23:00.000Z",
"deleted_at": null
},
"relationships": {
"person": {
"data": { "type": "people", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
},
"location": {
"data": { "type": "location", "id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210" }
}
}
}
}apps/api/src/database/entities/PersonLocation.ts ยท domain: financial-graph ยท tier: Supporting