内置查询
张记账的读取端点通过内置查询计算各项数字(#479)。内置查询是用张记账的查询语言写成的具名查询,页面带着几个参数(例如报表的日期)在你的账本上执行它。页面只负责排列查询结果,所以每个数字背后的逻辑都是一个可以在本页读到的查询。
你可以在查询页面打开某个数字背后的查询,页面当时使用的参数值已经填好,然后修改它:换一个日期范围、加入更多账户、换一种分组方式、画成图表。应用能显示的任何内容,你都可以自己查询并加以变化,无需等待新版本。
来自内置查询的数字旁边有 打开查询 操作。它会打开查询页面(/explore),把查询放进编辑器并执行。参数已经以值的形式写进查询,所以编辑器里的查询是完整的。你可以像对待其他查询一样修改它并再次执行,也可以用 query 指令把它保存到账本中。
内置查询把它的输入命名为参数,写作 :name,例如 WHERE date >= :from AND date <= :to。张记账执行查询时绑定参数的值。你在页面中输入的内容(例如搜索关键字)只会作为参数绑定,绝不会被拼接进查询文本。
在查询页面打开查询时,每个参数都会被替换为它的值,写法保证查询能原样读回该值:
| 类型 | 写法 | 示例 |
|---|---|---|
date |
YYYY-MM-DD |
2024-01-31 |
str |
放在单引号中;如果含有单引号,则放在双引号中 | 'Assets:Bank'、"O'Brien" |
set |
调用 set |
set('food', 'trip')、set() |
int |
数字本身,负数加括号 | 12、(-3) |
decimal |
保留小数位的数字,总带小数点,负数加括号 | 12.50、12.、(-0.5) |
bool |
TRUE 或 FALSE |
|
| 任意类型 | 没有值时为 NULL,通常表示不使用该筛选条件 |
NULL |
查询语言中的字符串没有转义序列:字符串一直延续到下一个同种引号为止,反斜杠保持原样,所以 'C:\temp\' 就是文本 C:\temp\。同时含有两种引号的文本会写成字符串的拼接,例如 ("it's " + '"quoted"')。
写入参数值后的查询返回的行与页面得到的完全相同。只有不带 AS 的列名会变化,因为列名就是该列的文本。
HTTP API
Section titled “HTTP API”这两个接口与其他 API 一样受身份认证保护。
列出内置查询
Section titled “列出内置查询”GET /api/query/builtins 列出所有内置查询,包括名称、说明、BQL 以及参数及其类型:
{ "data": [ { "name": "postings.between", "description": "Every posting between two ledger dates, both included, in ledger order.", "bql": "SELECT date, flag, payee, narration, account, position WHERE date >= :from AND date <= :to ORDER BY seq", "params": [ { "name": "from", "type": "date" }, { "name": "to", "type": "date" } ] } ]}写出带参数值的查询
Section titled “写出带参数值的查询”POST /api/query/builtins/{name}/text 返回名为 name 的查询,其中的参数已替换为 params 中的值,也就是查询页面收到的文本:
curl -X POST http://localhost:8000/api/query/builtins/postings.between/text \ -H 'Content-Type: application/json' \ -d '{"params": {"from": "2024-01-01", "to": "2024-01-31"}}'{ "data": { "query": "SELECT date, flag, payee, narration, account, position WHERE date >= 2024-01-01 AND date <= 2024-01-31 ORDER BY seq" }}把这段文本发送到 POST /api/query 即可执行。params 必须给出查询的每一个参数,且不能有多余的参数,每个值是对应类型的 JSON 值:
| 类型 | JSON 值 |
|---|---|
date |
字符串 YYYY-MM-DD |
str |
字符串 |
set |
字符串列表 |
int |
整数 |
decimal |
数字,或者 "12.50" 这样的字符串,以保留小数位 |
bool |
true 或 false |
| 任意类型 | null |
查询名称不存在时返回 HTTP 404。参数缺失、多余或类型不对时返回 HTTP 400,message 中会指出是哪个参数。
postings.between
Section titled “postings.between”两个账本日期之间(含首尾两天)的所有分录,按账本顺序排列。
| 参数 | 类型 | 值 |
|---|---|---|
from |
date |
第一天 |
to |
date |
最后一天 |
SELECT date, flag, payee, narration, account, positionWHERE date >= :from AND date <= :toORDER BY seqpostings.matching
Section titled “postings.matching”某个收款人、带有某些标签之一的交易的分录,按账本顺序排列;参数为 NULL 时不使用对应的筛选条件。
| 参数 | 类型 | 值 |
|---|---|---|
payee |
str |
收款人;NULL 表示任意 |
tags |
set |
交易须带有其中任一标签;NULL 表示任意 |
SELECT date, payee, narration, tags, account, positionWHERE (:payee IS NULL OR payee = :payee) AND (:tags IS NULL OR intersects(tags, :tags))ORDER BY seq报表页和首页(GET /api/statistic/summary、/api/statistic/graph 和 /api/statistic/{account_type})。范围是两个账本日期 from 和 to,含首尾两天;这些接口也接受时间点,表示该时间点在账本时区中所在的那一天。currency 是账本的运营货币。
- **估值。**汇总和排行按
to当天的价格估值。图表的每个点按它自己最后一天的价格估值,最后一个点按to的价格估值。价格可以反向使用;按成本持有、自身没有价格的持仓通过成本货币估值(见convert)。任何价格都换算不了的金额保留原货币,不计入以运营货币表示的合计。 - 图表每天、每周(周一到周日)或每月一个点,以它的第一天命名,因此第一周(月)和最后一周(月)可能超出范围;报表页面用范围的第一天标注第一个点。范围内没有分录的点沿用前一个点的净资产,最前面的点沿用
from前一天的净资产,并按它自己最后一天的价格估值(见report.net_worth_trend)。 - **限制。**图表最多 50,000 个点,约合 137 年的每日数据;按日请求更长的范围会得到 HTTP 400,请改为按周或按月。这些数字也遵守所有查询的限制:图表的点(每种货币一个值)计入结果大小限制(
ZHANG_QUERY_MAX_RESULT_VALUES),超出它或超出时间限制的图表同样得到 HTTP 400。图表的开销随所请求的范围增长,而不随范围之前的账本历史增长。
report.net_worth
Section titled “report.net_worth”to 当天结束时的净资产(资产与负债的余额),按当天的价格以 currency 估值,即汇总中的余额。balance 保留批次。
| 参数 | 类型 | 值 |
|---|---|---|
to |
date |
余额的日期 |
currency |
str |
估值所用的货币 |
SELECT sum(position) AS balance, units(sum(position)) AS units, convert(sum(position), :currency, :to) AS valueWHERE (under(account, 'Assets') OR under(account, 'Liabilities')) AND date <= :toreport.liabilities
Section titled “report.liabilities”to 当天结束时负债的余额,按当天的价格以 currency 估值。与账本中一样,它是负数。
| 参数 | 类型 | 值 |
|---|---|---|
to |
date |
余额的日期 |
currency |
str |
估值所用的货币 |
SELECT units(sum(position)) AS units, convert(sum(position), :currency, :to) AS valueWHERE under(account, 'Liabilities') AND date <= :toreport.flows
Section titled “report.flows”范围内的收入和支出,按 to 当天的价格以 currency 估值。与账本中一样,收入为负数。
| 参数 | 类型 | 值 |
|---|---|---|
from |
date |
第一天 |
to |
date |
最后一天 |
currency |
str |
估值所用的货币 |
SELECT root(account, 1) AS type, units(sum(position)) AS units, convert(sum(position), :currency, :to) AS valueWHERE (under(account, 'Income') OR under(account, 'Expenses')) AND date >= :from AND date <= :toGROUP BY typeORDER BY typereport.transaction_count
Section titled “report.transaction_count”范围内的交易笔数。balance ... with pad 生成的补齐交易(标记 P)不计入,余额断言不是交易。
| 参数 | 类型 | 值 |
|---|---|---|
from |
date |
第一天 |
to |
date |
最后一天 |
SELECT count(*) AS transactionsFROM #transactionsWHERE flag != 'P' AND date >= :from AND date <= :toreport.net_worth_trend
Section titled “report.net_worth_trend”范围内每个有分录的日、周或月结束时的净资产(资产与负债的余额),按它在范围内最后一天的价格以 currency 估值。OPEN ON :from 把 from 之前的一切逐批次替换为前一天的期初余额,因此累计的 balance 从期初余额开始,查询只对范围内的日期以及前一天所在的区间分组。interval 为 '1 day'、'1 week' 或 '1 month':date_bin 从 2001-01-01(既是周一,又是月初)开始划分的区间,就是日历上的日、从周一开始的周和月,每个区间以它的第一天命名。least 让最后一个区间的估值日期不超出范围。balance 保留批次,用于给没有分录的点估值。
这个查询只列出有分录的日、周或月,因此打开查询显示的行比图表的点少。图表把没有分录的日、周或月补上它之前的最后一个余额(包括期初余额),并按它自己在范围内的最后一天估值,与当天的 report.net_worth 的估值相同。查询语言目前还不能列出没有分录的日期。
| 参数 | 类型 | 值 |
|---|---|---|
from |
date |
第一天 |
to |
date |
最后一天 |
interval |
str |
'1 day'、'1 week' 或 '1 month' |
currency |
str |
估值所用的货币 |
SELECT date_bin(:interval, date, 2001-01-01) AS bucket, last(balance) AS balance, units(last(balance)) AS units, convert(last(balance), :currency, least(max(date_bin(:interval, date, 2001-01-01)) + interval(:interval) - 1, :to)) AS valueFROM OPEN ON :fromWHERE (under(account, 'Assets') OR under(account, 'Liabilities')) AND date <= :toGROUP BY bucketORDER BY bucketreport.changes
Section titled “report.changes”范围内每日、每周或每月各账户类型的变动,按它在范围内最后一天的价格以 currency 估值,即收支图表中的柱。区间与 report.net_worth_trend 相同;第一个区间只计入 from 当天及以后的分录。
| 参数 | 类型 | 值 |
|---|---|---|
from |
date |
第一天 |
to |
date |
最后一天 |
interval |
str |
'1 day'、'1 week' 或 '1 month' |
currency |
str |
估值所用的货币 |
SELECT date_bin(:interval, date, 2001-01-01) AS bucket, root(account, 1) AS type, units(sum(position)) AS units, convert(sum(position), :currency, least(max(date_bin(:interval, date, 2001-01-01)) + interval(:interval) - 1, :to)) AS valueWHERE date >= :from AND date <= :toGROUP BY bucket, typeORDER BY bucket, typereport.account_totals
Section titled “report.account_totals”某一类型(例如 'Expenses')的每个账户在范围内的变动,按 to 当天的价格以 currency 估值,按估值从小到大排列,即收入和支出的构成。
| 参数 | 类型 | 值 |
|---|---|---|
type |
str |
'Assets'、'Liabilities'、'Equity'、'Income' 或 'Expenses' |
from |
date |
第一天 |
to |
date |
最后一天 |
currency |
str |
估值所用的货币 |
SELECT account, units(sum(position)) AS units, convert(sum(position), :currency, :to) AS valueWHERE under(account, :type) AND date >= :from AND date <= :toGROUP BY accountORDER BY number(only(:currency, convert(sum(position), :currency, :to))), accountreport.top_postings
Section titled “report.top_postings”范围内某一类型账户的十笔最大分录,按 to 当天的价格以 currency 估值排序,即最大的支出和收入。possign 把收入和负债变为正数,因此最大的收入排在最前。任何价格都无法换算为 currency 的分录排在最后。account_balance 是该分录之后其账户以分录货币计的余额。
| 参数 | 类型 | 值 |
|---|---|---|
type |
str |
'Assets'、'Liabilities'、'Equity'、'Income' 或 'Expenses' |
from |
date |
第一天 |
to |
date |
最后一天 |
currency |
str |
估值所用的货币 |
SELECT date, time, timestamp, account, id, payee, narration, units(position) AS units, only(currency, account_balance) AS account_balance, convert(position, :currency, :to) AS valueWHERE under(account, :type) AND date >= :from AND date <= :toORDER BY currency(convert(position, :currency, :to)) = :currency DESC, number(possign(convert(position, :currency, :to), account)) DESCLIMIT 10账户页面显示该账户及其子账户,与账户树一致:它的流水、余额历史、合计和文档都涵盖整棵子树。Assets:Bank 的页面包含 Assets:Bank:Checking 的分录,流水中的每一行都注明分录所属的账户。余额用 convert 按今天的价格折算为运营货币,它会使用反向价格和持仓的成本货币。
含子账户的余额由这些查询的行相加得到:账户列表和账户页面把一个账户及其下所有账户在 accounts.balances 或 accounts.subtree_balances 中的行相加,与网页界面的账户树一致。要自己查询一棵子树的合计,可以用 under 筛选:
SELECT currency, sum(number) AS unitsWHERE under(account, 'Assets:Bank')GROUP BY currency账户页面的流水按 seq(张记账处理账本的顺序)合并 accounts.journal 和 accounts.balance_assertions 的行,最新的在前。因此一个断言紧跟在它的余额所包含的分录之后,也就是张记账检查它的位置:写了时刻的余额断言在当天该时刻之前的交易之后,普通的余额断言在写在它之前的补齐之后,balance ... with pad 在同一时刻的其他余额记录之后。它的余额就是所在位置的累计余额,它的 trx_id 是其检查结果的 id,与 GET /api/journals 列出它时使用的 id 相同;分录行的 trx_id 是其交易的 id。同一交易的各行按分录最新的在前,按多个批次记账的一笔分录显示为一行。在夏令时跳过某段时间的那天,决定顺序的是写下的时刻,所以写在跳过时段中的行可能排在存储时刻更早的行之前。
页面按每页 100 行列出流水(GET /api/accounts/{account}/journals?page=1&size=100),accounts.journal 的每一行(一笔分录)和每个断言各算一行,所以最后一页之前的每一页都是满的。一页用 accounts.journal_page 从流水的末尾读取它的分录;分录的数量就是这个查询的总行数,POST /api/query 带上 count_total 时会返回它。
accounts.list
Section titled “accounts.list”每个有 open 或 close 指令的账户,及其开户和销户日期、别名,按名称排序。它和 accounts.balances 一起组成账户列表:有分录但没有 open 指令的账户也会列出。
SELECT account, open, close, meta('alias') AS aliasFROM #accountsORDER BY accountaccounts.balances
Section titled “accounts.balances”每个有分录的账户自身分录的余额,按货币分行:数量、按今天的价格折算为运营货币的价值,以及第一笔分录的日期。账户列表把一个账户及其下所有账户的行相加,得到含子账户的余额。
| 参数 | 类型 | 值 |
|---|---|---|
operating_currency |
str |
账本的 operating_currency 选项 |
SELECT account, currency, sum(number) AS units, convert(sum(position), :operating_currency, today()) AS value, min(date) AS first_dateGROUP BY account, currencyORDER BY account, currencyaccounts.subtree
Section titled “accounts.subtree”一个账户及其有 open 或 close 指令的子账户。账户页面从中读取日期、状态和别名。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
SELECT account, open, close, meta('alias') AS aliasFROM #accountsWHERE under(account, :account)ORDER BY accountaccounts.subtree_balances
Section titled “accounts.subtree_balances”一个账户及其每个子账户的余额,与 accounts.balances 相同。账户页面显示这些行的合计,也就是对该账户的 balance 断言所检查的余额,以及该账户自身的余额。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
operating_currency |
str |
账本的 operating_currency 选项 |
SELECT account, currency, sum(number) AS units, convert(sum(position), :operating_currency, today()) AS value, min(date) AS first_dateWHERE under(account, :account)GROUP BY account, currencyORDER BY account, currencyaccounts.journal
Section titled “accounts.journal”该账户及其子账户的分录,按账本顺序排列,每笔分录一行,每行带有该账户及其子账户紧接其后的累计余额,包含所有货币;页面显示其中分录货币的那一项。balance ... with pad 生成的补齐交易与其他交易一样列出。按多个批次记账的一笔分录在分录表中每个批次一行,它们的 seq 和 posting_index 相同:按这两列分组后,各批次相加得到分录的数量,first() 取它的日期、收付款人等列,last() 取最后一个批次之后的余额。加上 ORDER BY seq DESC, posting_index DESC 可以像页面一样把最新的排在最前。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
SELECT first(date) AS date, first(time) AS time, first(timestamp) AS timestamp, first(flag) AS flag, first(id) AS id, first(account) AS account, first(payee) AS payee, first(narration) AS narration, seq, posting_index, sum(number) AS units, first(currency) AS currency, last(units(balance)) AS balanceWHERE under(account, :account)GROUP BY seq, posting_indexaccounts.journal_page
Section titled “accounts.journal_page”accounts.journal 的部分行,按账本顺序:流水的一页从末尾数起读取它的分录。由于一笔分录的各批次行前后相连,不论偏移多少,只构建这一页的分录。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
limit |
int |
分录数 |
offset |
int |
之前跳过的分录数 |
SELECT first(date) AS date, first(time) AS time, first(timestamp) AS timestamp, first(flag) AS flag, first(id) AS id, first(account) AS account, first(payee) AS payee, first(narration) AS narration, seq, posting_index, sum(number) AS units, first(currency) AS currency, last(units(balance)) AS balanceWHERE under(account, :account)GROUP BY seq, posting_indexLIMIT :limit OFFSET :offsetaccounts.balance_assertions
Section titled “accounts.balance_assertions”对该账户的余额断言,按 seq 最新的在前。actual 是断言所检查的该账户及其子账户的余额,也就是流水中它的 seq 所在位置的累计余额。pad 是 balance ... with pad 用来补齐的账户。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
SELECT date, time, timestamp, id, account, amount, actual, passed, pad, seqFROM #balancesWHERE account = :accountORDER BY seq DESCaccounts.balance_history
Section titled “accounts.balance_history”账户页面的余额历史图:该账户及其子账户在每个有分录的日子结束时的余额,按货币分开,按日期排序。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
SELECT date, currency, last(only(currency, units(balance))) AS balanceWHERE under(account, :account)GROUP BY date, currencyORDER BY date, currencyaccounts.documents
Section titled “accounts.documents”账户页面的文档:该账户及其子账户的 document 指令,按账本顺序排列。path 是文件相对于账本目录的路径,页面用它下载文件。
| 参数 | 类型 | 值 |
|---|---|---|
account |
str |
页面的账户 |
SELECT date, time, account, pathFROM #documentsWHERE source = 'directive' AND under(account, :account)流水页面(GET /api/journals)、新建交易表单建议的收款人和账户(GET /api/for-new-transaction)、文档页面(GET /api/documents)以及错误列表(GET /api/errors)。
journals.page
Section titled “journals.page”流水的一页,最新的在前:符合关键词、标签和链接的交易(包括补齐交易)与余额断言;参数为 NULL 时不使用对应的筛选条件。
| 参数 | 类型 | 值 |
|---|---|---|
keyword |
str |
搜索文本;NULL 表示不搜索。交易的收款人、描述、标签、链接或账户包含它(不区分大小写)时匹配;余额断言的账户或 Balance Check 字样包含它时匹配。它是普通文本,不是正则表达式。 |
tags |
set |
交易须带有其中任一标签;NULL 表示任意。余额断言没有标签 |
links |
set |
交易须带有其中任一链接;NULL 表示任意 |
size |
int |
每页的行数,1 到 1000 |
offset |
int |
该页之前的行数:(page - 1) × size |
SELECT seq, type, id, date, time, flag, payee, narration, tags, links, metasFROM #entriesWHERE (type = 'transaction' AND (:tags IS NULL OR intersects(tags, :tags)) AND (:links IS NULL OR intersects(links, :links)) AND (:keyword IS NULL OR icontains(payee, :keyword) OR icontains(narration, :keyword) OR any_icontains(tags, :keyword) OR any_icontains(links, :keyword) OR any_icontains(accounts, :keyword))) OR (type = 'balance' AND :tags IS NULL AND :links IS NULL AND (:keyword IS NULL OR icontains('Balance Check', :keyword) OR any_icontains(accounts, :keyword)))ORDER BY seq DESCLIMIT :size OFFSET :offset- 页数按
LIMIT和OFFSET之前的总行数计算。GET /api/journals和GET /api/errors的页大小size为 1 到 1000,默认 100;其他大小返回 HTTP 400,消息为size must be between 1 and 1000;超过最后一页的页码返回空页。 - 行按处理顺序排列,最新的在前:先按日期和书写的时刻;同一时刻内,余额条目(余额断言和所有标记为
P的交易)在其他交易之前,各自按文件中的顺序;balance ... with pad排在同一时刻的其他余额条目(包括它的补齐交易)之后,即张记账检查它的位置。因此余额断言紧挨在它的余额所包含的分录之上。在夏令时跳过某个时刻的那一天,写在跳过区间内的条目位置不变,但显示的是它存储的时刻,即向后推迟跳过区间的长度:纽约 2024-03-10 的02:30显示为03:30。 - 标记为
P的交易是补齐交易,页面显示为BalancePad条目。balance行是BalanceCheck条目,由journals.balance_checks补全。
journals.postings
Section titled “journals.postings”一些交易按书写的分录,按账本顺序排列:数量、数量是否由推算得出、所记入批次的单位成本,以及分录所在账户在该币种下记账前后的余额。
| 参数 | 类型 | 值 |
|---|---|---|
ids |
set |
交易的 id,即 journals.page 一页中的交易 |
SELECT id, posting_index, account, automatic, balanced, first(currency) AS currency, sum(number) AS number, count(*) AS lots, count(cost_number) AS lots_at_cost, min(cost_number) AS cost_number, max(cost_number) AS max_cost_number, min(cost_currency) AS cost_currency, max(cost_currency) AS max_cost_currency, number(last(only(currency, account_balance))) - sum(number) AS balance_before, number(last(only(currency, account_balance))) AS balance_after, first(metas) AS metasWHERE id IN :idsGROUP BY id, posting_index, account, automatic, balanced- 批次记账拆成多行的分录重新合为一行,数量相加。它的成本是所记入批次的单位成本,因此 10 个单位的
{{1000 USD}}成本为100 USD。减仓记入成本不同的多个批次时没有成本:此时cost_number与max_cost_number(或两个币种)不同,或lots_at_cost小于lots。 - 书写时没有金额的分录(
automatic)在流水中没有数量,只有推算出的数量。 balance_before和balance_after是分录所在账户在该币种下记账前后的余额:account_balance不受WHERE影响。
journals.balance_checks
Section titled “journals.balance_checks”一些余额断言:断言金额、账户的真实余额、两者之差,以及断言是否成立。
| 参数 | 类型 | 值 |
|---|---|---|
ids |
set |
断言的 id,即 journals.page 一页中的断言 |
SELECT id, account, amount, tolerance, actual, passed, amount - actual AS difference, actual + (amount - actual) AS assertedFROM #balancesWHERE id IN :idsasserted 是断言金额,按余额的小数位数书写。
journals.payees
Section titled “journals.payees”账本中交易的所有收款人,去重并排序,不含补齐交易的收款人。
SELECT DISTINCT payeeFROM #transactionsWHERE payee IS NOT NULL AND payee != '' AND flag != 'P'ORDER BY payeejournals.accounts
Section titled “journals.accounts”未关闭的账户,按名称排序。
SELECT accountFROM #accountsWHERE open IS NOT NULL AND close IS NULLORDER BY accountjournals.documents
Section titled “journals.documents”账本中的所有文档,最新的在前:document 指令,以及交易和分录在元数据中写下的文档。
SELECT date, time, path, account, transaction_idFROM #documentsORDER BY seq DESCpath 是下载文档所用的路径,相对于账本目录。分录写下的文档属于该分录的账户。
journals.errors
Section titled “journals.errors”账本错误的一页,先按文件、再按在文件中的位置排列。
| 参数 | 类型 | 值 |
|---|---|---|
size |
int |
每页的错误数,1 到 1000 |
offset |
int |
该页之前的错误数:(page - 1) × size |
SELECT id, kind, file, line, column, span_start, span_end, source, metasFROM #errorsLIMIT :size OFFSET :offset错误列表以文件和指令所在的行表示错误所在的位置(由 line 和 source 的行数得出);span_start 和 span_end 是写入时用来替换该指令的字节偏移。
预算页面读取 #budgets、#budget_definitions 和 #budget_events。月份以其第一天表示,例如 2024-06-01;没有指定月份时,页面使用账本时区中的当前月份。
budgets.month
Section titled “budgets.month”截至某个月的每个预算:该预算在 #budgets 中到这个月为止的最后一个月,即预算页面列出的内容。#budgets 中每个预算都有直到当前月份的每一个月,所以 last_month 就是所请求的月份,除非请求的是更晚的月份。预算在 last_month 之后不可能再有变动,所以 CASE 把它顺延过来:这个月以 available 开始,支出为零。activity 是一个数值,以预算的 currency 计。在该月之后才开始的预算不会列出。WHERE date <= :month 还让 #budgets 不再生成更晚的月份,所以账本中日期写到遥远未来的笔误不会造成影响。
| 参数 | 类型 | 值 |
|---|---|---|
month |
date |
该月的第一天 |
SELECT name, last(alias) AS alias, last(category) AS category, last(currency) AS currency, last(date) AS last_month, CASE WHEN last(date) < :month THEN last(available) ELSE last(assigned) END AS assigned, CASE WHEN last(date) < :month THEN 0 ELSE number(last(activity)) END AS activity, last(available) AS available, last(closed) AS closedFROM #budgetsWHERE date <= :monthGROUP BY nameORDER BY namebudgets.budget
Section titled “budgets.budget”单个预算:它的显示名称、分类、商品,以及其分录计入该预算支出的账户,来自没有月份的 #budget_definitions。没有这个预算时没有结果行。
| 参数 | 类型 | 值 |
|---|---|---|
name |
str |
预算 |
SELECT name, alias, category, currency, accountsFROM #budget_definitionsWHERE name = :namebudgets.budget_month
Section titled “budgets.budget_month”截至某个月的单个预算,与 budgets.month 相同。如果预算在该月之后才开始,则没有结果行,预算页面显示为没有分配、也没有支出。
| 参数 | 类型 | 值 |
|---|---|---|
name |
str |
预算 |
month |
date |
该月的第一天 |
SELECT name, last(alias) AS alias, last(category) AS category, last(currency) AS currency, last(date) AS last_month, CASE WHEN last(date) < :month THEN last(available) ELSE last(assigned) END AS assigned, CASE WHEN last(date) < :month THEN 0 ELSE number(last(activity)) END AS activity, last(available) AS available, last(closed) AS closedFROM #budgetsWHERE name = :name AND date <= :monthGROUP BY namebudgets.events
Section titled “budgets.events”某个月中 budget-add 和 budget-transfer 指令为预算放入的金额,最新的在前,金额按原样给出:转出为负数。
| 参数 | 类型 | 值 |
|---|---|---|
name |
str |
预算 |
month |
date |
该月的第一天 |
SELECT date, time, timestamp, type, amountFROM #budget_eventsWHERE name = :name AND type != 'close' AND yearmonth(date) = :monthORDER BY timestamp DESCbudgets.postings
Section titled “budgets.postings”某个月中预算的分录,最新的在前,每条分录附带其账户在该分录之后、以该分录货币计的余额:即其日期当时计入该预算的预算账户分录,由 account_budgets 判断,所以关闭后以其他预算重新开启的账户,其分录列在它所计入的预算中。预算页面把它们和 budgets.events 的事件按时间合并列出,最新的在前。
| 参数 | 类型 | 值 |
|---|---|---|
accounts |
set |
预算的账户,即 budgets.budget 的 accounts |
month |
date |
该月的第一天 |
name |
str |
预算 |
SELECT date, time, timestamp, account, id, payee, narration, units(position) AS units, only(currency, account_balance) AS balanceWHERE account IN :accounts AND yearmonth(date) = :month AND :name IN account_budgets(account, date)ORDER BY timestamp DESC商品的精度、前缀、后缀、舍入方式和分组来自它的 commodity 指令。账本持有多少该商品、持有在哪些批次中,以及它的价格,来自下面这些查询。持有量指资产(Assets)和负债(Liabilities)账户中的持有量,这些账户用 under 选出,查询因此只读取它们的分录。
commodities.totals
Section titled “commodities.totals”资产和负债账户持有的每种商品的数量,只列出有持有量的商品。没有结果行的商品持有量为零。
SELECT currency, sum(number) AS totalWHERE under(account, 'Assets') OR under(account, 'Liabilities')GROUP BY currencyHAVING sum(number) != 0ORDER BY currencycommodities.total
Section titled “commodities.total”资产和负债账户持有的某一种商品的数量。没有持有量时没有结果行。
| 参数 | 类型 | 值 |
|---|---|---|
commodity |
str |
商品 |
SELECT currency, sum(number) AS totalWHERE currency = :commodity AND (under(account, 'Assets') OR under(account, 'Liabilities'))GROUP BY currencyHAVING sum(number) != 0commodities.latest_prices
Section titled “commodities.latest_prices”每种商品以某种货币计价、截至今日的最新价格,及其日期和时间。汇率与估值所用的一致,来自引擎的价格表:该货币对在今日或之前、任一方向的最新报价,反向报价取其倒数。日期在未来的价格不是最新价格。
| 参数 | 类型 | 值 |
|---|---|---|
currency |
str |
报价货币,即运营货币 |
SELECT CASE WHEN currency = :currency THEN currency(amount) ELSE currency END AS currency, last(date) AS date, last(time) AS time, getprice(last(CASE WHEN currency = :currency THEN currency(amount) ELSE currency END), :currency, today()) AS rateFROM #pricesWHERE (currency = :currency OR currency(amount) = :currency) AND currency != currency(amount) AND date <= today()GROUP BY 1ORDER BY 1commodities.latest_price
Section titled “commodities.latest_price”某一种商品以某种货币计价、截至今日的最新价格,及其日期和时间。汇率与估值所用的一致,来自引擎的价格表:该货币对在今日或之前、任一方向的最新报价,反向报价取其倒数。日期在未来的价格不是最新价格。
| 参数 | 类型 | 值 |
|---|---|---|
commodity |
str |
商品 |
currency |
str |
报价货币,即运营货币 |
SELECT CASE WHEN currency = :currency THEN currency(amount) ELSE currency END AS currency, last(date) AS date, last(time) AS time, getprice(:commodity, :currency, today()) AS rateFROM #pricesWHERE ((currency = :commodity AND currency(amount) = :currency) OR (currency = :currency AND currency(amount) = :commodity)) AND currency != currency(amount) AND date <= today()GROUP BY 1commodities.lots
Section titled “commodities.lots”资产和负债账户持有的某种商品的批次:每个账户、每种成本和取得日期的数量,按账户排序,同一账户内最早取得的在前。仅标签不同的批次分开列出。不按成本持有的数量在每个账户中算作一个批次。
| 参数 | 类型 | 值 |
|---|---|---|
commodity |
str |
商品 |
SELECT account, cost_date, cost_number, cost_currency, cost_label, sum(number) AS unitsWHERE currency = :commodity AND (under(account, 'Assets') OR under(account, 'Liabilities'))GROUP BY account, cost_date, cost_number, cost_currency, cost_labelHAVING sum(number) != 0ORDER BY account, cost_date, cost_number, cost_labelcommodities.prices
Section titled “commodities.prices”某种商品以任何货币报价的所有价格,最早的在前。
| 参数 | 类型 | 值 |
|---|---|---|
commodity |
str |
商品 |
SELECT date, time, amountFROM #pricesWHERE currency = :commodityORDER BY date, time