DoubleEntryLedger.Balance (double_entry_ledger v0.1.0)
View SourceRepresents account balance components in the Double Entry Ledger system.
This module provides an embedded schema for tracking account balances with separate debit and credit components, along with the calculated net amount. It enables proper double-entry accounting operations by maintaining both sides of the ledger.
Structure
Balance contains three key fields:
amount: The net balancedebit: The cumulative debit entriescredit: The cumulative credit entries
Usage
Balance structs are typically embedded within Account records to track both posted (finalized) and pending balances separately. They're updated through transactions following double-entry accounting principles where:
For accounts with normal debit balance (assets, expenses):
- Debits increase the account balance
- Credits decrease the account balance
For accounts with normal credit balance (liabilities, equity, revenue):
- Credits increase the account balance
- Debits decrease the account balance
Key Functions
new/0- Creates a new Balance struct with zeroed fieldsupdate_balance/4- Updates a balance based on entry type and account typereverse_pending/4- Reverses a pending entry's effect on the balancereverse_and_update_pending/5- Combination of reversing and applying a new pending amount
Summary
Functions
Builds and returns a changeset for the balance struct.
Creates a new balance struct with default values of zero.
Reverses a pending amount and applies a new amount in a single operation.
Reverses the effect of a pending entry on a balance.
Updates a balance based on an entry and account type.
Types
Represents the balance components of an account.
This structure maintains both the separate debit and credit sides of an account's balance, as well as the calculated net amount following accounting standards.
Fields
amount: The net balancedebit: The cumulative sum of debit entriescredit: The cumulative sum of credit entries
This structure is used for both posted (finalized) and pending balances.
Functions
@spec changeset(t(), map()) :: Ecto.Changeset.t()
Builds and returns a changeset for the balance struct.
Creates an Ecto changeset to validate and prepare balance data for database operations. This is typically used when embedding balance data within an account record.
Parameters
balance- The balance struct to modifyattrs- Map of attributes to apply to the balance
Returns
- An Ecto.Changeset for the balance
Examples
iex> balance = DoubleEntryLedger.Balance.new()
iex> %Ecto.Changeset{valid?: true, changes: changes} = DoubleEntryLedger.Balance.changeset(balance, %{amount: 100, debit: 100})
iex> changes
%{amount: 100, debit: 100}
@spec new() :: t()
Creates a new balance struct with default values of zero.
This function initializes a Balance struct with all fields set to zero, suitable for new accounts or for resetting balances.
Returns
- A new Balance struct with zeroed fields
Examples
iex> DoubleEntryLedger.Balance.new()
%DoubleEntryLedger.Balance{amount: 0, credit: 0, debit: 0}
Reverses a pending amount and applies a new amount in a single operation.
This function is used when updating pending entries to a different amount, such as when modifying a hold or authorization. It first reverses the original amount and then applies the new amount.
Parameters
balance- The balance struct to updateamount_to_reverse- The original amount to reversenew_amount- The new amount to applye_type- The type of entry (:debit or :credit)a_type- The normal balance type of the account (:debit or :credit)
Returns
- An Ecto.Changeset with updated balance values
Error Handling
Returns an invalid changeset if attempting to reverse more than the current balance of the specific type (debit/credit).
Examples
iex> balance = %Balance{amount: -50, debit: 50, credit: 0}
iex> %Ecto.Changeset{valid?: true, changes: changes} = Balance.reverse_and_update_pending(balance, 50, 75, :debit, :credit)
iex> changes
%{amount: -75, debit: 75}
iex> balance = %Balance{amount: 50, credit: 50, debit: 0}
iex> %Ecto.Changeset{valid?: true, changes: changes} = Balance.reverse_and_update_pending(balance, 50, 75, :credit, :credit)
iex> changes
%{amount: 75, credit: 75}
iex> balance = %Balance{amount: -40, debit: 40, credit: 0}
iex> %Ecto.Changeset{valid?: false, errors: errors} = Balance.reverse_and_update_pending(balance, 50, 75, :debit, :credit)
iex> errors
[debit: {"Cannot reverse more than the current debit balance", []}]
iex> balance = %Balance{amount: 40, credit: 40, debit: 0}
iex> %Ecto.Changeset{valid?: false, errors: errors} = Balance.reverse_and_update_pending(balance, 50, 75, :credit, :credit)
iex> errors
[credit: {"Cannot reverse more than the current credit balance", []}]
@spec reverse_pending(t(), integer(), atom(), atom()) :: Ecto.Changeset.t()
Reverses the effect of a pending entry on a balance.
This function is used when canceling or removing pending entries
from an account's balance. It performs the opposite operation of
update_balance/4.
Parameters
balance- The balance struct to updateamount- The amount to reverse from the balancee_type- The type of entry being reversed (:debit or :credit)a_type- The normal balance type of the account (:debit or :credit)
Returns
- An Ecto.Changeset with updated balance values
Error Handling
Returns an invalid changeset if attempting to reverse more than the current balance of the specific type (debit/credit).
Examples
iex> balance = %Balance{amount: 50, debit: 50, credit: 0}
iex> %Ecto.Changeset{valid?: true, changes: changes} = Balance.reverse_pending(balance, 25, :debit, :debit)
iex> changes
%{amount: 25, debit: 25}
iex> balance = %Balance{amount: -50, debit: 0, credit: 50}
iex> %Ecto.Changeset{valid?: true, changes: changes} = Balance.reverse_pending(balance, 25, :credit, :debit)
iex> changes
%{amount: -25, credit: 25}
iex> balance = Balance.new()
iex> %Ecto.Changeset{valid?: false, errors: errors} = Balance.reverse_pending(balance, 50, :debit, :debit)
iex> errors
[debit: {"Cannot reverse more than the current debit balance", []}]
iex> balance = Balance.new()
iex> %Ecto.Changeset{valid?: false, errors: errors} = Balance.reverse_pending(balance, 50, :credit, :debit)
iex> errors
[credit: {"Cannot reverse more than the current credit balance", []}]
@spec update_balance(t(), integer(), atom(), atom()) :: Ecto.Changeset.t()
Updates a balance based on an entry and account type.
This function handles the core accounting logic of how entries affect balances differently based on the entry type (debit/credit) and the account type.
Parameters
balance- The balance struct to updateamount- The amount to apply to the balancee_type- The type of entry (:debit or :credit)a_type- The normal balance type of the account (:debit or :credit)
Returns
- An Ecto.Changeset with updated balance values
Accounting Logic
When entry type matches account's normal balance type:
- The amount increases the balance
- The corresponding debit/credit side increases
When entry type is opposite account's normal balance type:
- The amount decreases the balance
- The corresponding debit/credit side still increases
Examples
iex> balance = DoubleEntryLedger.Balance.new()
iex> %Ecto.Changeset{valid?: true, changes: changes} = DoubleEntryLedger.Balance.update_balance(balance, 50, :debit, :debit)
iex> changes
%{amount: 50, debit: 50}
iex> balance = DoubleEntryLedger.Balance.new()
iex> %Ecto.Changeset{valid?: true, changes: changes} = DoubleEntryLedger.Balance.update_balance(balance, 50, :debit, :credit)
iex> changes
%{amount: -50, debit: 50}