前のエントリではBeancountの概要を紹介した。本稿ではBeancountの文法の概要を説明したい。なお、公式のGetting Starterdのほうが当然に確実だし、例もそこのものを改変して使った部分がある。またBeancount Language Syntaxも参考になる。
Directive
記帳の単位はディレクティブ(Directive)と呼ばれる。
1970-01-01 open Equity:Opening-Balance 2000-01-01 open Assets:Checking ; 開始残高 2014-01-02 * "入金" Assets:Checking 1,000 JPY Equity:Opening-Balances 2014-01-03 document Assets:Checking "docs/2014-01-03-bank-statement.pdf"
こういうYYYY-mm-dd形式の日付、Directive種別のあとに、種別ごとに異なる追加データが続くのが基本形式だ。歴史的経緯でDirectiveはエントリ(Entry)と呼ばれることもある。
なお、セミコロンから行末まではコメントである。
Transaction - 取引記録
支払や入金やその他諸々の取引はTransaction (txn) directiveとして記録される。
2014-01-02 txn "取引先A" "入金" Assets:Checking 1,000 JPY Income:Business -1,000 JPY
種別のあとに続く文字列はそれぞれpayee, narrationと呼ばれる。また、後続のインデントされた勘定科目・金額等の情報はまとめてposting(s)と呼ばれる。
ただし、txnは最頻出なので、もう少し略記が可能だ
2014-01-02 * "入金" Assets:Checking 1,000 JPY Income:Business
まず、payeeが重要でないとか特定できないとかなら省略できる。それから、相手方勘定科目の金額は貸借バランスから自動計算できるので省略できる(auto-posting)。
また、ディレクティブ種別txnは省略してフラグだけ書くことができる。フラグ*はその取引が解決済みでこれ以上の処理を要しないことを意味する。フラグとして!を使うこともできて、これは何らかの意味で今後の確認や修正を要することを意味する。
2014-01-02 ! "取引先B" "販売" Assets:Receivable:売掛金 1,000 JPY Income:Business
たとえば、まだ解決していない売掛金や引き落とされていないクレジットカード利用分なんかは!としておいてもよいかもしれない。
2014-01-02 * "アジャイルレトロスペクティブズ(第2版)" ! Liabilities:Creditcards:Fooカード -3,080 JPY Expenses:Education:書籍
Transaction内の特定のpostingにだけフラグをつけることもできる。
2014-01-02 ! "技術書典" Assets:Cash -1,000 JPY
主に他のデータソースから自動生成されたtransactionなどで、仕訳を貸借バランスさせるための相手先勘定科目を特定できなかった場合などに!フラグを付けるのもよくある。
こういうバランスしていないtransactionはチェッカーに書けるとエラーが出るが、文法としては合法である。
Open/Close - 勘定科目の一生
上では特に勘定科目(Account)について説明しなかった。Postingはそれぞれに勘定科目を持っている。勘定科目は木構造(というか森)をなし、根から葉までのノードをコロンで結合して表記される。
森のそれぞれの根は複式簿記で標準的な五大要素だ。
- Assets (資産)
- Liabilities (負債)
- Income (収益)
- Expenses (費用)
- Equity (純資産)
第2階層以下の分類は好きなように構成すればよく、たとえば次のような感じにできるかもしれない。
* Assets:Bank:Foo銀行:普通預金
* Assets:Bank:Foo銀行:定期預金
* Assets:Bank:Bar銀行:外貨普通預金
* Assets:Cash:財布
* Assets:Prepaid:Bazカード
ノード名は大文字または数字で始まり、文字・数字・ハイフンを含むことができる。公式のPython実装では、第3階層以下の名前はカナや漢字等で始まることもできるが第2階層では許可されない。謎である。私は上記のように第2階層の名前を英語にして運用しているが、ソート順制御を兼ねて何らかの数字コードを接頭すれば日本語で書いてもよさそうだ。
勘定科目はopenディレクティブにより定義され、closeディレクティブにより任意に閉鎖できる。
2000-01-01 open Assets:3-預貯金:Baz銀行:普通預金 2000-01-01 * "給与" Assets:3-預貯金:Baz銀行:普通預金 1,000 JPY Income:1-給与 2010-03-31 * "解約" Assets:3-預貯金:Baz銀行:普通預金 -1,000 JPY Assets:2-現金:財布 2010-03-31 close Assets:3-預貯金:Baz銀行:普通預金 ; 解約
勘定科目は他のディレクティブで利用される前にopenされていなければならない。ただし、「前」というのは論理的な意味で、ファイル内の出現順には依存しない。すべてのディレクティブはその日付によって論理的に並び、かつopenは当日の先頭、closeは当日の末尾にあるものとして扱われる。
Balance - 残高アサーション
balanceディレクティブはそのアカウントの残高を宣言する。もし実際の記帳内容と宣言内容に齟齬があればチェッカーがエラーを吐いてくれる。
2000-01-01 * "ATM引き出し" Assets:3-預貯金:Baz銀行:普通預金 Assets:1-現金:財布 10,000 JPY 2000-01-02 balance Assets:1-現金:財布 12,345 JPY
財布の中身の実査額を記録したり、クレジットカード利用明細・銀行取引明細などから転記したりしておくと記帳ミスを検出できて便利だ。
なお、balanceディレクティブはopenと同様にその日付の先頭にある扱いになるので、当日txnがある場合は日付を実査日翌日にしないとアサーションに失敗する。
Pad - 残高調整
beancountによる記帳を始めるとき、開始残高をうまく合わせるのが難しいという問題が発生する。たとえば、5月分の現金支払記録をインポートするところから利用を始めるとしよう。
2026-05-01 open Assets:Cash 2026-05-03 * "A" ... 2026-05-20 * "Z" ... 2026-05-23 balance Assets:Cash 12,345 JPY
しかし、4月末の現金残高をどこかで開始残高として記録しておかないと5月23日の残高アサーションは成功しない。
padディレクティブは、このようなときに開始残高調整用のトランザクションを自動生成して直後のbalanceが合うようにしてくれる。
2026-05-01 pad Assets:Cash Equity:Opening-Balances
こんな感じに書いておくと、23日のアサーションが成功するような額のトランザクションを1日付で生成する。
続く
(2)に続く予定