协同办公

Odoo 活动为什么总显示 overdue / today / planned:`mail.activity.mixin` 的状态聚合与排错顺序讲透

很多人以为活动状态只是把截止日期和今天比一下。源码里真正起作用的是 `mail.activity.mixin` 对整条记录上所有活动做聚合,并且按用户时区换算当天边界。看懂这层机制,才能解释为什么同一条记录会突然变红、为什么有人看到 overdue 有人却看到 today。

协同办公
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 里一条记录显示为 overduetoday 还是 planned,并不是“取最新那条活动看看日期”这么简单。

更准确地说:

记录上的活动状态,是 mail.activity.mixin 对这条记录所有有效活动做聚合后得到的结果;只要其中有一条逾期,整条记录就会被判成 overdue。

这也是为什么销售、项目、审批、CRM 这些模块里,只要挂了活动,卡片颜色和提示就会跟着变。

如果你排查时只盯着某一条活动,很容易得出错误结论。


一、为什么它不是“单条活动状态”

addons/mail/models/mail_activity_mixin.py 里,activity_state 是一个计算字段。

源码逻辑很直接:

  • 如果活动集合里出现 overdue,整条记录就是 overdue
  • 否则只要出现 today,整条记录就是 today
  • 再否则只要出现 planned,整条记录就是 planned
  • 一条活动都没有,才是空值

这意味着它不是“下一条活动状态”,而是风险优先的聚合状态

所以一个常见误区是:

  • 用户看到表单里最上面那条活动是明天到期
  • 就以为记录应该显示 planned
  • 但实际上同一条记录下面可能还挂着一条昨天截止、尚未完成的活动

这时系统显示 overdue 才是对的。


二、为什么同一天里,不同用户看到的状态可能不同

很多团队第一次遇到这个现象时会怀疑缓存或者前端 bug。

其实源码已经写明了:

_search_activity_state() 和相关 SQL 计算会用 mail_activity.user_tz,如果没有,再回退到 utc

这背后的含义是:

  • 活动不是用“服务器今天”粗暴比较
  • 而是尽量按负责人自己的时区定义“今天是否已经过去”

因此:

  • 上海用户看到还是今天
  • 纽约用户那里可能已经是昨天或明天
  • 同一批活动在不同人界面上可能会落到不同状态边界

这不是不一致,而是系统在尽量贴近责任人的本地工作日。


三、为什么一条记录会突然从绿色变成红色

最常见的触发点有三个:

1)旧活动没关闭

业务已经推进到下一步,但旧活动没有 done,于是系统仍把它算在聚合里。

2)自动化又补出一条活动

某些模块、自动化规则、活动计划会在写入或完成动作后重新生成活动。用户以为“我明明处理过”,其实新活动已经补回来了。

3)时区边界跨天

昨天晚上的活动,在某个用户时区里已经跨过当天零点,于是状态自然从 today 变成 overdue


四、为什么搜索 activity_state 不能简单靠 ORM 拼域名

源码里 _search_activity_state() 没有走最朴素的 ORM 关系搜索,而是专门下了 SQL,把状态先映射成整数:

  • overdue = -1
  • today = 0
  • planned = 1

然后对每条业务记录取最小值。

为什么这么设计?

因为这正好符合“风险优先”原则:

  • 只要有逾期,最小值就是 -1
  • 没有逾期但有今天到期,就是 0
  • 都在未来,才是 1

这也说明一个重要边界:

前台看到的是聚合后的业务信号,不是单条 mail.activity 行本身。

如果你只在 mail.activity 上看单条数据,很容易和列表页、看板页的颜色不一致。


五、activity_date_deadline 为什么也容易让人误判

activity_date_deadline 通常会被理解成“下一条活动截止日期”。

从实现看,它取的是活动集合中的首条记录对应的截止日,而不是“最危险那条”或“最晚那条”统一口径。

所以两个字段不要混着理解:

  • activity_state:偏聚合风险信号
  • activity_date_deadline:偏界面展示和快速入口

如果你把后者当成前者的解释依据,经常会得到“怎么日期是明天但状态是 overdue”的错觉。


六、实战里最容易踩的三个误区

误区 1:只看当前打开的那条活动

实际要看整条记录所有未关闭活动

误区 2:以服务器时间判断 overdue

实际要看负责人时区、用户上下文和活动持有人。

误区 3:把活动状态当作纯前端颜色

实际上它是很多列表、看板、统计入口共用的后端业务字段。


七、排错顺序建议

遇到“为什么这条记录一直红着不变”时,我建议按这个顺序查:

  1. 先看该记录是否存在多条未完成活动
  2. 再看这些活动分别属于谁,负责人时区是什么
  3. 确认是否有自动化、活动计划或业务动作会重新补活动
  4. 区分你在看的是 activity_state 还是 activity_date_deadline
  5. 如果是搜索结果不对,再检查是否命中了 _search_activity_state() 的聚合逻辑

这个顺序比一上来怀疑缓存更靠谱。


八、对实施和开发的实际启发

这套设计说明 Odoo 对活动的定位不是“便签”,而是业务推进的风险灯

所以在做二开时,最好遵守这两个原则:

  • 不要随便制造隐形活动,否则看板颜色会失真
  • 不要在用户不知情时保留大量过期活动,否则所有记录都会长期泛红

如果团队想让活动真正变成协作工具,而不是噪音来源,关键不是多建几种活动类型,而是把“谁负责、什么时候关、是否自动续下一个动作”设计清楚。


最后一句

很多人把活动状态当成 UI 小细节,但源码给出的答案很清楚:

它本质上是 Odoo 用来表达“这条业务记录当前协作压力有多大”的聚合信号。

一旦从这个角度理解,很多看似古怪的颜色变化、搜索结果和跨时区现象,都会变得顺理成章。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。