ADR-0024: Manual transaction itemization
- Status: Accepted
- Date: 2026-08-15
- Deciders: maintainers
- Related: data-layer.md, ADR-0027 attachments
Context
Transactions are captured automatically with a single total. Users need to break a transaction into item lines (e.g. a food order split into dishes, or a bill with a discount line) — added, edited, and deleted by hand.
Decision
Manual itemization is stored as child rows of a transaction:
- New
transaction_itemstable (schema v7):id,transactionId(FK →transactions(id)ON DELETE CASCADE),name,amountMillis(signedLongminor units, ADR-0009),position. - The transaction's
amountMillisstays the authoritative total. Itemization never rewrites it. - Item costs are signed: a negative amount is a discount line. Zero new
parser work —
AmountParseralready parses a leading-, andMoneyText.ofrenders negatives. - Items share the transaction's currency — no per-item currency.
- The mismatch warning is informational and non-blocking: the item sum is computed in-memory in the ViewModel (ADR-0006) and compared against the transaction total. Save stays enabled.
- Save replaces the item set atomically (
deleteByTransaction+insertAllin a DAO@Transaction). Cascade delete comes free from the FK. - Export/import carry items as an additive field (export
version2); old files import fine, old readers ignore the new key.
Consequences
- Itemization is opt-in: transactions without items behave exactly as before.
- Validation blocks save only when an item has an unparseable cost or a
name-less item carries a cost (mirrors the existing
detail_cant_read_amountpattern). - The schema version is bumped to 7; feature work that needs its own schema change must take v8+.