QuickBooks 到 Beancount 迁移指南
阶段一:从 QuickBooks 导出数据
迁移五年的数据,第一步是把所有 QuickBooks 记 录以可用的格式导出来。QuickBooks 桌面版和 QuickBooks 在线版有不同的导出选项:
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 的默认导出不包含单个总分类账文件,你可以手动运行一个详细报表:
- 导航到报表 (Reports) 并找到按账户交易明细 (Transaction Detail by Account)(在某些 QBO 版本中是总分类账 (General Ledger))。
- 将报表期间 (Report period) 设置为完整的五年范围。
- 在报表选项下,将分组依据 (Group by) 设置为无 (None)(以便列出单个交易而不是小计)。
- 自定义列,至少包括:日期 (Date)、交易类型 (Transaction Type)、编号 (Number)、名称 (Name, Payee/Customer)、备注/描述 (Memo/Description)、账户 (Account)、借方 (Debit)、贷方 (Credit)(或单个金额列),以及余额 (Balance)。如果使用了类别 (class) 或地点 (location),也请包含它们。
- 运行报表,然后导出到 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
端点获取会计科目表,查询 JournalEntry
或 GeneralLedger
报表端点获取交易。有像 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 的发票号应该已经通过Num
或Memo
字段导出了——我们的脚本将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 的网页界面)中打开文件,并查看 Balances 或 Balance Sheet 部分。Beancount 中的每个账户余额都应与 QuickBooks 的试算平衡表相匹配。例如,如果 QuickBooks 显示应收账款 = $5,000,那么 Beancount 的Assets:Accounts Receivable
账户总额应为 $5,000(借方)。如果销售收入 = $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,应收账款为 $5,000,应付账款为 $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 的
pad
和balance
指令:对于每个账户,你可以从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 中,留存收益只是前几年的利润。你可以选择创建一个留存收益账户,并在每个新年的第一天将以前的利润转入其中,或者干脆让权益成为所有收入/费用的总和(这在报告的权益部分下显示)。为了透明度,一些用户每年都会做结账分录。这是可选的,主要用于展示。由于我们迁移了所有交易,如果你按年运行报告,每年的利润自然会累计。 -
比较检查: 设置期初余额后,在开始日期运行资产负债表,以确保一切正确(它应该显示那些期初余额与期初权益相抵为零)。