先说结论
Odoo 里的“从活动创建会议”不是前端 convenience,而是 mail.activity 与 calendar.event 的一条正式集成链。
活动不是丢给日历一个标题就结束;它会把业务记录语境、负责人、说明和原活动关联一起带进会议,并在后续改期时继续保持同步。
这也是为什么活动、会议、业务记录三者经常能在界面上彼此跳转而不散架。
一、创建会议时到底带了什么
addons/calendar/models/mail_activity.py 的 action_create_calendar_event() 很值得看。
它往日历动作里塞了很多默认值:
- 原活动类型
- 原业务模型和记录 ID
- 记录名称
- 活动说明
note - 参与人默认取负责人 partner
- 原活动 ID
- 初始日期取活动截止日
这说明它不是“创建一条新日历记录”,而是带着原业务上下文去开会。
二、为什么改活动截止日,会议时间也会跟着动
很多人觉得这很神奇,或者很烦。
源码里 write() 明确处理了这种同步:
- 如果活动已经绑定
calendar_event_id - 又有人修改了
date_deadline - 系统会尝试回推会议开始时间
但这里有个关键边界:
- 全天事件按日期差直接平移
- 非全天事件要结合用户时区,判断“日”是否改变
所以这不是简单把 datetime 全量覆盖,而是尽量保留会议原本时刻语义,只调整所属日期。
三、为什么这里必须关心时区
如果你粗暴地把活动截止日从 Date 映射到会议 Datetime,最容易出的问题就是:
- 用户改的是 3 月 20 日
- 会议却被推成前一天深夜或后一天凌晨
Odoo 在这里做的事,本质上是在防这种错位。
它先把旧开始时间转到用户时区,再算日期差,再回写新开始时间。这样做的目的不是完美,而是让“改日期”尽量保持人的直觉。
四、完成活动后的反馈为什么会进会议备注
_action_done() 也不是只把活动关掉。
如果这条活动绑了会议,反馈文本会被追加进 calendar.event.notes。
这点很像真实办公:
- 活动是待办入口
- 会议是协作容器
- 反馈则应该沉淀到会议上下文里
这意味着如果你把活动当作一次性按钮,可能会忽视它其实在给日历协作对象补历史。
五、最容易误解的边界
1)活动说明和会议备注不是同一字段
创建时会带过去,但后续不是“永远双向镜像”。
2)活动截止日也不是会议开始时间的简单别名
它们是桥接关系,不是同字段。
3)删活动不一定只影响活动
如果走 unlink_w_meeting(),关联会议也会一起删。
六、适合它的场景与不适合它的场景
适合:
- 售前跟进转会议
- 项目任务转评审会
- 人事流程转面谈/回访
不太适合:
- 只想记一个私人待办,但不需要形成正式会议上下文
- 频繁批量改日期且不希望影响已排好的会议时段
七、排错顺序
当用户说“我改了活动日期,会议怎么变了”时,按这个顺序查:
- 这条活动是否已绑定
calendar_event_id - 会议是不是全天事件
- 当前用户时区是什么
- 这次修改的是活动截止日还是直接改了会议开始时间
- 是否在完成活动时写入了反馈,导致会议备注看起来“被改过”
最后一句
Odoo 把活动转会议的真正目的,不是少点几次按钮,而是让待办、日历和业务记录共享同一个协作上下文。
理解这一点,你就知道这条桥接为什么值得被认真设计。
DISCUSSION
评论区