每月初都要做的一件事:把银行流水和收费系统对一遍。人工核对几百条,眼花的代价就是错账。于是写了个脚本。

#思路

  1. 读取银行导出的回执表(列名不稳定,需要做映射)
  2. 读取系统导出的应收名单
  3. 按"姓名 + 金额"匹配,允许金额有 0.01 的误差
  4. 输出三类结果:已缴、金额不符、未缴

#核心代码

import pandas as pd
from decimal import Decimal

bank = pd.read_excel("bank.xlsx")
expect = pd.read_excel("expect.xlsx")

bank["name"] = bank["姓名"].astype(str).str.strip()
bank["amt"] = bank["交易金额"].apply(lambda x: Decimal(str(x)).quantize(Decimal("0.01")))
expect["amt"] = expect["应收金额"].apply(lambda x: Decimal(str(x)).quantize(Decimal("0.01")))

paid = set()
diff = []
for _, row in expect.iterrows():
    hit = bank[(bank["name"] == row["姓名"]) &
               ((bank["amt"] - row["amt"]).abs() <= Decimal("0.01"))]
    if len(hit):
        paid.add(row["姓名"])
    else:
        near = bank[bank["name"] == row["姓名"]]
        diff.append({**row.to_dict(), "实际": near["amt"].tolist()})

unpaid = expect[~expect["姓名"].isin(paid)]
pd.DataFrame(diff).to_excel("金额不符.xlsx", index=False)
unpaid.to_excel("未缴名单.xlsx", index=False)
print(f"已缴 {len(paid)} 人,金额不符 {len(diff)} 人,未缴 {len(unpaid)} 人")

#三个必须处理的细节

  • 金额用 Decimal 不用 float:0.1 + 0.2 != 0.3 在核对场景下是致命的
  • 姓名去空格、去全角空格:str.strip() 不够,还要处理 \u3000
  • 重复交易:同月同名多笔要单独列出,不要自动合并
def norm(s):
    return str(s).replace("\u3000", "").replace(" ", "").strip()

#效果

原来半小时的活现在 3 秒出结果,而且不会再漏。人只负责复核脚本标出的异常项,这才是自动化该有的样子。