很多人对 Odoo 企业版 Account Follow-up 的第一印象是:
- 发票逾期;
- 系统发提醒;
- 财务跟催。
但如果只是这样,它根本不需要单独一套 follow-up 引擎。
把 account_followup 的 partner、followup line 和测试代码串起来看,会发现官方真正做的是一件更细的事:
它不是在“逾期时发消息”,而是在“客户维度上维护一套催收阶段与下一动作时间表”。
这套机制里,至少有四个核心边界:
- 当前客户属于哪个催收 level;
- 今天到底该不该执行下一次提醒;
- 哪些未核销行虽然逾期,但被排除出 follow-up;
- 创建活动时,到底该指派给谁。
一、真正的核心不是单张发票,而是 partner 级 follow-up 状态
源码里计算的重点不是某张 invoice,而是 res.partner 上这些字段:
followup_statusfollowup_line_idfollowup_next_action_datefollowup_responsible_idtotal_due / total_overdue / total_overdue_followup
这很说明问题。
Odoo 没把催收设计成“发票逐张发提醒”,而是:
- 先把客户所有相关未核销分录收敛起来;
- 再计算这个客户当前站在哪个催收阶段;
- 然后决定是否该执行下一次动作。
所以它更像一个 客户催收控制面板,不是发票通知器。
二、为什么 with_overdue_invoices 和 in_need_of_action 不是一回事
followup_status 里最关键的区分是:
with_overdue_invoicesin_need_of_actionno_action_needed
很多人容易把前两个混成一个意思:反正都有逾期。
其实源码语义完全不同。
with_overdue_invoices
表示:
- 客户确实还有逾期;
- 但现在还不到下一次催收执行点;
- 或刚刚执行完一轮,正在等下一步窗口。
in_need_of_action
表示:
- 当前催收等级已经触发;
- 且
followup_next_action_date已到; - 系统认为今天应该有人真正处理。
所以一个客户可以有逾期,但今天未必需要动作。
这正是企业催收里“有问题”和“该现在处理”之间的区别。
三、为什么 followup_next_action_date 很关键
这字段是整套引擎最容易被低估的地方。
每执行一次 follow-up,系统不会只是记一条日志,它还会根据当前 level 推算下一个动作日期。
account_followup.followup.line._get_next_date() 的逻辑大意是:
- 如果后面还有更高一级 level,就用
下一级 delay - 当前 delay算下次日期; - 如果没有下一级,就参考前一级或当前 delay 继续往后推。
测试里能看到这种效果:
- 第一次 follow-up 后,不是立刻又触发第二次;
- 而是把
followup_next_action_date推到未来某天; - 到期前状态可能只是
with_overdue_invoices; - 到期后才变成
in_need_of_action。
这说明系统不是“每天扫到逾期就催”,而是:
每次催收动作都会自己生成下一次节奏。
这就是典型的 dunning cadence,不是一次性消息发送。
四、为什么 no_followup 会影响状态而不只是隐藏行
account.move.line.no_followup 是另一个很关键的边界。
源码在汇总 overdue 金额时,会专门区分:
- 普通 overdue;
- overdue 但
no_followup=True; - 真正进入 follow-up 范围的 overdue。
所以 partner 上会有:
total_overduetotal_overdue_followup
这两个值不是同一个东西。
这意味着什么?
意味着你可以有:
- 客户账面上确实存在逾期;
- 但这些逾期行被标记为不参与催收;
- 最终客户状态可能直接变成
no_action_needed。
测试也覆盖了:
- 当所有未核销行都被设成
no_followup=True时; - 即使客观上逾期还存在;
- follow-up 状态也会被压成无需动作。
所以
no_followup不是 UI 勾选项,而是“从催收引擎里剔除这笔账”的业务开关。
五、为什么负责人选择不是简单取 partner.salesperson
_get_followup_responsible() 是很值得读的一段。
很多系统做催收责任人,会直接用:
- 客户销售;
- 或财务负责人。
Odoo 的逻辑更细,候选人是分层回退的:
- 如果 level 配置要求
account_manager,优先取 partner 的user_id; - 如果要求
salesperson,会优先看逾期发票上的invoice_user_id; - 若 partner 自己设置了
followup_responsible_id,也可直接接管; - 候选人失活时还要继续 fallback;
- 最后才回退到默认负责用户。
测试还特别覆盖了:
- 多张发票不同 salesperson 时,优先取残值更高那张对应的人;
- 如果该人已归档,不应该再被选中。
这说明 Odoo 的目标不是“找一个理论负责人”,而是:
尽量找到“对当前逾期款项最有上下文、且仍然活跃可处理”的那个人。
六、为什么活动创建可以一次分配给多个销售
当 follow-up line 配了 create_activity=True 且负责人类型是 salesperson 时,源码允许为多张逾期发票对应的多个销售同时建 activity。
这很有意思。
因为它说明官方默认不是一定要把所有催款责任压到一个人头上。
如果同一客户欠款分散在不同销售名下,那么系统会接受:
- 多个人各自收到要跟进的任务;
- 而不是强行只保留一个主负责人。
这更符合大客户、多单据、多销售协同场景。
七、为什么 follow-up line 不是“模板列表”,而是时间阶梯
account_followup.followup.line 里的核心不是模板本身,而是:
delaysend_emailsend_smscreate_activityactivity_default_responsible_typeauto_execute
这说明 line 的本质不是“第 N 封催款文案”,而是:
某个逾期阶段该采取什么动作。
你可以把它理解成催收时间阶梯:
- 逾期前 10 天提醒;
- 逾期后 10 天建活动;
- 逾期后 30 天升级动作。
line 里的 message template 只是动作载体,不是主角。
八、为什么 status 计算要基于所有未核销分录聚合
partner 的总欠款和逾期金额不是按 invoice 单独循环算的,而是直接对未核销 move line 聚合:
- 按 account_type;
- 按 overdue 与否;
- 按
no_followup; - 按 partner。
这带来两个结果:
- 性能更稳定:适合大批量客户催收面板;
- 视角更统一:同一客户下多张单据会自然合并成一个催收事实。
这再次说明它不是“逐单提醒器”,而是客户级引擎。
九、为什么执行 follow-up 后状态不会立刻清成正常
很多人会误解为:
- 我已经发过提醒;
- 那这次催收就结束了;
- 应该恢复正常。
并不是。
执行 follow-up 后,若发票仍未回款:
- partner 仍然可能处于
with_overdue_invoices; - 只是暂时不需要动作;
- 要等
next_action_date到了,再升级为in_need_of_action。
执行动作并不代表问题解决,只代表已进入下一个等待窗口。
这点对理解催收节奏非常关键。
十、实战里最容易误解的 4 个点
误区 1:逾期就一定要催
不对。
还要看 followup_next_action_date 是否已到。
误区 2:no_followup 只是界面排除
不是。
它会直接影响 follow-up 状态和可催金额。
误区 3:负责人永远是客户销售
也不对。
系统会结合发票销售、partner 负责人和 fallback 规则动态选人。
误区 4:催收执行一次后客户就“正常”
不对。
更多是从“该动作”切到“等待下一次动作”。
总结
把 account_followup 串起来看,你会发现 Odoo 企业版并不是做了一个“逾期发提醒”的小工具。
它真正做的是一套 partner 级催收状态机:
- 用 followup_line 表达逾期阶段;
- 用 followup_next_action_date 控制催收节奏;
- 用 no_followup 把特定分录排除出催收引擎;
- 用 responsible fallback 选出真正该跟的人;
- 用 activity / email / SMS 落地动作;
- 用 with_overdue_invoices / in_need_of_action 区分“有问题”和“该现在处理”。
所以 Odoo 企业版 Account Follow-up 的本质,不是“催款通知”,而是“围绕客户欠款状态维护一条时间化的催收工作流”。
这才是源码真正值得学的地方。
参考源码
- enterprise/account_followup/models/account_followup.py
- enterprise/account_followup/models/res_partner.py
- enterprise/account_followup/tests/test_account_followup.py
DISCUSSION
评论区