CompanyLocation
CompanyLocation is a pivot (bridge) entity that links a Company to a Location with junction-level metadata. It records whether a given address is the company's primary address (is_primary), whether it is the registered legal address (is_legal), and a human-readable label (e.g. "Headquarters", "Billing"). A partial unique index enforces at most one primary location per company at any given time (scoped to non-deleted rows). The entity carries no public UUID of its own; in JSON:API responses the id is borrowed from the linked Location record.
| Naming | Value |
|---|---|
| Object | CompanyLocation |
Resource type (JSON:API type) | location |
| Collection / records root | β (not a records root) |
| REST base | /v1/company-locations |
| Entity class | CompanyLocation |
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/company-locations | π‘ Planned |
| Retrieve | GET /v1/company-locations/{id} | π‘ Planned |
| Create | POST /v1/company-locations | π‘ Planned |
| Update | PATCH /v1/company-locations/{id} | π‘ Planned |
| Delete | DELETE /v1/company-locations/{id} | π‘ Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|---|---|---|---|---|
| is_primary | boolean | β Yes | Partial unique index: at most one row per company_pk WHERE deleted_at IS NULL AND is_primary IS TRUE | true / false | Marks this address as the company's primary location. Enforced unique per company among non-deleted rows. |
| is_legal | boolean | β Yes | No uniqueness constraint; multiple legal addresses allowed. | true / false | Marks this address as the company's registered legal address. Does not enforce uniqueness. |
| label | string | β Yes | varchar(255) | β | Free-text label describing the address role (e.g. "Headquarters", "Billing", "Warehouse"). |
| created_at | π system β Date | β Yes | Set on insert via MikroORM onCreate hook; never updated. | β | Timestamp when this companyβlocation pivot row was created. |
| deleted_at | Date | null | βͺ No | Nullable; null = active row. Soft-delete pattern β hard deletes are not performed. | ISO 8601 timestamp or null | Soft-delete timestamp. When set, the pivot is considered removed and filtered from all active queries. Drives the partial unique index on is_primary. |
Relationships
| Name | Type | Required | Description |
|---|---|---|---|
| company | to-one (ManyToOne) | β Yes | The Company that owns this address. FK: company_pk β core_api.companies.pk. Indexed via idx_company_locations_company_deleted (company_pk, deleted_at). |
| location | to-one (ManyToOne) | β Yes | The Location (atomic address record) this pivot points to. FK: location_pk β core_api.locations.pk. Indexed via idx_company_locations_location (location_pk). The Location's public location_id UUID is used as the JSON:API id for this pivot resource. |
System-computed
- created_at β set on INSERT via MikroORM @Property({ onCreate: () => new Date() }); no updated_at on this entity.
- deleted_at β written by soft-delete logic (never hard-deleted); filters the partial unique index for is_primary enforcement.
- Partial unique index uniq_company_locations_primary_company β enforced by the database: CREATE UNIQUE INDEX ... ON core_api.company_locations (company_pk) WHERE deleted_at IS NULL AND is_primary IS TRUE. Prevents duplicate primary addresses at the DB level.
- No public UUID on the pivot itself β JSON:API id in responses is the linked Location's location_id (gen_random_uuid on Location, not CompanyLocation).
- Pivot rows are written by the connector sync pipeline (enrichment / reconciliation) and via the POST /v1/workspaces/:workspace_id/companies/:company_id/locations endpoint. The
is_legalflag is set by the pipeline; users cannot toggle it. - Composite index idx_company_locations_company_deleted (company_pk, deleted_at) added in Migration20260416100000 to cover array-relationship traversal (companies β company_locations) after sequential-scan regressions.
Example
{
"data": {
"type": "location",
"id": "a3f7c281-9e4b-4d02-b1e5-6c8d2f3a0b12",
"attributes": {
"full_address": "15 Rue de la Paix, 75001 Paris, France",
"address_line1": "15 Rue de la Paix",
"address_line2": null,
"city": "Paris",
"region": "Γle-de-France",
"postal_code": "75001",
"country": "FR",
"is_primary": true,
"is_legal": true,
"label": "Headquarters",
"created_at": "2025-11-03T14:22:00.000Z",
"deleted_at": null
}
}
}apps/api/src/database/entities/CompanyLocation.ts Β· domain: financial-graph Β· tier: Supporting