跳到主要内容

72 篇博文 含有标签「accounting」

查看所有标签

Beancount 中未来交易的预测

· 阅读需 1 分钟
Mike Thrift
Mike Thrift
Marketing Manager

Beancount 有一个插件可以预测未来的周期性交易。如何在 beancount.io 中应用它?将以下内容放入你的账本文件中。

; import the plugin
plugin "fava.plugins.forecast"

; add a monthly HOA fee
2022-05-30 # "HOA fee [MONTHLY]"
Expenses:Hoa 1024.00 USD
Assets:Checking -1024.00 USD

预测插件截图

2022-05-30-预测插件

然后,你将在净利润图表中看到预测结果。

预测插件

上面的标签 [MONTHLY] 意味着它将永久循环。如果你有更多条件需要应用,请尝试 [MONTHLY UNTIL 2022-06-01][MONTHLY REPEAT 5 TIMES][YEARLY REPEAT 5 TIMES][WEEKLY SKIP 1 TIME REPEAT 5 TIMES]

使用 Beancount 制作和追踪发票

· 阅读需 6 分钟
Mike Thrift
Mike Thrift
Marketing Manager

一个纯文本模板、一个可重复的工作流程和一个简单的查询,即可解答“还有谁欠我钱?”


2022-02-12-crafting-tracking-invoices-with-beancount

开票工作有时感觉像是一件苦差事,卡在你已完成的工作和你正在等待的付款之间。 但是,良好的开票流程是健康现金流的支柱。 它具有双重作用:清晰地告知你的客户他们欠款多少以及何时付款,并为你的会计系统提供其所需的可靠事实。

虽然专用的 SaaS 应用程序可以发送流畅的自动化 PDF 文件,但它们通常需要按月付费,并将你的数据锁定在专有孤岛中。 使用 Beancount 的轻量级纯文本方法提供了一个强大的替代方案。 你可以将每张发票转换为一组清晰的会计分录,从而获得版本控制、强大的元数据和即时查询的所有好处,而无需订阅。


最小可行发票(你永远不应该跳过的字段)

在接触你的账本之前,你需要一张专业的发票。 格式可以很简单,但内容必须精确。 这些借鉴久经考验的小企业实践的字段是不可协商的。

  • 卖方详细信息: 你的公司名称和实际地址。
  • 客户详细信息: 你的客户的名称和(理想情况下)他们的地址。
  • 发票编号: 一个唯一的、连续的 ID,永不重复使用。 INV-045 紧随 INV-044 之后。
  • 开票日期和到期日期: 清楚地说明发票的开具日期和预期付款日期。
  • 项目明细: 对服务或产品的清晰描述,以及数量、单价和项目总计。
  • 小计、税金和总计: 显示计算过程,以便客户可以轻松理解。
  • 可选注释: 用于感谢、汇款指示或客户提供的采购订单号。

为了帮助你入门,我们创建了一组可立即编辑的模板,其中包含所有这些字段。 电子表格版本甚至会为你预先计算总计。

Note: Invoice templates are coming soon! In the meantime, you can create your own using the fields listed above in Google Docs, Word, Sheets, Excel, or any other document format.


在你的账本中记录发票

将发票 PDF 发送给客户后,你必须将其记录在 Beancount 中。 这是一个至关重要的步骤,它在收入产生时确认收入,而不仅仅是在收到付款时。 该过程涉及两个不同的交易。

1. 开具发票时:

你创建一个交易,将总金额从你的 Income 账户转移到 Assets:AccountsReceivable。 这会在你的账簿中创建一个数字借据。

; 2025‑07‑21 发票 #045  为 Acme Corp. 进行的网页设计冲刺
2025-07-21 * "Acme Corp" "INV-045 网页设计冲刺"
Assets:AccountsReceivable 3500.00 USD ; due:2025-08-04
Income:Design:Web
invoice_id: "INV-045"
contact_email: "[email protected]"
link: "docs/invoices/2025-07-21_Acme_INV-045.pdf"

在这里,你借记 AccountsReceivable 并贷记你的 Income 账户。 请注意丰富的元数据:到期日期、唯一的 invoice_id,甚至直接指向你发送的 PDF 的 link:

2. 客户付款时:

当现金存入你的银行账户时,你记录第二个交易以“结清”借据。 这会将余额从 AccountsReceivable 转移到你的支票账户。

2025-08-01 * "Acme Corp" "INV-045 付款"
Assets:Bank:Checking 3500.00 USD
Assets:AccountsReceivable
invoice_id: "INV-045"

Assets:AccountsReceivableINV-045 的余额现在为零,你的账簿完全平衡。

附加 PDF: link: 元数据键与 Fava(Beancount 的 Web 界面)一起使用时尤其强大。 Fava 将直接在交易视图中呈现可点击的链接,因此只需点击一下即可访问源文档。 此工作流程早在 2016 年就在一项功能请求中设想 (GitHub)。


一个查询即可列出所有未结发票

那么,还有谁欠你钱? 使用此系统,你无需在电子邮件或电子表格中查找。 你只需要一个简单的查询。

将以下内容保存为名为 open-invoices.sql 的文件:

SELECT
meta('invoice_id') AS id,
payee,
narration,
date,
number(balance) AS outstanding
WHERE
account = "Assets:AccountsReceivable"
AND balance != 0
ORDER BY
date;

现在,从你的命令行运行它:

bean-query books.beancount open-invoices.sql

几秒钟内,你将获得一份清晰的、最新的所有未结发票的账龄报告,其中显示发票 ID、客户、开票日期和欠款金额。 无需额外的软件。


自动化繁琐的工作

纯文本的优点在于可编写脚本。 你可以自动化此工作流程中繁琐的部分。

  • 模板 + Pandoc = PDF: 以 Markdown 格式维护你的发票模板。 一个小型 Python 脚本可以填充变量(客户名称、项目明细、发票编号),命令行工具 Pandoc 可以立即将其转换为专业的 PDF。
  • Git 预提交钩子: 如果你将账本存储在 Git 中,一个简单的预提交钩子可以在你保存工作之前运行检查。 它可以验证每个新的 invoice_id 是否唯一,交易过账是否平衡为零,以及 link: 元数据中引用的文件是否实际存在。
  • Cron 作业: 设置计划任务(cron 作业)以每晚运行你的 open-invoices.sql 查询并通过电子邮件向你发送摘要。 你将每天开始工作时确切地知道谁需要友好的提醒。

现实的警告

Beancount 是一个会计工具,而不是开票_服务_。 它不会自动向你的客户发送付款提醒或处理他们的信用卡付款。 工作流程是:你使用你选择的方法(例如上面的模板)创建并发送发票,然后在你的账本中记录会计分录。

对于大多数自由职业者和小商店来说,这个手动步骤对于一个完全拥有和控制的、防弹的、可审计的免费会计系统来说是一个很小的代价 (beancount.io)。


后续步骤

准备好控制你的开票了吗? 以下是开始的方法。

  1. 下载模板并使用它创建你的下一个真实发票,确保使用连续的发票编号。
  2. 将你发送的 PDF存储在专用文件夹中,例如 docs/invoices/,并在你的 Beancount 交易中使用 link: 元数据键来引用它们。
  3. 保存 open-invoices.sql 查询并将其作为你每周财务审查的一部分运行。

纯文本会计并不意味着放弃完善或控制——它只是意味着数据库是可 grep 的。 使用简单的模板和上面的代码片段,你将更快地获得付款_并且_保持你的账簿整洁。

Beancount 中的调整分录:月末财务核对

· 阅读需 5 分钟
Mike Thrift
Mike Thrift
Marketing Manager

会计工作并不在最后一笔销售款到账时就结束了。为了真实了解你的业务健康状况,你需要进行月末财务核对。在每个期末,你都需要进行调整分录——对日记账进行调整,将收入和支出正确地归入相应的期间,并确保资产负债表的准确性。

在纯文本 Beancount 账本中,这些关键分录是透明的、版本可控的,并且易于审计,将繁琐的任务转化为清晰且可重复的流程。

2022-01-25-adjusting-entries-in-beancount-your-month-end-tune-up


为什么调整分录很重要

进行这些调整是健全会计的基础。它们确保你的财务报表准确可靠。

  • 权责发生制准确性: 调整分录是权责发生制会计的引擎。它们将收入和成本移至实际赚取或发生的期间,而不管现金何时易手。这满足了构成现代会计基础的核心收入确认配比原则AccountingCoach.com)。

  • 可靠的关键绩效指标: 关键绩效指标的质量取决于其背后的数据。只有在正确过账递延、应计和估计后,毛利率、净收入和现金流量预测等指标才能反映真实情况(Corporate Finance Institute)。

  • 清晰的审计线索: 明确的月末调整可以创建清晰的财务推理记录。这有助于审计员(以及你未来的自己)轻松追踪变化的内容和原因,从而建立对你数字的信心(Accountingverse)。


六个常见类别(附 Beancount 代码片段)

以下是六种最常见的调整分录类型,以及如何在 Beancount 账本中记录它们的示例。请注意使用 adj:"accrual" 等元数据,以便以后轻松查找和分析这些分录。

1. 应计收入

这是指你已赚取但尚未开具账单或收到付款的收入。

2025-07-31 * "咨询——7 月工时"
Assets:AccountsReceivable 12000.00 USD
Income:Consulting
; adj:"accrual" period:"Jul-25"

2. 应计费用

你已发生但尚未支付的费用,例如将于下个月收到的水电费账单。

2025-07-31 * "律师——7 月预付金"
Expenses:Legal 2500.00 USD
Liabilities:AccruedPayables
; adj:"accrual"

3. 递延(预收)收入

这适用于客户提前付款的情况。你将随着时间的推移确认已赚取的那部分收入。

2025-07-31 * "年度 SaaS 预付款(确认 1/12)"
Liabilities:UnearnedRevenue 833.33 USD
Income:SaaS
; adj:"deferral"

4. 预付(递延)费用

当你预先支付费用时(例如年度保险费),你每月将其一部分计入费用。

2025-07-31 * "保险——预付费用中的 1 个月费用"
Expenses:Insurance 400.00 USD
Assets:PrepaidInsurance
; adj:"deferral"

5. 折旧和摊销

此分录将长期资产(例如计算机或车辆)的成本在其使用寿命内进行分配。

2025-07-31 * "Mac Studio 折旧"
Expenses:Depreciation 1250.00 USD
Assets:Computers:AccumDepr
; asset_id:"MAC-03" adj:"estimate"

6. 坏账准备

你预计无法收回的应收账款的估计值,记为坏账费用。

2025-07-31 * "坏账准备金(应收账款的 2%)"
Expenses:BadDebt 700.00 USD
Assets:AllowanceForBadDebt
; basis:"A/R" rate:0.02 adj:"estimate"

可重复的工作流程

为了保持月末结算的高效性和无差错性,请采用一致的工作流程。

  • 使用单独的文件。 将一个期间的所有调整保存在一个位置,例如 adjustments-2025-07.bean。在你的主账本文件中,使用 include 指令将其最后导入。这可确保在生成最终报告之前应用调整。

  • 标准化你的元数据。 始终使用一致的元数据键和值,例如 adj:"accrual|deferral|estimate"period:"Jul-25"。这使得查询和审查特定类型的调整变得轻而易举。

  • 运行预检。 在将更改提交到 Git 之前,对你的调整文件运行 bean-check 以捕获任何拼写错误或不平衡的过账。

  • 执行单行健全性检查。 此查询确认你该期间的所有调整是否平衡,让你确信没有引入错误。

    bean-query main.bean "SELECT account, SUM(number) WHERE meta('adj') AND meta('period') = 'Jul-25' GROUP BY account"

快速故障排除技巧 🤔

  • 你的 Liabilities:UnearnedRevenue 余额是否不断攀升? 检查你的合同里程碑。与你交付的工作相比,你确认收入的速度可能太慢。

  • 你的 Assets:PrepaidInsurance 余额是否为负? 你可能以比资产计划允许的速度更快的速度将其计入费用。仔细检查你的摊销计划。

  • 你的平均收款期 (DSO) 在应计后是否变得更糟? 你的应计收入可能掩盖了潜在的收款问题。将此 KPI 与应收账款账龄报告配对,以便在成为现金流问题之前发现逾期付款的客户。


结束语

调整分录可能会让人觉得乏味,但是当你比较“调整前”和“调整后”的损益表时,它们的价值就变得清晰可见——差异通常很大。使用 Beancount,这些调整变成了小的、可搜索的补丁,你可以像代码一样对其进行自动化和审查。

养成月末调整的习惯,你的数字将与你的工程一样准确。

祝你平衡愉快!

Beancount.io 革新个人财务管理

· 阅读需 4 分钟
Mike Thrift
Mike Thrift
Marketing Manager

传统的 Beancount 用户通常使用命令行工具或在私有网络中自建服务器,他们必须通过电脑或移动设备上的通用文本编辑器进行操作。Beancount.io 通过提供开源的 Android 和 iOS 移动应用以及安全的云服务来减少这些麻烦,让你的账本现在只需轻点几下即可通过指纹访问。

Beancount 是一种计算机语言,可以在文本文件中实现复式记账。一旦你在文件中定义了财务交易,它就会生成各种报告。这种语言的设计者 Martin Blais 认为,命令行记账具有许多优点——它快速、便携、开放且可定制。

我们强烈认同这一观点,并分享 Beancount 语言所带来的赋能感。我们希望做得更多——将这项技术介绍给更多人。这意味着我们必须提高可用性,使其更容易被更广泛的用户群体所使用。

2022-01-08-instant-access-to-your-beancount-cloud-ledger-anytime-anywhere

并非每个人都是命令行爱好者,这就是我们构建 Beancount.io 的原因——一个面向所有人的个人财务管理器。它的工作方式如下:

对于繁重的工作,Beancount 用户仍然可以使用他们的电脑,通过浏览器访问 https://beancount.io 或与 Dropbox 同步来编辑或查看账本。这既保留了命令行工具的灵活性,又没有失去云端解决方案的跨设备访问能力。

对于日常轻量级操作,例如即时添加条目,Beancount 用户可以使用移动应用连接到安全云。

负责此产品的后端工程师 Mike Thrift 说:

我过去每天都会设置提醒,让自己打开笔记本电脑并将记录输入到我的 Beancount 文件中。现在,有了 Beancount.io,我可以在需要时更轻松地修改我的账本,即使我在户外商店购物时也能做到。

Facebook 软件工程师 Zhi Li 告诉我们:

我已经将我所有的 Beancount 文件迁移到 Beancount.io,现在它非常适合我的日常使用。我购买了自动数据备份等专业版功能,但我认为你们还可以做更多事情来改进服务。

你现在可以在 https://beancount.io/sign-up/ 注册,或下载 iOSAndroid 应用。我们简化了注册流程,尽可能少地收集你的信息来启动服务。然后你将获得一个预设的空账本,你可以立即添加条目。

常见问题

Beancount.io 会将我的账本数据出售给任何第三方吗?

  • 不会。我们致力于确保你的数据安全和隐私,我们绝不会出售你的账本数据。

我的数据安全吗?

  • 是的。我们使用 AES256 保护你的电子邮件和账本,使用 BCrypt 保护你的密码,并使用 SSL 保护你的网络请求。

我的账本数据是端到端加密的吗?

  • 不是。由于技术限制,在生产服务器中索引账本文件时,我们仍然必须将你的数据解密到内存中。因此,我们限制我们任何团队成员的直接访问。不幸的是,由于成本高昂,我们无法在 Intel 的 SGX 或任何安全保险库中实现这一点。

这是一项未来几年我可以信赖的可靠服务吗?

  • 是的。我们最初于 2019 年 7 月 4 日推出了 Beancount.io,并且我们已经安全可靠地运营该服务两年多了。因此,我们没有理由不继续在未来提供服务。

我可以请求新功能并赞助项目吗?

QuickBooks 到 Beancount 迁移指南

· 阅读需 32 分钟
Mike Thrift
Mike Thrift
Marketing Manager

阶段一:从 QuickBooks 导出数据

迁移五年的数据,第一步是把所有 QuickBooks 记录以可用的格式导出来。QuickBooks 桌面版和 QuickBooks 在线版有不同的导出选项:

2021-12-01-from-quickbooks-to-plain-text-a-migration-playbook

1.1 QuickBooks 桌面版 – 导出选项

IIF (Intuit Interchange Format): QuickBooks 桌面版可以将列表(如会计科目表、客户、供应商)导出为 .IIF 文本文件。在 QuickBooks 桌面版中,进入 文件 (File) → 实用程序 (Utilities) → 导出 (Export) → 列表到 IIF 文件 (Lists to IIF),然后选择你需要的列表(例如,会计科目表、客户、供应商)。这将生成一个包含账户名称、类型和列表数据的文本文件。IIF 是一种专有但易于解析的纯文本格式。用它来获取你的会计科目表和联系人列表,以便在 Beancount 中参考。

总分类账/日记账(通过 CSV): 对于交易数据,QuickBooks 桌面版没有一键式完整导出功能,但你可以使用报表。推荐的方法是导出所需日期范围内的总日记账(所有交易)。在 QuickBooks 桌面版中,打开 报表 (Reports) → 会计与税务 (Accountant & Taxes) → 日记账 (Journal),将日期设置为从最早的交易到今天,然后点击 导出 (Export) → Excel。在移除报表页眉/页脚和空列后,将结果另存为 CSV。确保数值数据是干净的:包含小数(例如 3.00 而不是 3),没有多余的引号,CSV 中没有货币符号或双重负号。CSV 文件应包含 日期 (Date)、交易号 (Trans #)、名称 (Name)、账户 (Account)、备注 (Memo)、借方 (Debit)、贷方 (Credit)、余额 (Balance) 等列(或根据报表格式只有单个金额列)。

提示: QuickBooks 桌面版 2015+ 也可以通过 查找 (Find) 对话框导出交易。使用 编辑 (Edit) → 查找 (Find) → 高级 (Advanced),设置五年的日期范围,然后将结果导出为 CSV。警告: 某些版本将导出限制在 32,768 行。如果你的数据量非常大,请逐年(或分更小的块)导出以避免数据被截断,然后再将它们合并。确保日期范围不重叠以防止重复。

其他格式 (QBO/QFX/QIF): QuickBooks 桌面版可以通过 .QBO (Web Connect) 或 .QFX/.OFX 文件导入银行交易,但对于 QuickBooks 导出,这些不是常规选项。如果你的目标只是提取银行交易,你可能已经从银行那里获得了 QBO/OFX 文件。然而,对于完整的分类账导出,请坚持使用 IIF 和 CSV。QuickBooks 桌面版不能直接导出到 QIF (Quicken Interchange Format) 格式,除非使用第三方工具。如果你确实找到了获取 QIF 的方法,请注意一些账本工具(旧版的 Ledger 2.x)可以读取 QIF,但在我们的流程中,最好还是使用 CSV。

1.2 QuickBooks 在线版 – 导出选项

内置 Excel/CSV 导出: QuickBooks 在线版 (QBO) 提供了一个导出数据工具。进入 设置 ⚙ → 工具 (Tools) → 导出数据 (Export Data)。在导出对话框中,使用报表 (Reports) 标签选择数据(例如总分类账或交易列表),并使用列表 (Lists) 标签选择列表(会计科目表等),选择所有日期 (All dates),然后导出到 Excel。QuickBooks 在线版将下载一个 ZIP 文件,其中包含所选报表和列表的多个 Excel 文件(例如,利润表、资产负债表、总分类账、客户、供应商、会计科目表等)。然后你可以将这些 Excel 文件转换为 CSV 进行处理。

交易明细报表: 如果 QBO 的默认导出不包含单个总分类账文件,你可以手动运行一个详细报表:

  1. 导航到报表 (Reports) 并找到按账户交易明细 (Transaction Detail by Account)(在某些 QBO 版本中是总分类账 (General Ledger))。
  2. 报表期间 (Report period) 设置为完整的五年范围。
  3. 在报表选项下,将分组依据 (Group by) 设置为无 (None)(以便列出单个交易而不是小计)。
  4. 自定义列,至少包括:日期 (Date)、交易类型 (Transaction Type)、编号 (Number)、名称 (Name, Payee/Customer)、备注/描述 (Memo/Description)、账户 (Account)、借方 (Debit)贷方 (Credit)(或单个金额列),以及余额 (Balance)。如果使用了类别 (class) 或地点 (location),也请包含它们。
  5. 运行报表,然后导出到 Excel (Export to Excel)

这将生成一个包含所有交易的详细分类账。将其另存为 CSV。每一行代表一笔交易的一个分录 (split/posting)。之后你需要按交易对这些行进行分组以进行转换。

会计科目表及其他列表: QuickBooks 在线版可以通过 会计 (Accounting) → 会计科目表 (Chart of Accounts) → 批量操作 (Batch Actions) → 导出到 Excel (Export to Excel) 来导出会计科目表。这样做可以获取账户名称和类型。同样,如果你想保留名称作为元数据,也请导出客户、供应商等列表。

QuickBooks Online API (可选): 对于编程方法,Intuit 为 QBO 数据提供了 REST API。高级用户可以创建一个 QuickBooks Online 应用(需要开发者账户),并使用 API 以 JSON 格式获取数据。例如,你可以查询 Account 端点获取会计科目表,查询 JournalEntryGeneralLedger 报表端点获取交易。有像 python-quickbooks 这样的 Python SDK 可以封装 API。然而,使用 API 涉及 OAuth 身份验证,对于一次性迁移来说有些小题大做,除非你偏爱自动化。对于大多数情况,手动导出为 CSV/Excel 更简单且不易出错


阶段二:转换和清理数据

一旦你有了 CSV (和/或 IIF) 格式的 QuickBooks 数据,下一步就是将其转换为 Beancount 的纯文本账本格式。这包括解析导出的文件,将 QuickBooks 账户映射到 Beancount 的会计科目表,以及按 Beancount 语法格式化交易。

2.1 使用 Python 解析 QuickBooks 导出文件

使用 Python 将确保转换的准确性和可复现性。我们将概述两个关键任务的脚本:导入会计科目表转换交易

账户导入和映射: 在添加交易之前,在 Beancount 中设置好账户至关重要。QuickBooks 的账户有类型(银行、应收账款、费用等),我们将它们映射到 Beancount 的层级结构(资产、负债、收入、费用等)。例如,我们可以使用如下映射:

# QuickBooks 账户类型到 Beancount 根类别的映射
AccountTypeMap = {
'BANK': 'Assets',
'CCARD': 'Liabilities',
'AR': 'Assets', # 应收账款作为资产
'AP': 'Liabilities', # 应付账款作为负债
'FIXASSET': 'Assets',
'OASSET': 'Assets', # 其他资产
'OCASSET': 'Assets', # 其他流动资产
'LTLIAB': 'Liabilities', # 长期负债
'OCLIAB': 'Liabilities', # 其他流动负债
'EQUITY': 'Equity',
'INC': 'Income',
'EXP': 'Expenses',
'EXINC': 'Income', # 其他收入
'EXEXP': 'Expenses', # 其他费用
}

使用 QuickBooks 桌面版的 IIF 导出文件或 QBO 的账户列表 CSV,我们获取每个账户的名称和类型。然后:

  • 创建 Beancount 账户名称: QuickBooks 有时在账户名称中使用冒号 (:) 来表示子账户(例如 “Current Assets:Checking)。Beancount 使用相同的冒号表示法来表示层级。你通常可以直接重用该名称。如果 QuickBooks 账户名称不以类别开头,则在前面加上映射的类别。例如,一个类型为 BANK、名为 "Checking" 的 QuickBooks 账户在 Beancount 中将变为 Assets:Checking。一个 EXP (费用) 账户 "Meals" 变为 Expenses:Meals,依此类推。

  • 确保命名有效: 移除或替换任何可能混淆 Beancount 的字符。QuickBooks 允许名称中包含 &/ 等字符。明智的做法是剔除或替换特殊字符(例如,用 and 替换 &,移除斜杠或空格)。此外,确保转换后所有账户名称都是唯一的——QuickBooks 可能允许在不同父账户下有相同的子账户名,这没问题,但在 Beancount 中,完整的名称(包括父账户)必须是唯一的。如果需要,重命名或附加一个限定符以区分它们。

  • 输出账户开设指令: 在 Beancount 中,每个使用的账户都必须用 open 指令开设。你可以选择一个在第一笔交易之前的日期(例如,如果迁移 2019–2023 年的数据,对所有开设指令使用 2018-12-31 或更早的日期)。脚本将写出如下行: 2018-12-31 open Assets:Checking USD 2018-12-31 open Expenses:Meals USD 为每个账户都这样做(假设 USD 是主要货币)。为每个账户使用适当的货币(见下面的多币种说明)。

交易转换: 主要挑战是将 QuickBooks 导出的交易(CSV)转换为 Beancount 条目。每个 QuickBooks 交易(发票、账单、支票、日记账分录等)可能有多条分录(行),必须将它们收集到一个 Beancount 交易中。

我们将使用 Python 的 CSV 阅读器来迭代导出的行并累积成分录:

import csv
from collections import defaultdict

# 从 QuickBooks 日记账 CSV 中读取所有行
rows = []
with open('quickbooks_exported_journal.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for line in reader:
rows.append(line)

# 按交易分组(假设 'Trans #' 标识交易)
transactions = defaultdict(list)
for line in rows:
trans_id = line.get('Trans #') or line.get('Transaction ID') or line.get('Num')
transactions[trans_id].append(line)

现在 transactions 是一个字典,其中每个键是交易 ID/编号,值是该交易的分录列表。接下来,我们将每个组转换为 Beancount 格式:

def format_date(qb_date):
# QuickBooks 的日期可能是 "12/31/2019" 这样的格式
m, d, y = qb_date.split('/')
return f"{y}-{int(m):02d}-{int(d):02d}"

output_lines = []
for trans_id, splits in transactions.items():
# 如果需要,按行顺序对分录排序(通常它们是按顺序导出的)
splits = sorted(splits, key=lambda x: x.get('Line') or 0)
first = splits[0]
date = format_date(first['Date'])
payee = first.get('Name', "").strip()
memo = first.get('Memo', "").strip()
# 交易标题
output_lines.append(f"{date} * \"{payee}\" \"{memo}\"")
if first.get('Num'): # 如果有参考编号,则包含它
output_lines.append(f" number: \"{first['Num']}\"")
# 遍历每个分录/记账
for split in splits:
acct_name = split['Account'].strip()
# 将 QuickBooks 账户名映射到 Beancount 账户(使用之前的映射)
beancount_acct = account_map.get(acct_name, acct_name)
# 确定带符号的金额:
amount = split.get('Amount') or ""
debit = split.get('Debit') or ""
credit = split.get('Credit') or ""
if amount:
# 某些导出文件有一个 Amount 列(贷方为负数)
amt_str = amount
else:
# 如果有单独的 Debit/Credit 列
amt_str = debit if debit else f"-{credit}"
# 为安全起见,移除数字中的逗号
amt_str = amt_str.replace(",", "")
# 附加货币
currency = split.get('Currency') or "USD"
amt_str = f"{amt_str} {currency}"
# 分录的备注/描述
line_memo = split.get('Memo', "").strip()
comment = f" ; {line_memo}" if line_memo else ""
output_lines.append(f" {beancount_acct:<40} {amt_str}{comment}")
# 交易结束 – 空行
output_lines.append("")

这个脚本逻辑执行以下操作:

  • 将日期格式化为 Beancount 的 YYYY-MM-DD 格式。

  • 使用收款人 (Name) 和备注 (Memo) 作为交易的叙述。例如: 2020-05-01 * "ACME Corp" "Invoice payment" (如果没有收款人,你可以使用 QuickBooks 的交易类型或留空收款人引号)。

  • 如果存在参考编号(支票号、发票号等),则添加一个 number 元数据。

  • 迭代每一条分录行:

    • 使用字典 account_map(从会计科目表步骤中填充)将 QuickBooks 账户名映射到 Beancount 账户。
    • 确定金额。根据你的导出文件,可能有一个金额列(带有正/负值)或独立的借方和贷方列。上面的代码处理了这两种情况。它确保贷方表示为负金额,因为在 Beancount 中,每个记账都使用带符号的单个数字。
    • 附加货币(除非存在不同的货币列,否则假设为 USD)。
    • 用账户、金额和带有行备注的注释写入 Beancount 记账行。例如: Assets:Checking 500.00 USD ; Deposit Income:Sales -500.00 USD ; Deposit 这反映了一笔 500 美元的存款(从收入到支票账户)。
  • 列出所有分录后,用一个空行分隔交易。

多币种处理: 如果你的 QuickBooks 数据涉及多种货币,请在每个记账上包含货币代码(如上所示)。确保外币账户以该货币开设。例如,如果你有一个欧元银行账户,你会输出 open Assets:Bank:Checking EUR,并且该账户中的交易将使用 EUR。Beancount 支持多币种账本并会跟踪隐式转换,但如果你想在报表中转换为基础货币,可能需要添加汇率的价格条目。还建议在 Beancount 文件的顶部声明你的主要经营货币(例如,option "operating_currency" "USD")。

运行转换: 保存 Python 脚本(例如,qb_to_beancount.py)并在你导出的文件上运行它。它应该会生成一个包含所有账户和交易的 .beancount 文件。

2.2 处理边缘情况和数据清理

在转换过程中,请注意以下常见的陷阱以及如何解决它们:

  • 账户名称不匹配: QuickBooks 的账户名称可能与 Beancount 的层级名称冲突。例如,QuickBooks 可能有两个不同的父账户,每个都有一个名为 "Insurance" 的子账户。在 Beancount 中,Expenses:Insurance 必须是唯一的。在导出前通过重命名其中一个(例如,“Insurance-Vehicle” vs “Insurance-Health”)来解决此问题,或者在脚本中将它们映射到唯一的 Beancount 账户。一致的命名约定(无特殊字符,并使用层级结构)将省去很多麻烦。如果需要,使用重映射文件的方法:维护一个旧名称 → 新 Beancount 名称的 CSV 或字典,并在转换期间应用它(我们的示例代码使用了一个 account_map,并且可以从文件中加载覆盖项)。

  • 日期和格式: 确保所有日期格式一致。上面的脚本将 M/D/Y 规范化为 ISO 格式。另外,如果你的五年跨度跨越了年终,请注意财政年度与日历年度的问题。Beancount 不关心财政年度的界限,但你以后可能为了方便而按年拆分文件。

  • 数值精度: QuickBooks 处理货币到分,所以以分为单位工作通常没问题。理想情况下,CSV 中的所有金额都应有两位小数。如果任何金额变成了整数(没有小数)或带有逗号/括号(表示负数),请在脚本中清理它们(去除逗号,将 (100.00) 转换为 -100.00 等)。如果按照指示正确导出 CSV,应该已经避免了这些格式问题。

  • 负数和符号: QuickBooks 报表有时将负数显示为 -100.00(100.00),甚至在某些 Excel 导出中显示为 --100.00。清理步骤应该处理这些情况。确保每笔交易的借贷方总和为零。Beancount 会强制执行这一点(如果不平衡,导入时会抛出错误)。

  • 交易重复: 如果你必须分批导出交易(例如,逐年或逐账户),请小心合并它们,不要重叠。检查一年的第一笔交易是否也是前一批的最后一笔,等等。在边界处很容易意外复制一些交易。如果你怀疑有重复,可以按日期对最终的 Beancount 条目进行排序并查找相同的条目,或者使用 Beancount 的唯一交易标签来捕获它们。一种策略是将 QuickBooks 交易号作为元数据包含进来(例如,使用 Trans # 或发票号作为 txn 标签或 quickbooks_id 元数据),然后确保这些 ID 没有重复。

  • 不平衡的分录 / 暂记账户: QuickBooks 可能有奇怪的情况,比如一笔交易有不平衡,QuickBooks 会自动将其调整到“期初余额权益”或“留存收益”账户。例如,在设置初始账户余额时,QuickBooks 通常会将差额记入一个权益账户。这些会出现在导出的交易中。Beancount 将要求显式平衡。你可能需要引入一个用于期初余额的权益账户(通常是 Equity:Opening-Balances)来镜像 QuickBooks。在账本的第一天有一个建立所有账户期初余额的条目是很好的做法(见阶段五)。

  • 多币种边缘情况: 如果使用多币种,QuickBooks 的导出可能会以本国货币或其原生货币列出所有金额。理想情况下,获取每个账户的原生货币数据(QuickBooks 在线版的报表通常会这样做)。在 Beancount 中,每个记账都带有一个货币。如果 QuickBooks 提供了汇率或本国货币换算,你可以忽略这些,并依赖 Beancount 的价格条目。如果 QuickBooks 没有导出汇率,你可能需要手动添加关键日期的价格记录(例如,使用 Beancount 的 price 指令)以匹配估值。然而,对于基本的账本完整性,只要交易以其原始货币平衡就足够了——除非你想要相同的报告,否则不必明确记录未实现损益。

  • 应收账款 / 应付账款: QuickBooks 跟踪发票和账单的详细信息(到期日、支付状态等),这些在纯账本中不会完全转移。你会得到应收(A/R)和应付(A/P)的交易(发票增加 A/R,付款减少 A/R 等),但不会有发票文件或每个发票的客户余额。因此,迁移后,你应该验证 Beancount 中的 A/R 和 A/P 账户余额是否等于 QuickBooks 中客户/供应商的未结余额。如果你需要跟踪发票,可以使用 Beancount 的元数据(例如,包含一个 invoice 标签或链接)。QuickBooks 的发票号应该已经通过 NumMemo 字段导出了——我们的脚本将 Num 保留为交易元数据中的 number: "..."

  • 不活动或已关闭的账户: IIF 导出文件可能包含不活动的账户(如果你选择包含它们)。导入它们没问题(如果它们真的不活动,它们将没有交易并且余额为零)。你可以在最后一笔交易日期之后,用 close 指令在 Beancount 中将它们标记为已关闭。这可以使你的账本保持整洁。例如: 2023-12-31 close Expenses:OldAccount ; migrated after migration 这是可选的,主要是为了整洁。

通过仔细清理和映射上述数据,你将拥有一个在结构上与你的 QuickBooks 数据相匹配的 Beancount 账本文件。下一步是验证它在数值上也与 QuickBooks 相匹配。


阶段三:数据验证和对账

验证是会计数据迁移中至关重要的一步。我们需要确保 Beancount 账本与 QuickBooks 账簿精确到每一分钱。可以使用多种策略和工具:

3.1 试算平衡表对账

试算平衡表报告列出了所有账户的期末余额(带有借方和贷方或正/负标记),并且净额应为零。在两个系统中运行同一日期的试算平衡表是确认整体准确性的最快方法。

  • 在 QuickBooks 中: 运行最后一年的最后一天(例如,2023年12月31日)的试算平衡表 (Trial Balance) 报告。这份报告显示了每个账户的余额。导出它或记下关键数字。

  • 在 Beancount 中: 使用 Beancount 的报告功能生成试算平衡表。一个简单的方法是通过命令行:

    bean-report migrated.beancount balances

    balances 报告就是一个试算平衡表,列出了所有账户及其余额。你也可以在 Fava(Beancount 的网页界面)中打开文件,并查看 BalancesBalance Sheet 部分。Beancount 中的每个账户余额都应与 QuickBooks 的试算平衡表相匹配。例如,如果 QuickBooks 显示应收账款 = 5,000,那么BeancountAssets:AccountsReceivable账户总额应为5,000*,那么 Beancount 的 `Assets:Accounts Receivable` 账户总额应为 5,000(借方)。如果销售收入 = 200,000,那么Beancount中的Income:Sales应显示200,000*,那么 Beancount 中的 `Income:Sales` 应显示 200,000(贷方,如果使用将贷方显示为负数的试算平衡表,可能会显示为 -200,000)。

如果存在差异,找出它们:

  • 检查是否整个账户丢失或多余(我们是否忘记了一个账户,或者包含了一个在迁移期之前已经关闭的账户?)。
  • 如果余额不对,深入检查:QuickBooks 可以为该账户运行账户快速报告 (Account QuickReport) 或分类账明细,你可以将其与 Beancount 中该账户的登记簿 (bean-report migrated.beancount register -a AccountName) 进行比较。差异有时来自丢失的交易或重复的交易。

同时验证 Beancount 试算平衡表中所有账户的总和为零(它会打印一个总计,应该为零或非常接近零)。Beancount 强制执行复式记账,所以如果你有任何不为零的不平衡,意味着资产减去负债加权益不为零,表明存在问题(QuickBooks 通常也不允许这种情况,但如果某些数据丢失了可能会发生)。

3.2 账户余额比较

除了试算平衡表,你还可以比较特定的财务报表:

  • 资产负债表: 运行 QuickBooks 最终日期的资产负债表和 Beancount 的资产负债表 (bean-report migrated.beancount balsheet)。这与试算平衡表类似,但按资产、负债、权益组织。数字应该按类别对齐。为了更精细的检查,比较主要账户的总额:现金、应收账款、固定资产、应付账款、权益等。

  • 利润表 (损益表): 在 QuickBooks 和 Beancount 中运行五年期间(或逐年)的利润表 (bean-report migrated.beancount income 用于整个期间的利润表)。Beancount 的净收入应与 QuickBooks 每个期间的净收入相等。如果你迁移了所有五年,累计净收入应该匹配。你还可以比较单个收入和费用总额,以确保没有类别被遗漏或重复计算。

  • 随机抽样交易: 随机挑选几笔交易(特别是从每年和每个主要账户中挑选)并验证它们是否正确迁移。例如,在 QuickBooks 中找到 3 年前的一张发票,然后在 Beancount 文件中搜索其金额或备注(因为所有交易都是文本,你可以在文本编辑器中打开 .beancount 文件或使用搜索工具)。检查日期、金额和账户是否匹配。这有助于捕捉任何日期格式问题或账户映射错误。

3.3 自动化完整性检查

利用 Beancount 自身的验证工具:

  • bean-check: 运行 bean-check migrated.beancount。这将解析文件并报告任何语法或平衡错误。如果脚本遗漏了像未开设的账户或不平衡的交易之类的问题,bean-check 会标记出来。干净的通过(没有输出)意味着文件至少在内部是一致的。

  • 余额断言: 你可以在账本中为关键账户添加明确的余额断言作为额外检查。例如,如果你知道某个日期银行账户的余额,添加一行: 2023-12-31 balance Assets:Bank:Checking 10000.00 USD 然后 bean-check 将确保在该日期,账本中的余额确实是 $10,000。这是可选的,但对于非常重要的账户很有用。你可以从 QuickBooks 获取期末余额(例如,每年年底),并在 Beancount 文件中进行断言。如果任何断言失败,Beancount 将报告差异。

  • 试算平衡表滚动检查: 如果你愿意,可以进行逐期检查。对于每一年,比较净变化。例如,QuickBooks 2020 年的净收入与 Beancount 2020 年的净收入,等等,以确保每年都正确地结转到权益中(QuickBooks 在每个新年自动将净收入滚入留存收益;在 Beancount 中你只会看到累计的权益)。如果你看到差异,这可能表明特定年份的数据存在问题。

  • 交易计数和重复: 计算 QuickBooks 与 Beancount 中的交易数量。QuickBooks 不容易直接显示计数,但你可以通过计算 CSV 中的行数(每个交易标题与分录)来估计。在 Beancount 中,一个快速的方法是计算文件中 txn* " 的出现次数。它们应该等于或略高于 QuickBooks(如果你添加了期初余额交易或调整)。显著的不匹配意味着某些内容可能被遗漏或重复了。我们使用元数据中的唯一 ID 可以提供帮助:如果你怀疑有重复,可以在 Beancount 文件中搜索相同的支票号或发票号是否出现了两次。

  • 对账状态: 我们在脚本中根据 QuickBooks 的已清算状态包含了一个 rec: "y""n" 的元数据(在示例中为 rec)。这不是一个标准的 Beancount 功能(Beancount 不像 Ledger 那样跟踪已清算/待处理状态),但它可以作为有用的元数据。你可能需要验证在 QuickBooks 中所有已对账的交易都存在。最终,在 Beancount 中重新对账银行账户(使用你的银行对账单)可能是证明没有任何遗漏的最终证据。

通过执行这些验证,你可以建立对迁移保留了数据的信心。在这个阶段要花足够的时间——现在修复异常比几个月后依赖这些账簿时要容易得多。如果验证失败,常见问题包括:账户的期初余额丢失、交易日期超出范围,或条目的符号反转。一旦确定,这些都是可以修复的。


阶段四:提交到 Beancount 账本

在清理和验证之后,是时候将数据正式化到你的 Beancount 账本结构中了。“提交”在这里既指最终确定账本文件,也可能指将它们检入版本控制系统以备审计。

4.1 组织账本文件和配置

决定如何组织 Beancount 账本文件。对于五年的数据,你可以将所有内容保存在一个文件中,或者按年份或类别拆分。一个常见、清晰的结构是:

  • 主账本文件: 例如,ledger.beancount – 这是可以 include 其他文件的入口点。它可能包含全局选项,然后包含年度文件。
  • 账户文件: 定义会计科目表和期初余额。例如,accounts.beancount 包含所有 open 指令(由脚本生成)。你也可以在这里列出商品(货币)。
  • 交易文件: 每年一个,例如 2019.beancount, 2020.beancount 等,包含该年的交易。这使每个文件的大小易于管理,并允许你在需要时专注于某一年。或者,你可以按实体或账户拆分,但按时间拆分对于财务数据来说很直接。

主文件示例:

option "title" "我的商业账本"
option "operating_currency" "USD"

include "accounts.beancount"
include "2019.beancount"
include "2020.beancount"
...
include "2023.beancount"

这样,当你运行报告时,所有数据都会被汇总,但你保持了秩序。

Beancount 不要求多个文件——你可以只有一个大文件——但上述结构提高了清晰度和版本控制。根据 Beancount 的最佳实践,使用清晰的节标题并按逻辑对相关条目进行分组是很好的做法。

4.2 设置期初余额和权益

如果你的迁移不是从绝对零开始,你需要处理期初余额。两种情况:

  • 从零开始的账簿: 如果五年期始于业务成立之初(例如,你从 2019 年 1 月开始使用 QuickBooks,所有账户除了初始权益外都为零),那么你可能不需要一个单独的期初余额交易。2019 年的第一批交易(如向银行账户注入初始资金)将自然地建立期初余额。只需确保任何初始资本之前的留存收益都通过权益交易入账。

  • 中途开始的账簿(部分历史): 如果你更早开始使用 QuickBooks,而 2019 年是一个中点,那么截至 2019 年 1 月 1 日,每个账户都有一个结转余额。QuickBooks 会将这些作为期初余额或留存收益。在 Beancount 中,通常的做法是在你的开始日期前一天创建一个期初余额条目:

    • 使用一个名为 Equity:Opening-Balances(或类似名称)的权益账户来抵消所有期初金额的总和。
    • 例如:如果在 2018-12-31,现金为 10,000,应收账款为10,000,应收账款为 5,000,应付账款为 3,000(贷方),你会写一笔交易:20181231"OpeningBalances"Assets:Cash10000.00USDAssets:AccountsReceivable5000.00USDLiabilities:AccountsPayable3000.00USDEquity:OpeningBalances12000.00USD这使得OpeningBalances账户的余额为负数总和(–3,000(贷方),你会写一笔交易: `2018-12-31 * "Opening Balances"` ` Assets:Cash 10000.00 USD ` ` Assets:Accounts Receivable 5000.00 USD ` ` Liabilities:Accounts Payable -3000.00 USD ` ` Equity:Opening-Balances -12000.00 USD ` 这使得 `Opening-Balances` 账户的余额为负数总和(–12k),从而平衡了该条目。现在,所有资产/负债账户在 2019 年初都具有正确的余额。这应该与 QuickBooks 的任何“留存收益”或结转余额相对应。
    • 或者,使用 Beancount 的 padbalance 指令:对于每个账户,你可以从 Opening-Balances pad 它,并断言其余额。这是一种更自动化的方式。例如: 2018-12-31 pad Assets:Cash Equity:Opening-Balances 2018-12-31 balance Assets:Cash 10000.00 USD 这告诉 Beancount 插入任何必要的条目(到 Opening-Balances),以便现金在该日期等于 10000 USD。对每个账户都这样做。结果类似,但像第一种方法那样写一个明确的交易也很直接。
  • 留存收益: QuickBooks 不会明确导出一笔“留存收益”交易——它只是计算它。迁移后,你可能会注意到 Equity:Retained Earnings 是零,如果你没有创建它的话。在 Beancount 中,留存收益只是前几年的利润。你可以选择创建一个留存收益账户,并在每个新年的第一天将以前的利润转入其中,或者干脆让权益成为所有收入/费用的总和(这在报告的权益部分下显示)。为了透明度,一些用户每年都会做结账分录。这是可选的,主要用于展示。由于我们迁移了所有交易,如果你按年运行报告,每年的利润自然会累计。

  • 比较检查: 设置期初余额后,在开始日期运行资产负债表,以确保一切正确(它应该显示那些期初余额与期初权益相抵为零)。

4.3 最终确定和版本控制

现在数据已是 Beancount 格式并已结构化,明智的做法是将文件提交到版本控制仓库(例如,git)。对账本的每次更改都可以被跟踪,并且你有一个所有修改的审计追踪。这是纯文本会计的一个主要优势。例如,在 QuickBooks 中,更改可能不容易进行差异比较,但在 Beancount 中,你可以看到逐行的差异。正如一些用户指出的,使用 Beancount 你可以获得透明度以及在需要时恢复更改的能力——每个条目都可以追溯到变更历史。

考虑将这次初始迁移的提交标记为 v1.0 或类似标记,这样你就知道它代表了从 QuickBooks 导入时的账簿状态。今后,你将直接在 Beancount 中输入新交易(或从银行源导入等),并且你可以使用常规的软件开发实践(每月或每天提交,使用分支进行实验等)。

设置 Fava 或其他工具: Fava 是 Beancount 的一个 Web 界面,可以方便地查看报告。提交后,运行 fava ledger.beancount 来浏览财务报表,并最后一次与你的 QuickBooks 报告进行比较。你可能会在 UI 中更容易发现微小的差异(例如,一个本应为零但显示微小余额的账户表明有遗漏的结账分录或一个孤立的交易)。

命名约定和一致性: 你现在拥有完全的控制权,所以要确保一致性:

  • 所有账户都应该有清晰的名称,以大写的类别名称开头(Assets, Liabilities 等)。如果任何名称看起来奇怪(例如,由于 QuickBooks 的大小写不匹配导致的 Assets:assets:SomeAccount),在账户文件中重命名它们并更新交易(对文件进行快速查找/替换可以做到这一点,或者使用 Beancount 的 bean-format 或编辑器的多光标功能)。
  • 商品符号(货币代码)应该一致。对于美元,到处都使用 USD(而不是 $US$)。对于其他货币,使用标准代码(EUR, GBP 等)。这种一致性对于 Beancount 的价格查找和报告很重要。
  • 移除任何可能已创建的临时或虚拟账户(例如,如果你在脚本中使用 Expenses:Miscellaneous 作为未知账户的通用账户,尝试通过正确映射所有账户来消除它们)。

关闭 QuickBooks: 此时,你应该在 Beancount 中有与 QuickBooks 相匹配的并行账簿。有些人选择在短时间内并行运行两个系统,以确保没有遗漏。但如果验证是可靠的,你可以“关闭” QuickBooks 账簿:

  • 如果这是一个公司环境,考虑导出所有 QuickBooks 源文件(发票、账单、收据)作为记录,因为除非你手动附加,否则它们不会存在于 Beancount 中。
  • 保留 QuickBooks 数据的备份(公司文件和导出文件)。
  • 从今往后,将 Beancount 账本作为主要的记录系统。

通过将数据提交到 Beancount 账本,你已经完成了迁移流程。最后一步是进行审计并展示财务报表的一致性,以让你自己(以及任何利益相关者或审计师)满意迁移是成功的。


阶段五:迁移后审计和示例

为了说明迁移的成功,准备一份财务报表的前后对比,并可能提供交易的差异对比。这为账簿的一致性提供了证据。

5.1 验证财务报表

从 QuickBooks 和 Beancount 中生成相同日期的关键财务报告并进行比较:

  • 截至 2023 年 12 月 31 日的资产负债表: 逐行比较资产、负债和权益总额。它们应该匹配。例如,如果 QuickBooks 显示总资产 = 150,000总负债+权益=150,000**,** 总负债 + 权益 = 150,000,那么 Beancount 的资产负债表也应显示相同的总额。如果你对账户结构做了微调(比如合并了一些子账户),在比较时要考虑到这一点,或者分解到下一级以确保总和相等。

  • 2019–2023 年利润表: 确保每年(或整个期间)的总收入、总费用和净利润是相同的。如果 QuickBooks 在报告上进行了一些四舍五入,可能会出现微小差异,但交易通常精确到分,所以净利润应该是精确的。如果某年的利润不同,深入研究该年的数据——这通常是该时期有遗漏或重复条目的迹象。

  • 试算平衡表差异: 如果可能,创建一个电子表格,列出每个账户以及来自 QuickBooks 和 Beancount 的余额。由于我们期望它们匹配,这可能是一个全为零的差异列。这本质上是我们讨论过的试算平衡表交叉检查,但把它写出来有助于记录。

5.2 示例比较(迁移前 vs 迁移后)

下面是一个示例片段,展示了数据的一致性。假设我们 2023 年 12 月 31 日的 QuickBooks 试算平衡表是:

账户QuickBooks 余额 (2023年12月31日)
资产 (Assets)
Assets:Bank:Checking$12,500.00 (借方)
Assets:Accounts Receivable$3,200.00 (借方)
负债 (Liabilities)
Liabilities:Credit Card$-1,200.00 (贷方)
Liabilities:Loans Payable$-5,000.00 (贷方)
权益 (Equity)
Equity:Opening-Balances$-7,500.00 (贷方)
Equity:Retained Earnings$-2,000.00 (贷方)
Equity:Current Year Profit$0.00

在 Beancount 中,导入并记入所有截至 2023 年的交易后,bean-report balances(试算平衡表)输出:

账户Beancount 余额 (2023年12月31日)
资产 (Assets)
Assets:Bank:Checking12,500.00 USD (借方)
Assets:Accounts Receivable3,200.00 USD (借方)
负债 (Liabilities)
Liabilities:Credit Card-1,200.00 USD (贷方)
Liabilities:Loans Payable-5,000.00 USD (贷方)
权益 (Equity)
Equity:Opening-Balances-7,500.00 USD (贷方)
Equity:Retained Earnings-2,000.00 USD (贷方)
Equity:Profit (2019-2023)0.00 USD

(注意:权益部分的结构可能不同;关键是总额一致。在这里,Beancount 中的“利润 (2019-2023)”扮演了当年利润/留存收益合并的角色,显示为零是因为利润已结转至留存收益。)

如上所示,每个账户都精确到分。借方总额等于贷方总额。

此外,如果我们运行 2023 年的利润表:

  • QuickBooks: 收入 50,000,费用50,000,费用 48,000,净利润 $2,000。
  • Beancount: 收入 50,000,费用50,000,费用 48,000,净利润 $2,000(然后结转到留存收益或出现在年终资产负债表的权益部分)。

如果需要,你可以创建交易的差异对比,但由于 QuickBooks 数据不是账本格式,依赖报表更有效。可以将 QuickBooks CSV 和 Beancount 交易按日期排序,并比较关键字段作为最后检查(这可以在 Excel 中或用脚本完成)。然而,鉴于我们信任之前的验证,财务报表检查通常就足够了。

5.3 审计技巧

  • 如果审计师或利益相关者需要保证,请并排展示迁移前后的财务报表Beancount 的透明度实际上可以简化审计,因为你可以快速地从报表上的每个数字追溯到源条目(特别是使用 Fava 的下钻功能)。
  • 保留 QuickBooks 备份和导出的 CSV 文件作为审计追踪的一部分。记录迁移过程中所做的任何调整(例如,“为保持一致性将账户 X 重命名为 Y”或“为清晰起见将交易 Z 分为两个条目”,如果你做了这样的更改)。
  • 从今往后,在 Beancount 中实施定期检查。例如,每月对账银行账户并对其期末余额进行断言,有助于捕捉任何数据问题或录入错误。迁移提供了一个良好的基线;在新系统中保持纪律将确保持续的准确性。

最后,庆祝迁移完成:你已经成功地将五年的会计数据从 QuickBooks 转移到了 Beancount。数据现在以轻量级、版本控制的文本格式存在,具有完整的复式记账完整性。你导出了数据,用 Python 脚本转换了它,通过试算平衡表和报告验证了其完整性,并将其提交到一个组织良好的 Beancount 账本中。这个全面的过程确保了 Beancount 账本是你在五年期间 QuickBooks 账簿的准确、忠实的复制品,为你未来的精简会计工作奠定了基础。

Beancount 引入多文件支持

· 阅读需 2 分钟
Mike Thrift
Mike Thrift
Marketing Manager

自二月份以来,我们的许多客户一直在询问如何向一个账本添加多个文件。他们需要文件结构来归档或分类交易。因此,经过几个月的工作,我们很高兴地宣布此功能已免费发布。

以下是使用方法:

文件 > 创建新文件

2021-09-24-multi-file-one-ledger

前往左侧导航栏中的文件编辑器选项卡。然后,点击“文件”下拉菜单并选择“创建新文件”。

文件 > 创建新文件

命名新文件

为你的文件指定一个有效的文件名并保存。所有文件名必须以“.bean”结尾。

命名新文件

包含文件

这是一个关键步骤,你必须在 main.bean 中包含新创建的文件。

例如,如果你添加了 stock.bean,则在 main.bean 中指定 include "stock.bean"

包含文件

刷新并导航到文件

刷新页面,你将看到文件出现在“文件”下拉菜单中。

刷新并导航到文件

重命名或删除文件

导航到文件后,你可以在“编辑”下拉菜单中重命名或删除它。

重命名或删除文件

遇到问题?

请在 https://t.me/beancount 提问。

Fava 升级至 1.19 版本:主要变更与改进

· 阅读需 1 分钟
Mike Thrift
Mike Thrift
Marketing Manager

我们已将 MIT 许可的开源软件 Fava 升级至 1.19 版本。以下是自上次更新以来的变更内容:

  • v1.19 (2021-05-18)
    • conversioninterval 选项迁移至 default-page 选项
    • 添加选项 invert-income-liabilities-equity
    • 升级至 CodeMirror 6
    • 新增保加利亚语翻译
    • 其他小改进和错误修复
  • v1.18 (2021-01-16)
    • 一些小改进和各种错误修复。

感到兴奋吗?立即体验:https://beancount.io/ledger/

2021-07-16-upgrade-fava-to-1-19

有疑问?我们会在 https://t.me/beancount 等你 :)

理解 Beancount 中的摊销

· 阅读需 2 分钟
Mike Thrift
Mike Thrift
Marketing Manager

摊销是将支付款项分摊到多个时间段的会计处理方式。在 Beancount.io 中,你可以使用 fava.plugins.amortize_over 插件来实现这一点。

2021-01-09-amortize

如果没有摊销,如果你想为你的汽车投保 6 个月,费用为 600 美元。你必须将其记录为特定日期的一次性费用。

2017-06-01 open Assets:Bank:Checking
2017-06-01 open Assets:Prepaid-Expenses
2017-06-01 open Expenses:Insurance:Auto


2017-06-01 * "Pay car insurance"
Assets:Bank:Checking -600.00 USD
Assets:Prepaid-Expenses

然而,通过摊销,你可以将费用分摊到六个月,只需在文件顶部添加 plugin "fava.plugins.amortize_over",并在交易中使用 amortize_months: 6

plugin "fava.plugins.amortize_over"

2020-06-01 open Assets:Bank:Checking
2020-06-01 open Assets:Prepaid-Expenses
2020-06-01 open Expenses:Insurance:Auto

2020-06-01 * "Amortize car insurance over six months"
amortize_months: 6
Assets:Prepaid-Expenses -600.00 USD
Expenses:Insurance:Auto

然后,在日记账中,你将看到该交易被拆分为 6 笔分录。

2020-11-01 * Amortize car insurance over six months (6/6) am
2020-10-01 * Amortize car insurance over six months (5/6) am
2020-09-01 * Amortize car insurance over six months (4/6) am
2020-08-01 * Amortize car insurance over six months (3/6) am
2020-07-01 * Amortize car insurance over six months (2/6) am
2020-06-01 * Amortize car insurance over six months (1/6) am

摊销交易截图

Beancount 移动应用(iOS 和 Android 版)隆重推出

· 阅读需 2 分钟
Mike Thrift
Mike Thrift
Marketing Manager

我们很高兴地宣布 Beancount 发展史上的一个重要里程碑:我们专为 iOS 和 Android 平台打造的移动应用已正式上线!此次发布直接回应了社区最频繁的需求之一——在移动设备上高效管理你的账本。

Beancount iOS 应用

主要功能

  • 快速交易录入:随时随地无缝添加新交易
  • 移动财务报告:随时随地查看你的财务洞察
  • 直接账本编辑:通过优化后的网页视图完全访问你的账本文件
  • 跨平台同步:在所有设备上保持你的财务数据一致

2020-08-19-beancount-mobile-app

社区驱动开发

此次发布仅仅是我们移动之旅的开端。我们致力于根据你的需求和反馈来不断完善这些应用。欢迎加入我们在 Telegram 上的活跃社区:https://t.me/beancount,与我们讨论 Beancount,分享你的经验,并帮助塑造未来的功能。

我们的愿景

在 Beancount,我们的使命是赋能每个人实现更好的财务控制和理解。这些移动应用是实现这一目标的重要一步,未来我们还有更多激动人心的功能和工具在规划中。

从 App Store 下载从 Play Store 下载

净利润入门 — 以及如何在 Beancount 中追踪它

· 阅读需 5 分钟
Mike Thrift
Mike Thrift
Marketing Manager

在所有描述企业的数字中,有一个数字至关重要:净利润。它是衡量盈利能力的最终指标,也就是著名的“底线”。但它究竟意味着什么,以及如何使用像 Beancount 这样的纯文本会计系统精确地追踪它呢?

让我们来分解一下。

2020-03-12-net-income-101-how-to-track-it-in-beancount

净利润的真正含义

从本质上讲,净利润是指在支付了所有运营业务所需的全部费用后剩余的金额。这包括销售成本 (COGS)、所有运营费用、债务利息和税费。

它是公司财务状况最清晰的单一指标。对每个人来说,它都是一个关键数字:

  • 贷款人 通过它来衡量你的还款能力。
  • 投资者 将其视为潜在股息的来源或可用于增长的资本。
  • 创始人 依靠它来了解他们的财务状况并制定战略性再投资计划。

正净利润意味着你盈利。负净利润意味着你亏损。就这么简单。

核心公式(选择你需要的详细程度)

你可以使用不同程度的粒度来计算净利润。选择取决于你需要多详细的分析。

  • 完整公式: 收入 – 销售成本 – 运营费用 – 利息 – 税费 = 净利润
  • 标准公式: 收入 – 销售成本 – 费用 = 净利润
  • 快速估算公式: 总收入 – 总支出 = 净利润

关于成本的快速提示: 销售成本和运营费用之间有什么区别?销售成本 (COGS) 指的是与生产商品或服务直接相关的成本(例如,原材料、直接人工)。运营费用 指的是维持运营所需的其他一切费用,例如工资、租金、软件订阅、保险等。

一个小例子

让我们来看一个具体的例子。假设一家小型咖啡烘焙企业上个月的业绩如下:

项目金额示例分类账户
销售收入$60,000Income:Sales
销售成本(生咖啡豆)$20,000Expenses:COGS
运营费用$18,000Expenses:Ops:*
利息(设备贷款)$1,000Expenses:Interest
税费$1,000Expenses:Taxes

使用完整公式,计算很简单:

净利润=净利润 = 60,000 - 20,00020,000 - 18,000 - 1,0001,000 - 1,000 = 20,00020,000

该企业当月实现了 $20,000 的实际利润。

如何在 Beancount 中记录

Beancount 的结构非常适合这种情况。通过合理地组织你的账户,计算净利润就变成了良好簿记的自动副产品。

  • 将所有收入 标记在一个父级 Income 账户下,例如 Income:SalesIncome:Services
  • 拆分你的费用 以区分直接成本和间接成本。使用 Expenses:COGS 记录直接成本,并使用子账户(例如 Expenses:Ops:SalariesExpenses:Ops:Software)记录其他所有费用。
  • 将利息和税费过帐到它们自己的账户Expenses:InterestExpenses:Taxes)。这使得未来的分析,尤其是税务筹备,变得更加简单。
  • 定期平衡你的账簿。 基本会计等式 资产 = 负债 + 所有者权益 必须始终成立。

在一个周期结束时(例如,一个月),你可以使用 balance 断言来确认你的账户状态。这些条目不会移动资金;它们声明余额 应该 是多少,如果余额不正确,bean-check 将会报告错误。

; 2025-07-31 咖啡烘焙企业 — 7 月结账
2025-07-31 balance Assets:Bank:Operating 42000.00 USD
2025-07-31 balance Expenses:COGS 20000.00 USD
2025-07-31 balance Expenses:Ops 18000.00 USD
2025-07-31 balance Expenses:Interest 1000.00 USD
2025-07-31 balance Expenses:Taxes 1000.00 USD
2025-07-31 balance Income:Sales -60000.00 USD

一行净利润查询

这就是神奇之处。通过结构良好的分类账,你可以使用单个命令计算任何期间的净利润。

bean-query books.beancount \
"SELECT period, sum(number) WHERE account =~ '^(Income|Expenses)' \
AND year = 2025 GROUP BY month"

此查询将 2025 年 IncomeExpenses 账户中的所有值加总,并按月分组。在 Beancount 中,收入用负数表示,费用用正数表示,因此结果将是净利润的 负数

更好的是,你甚至不需要手动运行此查询。Fava(Beancount 的 Web 界面)会在其 报告 → 损益表 视图中自动为你绘制此图表。

可以自动化的快速洞察

由于你的分类账只是一个文本文件,因此你可以围绕它构建强大的自动化工作流程。

  • 每月电子邮件摘要: 设置一个 cron 作业来运行 bean-report books.beancount income_statement > net-income.txt,然后在每个月的第一天将生成的文本文件通过电子邮件发送给你自己。
  • 盈利能力警报: 使用 Git pre-commit 钩子运行查询来检查当月迄今的盈利能力。你可以将其编程为在净利润为负时拒绝提交,从而强制你确认亏损月份。
  • 情景建模: 想要对你的业务进行压力测试?只需将 books.beancount 复制到 scenarios/recession.bean,将你的 Income 条目减少 20%,然后重新运行你的净利润查询即可立即查看影响。

关键要点

  • 净利润是你的真正底线。目标是保持正净利润并了解其随时间的变化趋势。
  • Beancount 使底层数学变得清晰、可搜索和版本可控。没有隐藏的公式。
  • 通过规范的账户结构和单个查询,你将始终知道你的业务是否真正盈利。