Reference
Everything lives in the pledge schema. Use the functions and views below. The tables behind
them are internal.
Functions #
| Function | Does |
|---|---|
create_account |
Creates an account and returns it |
close_account |
Closes an account with a zero balance and returns it |
create_transfer |
Books one transfer and returns it |
create_transfers |
Books many transfers, all or nothing, and returns them |
request |
Builds one request for create_transfers |
new_transfer_id |
Makes a new transfer id, for safe retries |
verify |
Checks the whole ledger |
id_timestamp |
When an id was made (any of the three id types) |
pledge.create_account(
name text,
currency text,
allow_negative_balance boolean DEFAULT true,
allow_positive_balance boolean DEFAULT true,
metadata jsonb DEFAULT NULL
) RETURNS SETOF pledge.accounts_view
pledge.close_account(id pledge.account_id) RETURNS SETOF pledge.accounts_view
pledge.create_transfer(
from_account_id pledge.account_id,
to_account_id pledge.account_id,
amount numeric,
event_at timestamptz DEFAULT NULL,
metadata jsonb DEFAULT NULL,
id pledge.transfer_id DEFAULT NULL
) RETURNS SETOF pledge.transfers_view
pledge.create_transfers(
requests pledge.transfer_request[],
event_at timestamptz DEFAULT NULL, -- for requests without their own
metadata jsonb DEFAULT NULL -- likewise
) RETURNS SETOF pledge.transfers_view
pledge.create_transfers(VARIADIC requests pledge.transfer_request[])
RETURNS SETOF pledge.transfers_view
pledge.request(
from_account_id pledge.account_id,
to_account_id pledge.account_id,
amount numeric,
id pledge.transfer_id DEFAULT NULL,
event_at timestamptz DEFAULT NULL,
metadata jsonb DEFAULT NULL
) RETURNS pledge.transfer_request
pledge.new_transfer_id() RETURNS pledge.transfer_id
pledge.verify() RETURNS TABLE (check_name text, ok boolean, detail text)
pledge.id_timestamp(pledge.account_id | pledge.transfer_id | pledge.entry_id) RETURNS timestamptz
Views #
pledge.accounts_view: id, name, currency, balance, version,
allow_negative_balance, allow_positive_balance, metadata, created_at, updated_at,
closed_at.
pledge.transfers_view: id, from_account_id, to_account_id, amount, created_at,
event_at, metadata.
pledge.entries_view: id, account_id, transfer_id, amount (negative on the sending
side), account_previous_balance, account_current_balance, account_version, plus
created_at, event_at and metadata from the transfer.
Types #
| Type | Text form | Example |
|---|---|---|
pledge.account_id |
pla_… |
pla_01M72Y7TF2EWV94RPD57WD8XJJ |
pledge.transfer_id |
plt_… |
plt_01M72Y7TF5FRZAAHZ6ZFHEXJVP |
pledge.entry_id |
ple_… |
ple_01M72Y7TF5FRZAAHZ6ZFHEXJVQ |
Ids are UUIDv7 inside, so they sort by creation time. Cast to and from uuid with ::uuid and
::pledge.account_id. Ids of different kinds can’t be compared by mistake: account_id = transfer_id is an error.
pledge.transfer_request is the row type that pledge.request(...) builds.
Errors #
All errors are SQLSTATE P0001. DETAIL is a JSON object with the code and the keys listed
under Attributes. In Python these are attributes of the exception, with account_id as an
AccountId, transfer_id as a TransferId and balance as a Decimal. See
Errors for how to handle them.
| Code | Python class | Attributes | Message |
|---|---|---|---|
pledge.invalid_request |
InvalidRequest |
Amount (<amount>) must be a positive finite number, Cannot transfer to the same account (id=<id>), Account ids (from=<id>, to=<id>) must not be null, Transfer requests must not be null |
|
pledge.account_not_found |
AccountNotFound |
account_id |
Account (id=<id>) does not exist |
pledge.account_closed |
AccountClosed |
account_id |
Account (id=<id>, name=<name>) is closed |
pledge.account_already_closed |
AccountAlreadyClosed |
account_id |
Account (id=<id>) is already closed |
pledge.account_not_empty |
AccountNotEmpty |
account_id, balance |
Account (id=<id>, name=<name>) has a non-zero balance (<balance>) |
pledge.negative_balance_not_allowed |
NegativeBalanceNotAllowed |
account_id |
Account (id=<id>, name=<name>) does not allow negative balance |
pledge.positive_balance_not_allowed |
PositiveBalanceNotAllowed |
account_id |
Account (id=<id>, name=<name>) does not allow positive balance |
pledge.currency_mismatch |
CurrencyMismatch |
from_currency, to_currency |
Cannot transfer between different currencies (<from> and <to>) |
pledge.transfer_conflict |
TransferConflict |
transfer_id |
Transfer (id=<id>) already exists with different values |
pledge.duplicate_transfer_id |
DuplicateTransferId |
transfer_id |
Transfer id (<id>) appears more than once in the request |
pledge.immutable |
Immutable |
table |
pledge.<table>: rows cannot be removed (…), pledge.<table>: <columns> cannot be changed |
Python #
From pledge:
| Name | What |
|---|---|
AccountId, TransferId, EntryId |
Typed ids. TransferId.new() makes a new one |
LedgerError and the classes above |
Ledger errors, with .code, .message, .detail (the parsed JSON) and the attributes above |
translate(exc) |
Turns a psycopg or Django database error into a LedgerError, or None |
VerificationFailed |
Raised by assert_verified() |
From django_pledge.models:
| Call | Runs |
|---|---|
Account.objects.create(...) |
create_account |
account.transfer(to, amount, id=, event_at=, metadata=) |
create_transfer |
Transfer.objects.create(...) |
create_transfer |
Transfer.objects.bulk_create([...], event_at=, metadata=) |
create_transfers |
account.close() |
close_account |
Account.objects.verify(), assert_verified() |
verify |
Reading: account.history(), account.transfers(), account.balance_at(when),
account.is_closed, account.incoming, account.outgoing, account.entries. Async versions
work too: acreate, abulk_create, asave.
From django_pledge: AccountIdField, TransferIdField, EntryIdField, NumericField.
From django_pledge.json: LedgerJSONEncoder.
From django_pledge.admin: ReadOnlyAdmin.