Beancount复式记账初体验

  |   0 评论   |   0 浏览

背景

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')

参考