很多财务用户会天然认为:
- 发票什么时候该付,系统就看
date_maturity; - 催款、跟进、账龄分析,也都围着到期日转。
这在“普通付款条款”下大体没错,但一旦你启用了 提前付款折扣,Odoo 的时间语义就变复杂了。
去看 /home/ubuntu/odoo-temp/addons/account/models/account_move_line.py,你会发现 account.move.line 上除了 date_maturity,还有:
discount_datediscount_amount_currencydiscount_balancepayment_date
而 payment_date 的计算逻辑不是“直接等于 maturity”,而是:
如果当前还在折扣有效期内,就优先取
discount_date;否则回落到date_maturity。
这意味着 Odoo 在提前付款折扣场景下,其实承认了两套时间:
- 享受优惠的付款时间;
- 正常全额到期的付款时间。
一、discount_date 不是另一个 due date,而是“折扣窗口截止日”
在 /home/ubuntu/odoo-temp/addons/account/models/account_payment_term.py 里,付款条款计算结果会带出:
discount_percentagediscount_datediscount_balancediscount_amount_currency
这说明 Odoo 不把提前付款折扣理解为“付款时临时少收一点”,而是把它建模成:
在某个日期之前付款,可以按另一套金额规则结清。
所以 discount_date 表达的不是最终法律到期日,而是“折扣口径仍然有效的最后一天”。
二、payment_date 真正表达的是“当前下一步该看的付款时间”
源码里 _compute_payment_date() 的逻辑很直接:
- 如果有
discount_date,且今天还没超过这个日期,就把payment_date设成discount_date; - 否则就设成
date_maturity。
这个字段名字非常容易让人误解。
它不是“已经付款的日期”,也不是银行流水日期。
更准确地说,它表达的是:
当前阶段系统认定的“下一次关键付款节点”。
当折扣还有效时,关键节点不是最终到期,而是“再不付就拿不到折扣”的那个点; 当折扣过期后,关键节点才重新变成正常到期日。
三、为什么 Odoo 要这样设计
因为在业务上,提前付款折扣不是纯营销文案,而是一个真实的结算窗口。
对企业来说,下面这两句话语义完全不同:
- 4 月 30 日到期;
- 4 月 10 日前付款可按折扣金额结清,4 月 30 日前全额到期。
如果系统只保存一个 due date,它就无法区分:
- 现在该提醒客户“折扣快失效了”;
- 还是该提醒客户“已经正式逾期”。
所以 Odoo 用 payment_date 把这两个阶段压成一个“当前最相关日期”,方便搜索、催款和列表展示。
四、这会怎么影响催款与跟进语义
很多人觉得 follow-up 列表里日期“怪怪的”,根源就在这里。
因为在折扣仍有效时,系统认为你眼前最该关注的,是:
- 能不能在折扣窗口内回款;
- 而不是最终 full amount maturity。
所以如果你拿传统应收思路去理解,会觉得:
- 这张单明明月底才到期;
- 为什么系统现在就开始把 10 号当成关键日期?
答案是:
Odoo 并不是在说“正式逾期了”,而是在说“当前最值得行动的付款节点到了”。
五、为什么这和折扣金额字段绑在一起
discount_balance 和 discount_amount_currency 的存在说明,Odoo 不只是切换日期,还同时切换“如果现在付款,系统认为什么金额算结清”。
也就是说:
discount_date决定时间窗口;discount_balance/discount_amount_currency决定窗口内结清金额;payment_date决定当前列表和搜索优先看哪个时间点。
这三者是一组,不是孤立字段。
六、为什么它不是对账日期
再强调一次,payment_date 不是:
- 银行到账日
- payment register 的记账日
- reconciliation 完成日
它只是一个面向应收应付管理的“下一付款节点”字段。
所以如果你把它和真实支付事件混为一谈,就会得到很多错误解释。
七、排查“日期不对”时该怎么想
如果你看到某条应收行的 payment date 不是 maturity,请先问:
- 这张单的 payment term 是否启用了
early_discount; - 当前日期是否仍早于
discount_date; - 这条行是否因此把“折扣截止日”当作当前优先节点;
- 列表、搜索或 follow-up 看的到底是
payment_date还是date_maturity。
这样看,逻辑就会顺很多。
一句话记忆
在 Odoo 里,
date_maturity是最终到期语义,discount_date是折扣窗口边界,而payment_date是系统当前优先关注的下一付款节点。
DISCUSSION
评论区