View Source Bookk.Ledger (bookk v0.1.3)

A ledger is a book that holds accounts. Traditionally, ledgers would also hold the journal entries that changed the accounts but, in this library, persisting those journal entries is considered off scope. You may persist state the way the best fits your needs.

Summary

Types

t()

The struct that represents a ledger.

Functions

Checks whether the ledger is balanced.

Get an account from the ledger by its Bookk.AccountHead. If the account doesn't exist yet, then an account will be returned with empty state.

Creates a new Bookk.Ledger from its name and, optionally, a list of Bookk.Account.

Posts a Bookk.JournalEntry to a ledger. This means that the balance change described in each operation of the journal entry will be applied to their respective accounts of the ledger. If there's a change to an account that doesn't exist yet, then the account is first created.

Types

@type t() :: %Bookk.Ledger{
  accounts_by_name: %{required(name :: String.t()) => Bookk.Account.t()},
  name: String.t()
}

The struct that represents a ledger.

Fields

  • name: the name of the ledger;
  • accounts_by_name: a map of the accounts known by the ledger, grouped by their name.

Functions

@spec balanced?(t()) :: boolean()

Checks whether the ledger is balanced.

A ledger is considered balance when the some of balance from its debit accounts is equal the sum of balance from its credit accounts. You know if an account is a "debit account" or a "credit account" by the natural balance of its class.

See Bookk.AccountClass for more information on natural balance.

Examples

Is balanced when the ledger is empty:

iex> Bookk.Ledger.new("acme")
iex> |> Bookk.Ledger.balanced?()
true

Is balanced when the sum of debit accounts balances is equal the sum of credit accounts balances:

iex> ledger = Bookk.Ledger.new("acme")
iex> cash = fixture_account_head(:cash)
iex> deposits = fixture_account_head(:deposits)
iex>
iex> journal_entry = %Bookk.JournalEntry{
iex>   operations: [
iex>     debit(cash, 50_00),
iex>     credit(deposits, 50_00)
iex>   ]
iex> }
iex>
iex> Bookk.Ledger.post(ledger, journal_entry)
iex> |> Bookk.Ledger.balanced?()
true

Is unbalanced when the sum of debit accounts balances isn't equal the sum of credit accounts balances:

iex> ledger = Bookk.Ledger.new("acme")
iex> cash = fixture_account_head(:cash)
iex>
iex> journal_entry = %Bookk.JournalEntry{
iex>   operations: [
iex>     debit(cash, 50_00)
iex>   ]
iex> }
iex>
iex> Bookk.Ledger.post(ledger, journal_entry)
iex> |> Bookk.Ledger.balanced?()
false
Link to this function

get_account(ledger, head)

View Source
@spec get_account(t(), Bookk.AccountHead.t()) :: Bookk.Account.t()

Get an account from the ledger by its Bookk.AccountHead. If the account doesn't exist yet, then an account will be returned with empty state.

Examples

Returns the account when it exists in the ledger:

iex> ledger = %Bookk.Ledger{
iex>   name: "acme",
iex>   accounts_by_name: %{
iex>     "cash/CA" => %Bookk.Account{
iex>       head: fixture_account_head(:cash),
iex>       balance: 25_00
iex>     }
iex>   }
iex> }
iex>
iex> Bookk.Ledger.get_account(ledger, fixture_account_head(:cash))
%Bookk.Account{
  head: fixture_account_head(:cash),
  balance: 25_00
}

Returns an empty account when the it doesn't exist in the ledger:

iex> Bookk.Ledger.new("acme")
iex> |> Bookk.Ledger.get_account(fixture_account_head(:cash))
%Bookk.Account{
  head: fixture_account_head(:cash),
  balance: 0
}
Link to this function

new(name, accounts \\ [])

View Source
@spec new(name :: String.t(), [Bookk.Account.t()]) :: t()

Creates a new Bookk.Ledger from its name and, optionally, a list of Bookk.Account.

Link to this function

post(ledger, journal_entry)

View Source
@spec post(t(), Bookk.JournalEntry.t()) :: t()

Posts a Bookk.JournalEntry to a ledger. This means that the balance change described in each operation of the journal entry will be applied to their respective accounts of the ledger. If there's a change to an account that doesn't exist yet, then the account is first created.

Examples

When account doesn't exist then it gets created:

iex> ledger = Bookk.Ledger.new("acme")
iex>
iex> cash = fixture_account_head(:cash)
iex> deposits = fixture_account_head(:deposits)
iex>
iex> journal_entry = %Bookk.JournalEntry{
iex>   operations: [
iex>     debit(cash, 50_00),
iex>     credit(deposits, 50_00)
iex>   ]
iex> }
iex>
iex> updated_ledger = Bookk.Ledger.post(ledger, journal_entry)
iex>
iex> %Bookk.Account{balance: 50_00} = Bookk.Ledger.get_account(updated_ledger, cash)
iex> %Bookk.Account{balance: 50_00} = Bookk.Ledger.get_account(updated_ledger, deposits)

When account exists then it gets updated:

iex> ledger = Bookk.Ledger.new("acme")
iex>
iex> cash = fixture_account_head(:cash)
iex> deposits = fixture_account_head(:deposits)
iex>
iex> journal_entry = %Bookk.JournalEntry{
iex>   operations: [
iex>     debit(cash, 50_00),
iex>     credit(deposits, 50_00)
iex>   ]
iex> }
iex>
iex> updated_ledger =
iex>   ledger
iex>   |> Bookk.Ledger.post(journal_entry)
iex>   |> Bookk.Ledger.post(journal_entry) # post twice
iex>
iex> %Bookk.Account{balance: 100_00} = Bookk.Ledger.get_account(updated_ledger, cash)
iex> %Bookk.Account{balance: 100_00} = Bookk.Ledger.get_account(updated_ledger, deposits)