AuroraMeter. Schema. CreditTransaction
(Aurora Meter v1.0.0)
View Source
One append-only entry in a tenant's credit ledger, in micro-dollars.
kind says what happened and amount is the signed delta it applied to the
balance (grant positive; settle, debit and expire negative or zero;
hold and release zero, moving held_delta instead). balance_after and
held_after snapshot the row after the entry, so the log alone reproduces
every balance.
balance_after, held_after and promotional_after snapshot the row after
the entry, so the log alone reproduces every figure.
reference is the caller's idempotency key: unique per kind, so a retried
grant is returned instead of credited twice and a retried hold or debit is
refused. A hold carries status (:pending, :settled, :released) and,
once settled, settled_amount; a promotional grant may carry expires_at
and gets expired_at when AuroraMeter.Credits.expire_due/1 consumes it.
category names where a grant's money came from (:paid, :promotional,
:adjustment) and, on a reversal, marks :reversal, which is a refund or chargeback
taking a paid grant back. A reversal never consumes promotional credit and is
reported against grants rather than as spend.
A reversal is kind: :reverse from schema version 9 on, and was kind: :debit, category: :reversal before it. Both shapes are permanent, because
the log is append only. Ask reversal?/1 rather than matching on either.
seq is the order. It is a Postgres identity column, assigned in commit
order, and it is what every query that reconstructs what happened first sorts
by. inserted_at is a wall-clock stamp and is not monotonic, so it orders
nothing (findings L20, X213). hold_transaction_id names the hold a settle
or release row closes, and updated_at is stamped by the database the
first time the row is updated in place.
Summary
Functions
The grant categories.
Builds a changeset for a ledger entry.
The transaction kinds.
Whether this entry is a reversal: a refund or a chargeback taking paid credit back.
Types
@type category() :: :paid | :promotional | :adjustment | :reversal
@type kind() :: :grant | :hold | :settle | :release | :debit | :expire | :reverse
@type status() :: :pending | :settled | :released
@type t() :: %AuroraMeter.Schema.CreditTransaction{ __meta__: term(), amount: term(), balance_after: term(), category: term(), expired_at: term(), expires_at: term(), held_after: term(), held_delta: term(), hold_transaction_id: term(), id: term(), inserted_at: term(), kind: term(), metadata: term(), promotional_after: term(), reference: term(), seq: term(), settled_amount: term(), status: term(), tenant_key: term(), updated_at: term() }
Functions
@spec categories() :: [category()]
The grant categories.
Examples
iex> AuroraMeter.Schema.CreditTransaction.categories()
[:paid, :promotional, :adjustment]
@spec changeset(t(), map()) :: Ecto.Changeset.t()
Builds a changeset for a ledger entry.
@spec kinds() :: [kind()]
The transaction kinds.
Examples
iex> :hold in AuroraMeter.Schema.CreditTransaction.kinds()
true
Whether this entry is a reversal: a refund or a chargeback taking paid credit back.
Two row shapes mean it, and both are permanent. From schema version 9 a
reversal is written with kind: :reverse and category: :reversal. Before
that it was written with kind: :debit and category: :reversal, and those
rows are in the append-only log for ever, so every reader has to accept both.
This is the one predicate that knows that; no other module may re-derive it,
because a second copy is the one that gets the older shape wrong.
Examples
iex> AuroraMeter.Schema.CreditTransaction.reversal?(%{kind: :reverse, category: :reversal})
true
iex> AuroraMeter.Schema.CreditTransaction.reversal?(%{kind: :debit, category: :reversal})
true
iex> AuroraMeter.Schema.CreditTransaction.reversal?(%{kind: :debit, category: nil})
false
iex> AuroraMeter.Schema.CreditTransaction.reversal?(%{kind: :grant, category: :paid})
false