SPEC 006 Transaction Ledger - rafaelfqueiroz/allmywallet GitHub Wiki
SPEC-006 — Transaction Ledger
|
|
| Status |
Draft |
| PRD source |
F3 (FR-3.1–3.13), §7.1, DM-1, DM-5 |
| Depends on |
SPEC-003 |
| Required by |
SPEC-005, SPEC-007, SPEC-010, all report specs |
| Milestone |
M2 |
Description
The transaction ledger — the single source of truth for everything the product reports.
Two principles govern it. The ledger is authoritative and everything else is derived: positions, valuations, wallet allocations and every report figure are computed from transactions and can be rebuilt from them alone. If a cached derivation disagrees with a recomputation, the ledger wins. Every row is traceable: each transaction records whether it came from an import batch or manual entry, so any number in any report can be explained back to its source.
Also covers manual transaction management — essential both for assets outside B3 custody (a CDB the extract never showed) and for correcting import gaps.
Business rules
The ledger
| ID |
Rule |
| BR-006-01 |
Transaction is the single source of truth. Position, DailyValuationSnapshot and all report figures are derived and rebuildable from it (DM-4). |
| BR-006-02 |
Every transaction records its provenance: the originating ImportBatch, or a manual-entry marker. Provenance is visible in the UI, not merely stored. |
| BR-006-03 |
Transaction.status ∈ {active, unclassified, superseded}. Only active rows enter calculations. |
| BR-006-04 |
Transaction.natural_key is unique per user, enforced by a database constraint (DM-1). |
| BR-006-05 |
Supported types: buy, sell, dividend, JCP, rendimento, amortization, split, grupamento, bonificação, subscription, transfer in, transfer out, adjustment, leilão de frações (the cash for a bonificação fraction sold at auction — a provento, SPEC-014 BR-014-01), fração de bonificação (a bonificação fraction B3 removed — quantity leaves at unchanged total cost, SPEC-007 BR-007-05a), conversion out and conversion in. Conversion legs share a required group identifier; conversion_in carries an exact cost-basis amount, and both types have zero cash value. They are created, edited and deleted only as one complete group. (Amended 2026-09-18, #113, #121.) |
| BR-006-06 |
Monetary values are stored as exact decimals — never floating point — at 2 places for currency and 8 for quantities (CR-2). |
History & search
| ID |
Rule |
| BR-006-07 |
A chronological, paginated list across all assets and institutions. |
| BR-006-08 |
Filterable by date range, asset, asset class, transaction type, institution and wallet. |
| BR-006-09 |
Full-text search on asset code and name. |
| BR-006-10 |
Exportable to CSV honouring the active filters, with formula-injection neutralisation (SPEC-003, BR-003-13). |
Manual management
| ID |
Rule |
| BR-006-11 |
A transaction can be created manually with type, asset, date, quantity, unit price, fees and institution. |
| BR-006-12 |
Any transaction can be edited, whether manual or imported. |
| BR-006-13 |
Any transaction can be deleted, with a confirmation stating what will be recalculated. |
| BR-006-14 |
Editing or deleting triggers recalculation of every derived figure from that date forward, and affected reports refresh. |
| BR-006-15 |
Validation prevents impossible states — most importantly selling more than the quantity held at that date — with an explanation of why, not a silent rejection. |
| BR-006-16 |
An edited imported transaction is flagged is_user_modified; a later re-import must not overwrite the correction. |
| BR-006-17 |
Bulk operations: delete multiple, assign multiple to a wallet. |
| BR-006-18 |
A backdated transaction is accepted and triggers recalculation forward from its date, not merely from today. |
Acceptance Criteria
Out of Scope
- Cost basis and average price computation (SPEC-007) — this spec stores transactions, that one interprets them.
- Import parsing and reconciliation (SPEC-005).
- Wallet allocation mechanics (SPEC-010) — bulk assignment here delegates to it.
- Valuation and pricing (SPEC-008, SPEC-009).
- Transaction-level tax treatment or IR classification.
- A full audit trail of edits (who changed what, when) beyond the LGPD audit log in SPEC-004 — versioned transaction history is not in v1.
- Undo/redo of edits.
- Multi-currency transactions — BRL only.
Decision Logs
| # |
Decision |
Alternatives considered |
Rationale |
| DL-006-01 |
The ledger is the single source of truth; everything else is derived and rebuildable. |
Maintain positions as authoritative state updated in place |
Derived-and-rebuildable means any calculation bug is fixable by correcting the logic and replaying — no data migration, no permanently corrupted state. Authoritative positions would make every past bug a data problem. The automated rebuild test (DM-4) keeps this honest. |
| DL-006-02 |
Imported transactions are editable. |
Lock imported rows as immutable |
B3 extracts have gaps — missing history before the export range, unmapped types, assets outside custody. If the user cannot correct them, their numbers stay wrong and they leave. The is_user_modified flag protects corrections from being reverted by re-import. |
| DL-006-03 |
Edits recalculate forward from the transaction date, not from today. |
Recalculate everything; recalculate only current state |
Recalculating only current state leaves historical charts wrong — the Portfolio Value line would silently disagree with the transactions behind it. Full recalculation is correct but wasteful on a long history. Forward-from-date is both correct and bounded. |
| DL-006-04 |
Deletion is permitted, with recalculation disclosed beforehand. |
Soft-delete only; forbid deletion of imported rows |
Users make genuine mistakes — duplicate manual entries, wrong asset. Forbidding deletion pushes them to workarounds that corrupt the ledger worse. Disclosing the recalculation before confirming makes the consequence visible. |
| DL-006-05 |
Exact decimals, never floating point. |
Floating point for simplicity |
Floating point silently loses precision in accumulation — average cost over hundreds of transactions drifts, and the number stops matching the broker's. Unfixable after the fact without a full rebuild. |
| DL-006-06 |
unclassified rows are stored but excluded from calculations. |
Reject them at import; include with a best-guess type |
Storing keeps reconciliation honest (the row exists and is visible) while excluding keeps calculations correct. Including a guess would corrupt figures invisibly, which the PRD's "honest over impressive" principle forbids. |