Resolve entry identity via entry-id metadata; unfuse user references (libra-#42)
Approving a pending entry created with a reference (e.g. invoice
"42-144") 404'd with "Pending entry unknown not found": the list
endpoints recovered the entry id by parsing links for a libra- prefix,
but reference-bearing entries displace that link with the fused
"{reference}-{entry_id}" form, so the id surfaced as the literal
"unknown" and the approve call round-tripped it.
Make the entry-id transaction metadata the single canonical identity:
- _extract_entry_id() resolves metadata-first (libra- link parsing kept
only for pre-dfdcc44 ledger history); used by /entries/user,
/entries/pending, approve, and reject.
- Creation endpoints no longer fuse the reference with the entry id —
the user reference becomes its own sanitized link and round-trips
verbatim in API responses. Typed exp-/rcv-/inc- links stay as the
settlement-tracking handles.
- format_revenue_entry now writes entry-id metadata like its siblings
and sanitizes its reference link (was appended raw); generic
POST /entries sanitizes its reference link too.
- User-journal reference extraction skips all system link prefixes
(typed links used to leak into the reference field).
Contract documented in CLAUDE.md (Data Integrity → Entry Identity &
Links), pinned by tests/test_entry_identity_api.py and formatter
contract tests in test_unit.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
116df46d38
commit
15d9910073
6 changed files with 374 additions and 89 deletions
15
CLAUDE.md
15
CLAUDE.md
|
|
@ -209,7 +209,8 @@ entry = format_transaction(
|
|||
{"account": "Liabilities:Payable:User-abc123", "amount": "-50000 SATS"}
|
||||
],
|
||||
tags=["groceries"],
|
||||
links=["libra-entry-123"]
|
||||
links=["exp-a1b2c3d4e5f60708"], # typed settlement link; identity goes in entry-id metadata
|
||||
meta={"entry-id": "a1b2c3d4e5f60708"}
|
||||
)
|
||||
|
||||
# Submit to Fava
|
||||
|
|
@ -217,6 +218,8 @@ client = get_fava_client()
|
|||
result = await client.add_entry(entry)
|
||||
```
|
||||
|
||||
Prefer the purpose-built formatters (`format_expense_entry`, `format_income_entry`, …) over raw `format_transaction` — they write the `entry-id` metadata and typed links for you (see Data Integrity → Entry Identity & Links).
|
||||
|
||||
**Querying Balances**:
|
||||
```python
|
||||
# Query user balance from Fava
|
||||
|
|
@ -278,7 +281,8 @@ entry = format_transaction(
|
|||
{"account": "Assets:Lightning:Balance", "amount": "-50000 SATS"}
|
||||
],
|
||||
tags=["utilities"],
|
||||
links=["libra-tx-123"]
|
||||
links=["exp-0123456789abcdef"],
|
||||
meta={"entry-id": "0123456789abcdef"}
|
||||
)
|
||||
|
||||
client = get_fava_client()
|
||||
|
|
@ -306,6 +310,13 @@ result = await client.query(query)
|
|||
3. User accounts use `user_id` (NOT `wallet_id`) for consistency
|
||||
4. All accounting calculations delegated to Beancount/Fava
|
||||
|
||||
**Entry Identity & Links** (the contract `_extract_entry_id()` in views_api.py relies on):
|
||||
- The `entry-id` **transaction metadata** is the single canonical entry identifier. Every libra-authored entry formatter (`format_expense_entry`, `format_receivable_entry`, `format_income_entry`, `format_revenue_entry`) writes it. All id resolution (pending list, user journal, approve, reject) reads it — never parse links to recover an id.
|
||||
- Typed links `exp-{id}` / `rcv-{id}` / `inc-{id}` exist for **settlement tracking** (BQL queries match settlements to source entries by these links). They duplicate the id but are not the identity source.
|
||||
- A user-supplied `reference` (invoice number, receipt id) becomes **its own sanitized link**, verbatim — never fused with the entry id, never displacing a system link. Two entries sharing a reference share the link (desired Beancount semantics).
|
||||
- `ln-{payment_hash[:16]}` links mark Lightning payments.
|
||||
- Legacy ledger history (pre-dfdcc44) carries a single `libra-{id}` link and no `entry-id` metadata — `_extract_entry_id()` falls back to parsing it. Do not write `libra-` links in new code.
|
||||
|
||||
**Validation** is performed in `core/validation.py`:
|
||||
- Pure validation functions for entry correctness before submitting to Fava
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue