Skip to main content
InvoiceItem represents a single line item on an invoice — a discrete charge for a product or service with its own quantity, price, tax, and period metadata. It belongs exclusively to one Invoice via a mandatory many-to-one relation. Key associations are: Invoice (parent document), LedgerAccount (accounting chart of accounts classification), TaxRate (the well-catalog tax rate that was applied), and Media (an optional supporting document such as an image or receipt). The accounting_classification JSONB field carries the AI-produced Well-taxonomy posting intent used by the journal-entry builder to classify the line for double-entry accounting.

API operations

Data model

Attributes

Relationships

System-computed

  • invoice_item_id is generated by PostgreSQL gen_random_uuid() at insert time and is immutable thereafter.
  • created_at is set once by the MikroORM onCreate lifecycle hook; it is never updated.
  • updated_at is set by both onCreate and onUpdate hooks — it reflects the wall-clock time of the most recent write.
  • deleted_at is null on creation. Setting it to a non-null timestamp constitutes a soft delete. The partial indexes idx_invoice_items_invoice_active and idx_invoice_items_accounting_classification_status both carry WHERE deleted_at IS NULL to exclude deleted rows from hot read paths.
  • The composite unique constraint (invoice, line_id) is table-wide (not partial): it enforces that no two lines — active or soft-deleted — on the same invoice share the same line_id. A soft-deleted line_id cannot be reused for a new line on the same invoice without first hard-deleting the old row.
  • accounting_unit_price and accounting_line_total are derived by the FX-matching pipeline (invoice.service.ts + fx-rate.service.ts) when the line currency differs from the workspace’s functional currency. They are never computed by the API layer on a write request.
  • accounting_classification is written exclusively by the invoice-journal-entry-draft builder (services/accounting/invoice-journal-entry-draft.builder.ts) and classifyInvoiceItemAccounting(). It is never set by a direct API mutation. Its status field is indexed via a JSONB functional partial index (migration-only; MikroORM decorators cannot express JSONB key expressions or partial predicates, so schema:fresh diverges from production on this index).
  • discount defaults to 0 at the database level when not supplied by the connector.

Example

Source: apps/api/src/database/entities/InvoiceItem.ts · domain: financial-graph · tier: Supporting