Beancount复式记账初体验
背景
Beancount是一个复式记账工具,除了记录收支,还会记录账户的变动。
初体验
安装
sudo pip3 install beancount fava -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
其中 fava 是一个Web界面。
使用
入门
本节内容来自记账神器Beancount,将下面的内容,保存成文件 moneybook.bean
,
;【一、账本信息】
option "title" "我的账本" ;账本名称
option "operating_currency" "CNY" ;账本主货币
;【二、账户设置】
;1、开设账户
1990-01-01 open Assets:Card:1234 CNY, USD ;尾号1234的银行卡,支持CNY和USD
1990-01-01 open Liabilities:CreditCard:5678 CNY, USD ;双币信用卡
1990-01-01 open Income:Salary CNY ;工资收入
1990-01-01 open Expenses:Tax CNY ;交税
1990-01-01 open Expenses:Traffic:Taxi CNY ;打车消费,只支持CNY
1990-01-01 open Equity:OpenBalance ;用于账户初始化,支持任意货币
;2、账户初始化
2019-08-27 * "" "银行卡,初始余额10000元"
Assets:Card:1234 10000.00 CNY
Equity:OpenBalance -10000.00 CNY
;【三、交易记录】
2019-08-28 * "杭州出租车公司" "打车到公司,银行卡支付"
Expenses:Traffic:Taxi 200.00 CNY
Assets:Card:1234 -200.00 CNY
2019-08-29 * "" "餐饮"
Assets:Card:1234 -1100.00 CNY
Liabilities:CreditCard:5678 1100.00 CNY
2019-08-31 * "XX公司" "工资收入"
Assets:Card:1234 12000.00 CNY
Expenses:Tax 1000.00 CNY
Income:Salary
在命令行执行:
fava moneybook.bean
在浏览器中打开:http://localhost:5000,即可看到损益表等内容。
基础知识
Beancount中账户名支持层级,以英文冒号 :
分隔,如 Assets:Card:1234
。但第一层必须是以下五个账户之一,日常交易中涉及到的账户,一定可以归于其中某一类:
- 收入(Income):工资、投资收益等。
- 支出(Expenses):衣、食、住、行等。
- 资产(Assets):储蓄卡余额、支付宝余额、股票账户余额、房子、车子等。
- 负债(Liabilities):信用卡欠款、房贷、车贷等。
- 权益(Equity):这个账户比较特殊,在账户初始化、误差处理等少数场合使用。
账本拆分
如果按前面的方法记账,一段时间后会发现:随着交易增加,账本文件越来越大,维护不方便。
Beancount允许将账本拆分,然后通过 include
语法将账本进行关联起来。
将支付宝账单导出为Beancount
导出账单
登录支付宝网页版本,下载csv格式的账单。
转换格式
使用自定义脚本,可以根据自己情况略做修改:
支付宝账单中无法区分出来支付来源,所以下面脚本中统一将亲情号走Alipay,其它的走信用卡0271。
#!/usr/bin/python3
def do_import(file_name):
f = open(file_name, 'r', encoding="gb2312")
line = f.readline()
while line:
# do sth
if line[0:4] == '2020':
convert_line(line)
# at last, read again.
line = f.readline()
f.close()
def convert_line(line):
items = line.split(',')
datetime = items[2]
pay_datetimes = datetime.split(' ')
pay_date = pay_datetimes[0]
pay_time = pay_datetimes[1]
source = items[6].strip()
pay_to_who = items[7].strip()
pay_item = items[8].strip()
pay_cny = items[9].strip()
pay_note = items[14].strip()
if pay_item == "亲情卡":
print("{0} * \"{1}\" ".format(pay_date, "亲情号"))
print(" {0} {1} {2}".format("Expenses:LovePay", pay_cny, "CNY"))
print(" {0} -{1} {2}".format("Liabilities:Alipay", pay_cny, "CNY"))
else:
print("{0} * \"{1}\" \"{2}\" ".format(pay_date, pay_to_who, pay_item))
print(" {0} {1} {2}".format("Expenses:Live", pay_cny, "CNY"))
print(" {0} -{1} {2}".format("Liabilities:CreditCard:0271", pay_cny, "CNY"))
print("\n")
if __name__ == "__main__":
do_import('alipay_record_202012_part.csv')