以下情境为教学假设,人物与数字不代表经核实的真实案例。
利润表看一年,现金流量表看钱的进出,资产负债表看某一时点还剩什么。三张表里,资产负债表最像结果:前两张表的每一笔变化,最后都要落到资产、负债或权益的某个科目上。如果一张资产负债表能平衡,不代表账一定对;但如果它不平衡,前面两张表一定有一处没接上。
LedgerLite 的期末表只有六个科目:现金 95,000、应收 12,000、固定资产 3,000、递延收入 22,000、应付 8,000、权益 80,000。科目少,反而适合把三表勾稽的每一步看清楚。Tidecast 和 Nord Studio 没有完整资产负债表,但它们的应收与递延收入结构足够说明买断、订阅与服务三种生意的差异。
资产 = 负债 + 权益
资产负债表的基本恒等式是:
资产 = 负债 + 权益
资产是公司拥有的资源:现金、应收、设备。负债是欠别人的义务:递延收入、应付账款、借款。权益是资产减去负债后的剩余,属于所有者。恒等式不是一条需要「配平」的规则,而是复式记账的自然结果:每一笔交易至少影响两个科目,影响过后两边仍然相等。
LedgerLite 的期末表如下:
| 资产 | 金额 | 负债与权益 | 金额 |
|---|---|---|---|
| 现金 | $95,000 | 递延收入 | $22,000 |
| 应收 | $12,000 | 应付 | $8,000 |
| 固定资产 | $3,000 | 权益 | $80,000 |
| 合计 | $110,000 | 合计 | $110,000 |
六个科目各有来源。现金 95,000 来自 3.2 节的期末现金;应收 12,000 是已经确认但还没收到的收入;固定资产 3,000 是当年 3,000 美元设备投资,因为假设没有折旧,账面价值等于原值;递延收入 22,000 是已经收到但还没确认的收入;应付 8,000 是已经发生但还没支付的费用;权益 80,000 是资产减负债后的剩余。
这张表是简化表,不是法定报表。它没有存货、没有无形资产、没有借款、没有税款应收应付,也没有汇兑调整。简化假设是:没有折旧、没有利息、没有汇兑损益;没有分红;应付账款不变。只有在这组假设下,六个科目才能把三张表完整闭合。
应收与递延收入:两个方向的时间差
应收和递延收入是一对镜像科目:一个先确认后收款,一个先收款后确认。把它们放在一起看,才能理解营运资金为什么会是负的。
应收:已经确认但还没收到的钱
应收是利润表和现金流量表之间的第一个时间差。LedgerLite 在年内确认了 240,240 美元收入,但年末还有 12,000 美元没收到。这 12,000 不是坏账,也不是现金,而是一项资产:客户欠公司的钱。
案例事实表给了四个关于应收的数字:平均账期 Net 15;年末应收 12,000 美元,约 18 天收入;逾期 30 天以上占应收 6%;坏账准备 1%。把它们换成可复算的指标:
| 指标 | 公式 | 结果 |
|---|---|---|
| 应收周转天数 | $12,000 ÷ $240,240 × 365 | 18.2 天 |
| 应收相当于几个月 MRR | $12,000 ÷ $20,020 | 0.60 个月 |
| 逾期 30 天以上金额 | $12,000 × 6% | $720 |
| 坏账准备 | $12,000 × 1% | $120 |
| 净应收 | $12,000 − $120 | $11,880 |
18.2 天比 Net 15 的合同账期长约 3 天,说明年末有一部分款项在账期内但尚未到期,也有一部分已经逾期。6% 的逾期金额是 720 美元,1% 的坏账准备是 120 美元。案例的简化资产负债表按应收总额 12,000 列示,没有单列坏账准备;如果按净额列示,资产会减少 120,权益也会相应减少 120,恒等式仍然成立,但表上需要多一个「坏账准备」的 contra-asset 科目。本节沿用案例表的总额口径,不另设准备科目。
应收的变化方向也要和现金流量表对上。3.2 节说应收增加 2,000,所以期初应收是 12,000 − 2,000 = 10,000。应收增加 2,000 意味着当年确认的收入比收到的现金多 2,000,经营现金流因此减少 2,000。这个 2,000 不是损失,只是时间差;如果客户按 Net 15 付款,下一年它会变成现金。
把三个案例的应收放在一起,能看到账期结构:
| 案例 | 应收占年收入 | 隐含天数 | 合同账期 | 说明 |
|---|---|---|---|---|
| LedgerLite | 5.0% | 18.2 天 | Net 15 | 订阅客户,年末应收 $12,000 |
| Tidecast | 0% | 0 天 | 平台月结 30 天 | 年末已结算,案例按 0 应收处理 |
| Nord Studio | 7.3% | 26.6 天 | Net 30 | 服务为主,回款慢于 LedgerLite |
Nord 的 7.3% 乘以 365 等于 26.6 天,接近 Net 30 的账期;LedgerLite 的 5.0% 乘以 365 等于 18.2 天,接近 Net 15。账期越长,应收占收入的比例越高,现金被客户占用的时间也越长。Tidecast 是买断制,平台月结 30 天,但案例把年末应收按 0 处理,说明它的现金节奏主要由销量和平台结算决定,而不是由客户账期决定。
应收账龄、催收节奏和坏账准备的详细处理属于卷二《合同、应收与风险》的内容,那里会展开账龄表与催收节奏;本节只把应收作为资产负债表的一个科目,确认它和经营现金流的符号关系。
递延收入:已经收到但还没确认的钱
递延收入是第二个时间差,方向和应收相反。LedgerLite 的年付客户在年初支付全年费用,但公司只能按月确认收入;还没确认的部分就是递延收入。它是一项负债,因为公司还欠客户服务。
期末递延收入是 22,000 美元,期初是 14,000 美元,增加 8,000 美元。这个增加额在 3.2 节的经营现金流里是加项:客户先付钱,现金比收入多,所以要把 8,000 加回。它在资产负债表上则是负债增加 8,000:公司未来要交付对应服务。
| 指标 | 公式 | 结果 |
|---|---|---|
| 期末递延收入 | 案例表 | $22,000 |
| 期初递延收入 | $22,000 − $8,000 | $14,000 |
| 递延收入相当于几个月 MRR | $22,000 ÷ $20,020 | 1.10 个月 |
| 递延收入占年收入 | $22,000 ÷ $240,240 | 9.2% |
1.10 个月 MRR 说明期末递延收入大约是 1.1 个月的订阅收入。这个数字不需要和年付比例直接对应:年付客户占 30%,但年付订单的确认节奏、续费时点和退款条款都会影响期末余额。案例只给出 22,000 的期末值,本节不把它反推成客户数或年付金额。
递延收入让 LedgerLite 的营运资金变成负数:
营运资金 = 应收 − 递延收入 − 应付
= 12,000 − 22,000 − 8,000
= −18,000
负营运资金不是坏事,它意味着客户预收在给生意融资:公司先拿到现金,再按月交付服务。但它也不是免费的:递延收入是一笔必须履约的负债,如果客户流失、退款增加或年付比例下降,这笔负债会收缩,经营现金流会比净利润更早变差。
三个案例的营运资金方向不同:
| 案例 | 递延收入 | 应收 | 营运资金方向 | 谁在给生意融资 |
|---|---|---|---|---|
| LedgerLite | $22,000 | $12,000 | 负,−$18,000 | 客户预收 |
| Tidecast | $0 | $0 | 接近 0 | 无,现款或平台月结 |
| Nord Studio | 案例未给出 | 占年收入 7.3% | 偏正 | 开发者垫资 |
Tidecast 是买断制,售出即确认,没有预收义务,也没有应收,所以它的营运资金接近 0;现金节奏由销量和平台结算决定。Nord 是服务加产品:服务按 Net 30 开票,形成应收;产品按月订阅,是否形成递延收入取决于收款时点,案例没有给出余额。方向上看,Nord 更可能是「开发者先垫资」,而不是「客户先预收」。
预收、递延与确认时点的具体规则见 4.1 预收、递延与确认时点 ;本节只确认递延收入是负债,且它的增加是经营现金流的加项。
权益:资产减去负债后的剩余
权益是资产负债表上最容易被误解的科目。它不是现金,也不是「老板能拿走的钱」,而是资产减去负债后的剩余。LedgerLite 的期末权益是 80,000 美元:
权益 = 资产 − 负债
= 110,000 − (22,000 + 8,000)
= 80,000
权益的期初值可以从期初表倒推。3.2 节已经算出期初现金 33,701、期初应收 10,000、期初固定资产 0、期初递延收入 14,000、期初应付 8,000,所以:
期初资产 = 33,701 + 10,000 + 0 = 43,701
期初负债 = 14,000 + 8,000 = 22,000
期初权益 = 43,701 − 22,000 = 21,701
从期初权益到期初权益加净利润,正好得到期末权益:
| 权益桥 | 金额 | 来源 |
|---|---|---|
| 期初权益 | $21,701 | 期初资产 $43,701 − 期初负债 $22,000 |
| 加净利润 | +$58,299 | 利润表 |
| 减分红 | $0 | 案例假设无分红 |
| 期末权益 | $80,000 | 资产负债表 |
这里有两个容易混淆的点。第一,创始人薪酬 60,000 是利润表里的费用,不是分红;它已经通过净利润减少了权益,不能再从权益里减一次。第二,权益 80,000 不等于现金 95,000:公司还有 12,000 应收、3,000 固定资产和 30,000 负债,权益是这些科目共同作用后的剩余,不是某个银行账户的余额。
如果当年有分红,权益桥会多一项「减分红」,现金和权益会同时减少;案例假设没有分红,所以权益变化只来自净利润 58,299。这个假设让三表闭环更干净,也让读者能把注意力放在营运资金和资本开支上。
从期初到期末:三表闭环
把期初表和期末表放在一起,每一项变化都能在利润表或现金流量表里找到来源:
| 科目 | 期初 | 本期变化 | 期末 | 变化来源 |
|---|---|---|---|---|
| 现金 | $33,701 | +$61,299 | $95,000 | 现金流量表净现金增加 |
| 应收 | $10,000 | +$2,000 | $12,000 | 收入确认多于收款 |
| 固定资产 | $0 | +$3,000 | $3,000 | 设备投资 |
| 递延收入 | $14,000 | +$8,000 | $22,000 | 预收多于确认 |
| 应付 | $8,000 | $0 | $8,000 | 假设不变 |
| 权益 | $21,701 | +$58,299 | $80,000 | 净利润,无分红 |
| 资产合计 | $43,701 | +$66,299 | $110,000 | 现金、应收、固定资产变化之和 |
| 负债与权益合计 | $43,701 | +$66,299 | $110,000 | 递延收入与权益变化之和 |
期初表和期末表都平衡:
期初:43,701 = 22,000 + 21,701
期末:110,000 = 30,000 + 80,000
资产侧增加 66,299,等于现金增加 61,299 加应收增加 2,000 加固定资产增加 3,000;负债与权益侧增加 66,299,等于递延收入增加 8,000 加权益增加 58,299。两边增加额相等,不是巧合,而是因为每一笔变化都同时影响两侧:确认收入增加应收或现金,预收增加现金和递延收入,设备投资增加固定资产并减少现金,净利润增加权益并对应资产或负债的变化。
三张表的分工可以用一张表总结:
| 表 | 关键数字 | 如何进入资产负债表 |
|---|---|---|
| 利润表 | 净利润 $58,299 | 权益增加 $58,299 |
| 现金流量表 | 净现金增加 $61,299 | 现金增加 $61,299 |
| 资产负债表 | 应收 +$2,000、递延收入 +$8,000、固定资产 +$3,000 | 对应资产与负债科目 |
这张表也说明了为什么三表必须一起看。只看资产负债表,80,000 权益看起来很多,但资产里有 12,000 是应收,负债里有 22,000 是未来要交付的服务;只看利润表,58,299 净利润看起来不错,但现金增加了 61,299,差额来自预收和应收;只看现金流量表,61,299 净增加看起来健康,但没有说明这笔钱对应多少负债。
简化假设在这里起作用:没有折旧,所以固定资产从 0 到 3,000 只受资本开支影响;没有利息,所以权益变化只来自经营;没有汇兑,所以没有重估调整;没有分红,所以权益桥只有净利润一项。真实报表里,这些项目会各占一行,但勾稽逻辑不变。
把资产负债表写成代码
下面的 balance.ts 做三件事:校验资产等于负债加权益,计算两种营运资金口径,提供权益桥和递延收入、应收的差额函数。金额用整数美分,资产与负债科目必须是非负整数,权益在累计亏损时可以为负;不平衡时返回差额,而不是抛错,方便调用方打印完整的校验结果。
// file: src/fin/balance.ts
// tier: L1
// verify: node --test test/fin/balance.test.ts
import { type Cents, add, sub } from "../core/money.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type BalanceSheetInput = {
cash: Cents;
receivables: Cents;
fixedAssets: Cents;
deferredRevenue: Cents;
payables: Cents;
equity: Cents;
};
export type BalanceCheck = {
totalAssets: Cents;
totalLiabilities: Cents;
totalEquity: Cents;
liabilitiesAndEquity: Cents;
difference: Cents;
balanced: boolean;
workingCapital: Cents;
operatingWorkingCapital: Cents;
};
function nonNegative(value: Cents, label: string): Result<true> {
if (!Number.isInteger(value) || value < 0) {
return fail(`${label} must be a non-negative integer`);
}
return ok(true);
}
/**
* Checks assets = liabilities + equity and reports two working-capital views:
* working capital = cash + receivables - deferred - payables
* operating working capital = receivables - deferred - payables
* The second view removes cash, which is the point of the first view, so it
* shows whether customers or suppliers are funding the operating cycle.
* Asset and liability accounts must be non-negative; equity may be negative
* when accumulated losses exceed contributed capital.
*/
export function checkBalanceSheet(
input: BalanceSheetInput,
): Result<BalanceCheck> {
const fields: Array<[Cents, string]> = [
[input.cash, "cash"],
[input.receivables, "receivables"],
[input.fixedAssets, "fixed assets"],
[input.deferredRevenue, "deferred revenue"],
[input.payables, "payables"],
];
for (const [value, label] of fields) {
const checked = nonNegative(value, label);
if (!checked.ok) {
return checked;
}
}
if (!Number.isInteger(input.equity)) {
return fail("equity must be an integer number of cents");
}
const totalAssets = add(add(input.cash, input.receivables), input.fixedAssets);
const totalLiabilities = add(input.deferredRevenue, input.payables);
const liabilitiesAndEquity = add(totalLiabilities, input.equity);
const difference = sub(totalAssets, liabilitiesAndEquity);
return ok({
totalAssets,
totalLiabilities,
totalEquity: input.equity,
liabilitiesAndEquity,
difference,
balanced: difference === 0,
workingCapital: sub(
add(input.cash, input.receivables),
totalLiabilities,
),
operatingWorkingCapital: sub(
sub(input.receivables, input.deferredRevenue),
input.payables,
),
});
}
/** Ending equity = beginning equity + net income - distributions. */
export function equityBridge(
beginningEquity: Cents,
netIncome: Cents,
distributions: Cents = 0,
): Result<Cents> {
if (!Number.isInteger(beginningEquity)) {
return fail("beginning equity must be an integer number of cents");
}
if (!Number.isInteger(netIncome)) {
return fail("net income must be an integer number of cents");
}
const paid = nonNegative(distributions, "distributions");
if (!paid.ok) {
return paid;
}
return ok(sub(add(beginningEquity, netIncome), distributions));
}
/** Cash received before it is recognized: prepaid minus recognized revenue. */
export function deferredRevenue(
prepaidCents: Cents,
recognizedCents: Cents,
): Result<Cents> {
const prepaid = nonNegative(prepaidCents, "prepaid amount");
if (!prepaid.ok) {
return prepaid;
}
const recognized = nonNegative(recognizedCents, "recognized revenue");
if (!recognized.ok) {
return recognized;
}
if (recognizedCents > prepaidCents) {
return fail("recognized revenue cannot exceed prepaid amount");
}
return ok(sub(prepaidCents, recognizedCents));
}
/** Invoiced but not collected: invoiced minus collected. */
export function receivables(
invoicedCents: Cents,
collectedCents: Cents,
): Result<Cents> {
const invoiced = nonNegative(invoicedCents, "invoiced amount");
if (!invoiced.ok) {
return invoiced;
}
const collected = nonNegative(collectedCents, "collected amount");
if (!collected.ok) {
return collected;
}
if (collectedCents > invoicedCents) {
return fail("collected amount cannot exceed invoiced amount");
}
return ok(sub(invoicedCents, collectedCents));
}
checkBalanceSheet 的输出包含资产合计、负债合计、权益、负债加权益、差额、是否平衡,以及两个营运资金口径。workingCapital 把现金算进去,等于现金加应收减递延收入减应付;operatingWorkingCapital 把现金排除,只看应收、递延收入和应付,回答「客户和供应商谁在给运营周期融资」。LedgerLite 的两个值分别是 77,000 和 −18,000:前者是短期偿债视角,后者是运营资金结构视角。
equityBridge 把期初权益、净利润和分红串起来:21,701 + 58,299 − 0 = 80,000。deferredRevenue 和 receivables 分别计算「预收减确认」和「开票减收款」,它们是资产负债表上两个时间差科目的最小模型。LedgerLite 的输入是现金 9,500,000 美分、应收 1,200,000、固定资产 300,000、递延收入 2,200,000、应付 800,000、权益 8,000,000;输出资产合计 11,000,000、负债合计 3,000,000、差额 0。
代码不处理坏账准备、折旧、税款应收应付和汇兑调整。它只覆盖本章的简化口径;科目设置、月结和审计调整属于操作层流程,见 记账与月结 。
小结
- 资产负债表是时点快照:LedgerLite 期末资产 110,000 = 负债 30,000 + 权益 80,000。
- 应收 12,000 是已确认未收款,约 18.2 天收入;6% 逾期超过 30 天对应 720,坏账准备 1% 对应 120。
- 递延收入 22,000 是已收款未确认,约 1.1 个月 MRR;它让营运资金变成 −18,000,客户预收在给生意融资。
- 权益 80,000 是资产减负债的剩余,不是现金;期初权益 21,701 加净利润 58,299 等于期末 80,000(无分红)。
- 期初表可以由期末表和现金流倒推:现金 33,701、应收 10,000、固定资产 0、递延收入 14,000、应付 8,000、权益 21,701。
- 三表闭环:净利润进权益,净现金增加进现金,应收、递延收入和固定资产各自找到对应科目;无折旧、无利息、无汇兑是闭环成立的前提。
阅读导航:上一节:3.2 现金流量表:经营、投资、筹资 · 下一节:4.1 预收、递延与确认时点 。
常见错误与边界
- 把权益当现金。 LedgerLite 权益 80,000、现金 95,000,两者不相等;权益还要扣掉 22,000 递延收入和 8,000 应付,再加上 12,000 应收和 3,000 固定资产。
- 把递延收入当收入。 递延收入是负债,不是当期收入;它增加时现金比收入多,所以在现金流量表里加回,在资产负债表上记负债。
- 把应收当现金。 应收是资产,不是现金;应收增加 2,000 意味着经营现金流减少 2,000,而不是增加。
- 忘记期初表。 期末现金 95,000 需要期初 33,701 加净现金增加 61,299 才能解释;只列期末表,无法验证现金流。
- 用总额应收忽略坏账准备。 案例表按 12,000 总额列示;如果按 1% 计提坏账准备,净应收是 11,880,资产和权益各减少 120,表上要多一个准备科目。
- 把创始人薪酬当分红。 薪酬是利润表费用,已经通过净利润减少权益;分红是权益分配,会同时减少现金和权益。两者不能混为一谈。
- 把固定资产当费用。 设备投资 3,000 是投资现金流,形成固定资产;本节假设无折旧,所以固定资产账面价值不下降。真实报表里折旧会减少资产和权益。
- 忽略营运资金的符号。 应收增加占用现金,递延收入和应付增加提供现金;符号反了,经营现金流和资产负债表会对不上。
- 用不同舍入口径。 案例表用整数美元,代码用精确美分;税额和现金流的 0.20 美元差异如果处理不一致,期末现金会出现几分钱的不平衡。
- 认为资产负债表平衡就说明账对。 平衡只说明恒等式成立;科目分类错误、收入确认时点错误、漏记负债,都可能让一张平衡的表仍然是错的。三表要一起看。
本节的边界是「简化资产负债表的勾稽」:只覆盖现金、应收、固定资产、递延收入、应付和权益六个科目,不处理坏账准备、折旧、利息、汇兑、借款、税款应收应付和分红。它不涉及记账软件操作、发票模板和税务申报流程;这些属于操作层与税务章节。把这张表闭合之后,第 4 章会单独展开递延收入与收入质量,第 13 章会把三张表横向对照,回到「三个案例为什么估值不同」的主线。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。