Beyond the Void
BYVoid
Beancount复式记账(二):借贷记账法
本文简化字版由OpenCC转换

上一篇文章介绍了为什么要复式记账,以及Beancount的基本特点。这篇言归正转,直接从实践开始。

账户类别

复式记账的最基本的特点就是以账户为核心,Beancount的系统整体上就是围绕账户来实现的。之前提到的会计恒等式中有资产、负债和权益三大部分,现在我们再增加两个类别,分别是收入和支出。Beancount系统中预定义了五个分类:

  • Assets 资产
  • Liabilities 负债
  • Equity 权益(净资产)
  • Expenses 支出
  • Income 收入

这五类是Beancount的约定,除此了Equity之下一些特殊的账户外,没有任何预先定义的账户。用户可以定义各种各样的账户,Beancount对账户的组织是树形的,譬如我分别有这些资产账户:

Assets:Cash:JPY
Assets:Cash:USD
Assets:Bank:CH:UBS
Assets:Bank:CN:BoC
Assets:Bank:US:Chase:Checking
Assets:Bank:US:Chase:Saving
Assets:Bank:JP:SMBC:JPY
Assets:Bank:JP:SMBC:USD
Assets:Broker:US:IB
Assets:Points:Airline:JAL
Assets:Points:Airline:United

Beancount对这些账户的组织形式如下图:

账户

接下来是声明账户的语法。Beancount要求每个使用的账户必须声明开户时间,格式是YYYY-mm-dd。之后是关键词open,表示在这个日期开户(或者开始记账)。接下来是账户名称,格式用:隔开的树形语法,最后是(可以省略的)账户的货币种类。货币种类不需要事先定义,也没有系统内部的定义,一般来说我们使用三字母的货币代码,但其实可以用任何的名字(惟一的限制是大写字母和下划线)。我在这个例子中使用了USDJPYCNYCHF四个货币,以及我自定义的P_JALP_UA表示不同航空公司的里程。

2019-01-01 open Assets:Cash:JPY JPY
2019-01-01 open Assets:Cash:USD USD
2019-01-01 open Assets:Bank:CH:UBS CHF
2019-01-01 open Assets:Bank:CN:BoC CNY
2019-01-01 open Assets:Bank:US:Chase:Checking USD
2019-01-01 open Assets:Bank:US:Chase:Saving USD
2019-01-01 open Assets:Bank:JP:SMBC:JPY JPY
2019-01-01 open Assets:Bank:JP:SMBC:USD USD
2019-01-01 open Assets:Broker:US:IB USD, JPY, CHF
2019-01-01 open Assets:Points:Airline:JAL P_JAL
2019-01-01 open Assets:Points:Airline:United P_UA

账户如何组织分类完全看个人需求和喜好,譬如我先分账户类型,再分国家,然后是金融机构名,最后是具体账户。分类的作用是可视化的时候,可以看某个非字节点下面所有账户的汇总。

接下来是负债类别的账户,最常见的就是信用卡,组织方式和使用方法和资产账户没有什么区别,譬如:

2019-01-01 open Liabilities:CreditCard:US:Discover USD
2019-01-01 open Liabilities:CreditCard:JP:Rakuten JPY

在开始记账之前,还差最后一步,就是收入和支出类别的定义。Beancount把收入支出的类别也想象成了一个账户,在语法上和资产、负债类账户没有区别。譬如下面例子:

2019-01-01 open Expenses:Clothing
2019-01-01 open Expenses:Food:Dinner
2019-01-01 open Expenses:Transport:Airline
2019-01-01 open Expenses:Transport:Railway
2019-01-01 open Income:Salary
2019-01-01 open Income:Rebate

基本借贷记账

有了以上定义的账户以后,我们终于可以开始实践记账了。复式记账又叫作「借贷记账」。之所以这么叫,是因为每一条记录都至少有一条借记(Debit)和一条贷记(Credit)。可以看下面这个例子:

2019-01-01 * "日本航空" "纽约-东京"
  Expenses:Transport:Airline 1000 USD
  Liabilities:CreditCard:US:Discover -1000 USD

这个例子表示我在日本航空购买了纽约-东京的机票,消费1000美元(贷记),付款的信用卡Discover扣款1000美元(借记)。

Beancount基本的语法如下所示:

YYYY-mm-dd * ["payee"] "description"
  posting 1
  posting 2
  ...

第一行要有日期,接下来是*。收款者payee是可选的,如果*后面只有一个字符串,那就是省略了payee。从第二行开始,每一行开头空两个缩进,然后是账户名以及金额、货币。这里有一个要点:要保证所有条目的总和是0,否则就会出现Transaction does not balance: (xxx USD)这样的错误。这个要求很好理解,因为花出去的钱必须和账户上减少的钱一样,否则就是所谓的「账目不平」了。

Beancount语法的灵活性在于每个记账单元可以有任意多个条目(借记和贷记),只要保证它们的总和是0就可以。于是我们还可以这样记录:

2019-01-01 * "Walmart" "在超市买两件衣服和晚餐"
  Expenses:Clothing 20 USD
  Expenses:Clothing 10 USD
  Expenses:Food:Dinner 10 USD
  Liabilities:CreditCard:US:Discover -40 USD

更加复杂的例子可能是这样的:

2016-01-01 * "Google" "工资"
  Assets:Bank:US:Chase:Checking 500 USD
  Assets:Bank:US:Chase:Saving 1839.35 USD
  Assets:Pension:US:401k:PreTax 419.23 USD
  Assets:Pension:US:401k:PreTax 209.62 USD
  Expenses:Health:Insurance:Dental 3.14 USD
  Expenses:Finance:Insurance:TermLife 7.67 USD
  Expenses:Health:Insurance:Vision 0.98 USD
  Expenses:Tax:US:Federal 763.26 USD
  Expenses:Tax:US:Medicare 60.84 USD
  Expenses:Tax:US:SocialSecurity 260.15 USD
  Expenses:Tax:US:State:NY 212.59 USD
  Expenses:Tax:US:City:NYC 131.57 USD
  Expenses:Tax:US:State:NYDisability 1.2 USD
  Income:Salary:Regular -4192.31 USD
  Income:Allowance:TermLife -7.67 USD
  Income:Salary:401kMatch -209.62 USD

实际的记账中,一进一出的两个账户占了绝大多数。这个时候把正负的金额写两遍未免有点罗嗦了,所以Beancount还提供了金额插值的功能。简单说就是假设总和一定是0,在有N个账户的时候,只要求N-1个账户声明金额。于是最初的例子还可以写成:

2019-01-01 * "日本航空" "纽约-东京"
  Expenses:Transport:Airline 1000 USD
  Liabilities:CreditCard:US:Discover

在我的实践中,我只在正好是两个账户的时候才使用这个功能,因为可以避免重复的数字。但有多个账户的时候,把每个金额都写出来有助于避免错误,和「防御性编程」的理念一样。

在上面的例子中,我们还可以看出来,所有Expenses类别的账户都是正数,所有Income类别的账户都是负数。这是Beancount及类似工具使用负数来表示借记(Debit),正数表示贷记(Credit)的结果。简单来说,借记就是把账户上的资金移除,贷记就是增加账户的资金。同理,通常Assets类是正数,Liabilities类是负数。本质上每个账户的数值只有绝对值有意义,正负号并没有实际含义。

货币转换

如果在去国外旅游,免不了要进行货币转换。事实上Beancount本身没有定义任何货币,这也意味着你可以定义任何货币(或商品)。所以哪怕不出国,只要是记录了非主要货币类资产,譬如投资品,代金券,航空公司里程,那么就需要货币转换了。

在使用多个货币之前,需要先定义「工作货币」。工作货币可以不止一个,例如:

option "operating_currency" "JPY"
option "operating_currency" "USD"

定义了工作货币以后,在fava界面中可以看到工作货币单独列出的栏目。

Beancount货币转换的语法有两种,一种是使用@记录单位货币的转换价格,例如:

2019-01-01 * "日本航空" "纽约-东京"
  Expenses:Transport:Airline 1000 USD @ 110 JPY
  Liabilities:CreditCard:JP:Rakuten -110000 JPY

另一种方式我更常用,使用@@记录转换后的总额:

2019-01-01 * "日本航空" "纽约-东京"
  Expenses:Transport:Airline 1000 USD @@ 110000 JPY
  Liabilities:CreditCard:JP:Rakuten -110000 JPY

货币转换不一定只在一个账户上。下面的这个例子是以2.5日圆每点的价格,买了10000日本航空里程,但是付款的信用卡是以美元计价的,所以两遍都可以转换为25000日圆来平衡。

2019-01-01 * "日本航空" "购买里程"
  Assets:Points:Airline:JAL 10000 P_JAL @ 2.5 JPY
  Liabilities:CreditCard:US:Discover -220.0 USD @@ 25000 JPY

借贷管理

复式记账的强大之处是每个账户都有状态,而且每个操作都是原子的,这对复杂的资金进出记录非常有帮助。

生活中一个常见的例子是朋友之间的借钱和相互垫付,就拿我最近遇到一个例子来说吧,我和X、Y三人一起出游,从东京附近的横须贺坐船到猿岛,费用是每人1300日圆的船票和200日圆的登岛费,其中船票可以用信用卡支付,而登岛费只能付现金。我们一共需要付4500日圆,但是正好谁都没有这么多现金,于是决定我用信用卡付三人的船票,X用现金付三人的登岛费,最后再结算。

2019-05-25 * "猿岛" "渡轮"
  Expenses:Transport:Ferry 1300 JPY ; 个人渡轮费用
  Assets:Receivables:X 1300 JPY ; 对X应收账款
  Assets:Receivables:Y 1300 JPY ; 对Y应收账款
  Liabilities:CreditCard:JP:Rakuten -3900 JPY

2019-05-25 * "猿岛" "登岛费"
  Expenses:Transport:Attraction 200 JPY ; 登岛费
  Liabilities:Payable:X -200 JPY ; 欠X的钱

第二天,三人结算完毕,X付给我现金,Y转账给我。

2019-05-26 * "猿岛" "费用结算"
  Assets:Receivables:X -1300 JPY ; X偿还债务
  Liabilities:Payable:X 200 JPY  ; 偿还对X的债务
  Assets:Cash:JPY 1100 JPY ; X实际付给我的钱到账
  Assets:Receivables:Y -1300 JPY ; Y偿还债务
  Assets:Bank:JP:SMBC:JPY 1300 JPY ; Y实际付给我的钱到账

最终可以看出来,我在Expenses:Transport:Ferry类别消费1300 JPY,在Expenses:Transport:Attraction类别消费200 JPY,信用卡扣款3900 JPY,收到了1100 JPY的现金,Assets:Bank:JP:SMBC:JPY收到了1300 JPY的转账。

以上这种记账方法可以让资金的流动一目了然,类别和金额也准确无误。记录对他人的债权(应收账款)和欠他人的债务(应付账款),我分别使用了Assets:ReceivablesLiabilities:Payable下面的账户。

应收账款和应付账款的另一个用途是区分付款和到货时间。一般来说交易是当场进行的,一个账户的借记和另一个账户的贷记同时发生,但是有些时候付款和到货并不是同时发生的,如果需要精确区分发生的时间的话,可以用这种方法把他们分成两笔记录。

下面这个例子是,用信用卡买日本航空里程,但是不知道为什么过了1个月才到账:

2019-01-01 * "日本航空" "购买里程"
  Assets:Receivables:JAL 10000 P_JAL @@ 25000 JPY
  Liabilities:CreditCard:US:Discover -220.0 USD @@ 25000 JPY

2019-02-01 * "日本航空" "里程到账"
  Assets:Points:Airline:JAL 10000 P_JAL
  Assets:Receivables:JAL

这篇文章到此为止,Beancount最基本的用法已经介绍完毕了。用这些简单的语法,我们已经可以满足大部分的记账需求了,但Beancount的强大之处远远不止于此。

下一篇我会继续介绍Beancount的更多用法,包括如何高效对账和资产折旧。同时欢迎加入Beancount中文讨论:t.me/beancount_zh


上次修改时间 2019-05-27

相关日志