One pass over the LOW-tier review items plus two folded issues:
- Delete validate_journal_entry (dead since the Fava migration; it
validated the pre-string-amount model) with its exports, unused
crud imports, and tests. Beancount validates entries now.
- Migration m006: UNIQUE index on user_roles(user_id, role_id) after
deduping; assign_user_role inserts with ON CONFLICT DO NOTHING and
returns the existing assignment — closes the auto-assign
check-then-act race on concurrent logins.
- Extract _get_username_from_user_id (110 lines in views_api, fresh
LNbits Database per call inside per-row hot paths) into
user_lookup.py with one shared core-DB handle, a 60s TTL cache and
a batch get_usernames API (review #18).
- Receivable-entry responses report CLEARED, matching the flag the
formatter actually writes; PENDING misled the UI (libra-#35).
- Replace the remaining print() calls in tasks.py with logger.
- get_all_accounts derives valid roots from
account_utils.ACCOUNT_TYPE_ROOTS instead of a hardcoded tuple, and
the no-op per-test rate-limit reset is gone (libra-#54).
- Delete migrations_old.py.bak, MIGRATION_SQUASH_SUMMARY.md,
docs/PHASE*_COMPLETE.md and the rendered .html; .gitignore data/
(it holds the runtime .lnbits_auth_key secret).
- Track docs/CODE-REVIEW-2026-06.md with finding statuses updated for
the PR #55-#59 + chore/hygiene series.
- CLAUDE.md notes LNbits pins Pydantic v1: keep .dict(), don't
"modernize" to .model_dump().
Note: format_payment_entry's is_payable docstring (flagged in review
follow-up) turned out to be consistent with the body — no change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Settlement correctness cluster from CODE-REVIEW-2026-06 (#2, #8, #13)
plus libra-#38:
- format_net_settlement_entry now enforces the same inline balance
constraint as the fiat formatter (payment = receivable - payable
+ credit) and grows an optional credit leg. An unbalanced
settlement raises instead of reaching the ledger.
- on_invoice_paid settles only what the payment covers: a partial
payment clears that much receivable; excess (or a payment with
nothing owed) becomes user credit. Previously the full prior
balance was cleared against a smaller payment, shipping unbalanced
postings. Settlement links are attached only when the payment
clears the full open balance, and only for same-currency entries.
- get_unsettled_entries_bql returns each entry's real posting
currency (was hardcoded "EUR") and exact Decimal amount strings
(was float). /receivables/settle nets only entries denominated in
the settlement currency.
- fiat_rate/btc_rate metadata computed via Decimal (new
fiat_rate_metadata helper) instead of float division — cost-basis
records no longer carry float drift.
- format_posting_at_average_cost omits the cost braces when
cost_currency is unset ("SATS {}" is invalid Beancount).
- Underpay error payload serializes amounts as exact Decimal strings.
- validate_metadata catches decimal.InvalidOperation so bad
fiat_amount input becomes ValidationError (libra-#38); flipped the
tracking xfail.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refactors the data model to use a single 'amount' field for journal entry lines, aligning with the Beancount approach.
This simplifies the model, enhances compatibility, and eliminates invalid states.
Includes a database migration to convert existing debit/credit columns to the new 'amount' field.
Updates balance calculation logic to utilize the new amount field for improved accuracy and efficiency.
Refactors the accounting logic into a clean, testable core module, separating business logic from database operations.
This improves code quality, maintainability, and testability by creating a dedicated `core/` module, implementing `CastleInventory` for position tracking, moving balance calculations to `core/balance.py`, and adding comprehensive validation in `core/validation.py`.