Documentation
¶
Overview ¶
Package ledgo is a double-entry ledger with multi-currency and FX support.
Amounts are signed: a positive amount debits the account, a negative amount credits it. A transaction balances when its amounts sum to zero in every currency. Every account holds a single currency and must be created before it is used.
Index ¶
- Constants
- Variables
- func CheckEntries(entries []Entry, accounts map[string]Account) error
- func RegisterCurrency(code string, decimalPlaces int32)
- type Account
- type Entry
- type Ledger
- type Rate
- type Store
- type Transaction
- type TransactionBuilder
- func (b TransactionBuilder) Accounts() []Account
- func (b TransactionBuilder) Build() Transaction
- func (b TransactionBuilder) Credit(account Account, amount decimal.Decimal) TransactionBuilder
- func (b TransactionBuilder) Debit(account Account, amount decimal.Decimal) TransactionBuilder
- func (b TransactionBuilder) IsEmpty() bool
- func (b TransactionBuilder) Negated() TransactionBuilder
- func (b TransactionBuilder) WithRate(rate Rate) TransactionBuilder
Constants ¶
const MaxDecimalPlaces = 18
MaxDecimalPlaces is the most decimal places a currency can have: the stores' amount columns keep 18, and some databases round extra places silently.
Variables ¶
var ( ErrUnknownAccount = errors.New("ledgo: unknown account") ErrAccountConflict = errors.New("ledgo: account already exists with different settings") ErrCurrencyMismatch = errors.New("ledgo: entry currency differs from its account's") ErrInsufficientFunds = errors.New("ledgo: commit would break an account's balance limit") )
Errors about accounts.
var ( ErrUnknownCurrency = errors.New("ledgo: unknown currency") ErrTooManyDecimals = errors.New("ledgo: amount has more decimal places than its currency allows") )
Errors returned for amounts that don't fit their currency.
var ( ErrNoAccount = errors.New("ledgo: missing account") ErrZeroAmount = errors.New("ledgo: zero amount") )
Errors returned for invalid entries.
var ( ErrNoIdempotencyKey = errors.New("ledgo: missing idempotency key") ErrTooFewEntries = errors.New("ledgo: a transaction needs at least two entries") ErrUnbalanced = errors.New("ledgo: transaction does not balance") )
Errors returned by Transaction.Validate.
var ErrIdempotencyConflict = errors.New("ledgo: idempotency key reused with different content")
ErrIdempotencyConflict is returned when an idempotency key is committed again with different content.
var ErrInvalidRate = errors.New("ledgo: invalid FX rate")
ErrInvalidRate is returned for a rate with unknown or identical currencies, a non-positive value, or a missing source or time.
Functions ¶
func CheckEntries ¶
CheckEntries checks that every entry's account is in accounts and holds the entry's currency. Stores call it before writing entries.
func RegisterCurrency ¶
RegisterCurrency adds a currency that the ISO 4217 table doesn't know, such as crypto, or overrides one. Codes are case-sensitive. It is safe for concurrent use. It panics if decimalPlaces is not between 0 and MaxDecimalPlaces.
Types ¶
type Account ¶
type Account struct {
ID string
Currency string
DebitsMustNotExceedCredits bool
CreditsMustNotExceedDebits bool
}
Account holds money in a single currency. Its balance is debits minus credits.
The two limits work like TigerBeetle's account flags: a commit that would make debits exceed credits, or credits exceed debits, fails with ErrInsufficientFunds. Accounts with no limit, typically system accounts such as clearing, FX or revenue, are never locked by commits.
func (Account) CheckBalance ¶
CheckBalance returns ErrInsufficientFunds if balance (debits minus credits) breaks the account's limits.
type Entry ¶
Entry is one leg of a transaction. A positive Amount debits the account, a negative one credits it.
type Ledger ¶
type Ledger struct {
// contains filtered or unexported fields
}
Ledger validates accounts and transactions before handing them to a Store.
type Rate ¶
Rate is the FX rate the caller used: one unit of From buys Value units of To. It is stored with the transaction and never recomputed.
type Store ¶
type Store interface {
// CreateAccount registers an account. Creating the same account again is
// a no-op; with different settings it returns ErrAccountConflict.
CreateAccount(ctx context.Context, account Account) error
// Commit stores tx atomically. Committing the same idempotency key with
// the same content again is a no-op; with different content it returns
// ErrIdempotencyConflict. It returns ErrUnknownAccount,
// ErrCurrencyMismatch or ErrInsufficientFunds without storing anything.
Commit(ctx context.Context, tx Transaction) error
// Balance is the account's debits minus credits.
Balance(ctx context.Context, account string) (decimal.Decimal, error)
}
Store persists accounts and transactions. Implementations live in their own modules and must pass the ledgotest conformance suite.
type Transaction ¶
type Transaction struct {
IdempotencyKey string
// Reference links the transaction to what caused it, such as a payment ID.
Reference string
Entries []Entry
// Rate is the FX rate used, for transactions that convert currencies.
Rate *Rate
}
Transaction is a set of entries committed atomically.
func (Transaction) Fingerprint ¶
func (tx Transaction) Fingerprint() []byte
Fingerprint is a hash of the transaction's content, excluding its idempotency key. Stores use it to tell a retry from a conflicting reuse.
func (Transaction) Validate ¶
func (tx Transaction) Validate() error
Validate checks that tx is well formed and balances in every currency.
type TransactionBuilder ¶
type TransactionBuilder struct {
// contains filtered or unexported fields
}
TransactionBuilder assembles a Transaction from debits and credits. Each entry takes its currency from its account, and zero amounts are skipped, so optional legs such as a zero fee need no special casing. Builders are values: every method returns a new builder and leaves the receiver as is.
func NewTransaction ¶
func NewTransaction(idempotencyKey, reference string) TransactionBuilder
NewTransaction starts a transaction with its idempotency key and the reference to what caused it.
func (TransactionBuilder) Accounts ¶
func (b TransactionBuilder) Accounts() []Account
Accounts returns the distinct accounts the transaction touches, in the order they were added, so callers can create them before committing.
func (TransactionBuilder) Build ¶
func (b TransactionBuilder) Build() Transaction
Build returns the transaction. It does not validate it: Ledger.Commit does.
func (TransactionBuilder) Credit ¶
func (b TransactionBuilder) Credit(account Account, amount decimal.Decimal) TransactionBuilder
Credit adds a credit of amount to account.
func (TransactionBuilder) Debit ¶
func (b TransactionBuilder) Debit(account Account, amount decimal.Decimal) TransactionBuilder
Debit adds a debit of amount to account.
func (TransactionBuilder) IsEmpty ¶
func (b TransactionBuilder) IsEmpty() bool
IsEmpty reports whether the transaction has no entries, for instance because all its amounts were zero.
func (TransactionBuilder) Negated ¶
func (b TransactionBuilder) Negated() TransactionBuilder
Negated turns every debit into a credit and every credit into a debit, for example to move money back the way it came.
func (TransactionBuilder) WithRate ¶
func (b TransactionBuilder) WithRate(rate Rate) TransactionBuilder
WithRate records the FX rate used by a transaction that converts currencies.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
sqlstore
Package sqlstore is the database/sql ledgo.Store shared by the SQL backends.
|
Package sqlstore is the database/sql ledgo.Store shared by the SQL backends. |
|
Package ledgotest is the conformance suite every ledgo.Store must pass.
|
Package ledgotest is the conformance suite every ledgo.Store must pass. |
|
mysql
module
|
|
|
oracle
module
|
|
|
postgres
module
|