class

Finfry::Store

Inherits Reference < Object

Persists the Database to a single JSON file under the user's XDG data directory (override with FINFRY_DATA). Writes are atomic (temp file + rename) so a crash mid-write can't corrupt the ledger. Legacy single-entry files are migrated to double-entry on load, keeping a .bak backup.

Constants

BOOK_FILE = "finfry.json"

The visible per-directory book file. finfry discovers it by walking up from the current directory (like git's .git), so a project/folder can hold its own ledger.

Constructors

new(path : String = Store.default_path)
Source

Class methods

default_path

Resolve the active ledger: an explicit FINFRY_DATA override, else the nearest book file walking up from the current directory, else the global per-user ledger.

Source
discover_book

Nearest finfry.json at or above the current directory, or nil.

Source
global_path
Source

Instance methods

account_known?(name : String) : Bool
Source
account_meta(account : String) : Hash(String, String)
Source
account_policy
Source
add_recurring_rule(description : String, cadence : String, start_date : String, postings : Array(Posting), kind : String = "fixed") : RecurringRule
Source
balances(prefix : String | Nil = nil, up_to : String | Nil = nil) : Hash(String, Int64)

Net balance of every account (optionally restricted to a subtree, and/or to transactions on or before up_to). Returns account => signed cents.

Source
budgets
Source
changeset(summary : String, at : String, & : -> T) : T forall T

Run a block as a single reversible changeset. Mutations inside record how to undo themselves. Nested calls join the enclosing changeset, so an AI plan's many operations collapse into one undo unit. The changeset is only persisted if it actually changed something and the block didn't raise.

Source
changesets
Source
cleared?(account : String, id : Int32) : Bool
Source
cleared_balance(account : String) : Int64

Balance of the staged tier (cleared-but-not-committed).

Source
cleared_ids(account : String) : Array(Int32)

Transaction ids marked cleared against account's statement.

Source
deactivate_rule(id : Int32) : Bool
Source
declare_account(name : String) : Bool

Declare an account in the chart. Returns false if already declared.

Source
declared_accounts

Accounts explicitly declared in the chart.

Source
delete_transaction(id : Int32) : Transaction | Nil

Remove a transaction by id. Returns the deleted record, or nil if absent.

Source
due_entries
Source
generate_due(today : String) : Int32

Materialize every occurrence due up to today into the queue, advancing each rule's cursor so nothing is generated twice. Returns how many were added.

Source
known_accounts

Declared ∪ used — the accounts finfry treats as known. Feeds the AI's chart context, the accounts listing, and completions.

Source
last_reconciliation(account : String) : Reconciliation | Nil

The most recent finalized reconciliation for an account, if any.

Source
path
Source
reconcile!(account : String, statement : Int64, date : String) : Int32

Finalize a reconciliation: move every staged-cleared transaction into the committed tier and record the statement it was balanced against. The caller verifies the balance matches first. Returns the number locked in.

Source
reconciled?(account : String, id : Int32) : Bool
Source
reconciled_balance(account : String) : Int64

Balance of the committed tier (locked by past reconciliations).

Source
reconciled_ids(account : String) : Array(Int32)

Committed (reconciled) tier.

Source
reconciliations(account : String) : Array(Reconciliation)

Every finalized reconciliation for an account, oldest first.

Source
record(date : String, description : String, postings : Array(Posting), recurrence : String | Nil = nil) : Transaction

Build, validate, persist, and return a transaction. Raises Error if the postings don't balance.

Source
recurring_rules
Source
redo_last

Re-apply the change most recently removed by undo_last. Returns it, or nil if there's nothing to redo.

Source
remove_budget(account : String) : Bool
Source
remove_due_entries(ids : Array(Int32)) : Nil
Source
rename_account(from : String, to : String) : Int32

Rewrite every posting on from to to (also updating the chart and any budget keyed on it). Doubles as a merge when to already exists. Returns the number of postings rewritten.

Source
reverse(id : Int32, at : String, date : String) : Changeset | Nil

Correct an older change the proper accounting way: append a reversing entry. The original is never removed — transactions it added are negated by mirror-image postings, budget changes restored. Returns the reversing changeset, nil if id is unknown, or raises if it's already reversed.

Source
reversed?(id : Int32) : Bool

True if a reversing entry already undid changeset id.

Source
save
Source
set_account_meta(account : String, key : String, value : String) : Nil
Source
set_account_policy(policy : String) : Nil
Source
set_budget(account : String, limit : Int64) : Nil
Source
set_cleared(account : String, ids : Array(Int32), cleared : Bool) : Int32

Mark (or unmark) transactions as cleared against account. Reconciliation is bookkeeping metadata, not a ledger change, so it isn't journaled for undo. Returns the number of ids whose state actually changed.

Source
spent(account : String, month : String) : Int64

Net movement into an account subtree within a "YYYY-MM" month. For an Expenses account this is the amount spent.

Source
transactions
Source
undeclare_account(name : String) : Bool

Remove an account from the chart. Returns false if it wasn't declared. (If postings still reference it, it stays "known" via use.)

Source
undo_last

Undo the most recent change by removing it outright — as if it never happened. Safe precisely because it's the last change: nothing follows it. Returns the removed changeset, or nil if there's nothing to undo.

Source
unset_account_meta(account : String, key : String) : Bool

Remove a metadata key. Returns false if it wasn't set.

Source
used_accounts

Every distinct account a posting actually references, sorted.

Source