LabHub
学习 学习路径 课程

银行现场的语言

从分类账里找出借贷不平

在 LabHub 中继续学习

目标

从复式记账总账入手,将一张借贷不平的凭证定位到具体一行分录;在不删除原始记录的前提下更正它,再使余额表与总账保持一致。

为什么重要

在银行系统中,余额不是存储起来的值,而是从总账计算出的值。出于性能考虑,每个科目会同时保存余额列,但它只是总账的副本,而不是原始依据。

这一区分在调查中至关重要,原因如下。单个余额只是一个数字,仅凭这个数字无法判断其对错。复式记账总账则会自我校验——每笔交易都同时记录在借方和贷方,因此整个总账的借方合计与贷方合计必须相等;一旦这一恒等式被破坏,本身就是事故信号。随后还可以按凭证进一步缩小范围。

本次事件如下。客户的运维负责人联系称“结算余额不一致”。三天的总账中有两处偏差,但性质截然相反:一处是总账错误而余额表正确,另一处则是总账正确而余额表错误。如果不区分二者就直接处理,修正方向会恰好相反。

更正方式也与一般服务不同。不能通过 UPDATE 修改错误凭证。要先录入一张像镜像一样反转原凭证的冲销凭证来抵消它,再重新录入正确凭证。总账中必须并列保留这三张凭证,才能在两个月后的审计中解释当时发生了什么。

步骤

  1. 创建并运行 /root/bank/gen_ledger.py,生成 /root/bank/ledger.db。其中包含 10 个科目、201 张凭证和 430 行分录。
  2. 将按科目汇总的试算平衡表输出到 /root/bank/trial_balance.csv。表头为 code,name,debit,credit,balance,其中 balance 是借方合计减去贷方合计。一次也没有发生变动的科目也要保留。
  3. /root/bank/tb_totals.txt 中写四行。debit_total=credit_total=gap= 来自总账,cached_sum= 来自 accounts.cached_balance 的合计。
  4. 按凭证比较借方合计和贷方合计,找出不平的凭证,并在 /root/bank/broken_entry.txt 中写入 entry_id=debit=credit=gap=。将该凭证的分录原样输出到 /root/bank/broken_lines.csv,表头为 line_id,entry_id,account_code,dc,amount
  5. /root/bank/balance_diff.csv 中只保留余额表与总账汇总值不同的科目,表头为 code,cached,ledger,gapgapcached 减去 ledger
  6. 不要改动原凭证,录入两张更正凭证。冲销凭证登记为 JE-20260826-0134-R,重新记账凭证登记为 JE-20260826-0134-C,并同时写入 journaljournal_line
  7. 以总账汇总值重新计算并覆盖 accounts.cached_balance,然后在 /root/bank/recheck.txt 中写七行:debit_total=credit_total=gap=unbalanced_entries=original_credit=cached_sum=balance_diff_rows=
  8. /root/bank/ledger_report.md 中按 ## 무엇이 틀렸나## 어떻게 찾았나## 어떻게 고쳤나## 잔액 표는 왜 못 잡았나## 재발 방지 五个章节编写报告。

参考

创建总账快照

创建并运行 /root/bank/gen_ledger.py,生成 /root/bank/ledger.db。其中包含 10 个科目、201 张凭证和 430 行分录。

先创建 /root/bank,然后在其中使用 python3 创建 sqlite 数据库。数据库包含 accounts、journal、journal_line 三张表,分录共有 430 行。

编制试算平衡表

将按科目汇总的试算平衡表输出到 /root/bank/trial_balance.csv。表头为 code,name,debit,credit,balance,其中 balance 是借方合计减去贷方合计。一次也没有发生变动的科目也要保留。

分别计算每个科目的借方金额合计和贷方金额合计,balance 是借方合计减去贷方合计。这三天中一次也没有发生变动的科目也必须留在表中,因此请使用 LEFT JOIN。

分别统计总账与余额表

/root/bank/tb_totals.txt 中写四行。debit_total=credit_total=gap= 来自总账,cached_sum= 来自 accounts.cached_balance 的合计。

共有四个数字。前三个来自 journal_line,最后一个来自 accounts.cached_balance。本步骤要发现的是两张表的偏差大小不同。

定位到一张凭证和一行分录

按凭证比较借方合计和贷方合计,找出不平的凭证,并在 /root/bank/broken_entry.txt 中写入 entry_id=debit=credit=gap=。将该凭证的分录原样输出到 /root/bank/broken_lines.csv,表头为 line_id,entry_id,account_code,dc,amount

按凭证分组,比较借方合计和贷方合计。在 HAVING 子句中加入差值不为 0 的条件后,只会剩下一张凭证。不要修改分录值,按现状原样输出。

核对余额表与总账汇总

/root/bank/balance_diff.csv 中只保留余额表与总账汇总值不同的科目,表头为 code,cached,ledger,gapgapcached 减去 ledger

按科目并列放置 accounts.cached_balance 和总账汇总值,只保留不同的记录。gap 是 cached 减去 ledger。会得到两行,但产生偏差的原因截然相反。

通过冲销和重新记账进行更正

不要改动原凭证,录入两张更正凭证。冲销凭证登记为 JE-20260826-0134-R,重新记账凭证登记为 JE-20260826-0134-C,并同时写入 journaljournal_line

原凭证一行也不能改动。冲销凭证只交换原凭证的借方和贷方,金额则使用记录中的原值。随后再以正确金额录入一张凭证。

根据总账重建余额表

以总账汇总值重新计算并覆盖 accounts.cached_balance,然后在 /root/bank/recheck.txt 中写七行:debit_total=credit_total=gap=unbalanced_entries=original_credit=cached_sum=balance_diff_rows=

出现偏差时,始终修正副本。用总账汇总值覆盖 accounts.cached_balance。不要期望按凭证统计的不平数量变为 0——冲销凭证会与原凭证成对保留。

编写总账一致性报告

/root/bank/ledger_report.md 中按 ## 무엇이 틀렸나## 어떻게 찾았나## 어떻게 고쳤나## 잔액 표는 왜 못 잡았나## 재발 방지 五个章节编写报告。

需要五个章节。在“哪里出错了”一节写明凭证编号和差额;在“如何修正”一节写明冲销凭证编号;在“余额表为什么没发现”一节写明只有余额不一致的科目编号和金额。