Beancount复式记账(四):项目管理

Beancount系列文章的前三篇已经基本覆盖了常用的复式记账方法。记账本身是一门经验的学问,不仅包括账本身怎么记,还包括了「账本」怎么整理。这篇文章不涉及复杂的会计学概念,只是从更加微观的角度来讲一讲我实际记账过程中是怎么组织的。

版本管理

文本记账的最大优势就是它便于使用版本管理系统,像管理代码一样管理账本。最常见的代码版本管理工具就是git了,所以我推荐使用git管理Beancount的账本文件。使用git的好处是提升账本的可维护性,尤其是能够防止不小心改错、误删这样的动作,这在重构的过程中极其重要。

git如何使用我不再赘述,对于会写代码的人来说属于基本技能。即使不熟悉,网上的资料和教程也是汗牛充栋了,更有Sourcetree这样的可视化工具。

惟一需要注意的是,你需要保管好你的git仓库,尤其是要避免盲目上传到Github之类的网站。账本信息属于非常敏感的个人隐私,因为其中可以透露出的信息非常丰富,甚至超过了日记能包含的。如果要上传git仓库备份,至少要使用支持私人仓库的服务,或者自己搭建git服务器(支持SSH即可)。最好在上传之前对整个仓库加密,譬如使用git-crypt

标签

标签是一个讲交易组织归类的方法,是开支类别之外的另一个维度。每一个交易都能加上一个或多个标签:

2019-06-01 * "奥地利航空" "东京-维也纳" #2019-07-Europe-Trip
  Expenses:Transport:Airline 600 USD
  Liabilities:US:CreditCard:Citi

标签的作用是方便查询,在fava和bean-query中都可以按照标签来过滤。

为了避免重复,Beancount还提供了标签堆栈语法:

pushtag #2019-07-Europe-Trip

2019-06-01 * "奥地利航空" "东京-维也纳"
  Expenses:Transport:Airline 600 USD
  Liabilities:US:CreditCard:Citi

2019-06-01 * "奥地利航空" "维也纳-莫斯科"
  Expenses:Transport:Airline 100 USD
  Liabilities:US:CreditCard:Citi

poptag #2019-07-Europe-Trip

除了标签,Beancount还提供了一个类似的语法^,叫做链接(Link),本质上和标签是一样的作用,但是被建议用作将财务上关联的交易组织在一起的方法。常见的使用场合是有时间跨度的一笔交易,例如汇款和收款,短期的债务,按次记录但是按月征收的某些银行手续费,或者仅仅是两个目的一致的交易。

2016-05-03 * "Chase" "取现" ^2016-05-overdraft
  Assets:Bank:US:Chase:Checking -75 USD
  Assets:Cash:USD 50 USD
  Expenses:Finance:BankFee:Overdraft 25 USD

2016-05-05 * "Chase" "还清欠款" ^2016-05-overdraft
  Assets:Bank:US:Chase:Checking 25 USD
  Assets:Bank:US:Chase:Saving

多文件组织

到此为止我一直假设所有的Beancount记录都是在单一文件中的,这个文件会随着账目的增多越来越膨胀,直到用编辑器维护不便。使用单一文件就像把一个巨大的程序写到一个源文件中一样,阅读和修改都很困难。所以Beancount提供了include文件包含语法,用法和多数编程语言一样。

include后面紧跟着要引入的文件名,路径是相对于当前文件的。

; main.beancount
include "accounts.beancount"
include "categories.beancount"
include "books/books.beancount"

; books/books.beancount
include "2016.beancount"
include "2017.beancount"
include "2018.beancount"
include "2019.beancount"

利用这个简单的语法,一个巨大的账本就可以分成若干个较小的账本组合起来了。

要注意,到目前(Beancount 2.2.1)为止,标签堆栈和文件包含是不能组合使用的,也就是说标签堆栈内的include的文件不会自动加上标签。我在Beancount的问题列表提出了这个问题,作者的答复是也许以后会实现。

账本划分

接下来我终于要讲到我的经验之谈了。尽管有文件包含语法,但每条记录到底怎么划分还是一个见仁见智的话题。我使用了三种分割方法,分别是按日期划分、按类别划分和按账户划分。这三种划分方式都有道理,各有优劣。总而言之,划分的目的是减少错误的可能,降低维护成本,节约注意力资源。下面我详细说来。

按日期划分

按日期划分账本是最直接的分割方法,我们可以按年份或者月份创建文件(譬如2019.beancount),每个文件内只包含这段时间内的记账。一般来说除非是修正错误或者重构,旧的账不会再修改,使用当前的账本来记账,把过去的账分割储存是一种有效节约注意力资源的方法。

除了按照日历时间划分,还可以结合标签,把某些事件提取出来,最常见的是旅行。下面这个例子是我把2019年7月关于欧洲旅行的账目全部放到2019-07-Europe-Trip.beancount中,并且结合标签堆栈,把整个文件中的账目标记上#2019-07-Europe-Trip标签。

; 2019-07-Europe-Trip.beancount
pushtag #2019-07-Europe-Trip

2019-06-01 * "奥地利航空" "东京-维也纳"
  Expenses:Transport:Airline 600 USD
  Liabilities:US:CreditCard:Citi

...
poptag #2019-07-Europe-Trip

使用单独的文件来记录某个时间段的某类事件相关的开销的好处是,一旦这个事件结束,这个文件就可以进入封存状态了。此外,哪怕是不使用任何可视化工具(如fava),这个文件的可读性也非常高,甚至可以当作旅行日记了。

资产负债表

按类别划分

按类别划分指的是把统一类的重复记录整理到一起,便于根据时间纵向比较。譬如说每月的工资单、房租、水电费、定期投资、以及其他自动扣费的服务。

; Tokyo-Electric-Power.beancount

2018-04-25 * "东京电力" "电费"
  Expenses:Utility 4621 JPY
  Liabilities:JP:PrestiaVisa

2018-05-25 * "东京电力" "电费"
  Expenses:Utility 4956 JPY
  Liabilities:JP:PrestiaVisa

2018-06-25 * "东京电力" "电费"
  Expenses:Utility 6648 JPY
  Liabilities:JP:PrestiaVisa

2018-07-25 * "东京电力" "电费"
  Expenses:Utility 9394 JPY
  Liabilities:JP:PrestiaVisa

以上的例子是每月25日扣费的东京电力账目。这样记录的好处是每个月的开销一目了然,还可以观察不同月份的开销变化。如果可以从扣费的服务导出账单,就可以把单个来源放在一个文件。如果是手动记录,这种组织方式也可以最大程度上减少漏记的可能性。

按账户划分

我使用的第三种分割方法是按账户划分。按账户划分和按类别划分差不多是相互对称的两种划分方法,因为这种方法是以账目相关的账户为依据,分割出了单独的账本。按账户划分的最初目的是方便自动导入脚本,因为许多银行、信用卡都可以提供月对账单,非常适合自动转换为Beancount格式,减少人工。

但是完全按照账户的缺点也显而易见,那就是尤其和「按类别划分」冲突,所以我的实际手段是尽可能不使用这个划分方法。什么时候会使用呢?我的原则是和账户的关联性十分强的消费,包括银行利息、银行手续费、信用卡还款、返现返点、开户奖励、年费、账户间转账。

; AmexSPG.beancount
2019-02-01 * "American Express" "SPG年费"
  Expenses:Finance:CreditCardFee 33480 JPY
  Liabilities:JP:AmexSPG

2019-02-01 * "American Express" "Amex SPG开卡奖励"
  Assets:Points:US:Marriott 30000 P_MRT
  Income:OpeningBonus

2019-02-22 document Liabilities:JP:AmexSPG "AmexSPG/2019-02.pdf"

2019-03-11 * "American Express" "2019年2月账单还款"
  Assets:Bank:JP:SMBC -3254 JPY
  Liabilities:JP:AmexSPG

2019-03-22 document Liabilities:JP:AmexSPG "AmexSPG/2019-03.pdf"

2019-04-10 * "American Express" "2019年3月账单还款"
  Assets:Bank:JP:SMBC -63935 JPY
  Liabilities:JP:AmexSPG

2019-04-22 document Liabilities:JP:AmexSPG "AmexSPG/2019-04.pdf"

2019-05-10 * "American Express" "2019年4月账单还款"
  Assets:Bank:JP:SMBC -23718 JPY
  Liabilities:JP:AmexSPG

总结

我把我的原则总结成了以下规则,按顺序判断:

  • 如果该收支属于一个特别的事件,那么就将其加入这个事件对应的单独文件,例如#2019-07-Europe-Trip
  • 如果该收支是一个周期项目,但跟某个银行账户无关,则加入按类别划分的文件,例如水电费、房租、工资。
  • 如果该收支跟某个银行账户紧密相关,则加入该账户对应的文化,例如银行手续费、信用卡还款、返现。
  • 不属于以上情况,则加入按日期划分的文件。

以上就是我目前对账本管理的一些经验之谈,这些方法只是为了达成我前面提到的三个目的:减少错误的可能,降低维护成本,节约注意力资源。如果之后我发现了更好的划分方式,我会对账本重构,到时候再来更新这篇文章。

在以后的文章中(如果有的话),我还会介绍更多实际的记账方法,包括出差记账、更细致的资产记账,价格追踪,股票交易和多人记账。欢迎加入Beancount中文讨论:t.me/beancount_zh

以下链接是其他介绍:

相关日志