- 输入数据
- 付款明细、发票、供应商主数据
- 核心方法
- 精确匹配 + 模糊匹配(发票号变体、金额容差、日期窗口)
- 输出物
- 疑似重复付款对
同一张发票被付了两次,常见原因是重复录入、补付时没查原单、发票号换了一种写法。这个模型把付款记录按「同一供应商、同一金额」两两配对,再用三条规则判断哪些配对值得人工核实。结果是疑点清单,不是结论。
适用场景
- 付款前复核:出纳批量付款前,把待付清单和近半年已付记录放在一起跑一遍。
- 定期抽查:财务共享中心或内部审计按月、按季度全量筛查,替代人工抽样翻单。
- 专项审计:对某几家供应商或某个时间段集中核查。
输入数据要求
一张付款明细表,每行一笔付款。字段名可以不同,在工作流第 1 步改成下表的名称即可。
| 字段 | 是否必需 | 类型 | 说明 |
|---|---|---|---|
| 付款单号 | 必需 | 文本 | 每笔付款唯一 |
| 付款日期 | 必需 | 日期 | 格式 yyyy-MM-dd,用于计算两笔付款相隔天数 |
| 供应商编码 | 必需 | 文本 | 用编码而不是名称配对,名称写法不统一会漏配 |
| 发票号 | 必需 | 文本 | 必须按文本读取,否则开头的 0 会丢失 |
| 金额 | 必需 | 数值 | 保留两位小数 |
| 状态 | 可选 | 文本 | 值为「已作废」的记录会被排除;没有这一列就去掉工作流第 2 步的状态条件 |
| 供应商名称、发票日期、经办人、摘要 | 可选 | 文本 | 不参与判断,只带到结果里方便核实 |
判定规则
先把「同一供应商编码、金额完全相同、付款单号不同」的记录两两配对,每一对按下表从上到下判断,命中第一条就停止。
| 规则 | 条件 | 风险 | 常见原因 |
|---|---|---|---|
| R1 精确重复 | 发票号完全相同 | 高 | 同一张发票录入两次、补付未核对原单 |
| R2 发票号变体 | 发票号原文不同,但统一大写、去掉空格和符号、去掉开头的 0 之后相同 | 高 | 不同人录入时写法不同,如 FP-2026-0318 与 fp20260318 |
| R3 近似重复 | 发票号不同,且两笔付款相隔不超过 7 天 | 中 | 可能是重复开票,也可能是正常的连续采购,需要人工核实 |
不满足任何一条的配对不输出。比如每月固定金额的租金,两笔之间相隔一个月,发票号也不同,不会被标出。
工作流步骤
节点按 KNIME 英文名给出,iModel 中文界面的节点名以你所用的版本为准。示例数据已放在工作流内,导入后直接执行即可。
- 读取付款明细CSV Reader
读取
payments.csv。在列设置里把「发票号」「付款单号」「供应商编码」设为文本。换成自己的数据时,只改这一步的文件路径和列名。 - 排除无效记录Row Filter
保留「状态」不等于「已作废」且「金额」大于 0 的记录。
- 规范化发票号String Manipulation
新增列「发票号_规范」:
regexReplace(regexReplace(upperCase($发票号$), "[\\s_/.-]", ""), "^0+", "") - 转换日期、加序号String to Date&TimeCounter Generation
「付款日期」转为日期类型;新增从 1 开始的「序号」列,用于下一步去掉自己和自己、以及 A-B 与 B-A 的重复配对。
- 同供应商同金额配对JoinerRule-based Row Filter
把上一步的表同时接到 Joiner 的两个输入口,按「供应商编码」和「金额」内连接。随后只保留
$序号$ < $序号 (#1)$的行。 - 计算相隔天数Date&Time DifferenceMath Formula
计算两笔付款日期相差的天数,再取绝对值,列名「相隔天数」。
- 按规则判定Rule Engine
新增列「命中规则」,规则从上到下依次为:
$发票号$ = $发票号 (#1)$ => "R1 精确重复"$发票号_规范$ = $发票号_规范 (#1)$ => "R2 发票号变体"$相隔天数$ <= 7 => "R3 近似重复" - 整理输出Row FilterRule EngineSorterCSV Writer
去掉「命中规则」为空的行;按规则给出风险等级(R1、R2 为高,R3 为中);按风险等级、供应商编码排序;保留并重命名为结果表的列,写出结果文件。
参数
改完参数后重新执行即可。
| 参数 | 默认值 | 作用 | 怎么调 |
|---|---|---|---|
| 窗口天数 | 7 | R3 的时间窗口,在第 7 步规则里修改 | 付款频繁的供应商可以缩到 3 天;想多看一些疑点可以放宽,疑点数会明显增加 |
| 排除状态 | 已作废 | 第 2 步排除的状态值 | 按你的系统实际状态值填写,多个值在 Row Filter 里逐条添加 |
| 发票号规范化规则 | 大写、去空格与 _ / . -、去开头的 0 | R2 的判断依据,在第 3 步表达式里修改 | 如果发票号本身以 0 开头且有意义,删掉去 0 的那一层 |
用示例数据验证
运行后结果应与示例数据包里的 expected_result.csv 完全一致。数据中还埋了 5 类不应被标出的干扰项,如果结果多于 6 对,说明某一步条件写错了。
| 疑点 | 命中规则 | 供应商 | 金额 | 发票号 A | 发票号 B | 相隔天数 |
|---|---|---|---|---|---|---|
| D01 | R1 精确重复 | V005 | 36,850.00 | FP44031187 | FP44031187 | 12 |
| D02 | R2 发票号变体 | V008 | 58,200.00 | FP-2026-0318 | fp20260318 | 49 |
| D03 | R1 精确重复 | V012 | 12,480.50 | INV20260411 | INV20260411 | 3 |
| D04 | R2 发票号变体 | V021 | 9,635.00 | 00045871 | 45871 | 7 |
| D05 | R3 近似重复 | V017 | 27,300.00 | INV31905521 | INV31905562 | 2 |
| D06 | R3 近似重复 | V030 | 4,418.60 | FP77120934 | FP77120981 | 5 |
五类干扰项分别是:同一供应商每月固定金额的租金、两家不同供应商用了同一个发票号、重复单据中有一笔已作废、同一张发票分首付和尾款两次支付、同金额但相隔 8 天且发票号不同。
适用边界
- 金额必须完全相同。含税与不含税、四舍五入差几分钱的重复付款找不出来。需要金额容差时,把配对条件改为同供应商配对后再比较金额差,计算量会明显变大。
- 分批付款不在范围内。同一张发票拆成几笔、合计超过发票金额的情况,需要按发票号汇总已付金额再和发票金额比较,是另一个模型。
- 同一家公司用了两个供应商编码时找不出来。这属于供应商主数据问题,应先清理主数据,见供应商主数据异常模型。
- R3 的误报率高于 R1、R2。连续小额采购、固定周期结算都可能命中,建议先处理高风险疑点。
常见问题
数据量很大时会不会很慢?
配对只发生在同一供应商、同一金额之内,一般业务数据下配对数远小于记录数的平方。如果某家供应商有大量相同金额的记录(如固定服务费),可以先把这类供应商单独处理,或在第 2 步按时间段拆开跑。
付款数据在 ERP 数据库里,不是 CSV,怎么接?
把第 1 步换成数据库读取节点,查询出上表的字段即可,后面的步骤不用改。
能不能定期自动跑?
可以。把第 1 步的数据来源固定下来,把输出改为写入共享目录或发送邮件,再按日或按月定时执行。
结果能直接作为审计结论吗?
不能。模型输出的是需要核实的疑点,是否真的重复付款,要调取付款凭证、发票和银行回单确认。