Manual Valuation#
Manual Valuation is the UI-driven workflow that lets users update an asset's value for any given date. It operates through a two-step confirm-then-commit flow and is backed by the same Valuation / ReconciliationManager infrastructure used by Account Statement Reconciliation and Investment Account Reconciliation — but is distinct from both: it is triggered directly by the user through the web UI rather than by PDF-statement matching or API calls.
Scope: applies to all account types — depository, credit card, loan, property, vehicle, crypto, investment, and other asset/liability accounts .
The Valuation Model#
Valuation is an Entryable stored in the entries table. Its kind enum has three values :
kind | Purpose |
|---|---|
reconciliation | Manual or API-created balance anchor (default) |
opening_anchor | Balance set at account creation |
current_anchor | Synthetic current-day anchor |
Manual valuations always use kind: "reconciliation". Display names are generated by Valuation::Name based on both the kind and the account type.
Confirm-Then-Commit Flow#
The web UI enforces a preview step before any write is committed. Routes :
POST /valuations/confirm_create → ValuationsController#confirm_create
POST /valuations/:id/confirm_update → ValuationsController#confirm_update
POST /valuations → ValuationsController#create
PATCH /valuations/:id → ValuationsController#update
Create path:
- User submits amount + date →
confirm_createruns a dry-run reconciliation viaaccount.create_reconciliation(balance:, date:, dry_run: true). The dry run returns aReconciliationResultwithout saving. - The result is rendered in
_confirmation_contents.html.erb, showing the projected new balance (and cash/holdings split for investment accounts). - User confirms →
createcallsaccount.create_reconciliation(balance:, date:)for real , then redirects to the account page.
Update path: mirrors create — confirm_update dry-runs account.update_reconciliation(@entry, ..., dry_run: true) , then update commits . Notes can be updated independently without triggering reconciliation .
ReconciliationManager — Core Logic#
Account::Reconcileable is a concern mixed into Account that exposes create_reconciliation / update_reconciliation and delegates to Account::ReconciliationManager#reconcile_balance.
reconcile_balance does the following :
- Snapshot old balance for the date from the
balancestable. - Prepare valuation — finds the existing
Valuationentry for that date, or builds a new one withkind: "reconciliation". - Save (skipped on
dry_run: true). - Compute contribution — the delta between the new amount and the prior balance, fed to
GoalPledge::Reconciler. Investment accounts use thetransferpledge kind, so the delta is not treated as a deposit. - Enqueue sync via
sync_later, which triggersBalance::ForwardCalculatorto reproject balance history using the new valuation as an anchor point.
Returns a ReconciliationResult struct with old_balance, new_balance, old_cash_balance, new_cash_balance, and success? / error_message fields .
Investment accounts: derived_cash_balance preserves existing holdings by computing new_cash_balance = total_balance − existing_non_cash_balance . The confirmation UI surfaces this split .
Key Files#
| File | Role |
|---|---|
app/controllers/valuations_controller.rb | Confirm + commit actions; dry-run then real write |
app/models/valuation.rb | Valuation entryable model with kind enum |
app/models/account/reconcileable.rb | Account concern; exposes create/update_reconciliation |
app/models/account/reconciliation_manager.rb | Core balance-reconciliation logic |
app/models/valuation/name.rb | Context-aware display name generation |
app/views/valuations/_confirmation_contents.html.erb | Preview UI partial (handles all account types + investment split) |
Related Systems#
- Account Statement Reconciliation — compares uploaded PDF/CSV statements against the ledger; also uses
ReconciliationManagerto write anchorValuationentries, but is driven by statement ingestion rather than direct user input. - Investment Account Reconciliation — REST API path (
POST /api/v1/valuations) for setting portfolio total; sharesReconciliationManagerbut has no confirm step. See the Investment Account Reconciliation article. - Balance History / ForwardCalculator — explains how
Valuationanchor points override the calculated balance on a given date. See the Balance History System article.