Beancount 平行预算插件:当个人记账遇上事业单位平行记账

2026-05-10 blog life coding finance AI beancount LLM

从「平行记账」说起

如果你接触过政府会计制度,一定对「平行记账」这个词不陌生。

2019 年起,我国行政事业单位全面实行新政府会计制度,核心变化就是建立 财务会计预算会计 适度分离又相互衔接的「双体系」核算模式。简单说,就是同一笔经济业务,同时从两个维度反映:

  • 财务会计 — 权责发生制,反映单位的资产负债和运行成本
  • 预算会计 — 收付实现制,反映预算收支执行情况

这套模式有个形象的名字叫 平行记账。它在确保财务信息完整性的同时,又能直观看到预算执行进度——钱花到哪了、还剩多少、有没有超支。

什么?你说这是写给事业单位财务人员的,跟个人记账有什么关系?

别急。我在用 Beancount 做个人记账时发现,这种「平行」的思路,恰好能解决个人预算管理中长期困扰我的几个痛点。

Fava 自带预算功能的问题

Beancount 生态中,Fava 是最常用的 Web 前端。它确实自带了一套 budget 指令,但实际用过之后,槽点不少:

1. 不直观,学习成本高

内置的 budget 需要配合 bean-query 特定的查询语法才能看到预算执行情况。这意味着你没法在 Fava 的资产负债表上直接看到预算余额——你得再开一个单独的报表页面,还需要自己拼 BQL。

此外,budget 指令的语法固定,自定义灵活性差,例如只支持固定按照月份或者年为周期进行预算管理,不支持一次性预算或跨周期预算管理。对于装修这种一次性大额、餐饮这种每月定时充值的混合场景,内置 budget 没有一个直观的「周期开始自动充值、过往余额不累积」的展示机制。对于预算分类比较细的用户(比如我有餐饮、交通、购物、装修等多个预算),配置起来繁琐且不易维护。

3. 与账本体系割裂

最让我难受的是——预算信息是「外挂」的,不在标准的复式记账体系内。这意味着预算余额不是账户余额,不能通过 Fava 的账户树直接浏览,只能在Fava的支出页面中的某个子页面察看。没法用 bean-query 做自由查询,也没法和其他账户做交叉分析。

我们的方案:Beancount 平行预算

所以,我想能不能换一种思路——把预算本身做成账本的一部分

受事业单位平行记账的启发,我开发了 beancount_parallel_budget 插件。核心思想就是:让预算像会计科目一样存在,用复式记账的方式管理预算执行

账户体系

每一个预算类别,对应三个账户:

Equity:Budget:Expenses:{name}  ← 记录该预算下的实际支出
Equity:Budget:Balance:{name}   ← 预算余额(周期初=预算额,周期末=剩余)
Equity:Budget:Income:{name}    ← 预算收入(周期初调整时的反向账户)

关键逻辑:每个支出交易发生时,插件自动在原有记账分录上追加两条平行分录,将支出金额同步记到 ExpensesBalance 账户。这就像一个迷你「预算会计」体系,与你的「财务会计」(即平时的收支记账)并行运作。

周期自动充值

每个预算周期开始时,插件计算 Balance 的当前余额,自动补足(或扣回)到预算金额:

PeriodStartAdj = BudgetAmount - RunningBalance

如果预算 2000,上月末花了 1200 还剩 800,那下月 1 号自动补 1200,余额回到 2000。 如果超支了(余额 2230 > 预算 2000),自动扣回 230,余额回到 2000。

一个调整分录就完成了周期切换,干净利落。

一次性预算

对于装修、大件采购这种一次性支出,设置 once 类型,只在起始日做一次调整,后续不再充值。

上手使用

安装

git clone git@github.com:ertuil/beancount_parallel_budget.git ~/parallel_budget
export PYTHONPATH=~/parallel_budget:$PYTHONPATH

在账本中引入:

plugin "parallel_budget"

定义预算

custom 指令定义,非常直观:

2026-01-01 custom "parallel_budget" "Food" "monthly" 2000 USD
2026-01-01 custom "parallel_budget" "Renovation" "once" 20000 USD
2026-01-01 custom "parallel_budget" "Traffic" "monthly" 500 USD

格式:YYYY-MM-DD custom "parallel_budget" "<名称>" "<周期>" <金额> <币种>

自动匹配

当你的支出账户以预算名称为前缀时,自动标记:

2026-01-03 * "Walmart" "Weekly groceries"
  Expenses:Food:Grocery 120.50 USD
  Assets:CreditCard:Demo

插件自动追加平行分录:

2026-01-03 * "Walmart" "Weekly groceries"
  Expenses:Food:Grocery 120.50 USD
  Assets:CreditCard:Demo
  Equity:Budget:Expenses:Food 120.50 USD
  Equity:Budget:Balance:Food -120.50 USD

查看预算状态

由于预算余额就是真实的账户余额,在 Fava 里直接展开 Equity:Budget 分类就能看到:

  • Balance:Food 当前余额
  • Expenses:Food 已花费总额
  • Income:Food 累计充值金额

仓库还附带了一个 Fava Dashboard 配置文件和 CLI 报表脚本,输出效果:

┌────────────────────────────────────────────────────────────┐
│ 📊 预算执行报告                                             │
├───────────┬──────────┬──────────┬──────────┬───────┬───────┤
│ 类别      │ 预算     │ 已支出   │ 余额     │ 使用率│ 周期  │
├───────────┼──────────┼──────────┼──────────┼───────┼───────┤
│ Food      │ 2,000    │ 230      │ 1,770    │ 11.5% │ 月    │
│ Renovation│ 20,000   │ 780      │ 19,220   │ 3.9%  │ 一次  │
├───────────┼──────────┼──────────┼──────────┼───────┼───────┤
│ 合计      │ 22,000   │ 1,010    │ 20,990   │ 4.6%  │       │
└───────────┴──────────┴──────────┴──────────┴───────┴───────┘

与内置 budget 对比

特性内置 budgetparallel_budget
使用方式bean-query 专用语法标准复式记账账户
可视化需在 Fava 中启用 budget 报表Fava Equity 分类直接可见
周期管理自动交易驱动,余额自然归零并充值
灵活度固定语法自定义名称 + 前缀匹配
超支处理显示为负值自动扣回余额至预算额
与账本关系外挂式内嵌式,平行记账

一点感想

说起来很有意思:事业单位会计制度的「平行记账」设计,本质上是 在同一个账套中用两套核算维度满足不同管理需求。而我们做个人预算管理时,面临的其实是同样的问题——日常记账和预算管理,一个记录「发生了什么」,一个关注「还剩多少」,两者天然需要并行运作。

把预算做成账户而不是外挂功能之后,整个体验流畅了很多。账户树点开就能看,bean-query 随便查,Dashboard 随意定制——预算不再是「另一个报表」,而是账本有机的一部分。

项目地址:github.com/ertuil/beancount_parallel_budget

欢迎提 Issue 和 PR,欢迎 star ⭐


Perlica (AI 科研助理) 撰稿; Elliot 审稿

本人保留对侵权者及其全家发动因果律武器的权利

版权提醒

如无特殊申明,本站所有文章均是本人原创。转载请务必附上原文链接:https://www.elliot98.top/post/life/blog-beancount-parallel-budget/

如有其它需要,请邮件联系!版权所有,违者必究!