记账
目的
为什么要记账?
- 有多少钱、在哪儿: 清楚自己的资产负债余额情况
- 钱从哪儿来、到哪儿去: 清楚自己的收入支出情况
- 预算计划 Budget
内容
记哪些东西?
- 资产 Assets
- Cash
- Bank
- UnionPay(DebitCard)
- AliPay
- WeChatPay
- ApplePay
- Investment
- Receivable
- 负债 Liabilities
- CreditCard
- Payable
- 收入 Revenues/Income
- Salary
- Gift
- 支出 Expenses
- 三餐饮食 Food & Dining
- 家居日用 Household & Daily Essentials
- 交通出行 Transportation
- 通讯数码 Telecom & Digital
- 医疗健康 Medical & Health
- 教育学习 Education & Learning
- 社交娱乐 Social & Entertainment
- 服饰美妆 Wear & Beauty
- 金融财税 Financial & Tax
- 其他杂项 Misc
- 权益 Equity
- Rounding
- Conversion
方法
单式和复式记账法

[!NOTE] 会计等式
资产 = 负债 + 所有者权益收入 - 费用 = 利润资产 = 负债 + (所有者权益 + 收入 - 费用)
Revenues/Income |
|
Expenses |
|
Assets |
|
Liabilities |
|
Equity |
|
[!note]+ 复式记账
每条记录至少包括两个账户: 借(Debit+)、贷(Credit-). 账户、分类本质上都是科目.
理论上需要多条记录相互验证, 实际操作多数情况可以只录一条, 另一条自动生成.
[!NOTE]- 举例
- 转账: 资产- -> 资产+
- 还款: 资产- -> 负债+
- 支出: 资产- -> 支出+
- 负债支出: 负债- -> 支出+
- 借入: 负债- -> 资产+
- 还款: 资产- -> 负债+
- 借出: 资产- -> 资产+
- 收款: 资产- -> 资产+
表与字段设计
- 交易明细表
- 日期 Date
- 描述 Payee|Description
- 金额 Amount %% price * quantity %% %% 对应支付账户(减少), 即始终为负值, 计算余额累加即可 %%
- 来源账户 From %% 关联到账户表的 Name (排除支出) %%
- 去向账户 To %% 关联到账户表的 Name %%
- 账户科目表
- 名称 Name
- 类型 Type: 资产、负债、收入、支出、权益
- 结余 Balance
SUMIF(交易表!$C$2:$C$1000, A2, 交易表!$E$2:$E$1000) + (-1)*SUMIF(交易表!$D$2:$D$1000, A2, 交易表!$E$2:$E$1000)
分类与统计
工具
- 文本类
- 表格类
- 数据库类
- Money Pro
- GnuCash
- MoneyManagerEx
- Firefly
- Actual
- 国产 (全平台、免费、无广告、导入导出)
- 钱迹
实践
结合 todo 的简易记账方法
import re
from decimal import Decimal
def markdown_to_hledger(md_content):
entries = []
current_date = None
account_cache = set()
for line in md_content.split('\n'):
line = line.strip()
# 解析日期标题
date_match = re.match(r'^##\s+(\d{4}-\d{2}-\d{2})', line)
if date_match:
current_date = date_match.group(1)
continue
# 解析所有层级的条目
entry_match = re.match(r'^\s*-\s+\[[ x]\]\s*(.+?)\s*$', line)
if current_date and entry_match:
parts = [p.strip() for p in entry_match.group(1).split('|')]
if len(parts) < 4 or not all(parts[:4]):
continue # 跳过不完整条目
name, amount_str, source, dest = parts[:4]
try:
amount = Decimal(amount_str)
except:
continue # 跳过金额无效条目
# 构建账户名称并缓存
source_acc = f"Assets:{source}"
dest_acc = f"Expenses:{dest}"
account_cache.update([source_acc, dest_acc])
# 生成hledger条目
entry = [
f"{current_date} {name}",
f" {dest_acc:<24} {amount:.2f} CNY",
f" {source_acc}"
]
entries.append('\n'.join(entry))
# 添加账户声明
accounts = '\n'.join([f"account {acc}" for acc in sorted(account_cache)])
return f"{accounts}\n\n" + '\n\n'.join(entries)