OCR 最危险的不是识别失败,而是“识别得像成功了”,最后却把账单挂错采购单。
这篇文章主要参考了以下企业版源码与测试入口:
enterprise/account_invoice_extract_purchase/models/account_invoice.pyenterprise/account_invoice_extract_purchase/tests/test_invoice_extract_purchase.py
一、这个模块真正解决的不是表面动作,而是跨模块语义对齐
account_invoice_extract_purchase 解决的是 OCR 与采购单匹配的容错问题:单靠发票号未必准,所以还要看供应商、总额,甚至允许识别到部分采购行形成 subset 匹配,但又不能宽松到乱挂单。
如果只看 UI,很容易把它理解成一个按钮、一张表或一个新视图。但从 account_invoice_extract_purchase 的模型、测试和桥接关系看,官方真正关心的是:前台动作发生以后,后端主链路能不能继续保持同一套业务语义。
二、核心机制链路
1. 先把数字变成可匹配的 regex
transform_numbers_to_regex(string) 暗示 OCR 结果不会完美复刻单号,因此系统先把数字模式做容错匹配,而不是生硬全等比较。
2. 供应商与总额是第二道保险
测试 test_match_po_by_supplier_and_total 说明,只靠名字或编号不够,还要结合 supplier 与 total 缩小候选范围。
3. subset of order lines 是受控宽松,不是无限猜测
test_match_subset_of_order_lines / test_no_match_subset_of_order_lines 证明官方允许“账单覆盖采购单的一部分”,但也专门守住不能越界误挂。
三、最容易被误解的边界
- 看到 OCR 抓到一个编号就直接绑定 PO。
- 不核对 supplier / total,结果同编号或近似编号采购单被错误匹配。
- 把 subset 匹配理解成“差不多就行”,忽略 no_match 测试设下的边界。
这些误解之所以常见,往往是因为大家只看见“入口动作”,却没有继续追到模型方法、状态切换、聚合口径和测试场景里去看 Odoo 究竟把什么当成事实、把什么当成辅助信息。
四、实施与排查时,建议按这个顺序看
- 先看 OCR 结果是否通过 regex 得到合理候选。
- 再核对 supplier 与 total 是否缩到唯一或可信候选。
- 最后检查 bill lines 是否真的是该 PO 的完整或受控子集。
对企业版功能来说,排查顺序非常重要。很多看似是“结果不对”的问题,真正根因往往更早:字段上下文没带过去、桥接对象没建、状态机没推进、或者权限/公司边界一开始就错了。
五、结论
OCR 自动匹配采购单真正值钱的地方,不是命中率数字,而是它知道什么时候该大胆自动挂单,什么时候必须保守停下。
DISCUSSION
评论区