DoubleEntryLedger.Command.TransactionData (double_entry_ledger v0.1.0)

View Source

Provides the TransactionData embedded schema for the Double Entry Ledger system.

This module defines a schema that represents the core transaction information inside transaction commands, containing both the transaction status and a collection of related entries. It serves as an intermediate representation before a transaction is persisted to the database.

Structure

TransactionData contains:

  • status - The current state of the transaction (e.g., :pending, :posted)
  • entries - A list of EntryData structs representing the individual account entries

Validation Rules

The module enforces several business rules during validation:

  • Transactions must have at least 2 entries
  • Each entry must affect a different account (no duplicate account IDs)
  • Status values must be one of the allowed transaction states

Status Transitions

Different validation rules apply depending on the status transition:

  • When posting a transaction (status → :posted), entries are optional
  • When archiving a transaction (status → :archived), only status is validated
  • For all other changes, full validation rules apply

Usage Examples

Creating a new transaction with entries:

changeset = TransactionData.changeset(%TransactionData{}, %{
  status: :pending,
  entries: [
    %{account_id: "c24a758c-7300-4e94-a2fe-d2dc9b1c2db8", amount: 100, currency: :USD},
    %{account_id: "c24a758c-7300-4e94-a2fe-d2dc9b1c2db7", amount: -100, currency: :USD}
  ]
})

Updating a transaction's status to posted:

updated_changeset = TransactionData.update_command_changeset(existing_transaction, %{status: :posted})

Converting to a plain map:

map = TransactionData.to_map(transaction_data)
# %{status: :pending, entries: [%{account_id: "...", amount: 100, currency: :USD}, ...]}

Summary

Types

t()

Represents a transaction with its status and collection of entries.

Functions

Creates a changeset for validating TransactionData attributes.

Converts the given TransactionData.t struct to a map.

Creates a changeset specifically for transaction update commands, with conditional validation.

Types

t()

@type t() :: %DoubleEntryLedger.Command.TransactionData{
  entries: [DoubleEntryLedger.Command.EntryData.t()] | [],
  status: DoubleEntryLedger.Transaction.state() | nil
}

Represents a transaction with its status and collection of entries.

This type defines the structure of transaction data used in command payloads and includes:

  • status: The transaction's current state (e.g., :pending, :posted, :archived)
  • entries: A list of EntryData structs that make up the financial entries

This type is commonly used when creating or updating transactions through the command pipeline.

Functions

changeset(transaction_data, attrs)

@spec changeset(t(), map()) :: Ecto.Changeset.t()

Creates a changeset for validating TransactionData attributes.

This function builds an Ecto changeset that validates the required fields and structure of transaction data, enforcing business rules like requiring at least two entries with distinct account IDs.

Parameters

  • transaction_data: The TransactionData struct to create a changeset for
  • attrs: Map of attributes to apply to the struct

Returns

  • An Ecto.Changeset with validations applied

Validation Rules

  • Status must be a valid transaction state
  • Must have at least 2 entries
  • Each entry must affect a different account

Examples

iex> alias DoubleEntryLedger.Command.TransactionData
iex> attrs = %{status: :pending, entries: [
...>   %{account_address: "asset:account", amount: 100, currency: :USD},
...>   %{account_address: "cash:account", amount: -100, currency: :USD}
...> ]}
iex> changeset = TransactionData.changeset(%TransactionData{}, attrs)
iex> changeset.valid?
true

to_map(td)

@spec to_map(t()) :: map()

Converts the given TransactionData.t struct to a map.

Examples

iex> alias DoubleEntryLedger.Command.TransactionData
iex> transaction_data = %TransactionData{}
iex> TransactionData.to_map(transaction_data)
%{status: nil, entries: []}

update_command_changeset(transaction_data, attrs)

@spec update_command_changeset(t() | %{}, map()) :: Ecto.Changeset.t()

Creates a changeset specifically for transaction update commands, with conditional validation.

This function applies different validation rules based on the transaction status being updated. When posting or archiving a transaction, less strict validation is applied.

Parameters

  • transaction_data: The TransactionData struct to create a changeset for
  • attrs: Map of attributes to apply to the struct

Returns

  • An Ecto.Changeset with appropriate validations applied

Status-Based Rules

  • When transitioning to :posted without entries: Only validates status
  • When transitioning to :archived: Only validates status
  • For other changes: Applies full validation rules

Examples

iex> alias DoubleEntryLedger.Command.TransactionData
iex> # Posting without entries is allowed
iex> changeset = TransactionData.update_command_changeset(%TransactionData{}, %{status: :posted})
iex> changeset.valid?
true

iex> # Archiving is allowed
iex> changeset = TransactionData.update_command_changeset(%TransactionData{}, %{status: :archived})
iex> changeset.valid?
true