账务中台 · 2026-08 · 本地离线部署
5 家机构、4 种导出格式、2129 笔流水。期初建账后,账面余额与单据末行余额差 0.00 —— 账实相符是判断一套账做没做对的唯一标准。
问题
公司拨一笔备用金到经办人账户 → 经办人先行垫付采购 → 事后凭票报销回款。 同一笔资金走完这一圈,单式记账会把「备用金到账」和「报销回款」各记一次收入, 把垫付记成一次成本 —— 收入凭空翻倍,成本重复计入。
这类往来在任何有备用金、代垫、项目预付、多主体归集的场景里天天发生。 它不是加几个分类字段能解决的,是数据模型本身缺少表达能力。
原始单据 → 原始流水(只追加、不可修改)→ 标准化 → 记账凭证。每条结果都能回溯到文件名、页码、行号。
备用金到账记「银行存款增加 / 其他应付款增加」,报销回款记「银行存款增加 / 其他应收款减少」—— 两笔都不碰收入科目,垫付也只在往来科目里进出。收入与成本因此不会被重复计入。
已过账凭证不可修改删除。科目改了就生成红字冲销加新分录,前后差异写入审计日志。
实测
每家机构的导出格式都不一样,而且都不会告诉你解析对没对。所以每个解析器都要 自己证明自己没错:单据里本来就有的合计行、笔数、余额链,全部拿来做交叉验证。
| 解析器 | 格式 | 笔数 | 自校验方式 | 结果 |
|---|---|---|---|---|
| A 机构 · 账户流水 | PDF 20 页 | 436 | 末页合并统计 + 余额链 436/436 | 0 告警 |
| A 机构 · 账户流水 | XLSX | 437 | 表头校验 + 金额精度断言 | 0 告警 |
| B 机构 · 活期明细 | XLS | 639 | 收支合计行比对 | 0 告警 |
| C 机构 · 账户对账单 | PDF 加密 | 58 | 余额链 + 声明合计 + 声明笔数 | 0 告警 |
| D 机构 · 月结对账单 | 邮件文本 | 722 | 账单勾稽式 6 期 6/6 闭合 | 0 告警 |
逐笔断言「上一笔余额 + 本笔金额 = 本笔余额」。某份 20 页 PDF 的 436 环全部连续,等于数学证明没漏行、没切错数字。
单据末尾本来就印着收支合计,拿来和解析结果对账。声明合计与解析结果分毫不差才放行。
对账单页脚写着「共 58 笔」,必须解析出 58 笔才算通过。三道校验任一不符即告警,不会静默出错。
踩过的坑
同一笔交易,XLSX 版的对手方写「样例公司 12345」,PDF 版写成「样例公 司 12345 客户摘要」—— 文本形态不同,指纹就不同,重复导入没被拦住。 如果没发现,每换一种格式重导一次就会重复记一次账。
某家机构的 PDF 用 CID 字体,常见的 serverless PDF 库不带 CMap 字库,抽出来是空字符串;
换成带完整字库的 pdfjs-dist legacy 构建才拿到文本。另有一家导出的 .xls
实际是 .NET OpenXML 生成、带 x: 命名空间前缀,主流 Node Excel 库直接崩在读取阶段。
某份对账单 PDF 的文本层里,金额、余额、户名之间没有空格:
2025-08-25+4,320.904,320.90某某公司 6217…转账。
靠正则切分,再用余额链兜底 —— 一旦切错,余额链立刻断裂并报警。
成本核算
账实相符之后,很多原本靠对方口头告知的数字变成了可复核的:扫码收款、平台结算提现、 分期采购,每条渠道的实际成本都能从凭证里逐笔倒推出来。以下都是从上面这批数据跑出来的:
| 核算项 | 方法 | 结果 |
|---|---|---|
| 收款渠道手续费核算 | 收款金额与实际到账金额逐笔配对,20 组 | 0.300% / 0.600% 两档,均值 0.532% |
| 分期账单真实成本 | 名义手续费率按实际现金流求 IRR | 名义「8%」→ 实际年化 14.5% |
| 分期余额反解 | 由总价、期数、每期金额反解费率与各期未结余额 | 与第三方账面数交叉验证,差 4 元 |
| 在途分期台账 | 由月结单逐期明细还原未结清项与剩余期数 | 12 项,进行中 / 已结清 / 已退货三态自动收敛 |
第二行值得展开:商家报的「12 期 8% 手续费」是按总价平摊的名义口径,而本金是逐期归还的 —— 第 6 期时你只占用了一半资金,却仍按全额付手续费。按实际占用天数折算, 真实年化是 14.5%,接近名义值的两倍。这类口径差在设备采购、渠道账期里普遍存在。 第三行则是三个互相独立的来源彼此印证:总价、期数、每期金额反解出的未结余额, 与第三方账面数相差 4 元,说明这套账确实算对了。
资产核算
分期采购的手机、笔记本、服务器,账上通常只留下每月一笔扣款,很难回答两个真问题: 这台设备每天的持有成本是多少、什么时候换掉最划算。
把采购凭证、分期明细与处置回款挂到同一台设备上,这两个问题就变成了减法。 以一台分期采购、一年后转手的手机为例:
采购、分期扣款、维修、保险、处置回款全部关联到同一台设备,形成完整生命周期账。
车辆保险、年检、里程这类不走对账单的支出支持手工录入,与自动导入的凭证同表核算。
转手时按账面净值与实际回款自动生成处置损益分录,不需要人工算差额。
技术栈
全程 bigint 最小货币单位。只在解析边界一次性转换,小数位超出币种精度直接抛错,绝不静默舍入。
同一家机构的不同导出格式各自独立、各带版本号。新版解析器不会静默重写历史导入结果。
pre-commit 钩子拦截真实单据路径与 16–19 位长数字(卡号/证件号)。开发期间它拦下过 4 次,包括作者自己写的测试夹具。