TransactionDocument

TransactionDocument is a soft-deletable N:M join entity that links a Transaction to a Document (e.g. a bank-statement attachment or receipt). It lives in the core_api schema and mirrors the CompanyMedia / PersonMedia bridge-table shape. A partial unique index on (transaction_pk) WHERE deleted_at IS NULL enforces the current product invariant of one active attachment per transaction; a second transaction hard-delete CASCADE and a RESTRICT on the document side protect referential integrity. Workspace scope is inherited transitively through the Transaction relation โ€” no direct workspace FK is declared on this table.

NamingValue
ObjectTransactionDocument
Resource type (JSON:API type)transaction_document
Collection / records rootโ€” (not a records root)
REST base/v1/transaction-documents
Entity classTransactionDocument

Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.

API operations

OperationMethod & pathStatus
ListGET /v1/transaction-documents๐ŸŸก Planned
RetrieveGET /v1/transaction-documents/{id}๐ŸŸก Planned
CreatePOST /v1/transaction-documents๐ŸŸก Planned
UpdatePATCH /v1/transaction-documents/{id}๐ŸŸก Planned
DeleteDELETE /v1/transaction-documents/{id}๐ŸŸก Planned

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
created_at๐Ÿ”’ system โ€” timestamptzโœ… YesAuto-set via onCreate: () => new Date(). Not nullable.โ€”Timestamp at which this join row was created (i.e. the document was attached to the transaction).
deleted_attimestamptz | nullโšช NoNullable. Soft-delete sentinel. Participates in partial unique index uq_transaction_documents_one_active_per_transaction โ€” only one row with deleted_at IS NULL may exist per transaction_pk at any time.โ€”When set, the document is considered detached from the transaction. All active-join queries filter deleted_at IS NULL.

Relationships

NameTypeRequiredDescription
transactionto-one (ManyToOne)โœ… YesThe transaction this document is attached to. FK transaction_pk with ON DELETE CASCADE โ€” deleting the parent transaction removes this join row. Composite index idx_transaction_documents_transaction_deleted covers (transaction_pk, deleted_at) for the common read path.
documentto-one (ManyToOne)โœ… YesThe document being attached. FK document_pk with ON DELETE RESTRICT โ€” hard-deleting a Document that has live join rows raises a FK violation, forcing callers to go through DocumentService.softDelete. Index idx_transaction_documents_document supports the reverse-traversal path ('which transactions reference this document').

System-computed

  • pk โ€” auto-increment serial primary key, internal join only; never exposed via public API.
  • created_at โ€” set once on insert via onCreate: () => new Date() MikroORM hook.
  • deleted_at โ€” set to current timestamp by the service layer on detach (soft-delete); never set by the user directly.
  • The partial unique index uq_transaction_documents_one_active_per_transaction on (transaction_pk) WHERE deleted_at IS NULL is enforced by Postgres at the DB layer โ€” the service layer cooperates via a soft-delete-then-insert pattern on re-attach.
  • Workspace scope is inherited transitively through the Transaction relation; no explicit workspace_pk column exists on this table.

Example

{
  "data": {
    "type": "transaction_document",
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "attributes": {
      "created_at": "2026-04-22T09:14:32.000Z",
      "deleted_at": null
    },
    "relationships": {
      "transaction": {
        "data": { "type": "transaction", "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479" }
      },
      "document": {
        "data": { "type": "document", "id": "d290f1ee-6c54-4b01-90e6-d701748f0851" }
      }
    }
  }
}
Source: apps/api/src/database/entities/TransactionDocument.ts ยท domain: financial-graph ยท tier: Supporting