先说结论
Odoo CRM 的 Schedule Meeting,不是简单打开 calendar.event 的创建窗口。
源码真正做的是:
- 把当前商机上下文完整注入日历动作
- 根据已有会议分布,决定更适合周视图还是月视图
- 在 Reschedule 时,优先把你带回最相关的那场会
- 当会议真正创建后,再回写 CRM 跟进日志
所以它看起来像“系统懂你要安排什么”, 其实是 CRM 和 Calendar 在源码里提前对齐了业务语义。
一、为什么从商机点 Schedule Meeting,会自动带出客户和标题
action_schedule_meeting() 会往 action context 里塞很多默认值:
default_opportunity_iddefault_partner_iddefault_partner_idsdefault_team_iddefault_name
翻译成界面体验就是:
- 会议默认绑到当前 opportunity
- 联系人默认带上当前客户
- 参会人列表会把当前用户和客户一起带上
- team 也保留下来
- 会议标题直接继承商机名
所以它不是“从 CRM 跳到 Calendar”, 而是“带着 CRM 语义去打开 Calendar”。
二、smart calendar 为什么有时打开周视图,有时打开月视图
如果当前记录是 opportunity,
并且 smart_calendar=True,
Odoo 会调用 _get_opportunity_meeting_view_parameters()。
这个方法会先把与该机会关联的会议读出来, 再做一层判断:
- 没有会议:周视图,定位到现在
- 只有一场相关会议:周视图,定位到这场会开始日期
- 多场会议都落在同一周:周视图,方便连续排期
- 跨周分布:月视图,更适合全局看安排
这背后的产品思路很清楚:
安排会议时,用户需要的不是固定视图,而是“最能看清这笔机会时间结构”的视图。
三、为什么源码要特别处理 allday 和时区
_get_opportunity_meeting_view_parameters() 里有一段很容易被忽略:
- 普通会议时间会换算到用户时区
- 但 allday event 不按用户时区回算,而是直接用原始日期范围
原因是全天事件如果再做一次时区换算, 很容易看起来“前一天开始”或“后一天结束”, 把本来很直观的排期弄歪。
所以 Odoo 在这里的原则是:
- 精确时刻型会议 用用户时区理解
- 全天占位型会议 用自然日理解
这很符合销售日程的真实使用场景。
四、为什么系统优先看“未结束会议”而不是全部历史会议
源码不会把所有历史会议一股脑拿来决定初始视图。 它先筛:
- 还没结束的会议
- 如果没有,再退回全部会议
这说明 Odoo 认为用户打开“安排会议”时, 主要是在处理 接下来的节奏, 而不是回顾已经结束的拜访。
所以页面默认锚点应该尽量贴近未来, 而不是被一堆过去的会议拖偏。
五、Reschedule 为什么总爱跳到“我自己的那场”
action_reschedule_meeting() 在关闭 smart calendar 之后,
会额外找:
- 当前 opportunity 上
- 属于当前用户的第一条 activity
- 如果它挂着
calendar_event_id - 就把
initial_date锚到那场会议开始时间
也就是说,Reschedule 的思路不是“重开整张日历给你自己找”, 而是:
优先把你带回你现在最可能要改的那一场。
这对销售来说非常合理。 因为 reschedule 通常不是重新规划整个账户, 而是改动一场具体会。
六、为什么从 activity 创建会议,也会继承同样的 CRM 上下文
mail.activity.action_create_calendar_event() 在 CRM 场景下会再补一层:
- 如果这条 activity 对应的 calendar event 连着 opportunity
- 就重用
opportunity.action_schedule_meeting(smart_calendar=False)的 context - 再把
initial_date锚到现有会议开始时间
这意味着:
- 从商机表单点 Schedule Meeting
- 从 activity 点 Create Event
两条路虽然入口不同, 但 Odoo 在尽量维持同一套会议上下文语义。
这就是为什么用户会感觉 CRM、活动和日历三者衔接得比较自然。
七、会议创建后为什么会自动写回 CRM
calendar.event.create() 在 CRM 扩展里还做了一件事:
- 如果事件绑着
opportunity_id - 且不是从 activity 反推出来的现成事件
- 就调用
event.opportunity_id.log_meeting(event)
这让会议不只是日历对象, 它也会变成 CRM 跟进历史的一部分。
换句话说,Odoo 不把 meeting 当孤立动作, 而是当作机会推进链路上的正式信号。
一句话记忆法
Odoo CRM 的会议安排不是“打开日历”,而是“带着商机上下文打开最相关的时间视角,并把会议再写回销售推进链路”。
DISCUSSION
评论区