| journal_entry_id | string, UUID | ✅ Yes | unique; defaultRaw: gen_random_uuid() | — | Public identifier for the journal entry. Generated by PostgreSQL on insert. Used in all external API references. |
| entry_number | string | ✅ Yes | length ≤ 50; composite unique (workspace_pk, entry_number, fiscal_year). Immutable once status = VALIDATED (enforced at service layer, not DB). | — | Human-readable accounting reference number assigned by the posting pipeline or connector (e.g. VTE-2026-0042). Must be unique within the workspace × fiscal_year pair. |
| entry_date | string (ISO date, YYYY-MM-DD) | ✅ Yes | columnType: date | — | Accounting date of the entry. Determines which fiscal period and year it falls into. Indexed together with workspace for date-range queries. |
| label | string | ⚪ No | length ≤ 500; nullable | — | Free-text description of the entry (e.g. invoice reference, payment description). Shown in the accounting ledger UI. |
| status | string (enum: JournalEntryStatusEnum) | ✅ Yes | default: DRAFT; nativeEnumName: journal_entry_status_enum | DRAFT, VALIDATED, LOCKED | Lifecycle state of the entry. DRAFT = editable; VALIDATED = frozen (entry_number immutable, validated_at/validated_by set); LOCKED = archived, cannot be modified or reversed. |
| validated_at | string (ISO 8601 timestamp) | ⚪ No | nullable | — | Timestamp when the entry was moved to VALIDATED status. Null while status is DRAFT or LOCKED. |
| fiscal_year | integer | ✅ Yes | columnType: integer; part of the composite unique constraint (workspace_pk, entry_number, fiscal_year) | — | Four-digit fiscal year the entry belongs to (e.g. 2026). Combined with entry_number to guarantee uniqueness per workspace. |
| fiscal_period | integer | ⚪ No | nullable; CHECK: fiscal_period IS NULL OR (fiscal_period >= 1 AND fiscal_period <= 13) | 1–13 or null | Accounting period within the fiscal year. Supports up to 13 periods (some jurisdictions use a 13th adjustment period). Null when period-level granularity is not required. |
| source_entity_type | string | ⚪ No | length ≤ 100; nullable. Soft FK — no database foreign key constraint. | — | Polymorphic soft reference: the entity type that triggered this entry (e.g. ‘invoice’, ‘transaction’). Used together with source_entity_id to trace the posting origin without a hard FK. |
| source_entity_id | string | ⚪ No | length ≤ 50; nullable. Soft FK — no database foreign key constraint. | — | Polymorphic soft reference: the public identifier of the entity that triggered this entry (e.g. the external invoice id from the connector). Paired with source_entity_type. |
| posting_idempotency_key | string | ⚪ No | length ≤ 160; nullable. Partial unique index on (workspace_pk, posting_idempotency_key) WHERE deleted_at IS NULL AND posting_idempotency_key IS NOT NULL — lives in migration, not the entity decorator. | — | Deduplication key used by the automated posting pipelines to prevent re-posting the same source event. A second attempt with the same key will conflict at DB level rather than create a duplicate entry. |
| posting_metadata | object (JSONB) | ⚪ No | nullable; type: jsonb | — | Arbitrary structured metadata attached by the posting pipeline at creation time (e.g. builder version, trigger context). No schema enforcement at the DB level. |
| created_at | string (ISO 8601 timestamp), 🔒 system | ✅ Yes | onCreate: () => new Date() | — | Timestamp set automatically by MikroORM lifecycle hook when the record is first persisted. Not client-settable. |
| updated_at | string (ISO 8601 timestamp), 🔒 system | ✅ Yes | onCreate + onUpdate: () => new Date(). DB column is NOT NULL (TIMESTAMPTZ NOT NULL DEFAULT now()). | — | Timestamp updated automatically on every persist. Tracks last modification time. Not client-settable; NOT NULL at DB level. |
| deleted_at | string (ISO 8601 timestamp) | ⚪ No | nullable; soft-delete sentinel | — | When non-null the entry is soft-deleted. All repository queries must filter deleted_at IS NULL. The posting_idempotency_key partial unique index also gates on this column. |