# `Bookk.InterledgerEntry`
[🔗](https://github.com/rwillians/bookk/blob/v1.0.0/lib/bookk/interledger_entry.ex#L5)

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.

## Related

- `Bookk.Notation`;
- `Bookk.NaiveState`;
- `Bookk.JournalEntry`.

# `t`

```elixir
@type t() :: %Bookk.InterledgerEntry{
  entries_by_ledger_id: %{
    required(ledger_id :: String.t()) =&gt; 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.

# `balanced!`

```elixir
@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?`

```elixir
@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`

```elixir
@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`

```elixir
@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?`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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))
      ])}
    ]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
