Appearance
Entity: document
Entity Type: Database table
Description: A metadata record about an uploaded file attached to a building — invoices, photos, inspection reports, contract PDFs, and similar. The binary itself is stored and streamed via the separate file entity, referenced 1:1 through fileId (UNIQUE — one document row per registered file, never shared). document has no top-level API of its own; every route is scoped under /v1/buildings/:buildingId/documents. Rows are created only as the side effect of a file-upload confirmation (ConfirmDocumentUploadUseCase), never through a standalone create endpoint.
Referenced by: contract.documentId and invoice.fileId (both nullable FKs to document.id — the invoice column's name is historical and does not point at file).
Data Attributes Table
| Attribute Name | Description | Data Type | Default Value | Required (= Nullable) | Unique | Format | Validations | Index | Example |
|---|---|---|---|---|---|---|---|---|---|
| id | Primary key of the entity. | UUID | uuidv7() — DB default | Yes | Yes | UUID v7 | - | Primary Key | 018fa51f-fda1-79f4-8461-2cb8f1cabc10 |
| tenantId | Tenant this record belongs to. Bound from current_setting('app.tenant_id'), never accepted from the caller. | UUID | current_setting('app.tenant_id')::uuid — DB default | Yes | No | UUID v7 | Foreign Key → tenant; must exist | - | 018fa51f-fda3-7c63-b5a9-3fa33dc989de |
| buildingId | Building this document belongs to. Immutable after creation — no write path patches it. | UUID | - | Yes | No | UUID v7 | Foreign Key → building.id; must exist | name: idx_document_building, type: btree (partial, WHERE deleted_at IS NULL) | 018fa51f-fda4-7e95-88e8-5f5675f0ddf8 |
| fileId | The uploaded file this document's metadata describes. Immutable after creation. | UUID | - | Yes | Yes | UUID v7 | Foreign Key → file.id; must exist | UNIQUE(file_id) | 018fa51f-fda5-7123-9a12-1a2b3c4d5e6f |
| name | Display name of the document. | String | - | Yes | No | - | 1–255 characters (document_name_len CHECK) | - | Faktura_2026_03.pdf |
| category | Document category. | String (enum) | - | Yes | No | enum: enmsDocumentation, energy, invoices, photoDocumentation, inspections, contractManagement, other | Must be one of the listed values | name: idx_document_building_cat (building_id, category), type: btree (partial, WHERE deleted_at IS NULL) | invoices |
| medium | Commodity this document's invoice concerns. Only meaningful — and only settable — when category = invoices. | String (enum), nullable | null | No | No | enum: electricity, gas, water, heat | Must be null unless category = invoices, in which case must be one of the listed values (document_medium_only_invoices CHECK) | - | electricity |
| labels | Freeform classification tags. | String[] (enum subset) | {} | Yes | No | array subset of: pasport, important, expiring | Every element must be one of the listed values (document_labels_allowed CHECK) | name: idx_document_labels, type: GIN | ["important"] |
| note | Free-text note. | String, nullable | null | No | No | - | Max 2000 characters (document_note_len CHECK) | - | Signed 2026-03-01 |
| createdAt | Timestamp of entity creation. Immutable. | Timestamp with time zone | now() | Yes | No | ISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ | - | - | 2026-03-01T09:00:00Z |
| updatedAt | Timestamp of last update. | Timestamp with time zone | now() | Yes | No | ISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ | - | - | 2026-03-02T09:00:00Z |
| createdBy | Actor who created this record. | String | - | Yes | No | type:actor | - | - | user:018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2 |
| updatedBy | Actor who last updated this record. | String | - | Yes | No | type:actor | - | - | user:018ed0b3-c298-7c7a-96d5-8b36f5a7f8d2 |
| deletedAt | Timestamp of soft delete. Once set, immutable. | Timestamp with time zone, nullable | null | No | No | ISO 8601 — YYYY-MM-DDTHH:mm:ss.SSSZ | Active records: WHERE deletedAt IS NULL | name: idx_document_deleted_at, type: btree (partial, WHERE deleted_at IS NOT NULL) | null |
Audited fields
Recorded on created (in full), updated (changed only) and deleted (in full): buildingId, fileId, name, category, medium, labels, note.
Excluded: none.
Every entry carries subjectEntityType = building / subjectEntityId = document.buildingId — document does not self-subject, matching its building-scoped reading surface. Registered for entityName resolution — resolves to name (architecture 61-audit-log.md §7.4 in the code repo).