Documentation Index
Fetch the complete documentation index at: https://docs.ntropii.com/llms.txt
Use this file to discover all available pages before exploring further.
ExpenseRow captures one expense receipt with provenance back to the originating ingest.submitted_records row. Stored in ledgers.expenses. Migration: 004_expenses.sql.
"expenses" subledger type on import.
Type-specific fields
| Field | Type | Purpose |
|---|---|---|
event_id | UUID | Provenance — points to the originating ingest.submitted_records row. Joined during HITL review to surface the original receipt’s raw payload. |
vendor | str | None | Supplier name. Required outside NEEDS_ATTENTION. |
amount_gross | Decimal | None (gt=0) | Receipt total. Receipts are positive expenses — a negative is either an extraction bug or a credit note (separate type). Required outside NEEDS_ATTENTION. |
currency | CurrencyCode | None | ISO-4217. Required outside NEEDS_ATTENTION. |
expense_date | date | None | Date on the receipt. |
payment_method | str | None | "card", "cash", "bank_transfer", etc. Free-form. |
line_items | list[dict] | None | Optional sub-lines for multi-line receipts. |
vat_amount | Decimal | None (ge=0) | VAT portion. Zero allowed (zero-rated items); negative isn’t a normal category. |
notes | str | None | Free-form. |
category | str | None | GL-code-shaped classification. Auto-proposed, HITL-correctable. |
category_source | str | None | "vendor_lookup" | "llm" | "manual". |
confidence | Decimal | None (0 ≤ x ≤ 1) | Classifier confidence; drives HITL routing. |
GL-handoff fields
Populated by the post step, never written by runbook code directly.| Field | Type | Purpose |
|---|---|---|
approved_at | datetime | None | Set by the runbook’s HITL approval step. |
posted_to_gl | bool | True once the GL post succeeded. |
posted_journal_ref | str | None | Provider-assigned journal id from the successful create call. |
Loose-on-NEEDS_ATTENTION contract
vendor, amount_gross, currency are typed Optional so the insert_needs_attention path can build a “ghost row” with NULLs in the typed columns. Once the row leaves NEEDS_ATTENTION (i.e. HITL fixed the issue), these MUST be populated.
Enforced two ways:
_strict_fields_present_when_not_needs_attention— Pydanticmodel_validatorraises at row construction.- DB CHECK constraint mirrors the same rule (defense in depth).
vat_amount <= amount_gross — catches the common extraction bug where the agent confused gross with net somewhere on the receipt. Skipped on NEEDS_ATTENTION rows.
GL handoff — ExpenseRow.propose_for_gl
APPROVED expense rows to a list of BillProposals — one bill per row (no merging — each receipt is its own bill in the GL). Defaults to the unified Bill primitive (AP / supplier invoice); Xero’s deprecated Expense Claims module isn’t used.
Field mapping:
| Row field | → | Bill field |
|---|---|---|
vendor | → | supplier (LinkedSupplierInput by display_name) |
amount_gross | → | total + a single BillLineItem.total_amount |
currency | → | currency |
expense_date | → | bill_date |
vat_amount | → | total_tax |
category | → | BillLineItem.ledger_account.nominal_code |
notes | → | notes |
"expenses:{task_id}:{row.id}".
Rejects non-APPROVED rows — only signed-off bills go to the GL.
Mutation helpers — ntro.subledger.types.expenses_mutations
Pure-domain helpers for HITL row actions. No DB access — transports reuse them consistently.
EditableExpenseField enumerates which columns the UI can edit per cell: vendor, currency, expense_date, payment_method, notes, category, category_source, amount_gross, vat_amount. Anything else returns INVALID_FIELD.
can_reject only allows reject from NEEDS_ATTENTION (or no-op from REJECTED); other statuses return INVALID_TRANSITION.
Canonicalisation — canonicalize_tabular_expense_row
Maps loose extraction keys onto the strict ExpenseRow field names — handles the common alias variants (amount, total, gross_total → amount_gross; tax, vat → vat_amount; etc.). Used by tabular ingest (expense-processor parsing a CSV) so runbook authors don’t re-implement column-name normalisation.
Related
Subledgers overview
Row base + SubledgerStatus lifecycle.journal_proposals
The other shipped platform type.
General ledgers
Posting
BillProposal to the external GL.Typing
CurrencyCode, ForgivingDate, Period — used in ExpenseRow field types.