很多人说到 Odoo 催款,脑海里的画面只有一件事:
- 发票到期;
- 系统判逾期;
- 发邮件催客户。
但如果你真的想把 Odoo 的应收跟进跑稳,光盯“overdue”远远不够。
从 /home/ubuntu/odoo-temp/addons/account/models/account_move_line.py 和 account_move.py 能看到,官方在基础层先处理的是:
- 哪些 journal item / move 应该参与 follow-up;
- 当前应优先看的日期到底是
payment_date还是date_maturity; - 哪些单据根本应该被排除在 follow-up 之外;
- 跟进动作需要如何落到 activity 这种可闭环对象上。
所以催款不是一个“发不发提醒”的按钮,而是一条分层链:先筛对象,再识别当前催款节点,再决定提醒级别,最后把动作挂到负责人的 activity 上。
一、no_followup 说明官方首先在做“对象筛选”
account.move.line 上有 no_followup,帮助文本直接写着:
Exclude this journal item from follow-up reports.
并且 _compute_no_followup() 默认会让 journal_id.type == 'general' 的行标成不参与 follow-up。
这背后是个非常现实的判断:
- 不是所有会计分录都适合进入应收催款视角;
- 只有真正属于客户欠款、待回款语义的 receivable 项,才应该进入 follow-up;
- 纯总账调整、手工杂项分录如果一股脑塞进催款体系,只会让列表失真。
误区 1:以为所有带 receivable / payable 的行都应该被催
未必。
Odoo 明确给了排除入口,就是承认某些行虽然会计上挂在相关科目,业务上却不该进入催款节奏。
二、move.no_followup 说明它不是“单行技巧”,而是单据级语义
account_move_line.py 的 _inverse_no_followup() 很有意思:
- 如果一张 invoice 某条 line 被改成 no_followup;
- 系统会把这个语义提升回 move 层;
- 再同步到这张单相关的应收应付行。
这说明官方不希望 follow-up 规则停留在“某一条 journal item 的小修小补”,而更希望它表达:
这张业务单据整体要不要参与后续催款。
这对实施很重要。
如果你们经常做:
- 内部冲账单;
- 特殊争议账款;
- 暂不催收的协议单;
- 已转人工法务流程的应收;
那更合理的处理不是在报表里手工忽略,而是明确利用 no_followup 这类语义边界。
三、payment_date 说明 follow-up 看的不是单一 due date
在 account_move_line.py 里,_search_payment_date() 的逻辑很值得反复看。
它不是简单拿 date_maturity 搜索,而是分三段:
- 如果
discount_date还没过,就按discount_date来判断; - 如果
discount_date已过,就退回date_maturity; - 如果压根没有
discount_date,就直接用date_maturity。
这意味着 follow-up 基础层看的是:
当前最值得行动的付款节点。
所以一张启用了提前付款折扣的发票,虽然法律到期日在月底,但在折扣窗口还有效时,系统会优先把“折扣即将失效”的那天当成当前 follow-up 节点。
误区 2:把 follow-up 的日期理解成永远等于 due date
这在普通付款条款下大致成立,但在 early discount 场景下就会错。
你如果只拿 date_maturity 理解催款,就会觉得系统“提醒太早”;
其实系统在说的不是“正式逾期”,而是“当前最值得催的付款窗口”。
四、follow-up levels 的意义,本质是给不同风险阶段配置不同动作强度
虽然当前本地源码仓库没有完整的 follow-up enterprise 模块,但结合 Odoo 的核心应收字段设计,follow-up levels 的角色其实很好理解:
- 刚到关键 payment date 时,动作轻一点;
- 继续未付时,提醒更强;
- 再往后,可能升级为电话、人工介入、责任人待办。
也就是说,follow-up level 不是“第几封邮件”的技术字段,而是逾期风险分层。
它的价值在于把提醒动作标准化,而不是让每个财务人员按个人习惯自由发挥。
五、为什么 activity 比单纯邮件更像真正的催款闭环
如果你只发 follow-up email,会发生什么?
- 邮件发出去了;
- 客户没回;
- 团队内部没人明确接下一步;
- 结果还是要靠群里吼一声“谁跟一下”。
而 Odoo 更稳的协同机制一直是 mail.activity。
这点在其他模块已经非常明显,在应收催款里同样成立:
- email 解决“对外通知”;
- activity 解决“对内归责”;
- 二者结合,才是完整 follow-up。
所以当某个 follow-up level 不再只是温和提醒,而需要:
- 销售跟客户确认;
- 财务电话追款;
- 客服核对争议;
- 负责人复盘异常账款;
activity 才是真正能把下一步落到人头上的对象。
六、为什么“逾期提醒”和“责任分派”必须拆开看
许多团队做催款失败,是因为把两件事混为一谈:
- 客户有没有被提醒
- 内部有没有人接着推进
前者靠 follow-up message 就够; 后者必须靠 activity、owner、deadline 这类机制。
所以如果你看到:
- 催款邮件按时发;
- 但老账还是没人接;
问题往往不是 follow-up levels 不够多,而是 activity 链根本没接上。
七、实战上更稳的 follow-up 设计方式
一个更健康的做法通常是:
第一层:先清理对象边界
利用 no_followup 排掉不该进催款报表的单据。
第二层:确认 payment_date 语义
尤其是 early payment discount 场景,不要只盯 date_maturity。
第三层:把 follow-up levels 当作动作强度分级
轻提醒、正式催收、升级处理,各自定义清楚。
第四层:把高等级 follow-up 接成 activity
否则系统只是在“发消息”,不是“推进回款”。
八、排查“为什么催款逻辑总让人觉得不对”时该看什么
建议按这个顺序:
- 这条应收是否本来就应该参与 follow-up;
- 是否被
no_followup排除了; - 当前看的是
payment_date还是date_maturity; - 是否存在 early discount 导致关键日期前移;
- follow-up level 配的是消息动作还是责任动作;
- 有没有把高风险阶段接到 activity;
- 团队是否把“发出提醒”错当成“完成跟进”。
一句话记忆
Odoo 催款不是“逾期就发邮件”,而是“先筛出该催的应收,再按当前 payment_date 判断风险阶段,用 follow-up levels 设定提醒强度,并在需要时把下一步交给 activity 负责人”的分层链路。
DISCUSSION
评论区