写在前面: 面对全面放开后多样的消费机会,开发者们如何在保障品质生活的同时,借助开源工具,全面、科学地规划和管理个人财务,把握资金动向,避开消费陷阱?
LigaAI 特邀我司 Nerd4me 大佬分享其个人财务管理经验,系列共分「概念篇」和「实战篇」。本篇「实战篇」,将展开介绍开发者如何使用 Beancount 系统且便捷地记账。
Beancount 是一个基于纯文本的开源记账软件,提供了一系列开箱即用的命令行工具,以及一套简洁、实用且美观的 Web UI。
其核心记账逻辑是「复式记账法」,即每一笔交易至少关联两个交易账户,一借一贷,数额相等。特别的是, 复式记账法规定「收入为负,支出为正」 —— 在《「钞能力养成指北」前传》,我们也详细解释了这点,大家可以按需补课。
新手指南:Why Beancount?
市面上有非常多成熟、知名的记账工具,为什么我仍强烈推荐使用 Beancount?出于以下几点考虑:
- Beancount 采用改良版的复式记账方案,「正负」代替「借贷」 对非会计专业的朋友更友好;
- 可直接在本地运行。相比基于云端的工具,数据安全更有保障,无需担心个人账务泄漏;
- 纯文本文件管理账目,便于记录和存储。 基于 Beancount 语法生成的文件,很容易阅读;
- 提供完整的命令行工具集和可视化工具
Fava
(内含大量财报模板),支持基于类 SQL 查询,可轻松生成各种复杂的报表数据;- 账本既是用户的输入文件,也是软件的「数据库」,可实现数据和工具的「无痛搬家」。
Beancount 是 Ledger-Like 家族的杰出成员。对比背靠 C++
的 Ledger,基于 Python
的 Beancount 应用轻便,新增插件或二次开发更简单;多货币支持等丰富而强大的功能,能够很好地覆盖各种记账、查账场景。
事实上,Beancount 并不知道什么是「货币」。它只是记录了「通货 Commodity」的变化,而通货完全由用户定义。 因此,Beancount 可以记录含货币在内任何东西的变化,如年假天数、股票、航空里程、信用卡积分……
当然,也可以用来数豆子——这也是「Beancount」名字的由来 >A<
英勇黄铜:基本环境搭建
迈出「Beancount 记账」的第一步,需要安装 Python3
环境和对应包。
pip install beancount fava
使用下列命令,生成一个官方提供的示例。
bean-example > example.beancount
通过 fava
命令运行 Web UI 。默认情况下的 Web UI 会运行在 http://localhost:5000
中。
fava -H 0.0.0.0 example.beancount
不屈白银:Beancount 语法入门
环境搭建后,我们需要制作账本来承载账目信息。Beancount 作者在 Beancount User's Manual 中提供了非常详尽的使用说明,大家可以根据实际需求自行创建。
下面以《「钞能力养成指北」前传》中「老王煎饼摊」为例,逐步演示如何使用 Beancount 编写账本并制成报表。
1. 制作账本
新建一个纯文本文件并保存为 laowang.bean
。在任意文本编辑器打开,只需简单两步就能完成账本内容的编写。
第一步:设立交易账户
使用 open 命令设立交易账户,语法结构为开户时间 open 账户类型:账户名称 货币类型;备注
。
其中,开户时间应早于该账户关联的首笔交易产生的时间;同时,在复式记账法中,交易账户分为五大类型:
- Assets - 资产类账户:现金、存款、基金、股票、房子、车子、借出账款等;
- Liabilities - 负债类账户:信用卡、贷款、花呗、向他人借款等;
- Income - 收入类账户:工资、奖金、专利、投资收益等;
- Expenses - 费用类账户:衣食住行等一切花销、过年发出的红包等;
- Equity - 权益类账户:记账前已有的资产或负债,一般在账户初始化时设置。
1970-01-01 open Income:Sales CNY
1970-01-01 open Assets:Bank:Saving:ICBC CNY
1970-01-01 open Assets:Fixed CNY ;固定资产
1970-01-01 open Liabilities:CreditCard:ICBC CNY
1970-01-01 open Expenses:Food CNY
第二步:编写交易记录
Beancount 是复式记账工具,其账目分录至少包含三行信息:交易详情、支出详情和收入详情。
交易详情记录了交易时间、收款人信息和交易备注,格式为 YYYY-MM-DD */! "" ""
。
- 「 */! 」为对账标识符: * 表示该交易已确认,! 表示该交易存疑/待确认。
- 双引号中的为选填内容,分别记录「收款人 Payee」和「交易备注 Narration」。
支出/收入详情记录出/进账的账户名称、交易数额和货币类型,出账为负,进账为正,正负金额相加为零。
2023-01-01 * "xx公司" "购买设备"
Assets:Bank:Saving:ICBC -1,000.00 CNY
Assets:Fixed 1,000.00 CNY
2023-01-01 * "采购食材"
Liabilities:CreditCard:ICBC -500.00 CNY
Expenses:Food 500.00 CNY
把「老王煎饼摊」的账本补充完整,就会得到下面这个 laowang.bean
文件。
1970-01-01 open Income:Sales CNY
1970-01-01 open Assets:Bank:Saving:ICBC CNY
1970-01-01 open Assets:Fixed CNY ;固定资产
1970-01-01 open Liabilities:CreditCard:ICBC CNY
1970-01-01 open Expenses:Food CNY
2023-01-01 * "xx公司" "购买设备"
Assets:Bank:Saving:ICBC -1,000.00 CNY
Assets:Fixed 1,000.00 CNY
2023-01-01 * "采购食材"
Liabilities:CreditCard:ICBC -500.00 CNY
Expenses:Food 500.00 CNY
2023-01-02 * "煎饼销售收入"
Income:Sales -1,000.00 CNY
Assets:Bank:Saving:ICBC 1,000.00 CNY
2023-01-02 * "质量问题食材退货"
Expenses:Food -100.00 CNY
Liabilities:CreditCard:ICBC 100.00 CNY
2. 生成财务报表
使用 Beancount 命令行工具集中的 bean-report
命令,可以实现各类财务报表的自动生成。
比如, income
子命令能生成损益表,balsheet
可以统计资产负债情况,balances
用于查询各账户余额等。
(BEANCOUNT) bean-report laowang.bean balances
Assets:Bank:Saving:ICBC
Assets:Fixed 1000.00 CNY
Equity
Expenses:Food 400.00 CNY
Income:Sales -1000.00 CNY
Liabilities:CreditCard:ICBC -400.00 CNY
账户余额表指出,老王持有 1,000 元的固定资产、在食材上花费 400 元、煎饼营业收入 1,000 元、信用卡欠款 400 元。
对许多非技术背景的朋友来说,用命令行呈现的数据有点不太好阅读。没关系,Beancount 自己会出手:Beancount 为我们提供了一个可视化工具 Fava
。
安装 Fava
后,使用 fava laowang.bean
命令启动 Web UI 服务。在浏览器中打开 http://localhost:5000/
,就可以获得表格式的报表数据。
荣耀黄金:进阶场景说明
在现实生活中,除了「一对一」交易,我们还会遇到很多涉及多个账户的「一对多」和「多对多」的情况。下面几个场景展示了 Beancount 和复式记账法在处理复杂交易方面的优势。
场景 1 :发工资
应聘谈薪,我们关心「税前工资」和「到手工资」。那么,每月发放的工资、奖金,缴纳的五险一金和个人所得税等费用,在 Beancount 中应该如何入账呢?
2023-01-10 * "Some Company" "工资2022-12"
Income:SomeCompany:Salary -20,000.00 CNY ;应发工资
Income:SomeCompany:Benefits -500.00 CNY ;节日福利
Income:SomeCompany:HousingFund -2,000.00 CNY ;住房公积金单位扣除
Assets:Government:HousingFund 4,000.00 CNY ;住房公积金缴纳
Expenses:Government:SocialSecurity 1,500.00 CNY ;社保缴纳
Expenses:Government:IncomeTax 3,000.00 CNY ;个税缴纳
Assets:Bank:Saving:ICBC 14,000.00 CNY ;实发工资
不难看出,复式记账法的优势之一是能够清晰地展示同一笔交易中,资金在各个账户之间的流动关系,反映资金运动的来龙去脉。
场景 2 :记录房产
对于房产、车辆等固定资产,我们不只关心其购入时的价值,也同样在乎其后续的升/贬值情况,即当前市场估值。
Beancount 不预先定义任何货币,我们可以创建人民币 CNY
的变种货币 CNY.UNVEST
,赋予房子一个人民币变种价格(即估值价格) 。
这样,在以人民币 CNY
展示总资产时,我们既能在价格页查看房子的当前市值估值,也不会影响总资产的统计。
;创建房产货币
2018-06-01 commodity HOUSE.ABC
name: "房产名称"
2018-06-01 open Assets:Property:CS:ABC HOUSE.ABC
;房产购买
2018-06-01 * "XX地产" "房产名称"
Assets:Property:CS:ABC 1 HOUSE.ABC {2,000,000.00 CNY} ;房产购买价格
Assets:Bank:Saving:ICBC 600,000.00 CNY ;首付款
Liabilities:Bank:BOC:MortgageLoan 1,400,000.00 CNY ;抵押贷款
;房产价格
2018-06-01 price HOUSE.ABC 2,000,000.00 CNY ;买入成本
2018-06-01 price HOUSE.ABC 2,000,000.00 CNY.UNVEST ;估值价格
2023-01-17 price HOUSE.ABC 2,500,000.00 CNY.UNVEST ;估值价格
场景 3 :AA 制消费分摊
AA 制的费用分摊、信用卡分期还款等交易记账是典型的「一对多」场景。
小红和小明外出吃饭一共花费 500 元,由小红先行支付;小明几天后想起,将 AA 的费用转账还给小红。在小红的账本中,这笔钱应该这样记录:
2023-01-13 * "xx 饭店" "和小明吃饭"
Assets:VA:Wechat -500.00 CNY ;微信零钱付款
Expenses:Food:DiningOut 250.00 CNY ;AA我(小红)的一半
Assets:Receivables:Xiao-Ming 250.00 CNY ;AA小明的一半
2023-01-17 * "小明" "AA吃饭收款"
Assets:VA:Wechat 250.00 CNY
Assets:Receivables:Xiao-Ming -250.00 CNY
场景 4 :货币转换
出入境限制全面放开,许多好友也都选择出国游玩散心。在消费记账时,Beancount 如何处理不同货币之间的汇率转化关系?
在登记交易时,使用 @@
即可连接两种互相转换的货币。本例中,信用卡支出的 650 元人民币正是由 100 美元转换而来。
2023-01-17 * "在免税店买东西"
Assets:Cash -200.00 USD ;现金支付
Liabilities:CreditCard:ICBC -650.00 CNY @@ 100.00 USD ;信用卡付款
Expenses:Clothing:Pants +150.00 USD
Expenses:Clothing:Shoes +150.00 USD
场景 5 :使用 DSL 进行复杂查询
除了 bean-report
命令外,Beancount 还提供 bean-query
工具,支持 SQL
语句查询,以满足更复杂的数据统计。感兴趣的朋友可以在 Beancount – Query Language 文档中了解。
这里分享一下让 Beancount 回答「我都在哪些加油站加过几次油」的操作指令。
超凡大师:最佳实践分享
看到这里的朋友们,恭喜你们已经学会了 Beancount 复式记账的常用语法和操作。它们可以满足生活中绝大多数消费和交易的记账需求。下面分享的是一些个人实践的经验总结,希望能更好地帮你开启「Beancount 之旅」。
1. 账本编辑器的选择
我选择 VSCode
作为账本文件编辑器,搭配 Lencerf/vscode-beancount
插件一起使用,可以自动为 .bean
或者 .beancount
文件加上语法高亮、补全账户名,还可以实现金额数据的自动对齐。
具体的配置操作请查看作者在 Github
上提供的帮助文档了解。
2. 账户开户日期的设立
前面提到,交易账户的开设日期应早于该账户首笔交易产生的时间。这里也补充一些比较有意思的开户事件,供诸位参考。
- Expenses 账户可以使用自己的出生日期作为开户日期;
- Income 账户可以按来源分类后再选择日期,如
Income:SomeCompany:Salary
的开户时间是该公司的入职日期; - Assets 和 Liabilities 账户中的借/贷记卡的开设日期,与银行的开户日期保持一致。
3. 分割账本文件结构
记账是一个长期习惯,但是随着时间累积,账本文件越变越大,只在一个文件中编写和记录就会变得极其不便。
因此,在创建账本时,我们可以提前规划账本分类,使用 Beancount 的 include
语法对账本进行结构分割。这样就可以在一个 Beancount 文件中包含其他的文件,就像下面这样:
.
├── 2022
│ ├── 09.bean
│ ├── 10.bean
│ ├── 11.bean
│ ├── 12.bean
│ ├── __index.bean
│ ├── creditcard.bean
│ ├── event.bean
│ ├── forecast.bean
│ ├── income.bean
│ ├── loan.bean
│ └── transfer.bean
├── 2023
│ ├── 01.bean
│ ├── __index.bean
│ ├── forecast.bean
│ ├── income.bean
│ ├── insurance.bean
│ ├── loan.bean
│ └── transfer.bean
├── account
│ ├── __index.bean
│ ├── assets.bean
│ ├── equity.bean
│ ├── expenses.bean
│ ├── income.bean
│ └── liabilities.bean
├── commodity
│ ├── __index.bean
│ └── fund.bean
├── doc
│ └── __index.bean
└── main.bean
4. 定期对账
账本的定期对账(或称定期断言,Balance Assertion)就像写代码时的「Ctrl + S」或「Command + S」,无事发生自动隐形,关键时刻出手「救命」。
断言就是告诉 Beancount,在某个日期前,这个账户有多少余额(设值为 A) 。如果 Beancount 计算的该时间点前的交易余额 B,与余额 A 不相等,那它就会报错。此时,我们只需检查两次断言日期之间的交易,就能快速定位错账。
使用 balance
命令标记某个日期前的账户余额(区分 bean-report
命令中的 balance
子命令),让 Beancount 自动核对账本中的账户余额是否与实际金额相等。
2023-01-01 balance Assets:Bank:Saving:ICBC 10,000.00 CNY
需要注意,断言声明的是所给日期开始前的余额,即当日的交易不算在内。也就是说,上面的断言表示,在 2023 年元旦前,即截止至 2022 年 12 月 31 日,工商银行储蓄账户余额为 10,000 元。
后记
在 wzyboy 的《Beancount——命令行复式簿记》一文中,我第一次了解到「记账神器 Beancount」;2022 年 9 月前后,我正式使用 Beancount 管理个人财务。如今睡前打开 VSCode
记录当天的资金变动,已经成为固定活动,而在每日的记账、查账中,我也获益良多。
本文分享的 Beancount 入门级操作指令,可以满足(个人)日常记账的简单需求;Beancount 还有许多证券投资、基金净值更新等高级应用,大家可以自行挖宝~
新的一年,坚持记账,搞钱发财!
编者语:# 编程之外 是 LigaAI 开设的全新栏目。在这里,我们将与开发者朋友们一起发现和分享生活中的「技术时刻」,并通过极具创意与实用的「生活代码」,感受「技术改善生活」的真谛。如果你也对「代码提升幸福感」感兴趣,欢迎关注 LigaAI@SegmentFault。
LigaAI 是新一代智能研发协作平台,我们关注研发协作与效率,重视开发者个人的价值创造。往期文章中,我们分享了许多敏捷开发、项目管理、个人成长与提升的文章,欢迎朋友们阅读交流。
体验新一代智能研发协作,请 点击这里 展开了解。LigaAI 助力开发者扬帆远航,期待与你一路同行!