我们如何分摊开支

本文解释了我如何与妻子分摊开支。这个过程有些复杂,但我已建立了一套高效可行的系统;不过对大多数人来说,这并不容易实现,因此我决定详细描述它,以帮助他人设计类似的流程。

上下文

我们都是职场专业人士,决定大致按 70%/30% 的比例分摊所有开支。这并非硬性规定,而是我们努力达成的目标,但我们将其视为严格标准进行追踪,因为它是一个很好的近似值。我们有两类共同开支:

  • 共同开支。 我们之间的共同开销,例如房租、与朋友外出用餐等。

  • Kyle 的开支。 我们孩子的相关开销,例如托儿所、纸尿裤、保姆、婴儿食品等。

这两类开支的处理方式截然不同,因为我们把孩子的开支当作一个独立项目来记账。(如果你更喜欢图示,本文末尾提供了一个系统概览图。)

共同开支

对于我们的共同开支,我在个人账本文件中为她设立了一个账户。这种方法简单直接,我不太想为所有共同开支单独维护一个账本。对我来说,仅追踪她在该账户中的余额就已足够。你可以将这个账户想象成一张信用卡(我是授信方),她通过转账或自行支付共同开支来偿还。

我在个人账本(blais.beancount)中声明了一个 Assets:US:Share:Carolyn 账户。

我的共同开支

每当我产生属于我们两人的开支时,我会正常记账,但会添加 #carolyn 标签:

2018-12-23 * "WHISK" "Water refill" #carolyn
  Liabilities:US:Amex:BlueCash  -32.66 USD
  Expenses:Food:Grocery

系统会自动将其转换为:

2018-12-23 * "WHISK" "Water refill" #carolyn
  Liabilities:US:Amex:BlueCash  -32.66 USD
  Expenses:Food:Grocery          19.60 USD
  Assets:US:Share:Carolyn       13.06 USD
    share: TRUE

这由我开发的一个自定义插件完成,该插件根据我们之间的约定规则拆分开支(另见Akkukis 开发的此插件)。在本例中,32.66 的 40%(即 13.06)被转至她的账户。请注意,对我而言,这是一个资产账户,因为她欠我这笔钱。

她的共同开支

我们还需要追踪她为共同开支所花费的钱。由于她不使用 Beancount,我设置了一个 Google Sheets 文档,她可以在其中向特定工作表添加行。该表格包含以下字段:日期、描述、账户、金额。我尽量保持简单。

然后,我编写了一个extract_sheets.py 脚本,可自动下载这些数据,并每次覆盖写入一个专用文件。该账本(carolyn.beancount)的内容如下:

pushtag #carolyn
...
2017-05-21 * "Amazon" "Cotton Mattress Protector in White"
  Expenses:Home:Furniture    199.99 USD
  Assets:US:Share:Carolyn  -199.99 USD
...
poptag #carolyn

所有这些交易都通过 pushtag/poptag 指令标记为 #carolyn。插件会将其转换,以减少我应承担的部分金额:

2017-05-21 * "Amazon" "Cotton Mattress Protector in White" #carolyn
  Expenses:Home:Furniture    119.99 USD
  Assets:US:Share:Carolyn  -119.99 USD
    share: TRUE

这些条目通常会减少她的资产账户相应金额,从而扣除她为我支付的共同开支部分。

我通过单条命令生成该文件,如下所示:

extract_sheets.py --tag='carolyn' --sheet='Shared Expenses' '<id>' 'Assets:US:Share:Carolyn' > carolyn.beancount

在我的个人账本中,我引入此文件,将这些开支与我的其他条目合并。我还定义了一个查询,用于生成她的账户对账单。在 blais.beancount 中:

include "carolyn.beancount"

2020-01-01 query "carolyn" "                                                                                                                                                                                                                                                                                                                                                                                                           
 select date, description, position, balance                                                                                                                                                                                                                                                                                                                                                                                            
 from open on 2017-01-01                                                                                                                                                                                                                                                                                                                                                                                                                
 where account ~ 'Assets:US:Share:Carolyn'                                                                                                                                                                                                                                                                                                                                                                                             
"

审核与对账单

为了生成一份供她审核的对账单(并发现偶尔的数据录入错误),我只需将该账户的日记账导出为 CSV 文件,并上传到她输入开支的同一 Google Sheets 文档中的另一个工作表:

bean-query -f csv -o carolyn.csv --numberify $L run carolyn
upload-to-sheets -v --docid="<id>" carolyn.csv:"Shared Account (Read-Only)"

这让她可以轻松核对所有记入她与我之间余额的金额,并在出现错误时指出(总会有一些错误)。此外,所有信息都集中在一个地方——保持简洁非常重要。最后,我们可以利用 Sheets 的协作功能进行沟通,例如添加评论、高亮文本等。

请注意,该系统的优势在于能正确累计我的开支:对于我支付的项目,减少我应承担的部分;对于她支付的项目,则计入她应承担的部分。

对账我们的共同开支

最后,为了结清该账户,我妻子(或我,但通常是她)会向我的账户转账,我将这笔款项记账以减少她的累计余额:

2019-01-30 * "HERBANK EXT TRNSFR; DIRECTDEP"
  Assets:US:MyBank:Checking  3000 USD
  Assets:US:Share:Carolyn

通常她每隔一两个月会操作一次。她一边在笔记本电脑上忙活,一边随意问:"嘿,我现在的余额是多少?可以转账了?" 由于系统精确记录了所有数据,因此无需纠结细节,她只需转账一个近似金额即可,这不会影响她账户的记录。

子女开支

我设计了一套完全不同的系统来追踪我们孩子的开支。对于 Kyle,我确实希望追踪与他相关的所有现金流动和开支,无论由谁支付。能够向账本提出这样的问题很有意思,例如:"他的教育花了多少钱?" 或 "我们总共在尿布上花了多少?"。此外,我们通常为 Kyle 支付不同的开销,例如我负责托儿所费用(毕竟我是会计控,这并不奇怪),而他母亲则负责购买所有衣物并准备他的饮食。为了全面掌握与他相关的所有成本,我们必须正确记录这些支出。

一个有趣的细节是,用前述方法很难实现这一点,因为我必须为他使用的所有支出账户建立镜像。这会让我的个人账本变得非常混乱。例如,我希望将尿布记入 Expenses:Pharmacy,但我自己也有个人的药房开支。因此,理论上我需要为他和我分别设立独立账户,例如 Expenses:Kyle:PharmacyExpenses:Pharmacy。这需要对所有我们为他使用的账户都进行类似处理。但我没有这样做。

我在个人账本中的子女开支

与其那样做,我更希望我的个人账本将我为他产生的所有开销统一记入一个类别: 支出:Kyle,而所有详细信息则在共享账本中追踪。但我仍需将这些开销记入某个类别,而这些记录会出现在我的个人账本中——没有其他选择(我不会专门办一张信用卡来支付他的开销,那太繁琐了,所以我必须找到一种方法)。

我通过像往常一样将开销记入我自己的支出账户来实现这一点,但为这笔交易添加标签 #kyle:

2019-02-01 * "AMAZON.COM" "MERCHANDISE - Diapers size 4 for Kyle" #kyle
  Liabilities:US:Amex:BlueCash  -49.99 USD
  Expenses:Pharmacy

此外,我使用了一个不同的插件,它会 自动 将这些交易转换为:

2019-02-01 * "AMAZON.COM" "MERCHANDISE - Diapers size 4 for Kyle" #kyle
 Liabilities:US:Amex:BlueCash  -49.99 USD
 Expenses:Kyle                   49.99 USD
   diverted_account: "Expenses:Pharmacy"

因此,从我的个人角度看,所有这些开销都会被记入我的“Kyle 项目”账户。这通过 divert_expenses 插件实现,配置如下:

plugin "beancount.plugins.divert_expenses" "{                                                                                                                                                                                                                                                                                                                                                                                           
  'tag': 'kyle',                                                                                                                                                                                                                                                                                                                                                                                                                         
  'account': 'Expenses:Kyle'                                                                                                                                                                                                                                                                                                                                                                                                             
}"

“diverted_account” 元数据用于记录原始账户,之后另一个脚本会利用它生成一份专门记录我为他支出的账本文件(详见下文)。

我在 Kyle 的账本中的孩子开销

现在,由于我们将 Kyle 的开销视为他自己的项目,我必须为他维护一套独立的账本。我会自动从我的个人账本中提取上一节所述的交易,并将其自动转换为一份专门记录他父亲(即我)支出的文件。这通过调用 extract_tagged 脚本来完成:

extract_tagged.py blais.beancount '#kyle' 'Income:Dad' --translate "Expenses:Kyle:Mom=Income:Mom" > dad.beancount

上一节中的匹配交易在其中将呈现为如下形式:

2019-02-01 * "AMAZON.COM" "MERCHANDISE - Diapers size 4 for Kyle" #kyle
  Income:Dad      -49.99 USD
  Expenses:Pharmacy   49.99 USD

如你所见,该脚本通过使用 divert_expenses 插件在上一节中保存的元数据,成功在 Kyle 的账本中还原了原始账户名称。同时,它还将付款来源记入一个统一账户,表明资金来自他父亲(收入:爸爸)。我不需要在 Kyle 的账本中追踪我使用了哪种支付方式(例如信用卡),这对他是无关紧要的。

该账本包含了我至今为止为他支付的所有开销总额。每次运行该脚本时,该文件都会被完全覆盖(这是一个纯自动生成的输出,我从不手动编辑它;如果我需要修改,我会在个人账本文件中进行)。

她的孩子开销

为了追踪她为 Kyle 支出的费用,我们使用与管理我们共同账户相同的方法(和程序),从同一份 Google Sheets 文档中的另一个工作表中提取“Carolyn 为 Kyle 的支出”:

extract_sheets.py --sheet='Kyle Expenses (Regular)' '<id>' 'Income:Mom' > mom.beancount

这会导入如下形式的交易:

2018-09-23 * "SPROUT SAN FRANCISCO" "Clothing for Kyle 9-12 months"
  Expenses:Clothing   118.30 USD
  Income:Mom      -118.30 USD

支出账户从工作表中提取——有时我需要手动稍作修正,因为它们可能无法完全与我们的账本一致——而收入则显示这笔支出由他母亲承担。

整合所有内容

最后,我们需要将所有这些文件整合起来。我创建了一个顶层的 kyle.beancount 文件,它简单声明了他所有的账户类别,并包含他母亲和父亲的账本文件。我们共有三个文件:

dad.beancount
mom.beancount
kyle.beancount -> includes transactions from dad.beancount and mom.beancount

然后,我可以在 kyle.beancount 上运行 bean-web 或 bean-query。在该账本中有两个值得关注的方面:

  1. Kyle 开销的分布情况,即抚养一个孩子总共花费了多少(无论谁支付)。

  2. 我们各自贡献的差异。

为了对齐(2),我们基本上比较 收入:妈妈收入:爸爸 账户的余额。这可以“手工”完成,通过视觉观察(使用计算器),但由于 Beancount 的 API 轻松地允许从账本中提取任何数据,我编写了一个简单的脚本来完成这项工作:计算总收入贡献总额,根据我们选定的比例拆分出预期数值,与每位家长的实际贡献进行对比,并直接输出差额:

$ reconcile_shared.py kyle.beancount

Total contributions:                       71009.30

Dad expected contribution:              42605.58
Dad actual contribution:                42906.58

Mom expected contribution:              28403.72
Mom actual contribution:                28102.72

Mom OWES Dad:       301.00

对账后,最终结果应为零。

对儿童账本进行对账

为了弥补差额,使我们对凯尔成长的支出符合之前约定的 60%/40% 比例,上一节中,我妻子需要向我转账 301 美元。当然,我们实际上并不会真的转账,我只是在我的账本中为她共享账户添加一笔虚拟转账来记录差额。要做到这一点,我只需在 blais.beancount 中插入如下交易:

2019-02-09 * "Transfer to reconcile Kyle's expenses" #kyle
  Assets:US:Share:Carolyn         301.00 USD
  Expenses:Kyle:Mom

当这笔交易被导入到 dad.beancount 中时(如前所述),它将显示为:

2019-02-09 * "Transfer to reconcile Kyle's expenses" #kyle
  Income:Mom             -301.00 USD
  Income:Dad              301.00 USD

之后,重新回到凯尔的账本,再次导入所有交易(这通过 Makefile 完成),将显示余额已更新为 0:

$ reconcile_shared.py kyle.beancount

Total contributions:                       71009.30

Dad expected contribution:              42605.58
Dad actual contribution:                42605.58

Mom expected contribution:              28403.72
Mom actual contribution:                28403.72

Mom OWES Dad:       0.00

系统概览

以下是一个图表,整体呈现了整个系统的工作方式:

我("爸爸")仅通过 Emacs 使用 Beancount。卡罗琳("妈妈")只与一个包含三个工作表的 Google Sheets 文档交互。我从她填写的表格中提取她共同支出的记录,并将其导入我的个人账本;同样,我也从另一个类似文档中提取她为凯尔支付的费用,并从我的个人账本中生成我自己的凯尔支出。这两个文档最终被合并到一个专门用于凯尔支出的顶层账本中。

我不会声称这个系统简单或总是实时更新。我通常每季度更新一次所有内容,并在每次审查时修正一些输入错误。我通过 Makefile 自动化了大部分流程,但坦白说,我更新得不够频繁,以至于无法记住所有组件的位置,因此每次都需要花些时间翻阅我的 Makefile,弄清楚各部分的作用(事实上,我写这篇文章的动机正是为了牢固地巩固自己的理解,以便未来参考)。令人惊讶的是,这套系统从未出过问题,每次我都能找到所有组件并让它正常运行。

我们两人之间有足够的零钱,因此无需每周都紧急对账。我妻子通常支付更多共同支出,而我则支付更多凯尔的开销,因此我经常需要在两者之间进行转账以平衡收支,整体差距并不大——凯尔支出的不足恰好抵消了我在共同支出上的不足。

结论

希望这对一些尝试使用复式记账法解决问题的人有所帮助。请在 Beancount 邮件列表中提问或评论,或在此文档下方留言。