DoubleEntryLedger.Command.TransactionData (double_entry_ledger v0.1.0)
View SourceProvides 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
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
@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
@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 forattrs: 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
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: []}
@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 forattrs: Map of attributes to apply to the struct
Returns
- An Ecto.Changeset with appropriate validations applied
Status-Based Rules
- When transitioning to
:postedwithout 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