很多实施项目都会遇到一种很典型的抱怨:
- 银行流水规则明明配了;
- 系统也能给出差不多正确的建议;
- 但真正想“自动完成”时,总还有一点别扭。
问题通常不是 有没有 reconciliation model,而是没有看懂 /home/ubuntu/odoo-temp/addons/account/models/account_reconcile_model.py 里真正表达的 pipeline。
Odoo 的银行对账模型不是“命中条件就记一笔账”这么直线。它实际上在串三件事:先识别流水,再判断是否只做 partner 映射或建议展示,最后才决定能不能自动完成并附带 write-off 行。
一、account.reconcile.model 先是路由器,再是模板
旧文章已经解释过 reconciliation model 不是普通模板;这次要更进一步。
从字段设计看,模型的第一职责是给 statement line 分流:
match_journal_idsmatch_amount/match_amount_min/match_amount_maxmatch_label/match_label_parammatch_partner_idstrigger
这说明它先回答的是:
- 这条流水属于不属于我;
- 属于以后,我是给建议,还是允许系统直接完成。
所以真正的实现思路不是“模板套流水”,而是“先命中一类流水,再决定接下来走哪条自动化深度”。
二、trigger 决定的不是 UI 按钮,而是自动化责任边界
源码里 trigger 只有两个核心值:
manualauto_reconcile
很多人会把它理解成“是否少点一次按钮”。这太浅了。
manual 的真实语义是:
- 规则可以参与判断;
- 系统可以把它作为建议给你;
- 但最终闭环仍由人确认。
auto_reconcile 的真实语义是:
- 一旦命中条件,系统把这条规则视为足够可信;
- 不只展示建议,而是允许自动完成对账。
所以这里不是前端交互差异,而是企业愿意把多少判断权交给规则。
银行手续费、固定通道费、结构稳定的支付平台回款,通常比较适合 auto_reconcile;
而摘要很脏、备注经常变化、业务例外很多的流水,哪怕能大致猜对,也更适合停在 manual。
三、can_be_proposed 解释了为什么有些规则“能命中却不出现建议”
源码里 _compute_can_be_proposed() 很关键:
model.can_be_proposed = not model.mapped_partner_id and (
model.match_label or model.match_amount or model.match_partner_ids or model.trigger == 'auto_reconcile'
)
这段逻辑其实在说:
- 不是所有 model 都会以“建议条目”形式出现;
- 某些模型的职责只是做 partner mapping;
- 只有满足一定条件的模型,才会被当成可提议的规则。
这能解释一个常见误区:
误区 1:以为每个 reconciliation model 都该在界面里显眼出现
不一定。
有些模型更像“后台识别器”,不是“前台建议器”。
Odoo 在这里做了明确分层:
- 有些规则负责识别 partner;
- 有些规则负责展示建议;
- 有些规则负责自动闭环。
如果你把三种职责混成一种,就会觉得系统行为怪。
四、partner mapping 是一条单独的轻量 pipeline
_compute_partner_mapping() 进一步说明了这一点:
is_partner_mapping = model.match_label and len(model.line_ids) == 1 and model.line_ids[0].partner_id and not model.line_ids[0].account_id
也就是说,只有在这种结构下,模型才会被识别成 mapped_partner_id:
- 有
match_label - 只有一条 model line
- 这条 line 指向 partner
- 但没有 account
这特别有意思。
它表明官方显式支持一种场景:
先通过流水标签识别交易对手方,但暂时不直接落会计科目。
这类规则最适合:
- 平台名、收款户名、付款户名比较稳定;
- 先把 partner 锁准,比先决定科目更重要;
- 后续再让 open items / invoices / bills 去参与真正匹配。
所以 partner mapping 不是“配不完整的规则”,而是一个刻意保留的中间层。
五、write-off line 不等于“差一点就自动补平”
account.reconcile.model.line 这层也很值得看。
它支持的 amount_type 包括:
fixedpercentagepercentage_st_lineregex
并且 amount_string 的 help 明确写的是:给 writeoff line 的值。
这说明 Odoo 对 write-off 的理解不是“模糊匹配失败后的兜底”,而是:
当你已经确定这类差额有稳定会计解释时,可以把差额结构显式建模成 write-off line。
比如:
- 固定银行手续费;
- 按流水比例扣的平台手续费;
- 从 label 里抽取手续费或净额;
- 特定渠道总会出现的小额 rounding difference。
这时 write-off line 才是合理的。
误区 2:把 write-off 当成所有自动对账失败的万能补丁
这会很危险。
因为 write-off 的前提不是“差额存在”,而是“差额有稳定、可审计、可复用的业务含义”。
如果今天挂手续费,明天挂折扣,后天挂退款尾差,都塞进同一条自动 write-off 规则,那自动化确实会提升,但账也会越来越脏。
六、为什么 regex 金额提取很强,但也最容易失控
model line 支持 amount_type = 'regex',而且源码还会校验 regex 合法性。
这意味着你可以直接从银行备注里抽金额。
这个能力很强,但它真正适合的是:
- 银行文本格式高度稳定;
- 某些费用金额不在结构化字段里;
- 你已经确认抽取逻辑不会因为银行文案改版而漂移。
否则就会出现最麻烦的情况:
- 规则一直命中;
- 金额也一直“像是对的”;
- 但其实抽错了字段,系统还自动完成了。
所以 regex 适合高收益、低变异的场景,不适合一切“先凑合跑起来再说”的项目。
七、真正的 auto match pipeline,可以这样理解
把整条链简化后,Odoo 的思路大概是:
- 先按 journal / amount / label / partner 条件筛出候选规则;
- 判断这条规则是 partner mapping、proposal 还是 auto_reconcile;
- 如果需要补差额,再按 line_ids 生成明确结构的 write-off lines;
- 只有在规则可信度足够高时,才允许自动闭环。
这也是为什么许多项目“感觉只差一点自动化”:
- 不是模型没命中;
- 而是中间层职责混了;
- 或者 write-off 设计太贪心;
- 或者 partner 识别和会计落账被强行绑成一步。
八、实施时最稳的配置顺序
如果你要把银行对账从“纯人工”推进到“半自动 / 自动”,更稳的顺序通常是:
第一步:先做 partner mapping
先让稳定流水能认出谁。
第二步:再做 manual proposal
让系统能给出高质量建议,但保留人工审核。
第三步:最后只把少数高确定性规则切到 auto_reconcile
例如固定手续费、结构极稳的平台扣费。
第四步:write-off 只覆盖少数真正可审计的差额
不要让 write-off 代替分析。
九、排查“为什么还不能全自动”时该看什么
建议按这个顺序查:
- 规则到底是想做 partner mapping、proposal,还是 auto reconcile;
trigger是否真的允许自动完成;can_be_proposed是否让它能进入建议层;line_ids里的 write-off 设计是否过度承担了不稳定差额;- label regex 是否足够稳定;
- 有没有把“识别 partner”和“决定科目”错误地绑成一步。
一句话记忆
Odoo 银行对账自动化不是“命中规则就补一笔”,而是“先识别流水、再决定建议层级、最后在可信前提下用 write-off 完成闭环”的分层 pipeline。
DISCUSSION
评论区