DoubleEntryLedger.Entryable protocol (double_entry_ledger v0.2.0)

View Source

Protocol defining helper functions for working with entry data in the Double Entry Ledger system.

This protocol provides a consistent interface for operations on different entry types, allowing the same functions to work with both persisted Entry structs and entries still in Ecto.Changeset form. It abstracts away implementation details so that higher-level accounting logic can focus on business rules rather than data structure concerns.

Key Functions

  • debit_sum/2 - Accumulates the sum of debit entries
  • credit_sum/2 - Accumulates the sum of credit entries
  • uuid/1 - Retrieves the account UUID associated with the entry
  • currency/1 - Retrieves the currency of the entry

Implementations

The protocol is implemented for:

  • Entry - For working with persisted entry records
  • Ecto.Changeset - For working with entries still being validated

Usage Examples

The protocol enables generic functions that can operate on collections of mixed entry types:

defmodule DoubleEntryLedger.TransactionValidator do
  alias DoubleEntryLedger.Entryable

  # Works with both Entry structs and changesets
  def balance_entries?(entries) do
    debit_sum = Enum.reduce(entries, 0, &Entryable.debit_sum/2)
    credit_sum = Enum.reduce(entries, 0, &Entryable.credit_sum/2)
    debit_sum == credit_sum
  end

  # Group entries by currency
  def group_by_currency(entries) do
    Enum.group_by(entries, &Entryable.currency/1)
  end
end

Summary

Types

t()

All the types that implement this protocol.

Functions

Returns the sum of credit entries.

Retrieves the currency of the entry.

Returns the sum of debit entries.

Retrieves the UUID of the entry.

Types

t()

@type t() :: term()

All the types that implement this protocol.

Functions

credit_sum(entry, acc)

@spec credit_sum(t(), integer()) :: integer()

Returns the sum of credit entries.

Accumulates the amount of entries with type :credit into the accumulator.

Examples

# Using with Entry struct
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> credit_entry = %Entry{type: :credit, value: %{amount: 500, currency: :USD}}
iex> Entryable.credit_sum(credit_entry, 100)
600
iex> debit_entry = %Entry{type: :debit, value: %{amount: 500, currency: :USD}}
iex> Entryable.credit_sum(debit_entry, 100)
100

# Using with Changeset
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> alias Ecto.Changeset
iex> changeset = Changeset.change(%Entry{}, %{type: :credit, value: %{amount: 500, currency: :USD}})
iex> Entryable.credit_sum(changeset, 100)
600

currency(entry)

@spec currency(t()) :: atom()

Retrieves the currency of the entry.

Examples

# Using with Entry struct
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> entry = %Entry{value: %{amount: 500, currency: :USD}}
iex> Entryable.currency(entry)
:USD

# Using with Changeset
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> alias Ecto.Changeset
iex> changeset = Changeset.change(%Entry{}, %{value: %{amount: 500, currency: :USD}})
iex> Entryable.currency(changeset)
:USD

debit_sum(entry, acc)

@spec debit_sum(t(), integer()) :: integer()

Returns the sum of debit entries.

Accumulates the amount of entries with type :debit into the accumulator.

Examples

# Using with Entry struct
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> debit_entry = %Entry{type: :debit, value: %{amount: 500, currency: :USD}}
iex> Entryable.debit_sum(debit_entry, 100)
600
iex> credit_entry = %Entry{type: :credit, value: %{amount: 500, currency: :USD}}
iex> Entryable.debit_sum(credit_entry, 100)
100

# Using with Changeset
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> alias Ecto.Changeset
iex> changeset = Changeset.change(%Entry{}, %{type: :debit, value: %{amount: 500, currency: :USD}})
iex> Entryable.debit_sum(changeset, 100)
600

uuid(entry)

@spec uuid(t()) :: String.t()

Retrieves the UUID of the entry.

Examples

# Using with Entry struct
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> entry = %Entry{account_id: "550e8400-e29b-41d4-a716-446655440000"}
iex> Entryable.uuid(entry)
"550e8400-e29b-41d4-a716-446655440000"

# Using with Changeset
iex> alias DoubleEntryLedger.Entry
iex> alias DoubleEntryLedger.Entryable
iex> alias Ecto.Changeset
iex> changeset = Changeset.change(%Entry{}, %{account_id: "550e8400-e29b-41d4-a716-446655440000"})
iex> Entryable.uuid(changeset)
"550e8400-e29b-41d4-a716-446655440000"