# `Bookk.NaiveState`
[🔗](https://github.com/rwillians/bookk/blob/v1.0.0/lib/bookk/naive_state.ex#L1)

A state struct that holds multiple ledgers. It's considered "naive"
because it doesn't hold any information regarding the events that
led to the current state.

## Related

- `Bookk.Notation`;
- `Bookk.InterledgerEntry`;
- `Bookk.Ledger`.

# `t`

```elixir
@type t() :: %Bookk.NaiveState{
  ledgers_by_id: %{required(id :: String.t()) =&gt; Bookk.Ledger.t()}
}
```

The struct representing a naive state.

## Fields

- `ledgers_by_id`: the ledgers known by the state, grouped by their id.

# `balanced?`

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

Checks whether the state is balanced.

A state is considered balance when the sum 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

Balanced:

    iex> state = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(25)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(25))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.balanced?(state)
    true

Unbalanced:

    iex> state = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(25)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(10))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.balanced?(state)
    false

# `diff`

```elixir
@spec diff(a :: t(), b :: t()) :: Bookk.InterledgerEntry.t()
```

Calculates a `Bookk.InterledgerEntry` represending the diff between
two `Bookk.NaiveState` where, if such interledger entry were to be
posted to state "a", it would become equal to state "b".

## Examples

    iex> a = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(25)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(25))
    iex>   ])
    iex> ])
    iex>
    iex> b = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(100)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(25)),
    iex>     Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(75))
    iex>   ]),
    iex>   Bookk.Ledger.new("foo", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(75)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(75))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.diff(a, b)
    Bookk.InterledgerEntry.new([
      {"acme", Bookk.JournalEntry.new([
        debit(fixture_account_head(:cash), Decimal.new(75)),
        credit(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(75))
      ])},
      {"foo", 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 wether the given state is empty (no ledgers with balance).

## Examples

    iex> Bookk.NaiveState.new()
    iex> |> Bookk.NaiveState.empty?()
    true

    iex> state = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(0)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(0))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.empty?(state)
    true

    iex> state = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(10)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(10))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.empty?(state)
    false

# `get_ledger`

```elixir
@spec get_ledger(t(), String.t()) :: Bookk.Ledger.t()
```

Get's a ledger from the state by its id. If the ledger doesn't exist
in the state yet, then a new empty ledger will be returned.

## Examples

Returns an empty ledger when requested ledger doesn't exist in
state:

    iex> Bookk.NaiveState.get_ledger(%Bookk.NaiveState{}, "acme")
    %Bookk.Ledger{id: "acme"}

Returns the ledger when it exists in state:

    iex> state = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("foo", [
    iex>     Bookk.Account.new(fixture_account_head(:cash))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.get_ledger(state, "foo")
    Bookk.Ledger.new("foo", [
      Bookk.Account.new(fixture_account_head(:cash))
    ])

# `merge`

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

Merges a set of states into one.

## Examples

    iex> a = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5))
    iex>   ])
    iex> ])
    iex>
    iex> b = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(5))
    iex>   ]),
    iex>   Bookk.Ledger.new("user(12345)", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(5)),
    iex>   ])
    iex> ])
    iex>
    iex> c = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("foo", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(25)),
    iex>     Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(25))
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.merge([a, b, c])
    Bookk.NaiveState.new([
      Bookk.Ledger.new("acme", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5)),
        Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(5))
      ]),
      Bookk.Ledger.new("foo", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(25)),
        Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(25))
      ]),
      Bookk.Ledger.new("user(12345)", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5)),
        Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(5)),
      ])
    ])

# `merge`

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

Merges two states into one.

## Examples

    iex> a = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5))
    iex>   ])
    iex> ])
    iex>
    iex> b = Bookk.NaiveState.new([
    iex>   Bookk.Ledger.new("acme", [
    iex>     Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(5))
    iex>   ]),
    iex>   Bookk.Ledger.new("user(12345)", [
    iex>     Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5)),
    iex>     Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(5)),
    iex>   ])
    iex> ])
    iex>
    iex> Bookk.NaiveState.merge(a, b)
    Bookk.NaiveState.new([
      Bookk.Ledger.new("acme", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5)),
        Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "12345"}}), Decimal.new(5))
      ]),
      Bookk.Ledger.new("user(12345)", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(5)),
        Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(5)),
      ])
    ])

# `new`

```elixir
@spec new([Bookk.Ledger.t()]) :: t()
```

Produces a new state struct from a set of ledgers.

# `post`

```elixir
@spec post(t(), Bookk.InterledgerEntry.t()) :: t()
```

Posts a `Bookk.InterledgerEntry` to the state, appling changes in
balance to multiple accounts accross multiple ledgers.

## Examples

    iex> use Bookk.Notation
    iex>
    iex> user_id = "123"
    iex> deposited_amount = Decimal.new(500)
    iex>
    iex> journal_entry =
    iex>   journalize using: DummyChartOfAccounts do
    iex>     on ledger(:acme) do
    iex>       debit account(:cash), deposited_amount
    iex>       credit account({:unspent_cash, {:user, user_id}}), deposited_amount
    iex>     end
    iex>
    iex>     on ledger({:user, user_id}) do
    iex>       debit account(:cash), deposited_amount
    iex>       credit account(:deposits), deposited_amount
    iex>     end
    iex>   end
    iex>
    iex> Bookk.NaiveState.new()
    iex> |> Bookk.NaiveState.post(journal_entry)
    Bookk.NaiveState.new([
      Bookk.Ledger.new("acme", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(500)),
        Bookk.Account.new(fixture_account_head({:unspent_cash, {:user, "123"}}), Decimal.new(500))
      ]),
      Bookk.Ledger.new("user(123)", [
        Bookk.Account.new(fixture_account_head(:cash), Decimal.new(500)),
        Bookk.Account.new(fixture_account_head(:deposits), Decimal.new(500))
      ])
    ])

---

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