DoubleEntryLedger.Command.AccountData (double_entry_ledger v0.1.0)

View Source

Schema for account data payload embedded in commands.

This embedded schema captures the minimal set of attributes required to describe an account at the moment a command is issued. It is used to validate and cast incoming payloads.

Summary

Types

t()

Embedded account data captured with a command payload.

Functions

Builds an Ecto.Changeset for AccountData.

Converts an AccountData struct into a plain map with the same fields unless they are nil.

Builds an update changeset for AccountData that only allows modification of certain fields.

Types

t()

@type t() :: %DoubleEntryLedger.Command.AccountData{
  address: String.t() | nil,
  allowed_negative: boolean() | nil,
  context: map() | nil,
  currency: DoubleEntryLedger.Utils.Currency.currency_atom() | nil,
  description: String.t() | nil,
  name: String.t() | nil,
  normal_balance: DoubleEntryLedger.Types.credit_and_debit() | nil,
  type: DoubleEntryLedger.Types.account_type() | nil
}

Embedded account data captured with a command payload.

Fields:

  • currency: ISO currency code represented as an enum (from Currency.currency_atoms/0)
  • name: Human-readable account name
  • address: Unique account identifier (e.g. "account:main")
  • description: Optional description
  • context: Arbitrary metadata map for additional context
  • normal_balance: Either :debit or :credit (from Types.credit_and_debit/0)
  • type: Account category/type (from Types.account_types/0)
  • allowed_negative: Whether the account is allowed to have a negative balance

Functions

changeset(account_data, attrs)

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

Builds an Ecto.Changeset for AccountData.

Casts: [:currency, :address, :name, :description, :context, :normal_balance, :type, :allowed_negative] Validates required: [:currency, address, :type]

Examples

iex> alias DoubleEntryLedger.Command.AccountData
iex> alias DoubleEntryLedger.Utils.Currency
iex> alias DoubleEntryLedger.Types
iex> attrs = %{
...>   currency: hd(Currency.currency_atoms()),
...>   address: "account:main",
...>   type: hd(Types.account_types())
...> }
iex> changeset = AccountData.changeset(%AccountData{}, attrs)
iex> changeset.valid?
true

iex> alias DoubleEntryLedger.Command.AccountData
iex> changeset = AccountData.changeset(%AccountData{}, %{})
iex> changeset.valid?
false
iex> required = [:currency, :address, :type]
iex> required -- Keyword.keys(changeset.errors)
[]

to_map(account_data)

Converts an AccountData struct into a plain map with the same fields unless they are nil.

The resulting map may include these keys: :currency, :name, :description, :context, :normal_balance, :type, :allowed_negative

Examples

iex> alias DoubleEntryLedger.Command.AccountData
iex> alias DoubleEntryLedger.Utils.Currency
iex> alias DoubleEntryLedger.Types
iex> data = %AccountData{
...>   currency: hd(Currency.currency_atoms()),
...>   name: "Cash",
...>   type: hd(Types.account_types()),
...>   address: "account:main"
...> }
iex> map = AccountData.to_map(data)
iex> Map.keys(map) |> Enum.sort()
[:address, :currency, :name, :type]
iex> map.address == data.address and map.currency == data.currency and map.type == data.type and map.name == "Cash"
true

update_changeset(account_data, attrs)

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

Builds an update changeset for AccountData that only allows modification of certain fields.

Unlike the main changeset, this function only allows updates to fields that are safe to modify after account creation: description and context. Critical fields like currency, name, and type are not allowed to be updated through this changeset.

Casts: [:description, :context] Validates: No additional validations beyond casting

Parameters

  • account_data - The AccountData struct to update
  • attrs - Map of attributes to update

Returns

  • Ecto.Changeset.t() - Changeset with only update-safe fields cast

Examples

iex> alias DoubleEntryLedger.Command.AccountData
iex> existing_data = %AccountData{
...>   currency: :USD,
...>   address: "account:main",
...>   name: "Cash Account",
...>   type: :asset,
...>   description: "Old description"
...> }
iex> update_attrs = %{
...>   description: "Updated description",
...>   context: %{department: "finance"}
...> }
iex> changeset = AccountData.update_changeset(existing_data, update_attrs)
iex> changeset.valid?
true
iex> changeset.changes.description
"Updated description"
iex> changeset.changes.context
%{department: "finance"}

iex> alias DoubleEntryLedger.Command.AccountData
iex> existing_data = %AccountData{currency: :USD, name: "Cash", type: :asset}
iex> # Attempting to update restricted fields should have no effect
iex> invalid_update = %{
...>   name: "New Name",
...>   address: "account:main2",
...>   currency: :EUR,
...>   type: :liability,
...>   description: "Valid update"
...> }
iex> changeset = AccountData.update_changeset(existing_data, invalid_update)
iex> changeset.valid?
true
iex> # description and name should be in changes, not the restricted fields
iex> Keyword.equal?(Map.keys(changeset.changes), [:description, :name])
true
iex> changeset.changes.description
"Valid update"