Bookk.InterledgerEntry (bookk v1.0.0)

Copy Markdown View Source

An interledger entry is a collection of journal entries affecting multiple ledgers that must be transacted under the same accounting transaction. It's somewhat analogous to an Ecto.Multi holding multiple operations or a git commit that affect multiple files.

Summary

Types

t()

The struct that represents an interledger entry.

Functions

Raises Bookk.UnbalancedError if the interledger entry is unbalanced, otherwise returns the entry.

Checks whether the interledger entry is balanced. It is balance if all of its journal entries are balanced.

Compacts the interledger entry by merging all journal entries that target the same ledger into a single journal entry.

Calculates a Bookk.InterledgerEntry represending the diff between two Bookk.InterledgerEntry where, if the diff interledger entry were to be merged with interledger entry "a", it would become equal to interledger entry "b".

Checks whether an interledger entry is empty. It is empty when it has now journal entries or when all its journal entries are empty.

Get the journal entries for a given ledger id.

Merges a set of interledger entries into one.

Merges two interledger entries into one.

Creates a new interledger entry from a list of ledger + journal entry tuple.

Produces a new interledger entry that is equaly opposite of the given interledger entry, meaning its capable of reverting all the changes that the given entry causes.

Given an interledger entry, it returns all its journal entries in the form of a list of tuples where the first element is the ledger's name and the second element is a list of journal entries that are meant to be posted to such ledger.

Types

t()

@type t() :: %Bookk.InterledgerEntry{
  entries_by_ledger_id: %{
    required(ledger_id :: String.t()) => Bookk.JournalEntry.t()
  }
}

The struct that represents an interledger entry.

Fields

An interledger entry is composed of:

  • entries_by_ledger_id: the map of journal entries that are included in the interledger entry, grouped by the name of the ledger against which they should be posted.

Functions

balanced!(entry)

@spec balanced!(t()) :: t()

Raises Bookk.UnbalancedError if the interledger entry is unbalanced, otherwise returns the entry.

iex> interledger_entry = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(30))
iex>   ])},
iex> ])
iex>
iex> Bookk.InterledgerEntry.balanced!(interledger_entry)
** (Bookk.UnbalancedError) The interledger entry is unbalanced!


iex> interledger_entry = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(30)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(30))
iex>   ])},
iex> ])
iex>
iex> Bookk.InterledgerEntry.balanced!(interledger_entry)
Bookk.InterledgerEntry.new([
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(30)),
    credit(fixture_account_head(:deposits), Decimal.new(30))
  ])}
])

balanced?(interledger_entry)

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

Checks whether the interledger entry is balanced. It is balance if all of its journal entries are balanced.

Examples

Balanced entry:

iex> interledger_entry = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(30)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(30))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.balanced?(interledger_entry)
true

Unbalanced entry:

iex> interledger_entry = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(30))
iex>   ])},
iex> ])
iex>
iex> Bookk.InterledgerEntry.balanced?(interledger_entry)
false

compact(entry)

@spec compact(t()) :: t()

Compacts the interledger entry by merging all journal entries that target the same ledger into a single journal entry.

Examples

iex> interledger_entry = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])},
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.compact(interledger_entry)
Bookk.InterledgerEntry.new([
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(20)),
    credit(fixture_account_head(:deposits), Decimal.new(20))
  ])}
])

diff(a, b)

@spec diff(a :: t(), b :: t()) :: t()

Calculates a Bookk.InterledgerEntry represending the diff between two Bookk.InterledgerEntry where, if the diff interledger entry were to be merged with interledger entry "a", it would become equal to interledger entry "b".

Examples

iex> a = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(50)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(50)),
iex>   ])},
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(25)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(25)),
iex>   ])},
iex> ])
iex>
iex> b = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(100)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(100)),
iex>   ])},
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(50)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(50)),
iex>   ])},
iex> ])
iex>
iex> Bookk.InterledgerEntry.diff(a, b)
Bookk.InterledgerEntry.new([
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(75)),
    credit(fixture_account_head(:deposits), Decimal.new(75)),
  ])}
])

empty?(interledger_entry)

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

Checks whether an interledger entry is empty. It is empty when it has now journal entries or when all its journal entries are empty.

See Bookk.JournalEntry.empty?/1 to learn more about empty journal entries.

Examples

Is empty when there's no entries:

iex> Bookk.InterledgerEntry.empty?(%Bookk.InterledgerEntry{})
true

Is empty when all entries are empty:

iex> interledger = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(0))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.empty?(interledger)
true

Is not empty when at least one entry isn't empty:

iex> interledger = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(1))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.empty?(interledger)
false

get_journal_entries(entry, arg)

@spec get_journal_entries(t(), ledger_id :: String.t()) :: [Bookk.JournalEntry.t()]

Get the journal entries for a given ledger id.

Examples

When exists journal entries for the given ledger id:

iex> interledger_entry = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(50)),
iex>     credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(50))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.get_journal_entries(interledger_entry, "acme")
[
  Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(50)),
    credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(50))
  ])
]

Returns an empty array when there's no journal entries for the given ledger id:

iex> Bookk.InterledgerEntry.new([])
iex> |> Bookk.InterledgerEntry.get_journal_entries("acme")
[]

merge(list)

@spec merge([t()]) :: t()

Merges a set of interledger entries into one.

Examples

iex> a = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])}
iex> ])
iex>
iex> b = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.merge([a, b])
Bookk.InterledgerEntry.new([
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(10)),
    credit(fixture_account_head(:deposits), Decimal.new(10))
  ])},
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(10)),
    credit(fixture_account_head(:deposits), Decimal.new(10))
  ])}
])

merge(a, b)

@spec merge(t(), t()) :: t()

Merges two interledger entries into one.

Examples

iex> a = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])}
iex> ])
iex>
iex> b = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.merge(a, b)
Bookk.InterledgerEntry.new([
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(10)),
    credit(fixture_account_head(:deposits), Decimal.new(10))
  ])},
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(10)),
    credit(fixture_account_head(:deposits), Decimal.new(10))
  ])}
])

new(entries \\ [])

@spec new([entry]) :: t()
when entry: {ledger_id :: String.t(), Bookk.JournalEntry.t()}

Creates a new interledger entry from a list of ledger + journal entry tuple.

Examples

iex> Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(50)),
iex>     credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(50))
iex>   ])},
iex>   {"user(12345)", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(50)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(50))
iex>   ])}
iex> ])
%Bookk.InterledgerEntry{
  entries_by_ledger_id: %{
    "acme" => [
      Bookk.JournalEntry.new([
        debit(fixture_account_head(:cash), Decimal.new(50)),
        credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(50))
      ])
    ],
    "user(12345)" => [
      Bookk.JournalEntry.new([
        debit(fixture_account_head(:cash), Decimal.new(50)),
        credit(fixture_account_head(:deposits), Decimal.new(50))
      ])
    ]
  }
}

reverse(entry)

@spec reverse(t()) :: t()

Produces a new interledger entry that is equaly opposite of the given interledger entry, meaning its capable of reverting all the changes that the given entry causes.

Examples

Reverses all of its journal entries:

iex> interledger = Bookk.InterledgerEntry.new([
iex>   {"acme", Bookk.JournalEntry.new([
iex>     debit(fixture_account_head(:cash), Decimal.new(10)),
iex>     credit(fixture_account_head(:deposits), Decimal.new(10))
iex>   ])}
iex> ])
iex>
iex> Bookk.InterledgerEntry.reverse(interledger)
Bookk.InterledgerEntry.new([
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:deposits), Decimal.new(10)),
    credit(fixture_account_head(:cash), Decimal.new(10))
  ])}
])

to_journal_entries(interledger)

@spec to_journal_entries(t()) :: [{ledger_id :: String.t(), Bookk.JournalEntry.t()}]

Given an interledger entry, it returns all its journal entries in the form of a list of tuples where the first element is the ledger's name and the second element is a list of journal entries that are meant to be posted to such ledger.

Examples

Returns a list of tuple where the first element is the ledger name and the second element is a journal entry:

iex> interledger = Bookk.InterledgerEntry.new([
iex>  {"acme", Bookk.JournalEntry.new([
iex>    debit(fixture_account_head(:cash), Decimal.new(50)),
iex>    credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(50))
iex>  ])},
iex>  {"user(12345)", Bookk.JournalEntry.new([
iex>    debit(fixture_account_head(:cash), Decimal.new(50)),
iex>    credit(fixture_account_head(:deposits), Decimal.new(50))
iex>  ])},
iex> ])
iex>
iex> Bookk.InterledgerEntry.to_journal_entries(interledger)
[
  {"acme", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(50)),
    credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(50))
  ])},
  {"user(12345)", Bookk.JournalEntry.new([
    debit(fixture_account_head(:cash), Decimal.new(50)),
    credit(fixture_account_head(:deposits), Decimal.new(50))
  ])}
]