“三方匹配”最容易被简化成一句话:采购、收货、发票对得上就付款。可一旦落到系统里,你会发现真正难的是把这些业务判断压缩成 可计算、可追责、可人工干预 的状态。account_3way_match 做的正是这件事。
主要参考:
enterprise/account_3way_match/models/account_invoice.pyenterprise/account_3way_match/views/account_invoice_views.xmlenterprise/account_3way_match/tests/*
一、release_to_pay 不是备注字段,而是账单级决策结果
account.move 上的 release_to_pay 会在 _compute_release_to_pay() 里统一计算。逻辑很直接:
- 已付款或不是相关 invoice 的,直接视为
no; - 如果启用强制状态,就使用人工值;
- 否则根据各 invoice line 的
can_be_paid汇总出账单级状态。
这说明系统并不是在“显示一个建议”,而是在形成一个 付款放行结论。企业里这类字段一旦被展示给 AP 团队,就会直接影响后续审批动作。
二、真正的关键在 invoice line:每一行都先判断能不能付
_can_be_paid() 会先检查 invoice line 是否挂到了采购行,再看价格是否一致。如果采购价和发票价换算后不一致,直接进 exception。这非常符合现实:数量对上了但价格错了,也不该自动放行。
接着系统按采购产品的开票策略走两条分支:
purchase:按订购数量判断;- 其他情况:按到货数量判断。
这就是企业版三方匹配的本质:不是所有品类都按同一规则放行,而是跟采购策略绑定。
三、ordered qty 和 received qty 两套规则,解决的是不同风险
_can_be_paid_ordered_qty() 关注的是“你开的票有没有超过订购量”;_can_be_paid_received_qty() 关注的是“你开的票有没有超过已收货量”。前者更适合按订购开票的场景,后者更适合必须到货才能付款的场景。
对业务新人来说,最容易混淆的点是:为什么有时未收货也能 yes?答案不在财务,而在采购策略。如果产品本来就是按 ordered quantities 付款,系统就不会拿 received qty 当唯一标准。
四、exception 状态的意义,是把问题显式推给人
如果账单里有一行 exception,整张 bill 就会进入 exception。这是一种非常保守但合理的企业选择:付款风险按最坏一行兜底,而不是按平均分通过。
同时企业版也留了 force_release_to_pay 和 release_to_pay_manual,允许人工 override。这里的设计很成熟:
- 默认让系统判;
- 出现边界情况时允许人工兜底;
- 人工干预被显式记录,而不是偷偷改底层数据。
五、实战中最常见的误区
- 以为只要 PO、收货、发票三个对象存在就算三方匹配。真正决定状态的是行级数量和价格规则。
- 以为 exception 就等于系统错了。很多时候 exception 恰恰是在提醒你:业务规则没有统一。
- 以为人工 override 很危险。其实危险的是没有 override 通道,只能通过改原始单据来绕过控制。
六、落地建议
- 先梳理采购品类到底按 ordered 还是 received 付款,再启用自动放行判断。
- 财务培训时把
yes / no / exception的业务含义说清楚,不要只讲界面颜色。 - 对频繁出现
exception的供应商,优先排查价格差异和采购策略设置,而不是怪系统太严格。
七、结论
account_3way_match 的价值,不是多了一个“Should Be Paid”字段,而是把采购、收货、发票之间那条最容易扯皮的边界,变成了系统可执行、人工可复核的付款放行机制。
DISCUSSION
评论区