AccountGroup
An AccountGroup
groups Accounts
together, and can optionally impose
Account Behaviors. An AccountGroup
can include other AccountGroups
.
Optionally, each member Account of an AccountGroup
can have a code
, which can be used
when rendering a Transaction via Vouchers.
TODO: Balance Roolup invert sign
For example, you could have an AccountGroup
for each User on your platform:
/acctgrp/acctgrp_beefca01
{
"id": "acctgrp_beefca01",
"ledger_id": "ldgr_beefca00",
"metadata": {
"platfrom_user_id": 1
},
"members": [
{
"code": "INVESTMENTS",
"account_id": "acct_beefca02"
},
{
"code": "CASH",
"account_id": "acct_beefca03"
},
{
"code": "EXTERNAL_CHECKING",
"account_id": "acct_beefca04"
},
{
"account_id": "acct_beefcaff"
}
]
}
Then, you could have the following Vouchers:
/ledger/ldgr_beefca00/vouchers
[
{
"id": "voucher_00001",
"code": "DEPOSIT",
"params": [
{
"name": "user_acctgrp",
"type": "acctgrp"
},
{
"name": "amount",
"type": "number"
}
],
"transactions": [
{
"postings": [
{
"acct": "params.user_acctgrp.EXTERNAL_CHECKING",
"debit": "params.amount",
"commodity": "'cmm_USD'"
},
{
"acct": "params.user_acctgrp.CASH",
"credit": "params.amount",
"commodity": "'cmm_USD'"
}
]
}
]
},
{
"id": "voucher_00002",
"code": "PURCHASE",
"params": [
{
"name": "user_acctgrp",
"type": "acctgrp"
},
{
"name": "amount",
"type": "number"
},
{
"name": "ticker",
"type": "commodity"
},
{
"name": "price_per_stock",
"type": "number"
}
],
"transactions": [
{
"postings": [
{
"acct": "params.user_acctgrp.CASH",
"debit": "params.amount * params.price_per_stock",
"commodity": "'cmm_USD'"
},
{
"acct": "params.user_acctgrp.INVESTMENTS",
"credit": "params.amount",
"commodity": "params.ticker"
}
]
}
]
}
]
Then, in your backend you would have the following functions to implement the business logic for deposits and stock purchases:
async function deposit(user_id: string, amount: number) {
const user_acctgrp = database.getUser(user_id).fineng_acctgrp_id;
return await fineng.postVoucher({
code: "DEPOSIT",
params: {
user_acctgrp,
amount: amount * 100,
},
});
}
async function purchase_stock(user_id: string, ticker: string, amount: number) {
const currentPrice = market.getPriceForTicker(ticker);
const totalCost = amount * currentPrice;
const user_acctgrp = database.getUser(user_id).fineng_acctgrp_id;
const cash_account = fineng.resolveAccountGroupMember(user_acctgrp, "CASH");
const usd_balance = cash_account.balance.normalize("USD");
if (usd_balance < totalCost) {
throw new Error("insufficient balance, you need ${totalCost} but you only have ${usd_balance}");
}
return await fineng.postVoucher({
code: "PURCHASE",
params: {
user_acctgrp,
amount: amount * 100,
ticker,
price_per_stock: currentPrice * 100,
},
});
}