先说结论
在 Odoo 里,会议邀请不是一个简单的 partner 列表。
calendar.attendee 之所以存在,是因为系统需要单独维护:
- 参会人的响应状态
- 每个参会人的邀请 token
- 邮件时区与默认收件逻辑
- 邀请、取消、提醒等邮件发送动作
所以最短结论是:
partner_ids只表示“谁在名单上”,calendar.attendee才表示“这个人如何参与这场会议”。
为什么 attendee 要有自己的 access token
calendar.attendee 每条记录默认生成一个 access_token。
这说明 Odoo 不是把所有参会人都绑在同一个笼统入口上,而是给每位参会人准备可识别、可追踪的邀请上下文。
这样做的价值在于:
- 外部参会人可以通过邮件入口识别自己对应的邀请
- 响应动作能回写到正确 attendee
- 不同人之间不会共用一个脆弱链接身份
为什么 organizer 默认会被标成 accepted
create() 里,如果 attendee 对应当前用户且没有显式 state,就默认给 accepted。
这个细节很合理。
因为发起者通常不是“待确认参会”,而是天然的组织者。
如果组织者自己也被放在 needsAction,很多会议状态会显得很怪:
- 看起来连主持人都没接受
- 统计上永远有一个待确认
- 后续自动化也容易误判
为什么邮件默认值是围绕 attendee,而不是 event
_mail_template_default_values() 里,模板默认模型语境其实是 attendee:
email_from来自 event organizerlang来自 attendee 的 partner 语言use_default_to=True
这说明 Odoo 在邀请邮件上优先考虑的是:
- 同一场会议,不同人可以看到不同语言与时区
- 邮件对象是“某人收到某场邀请”
- 不是一封完全相同的 bulk mail 扔给所有人
这很符合会议协作的真实需求。
为什么发邀请时要给每个人单独渲染并附上 ICS
_notify_attendees() 做的事情很完整:
- 按 attendee 单独渲染 body / subject / sender
- 准备 ICS 文件
- 如果模板自带附件,还会复制出每位 attendee 自己的一份附件集
- 最后再批量发送 mail messages
这说明 Odoo 的日历邮件不是群发纯文本提醒,而是每个参会人一份可操作的邀请包。
ICS 在这里尤其重要。
因为它决定了:
- 外部日历能否正确识别邀请
- 对方是否能一键接受 / 拒绝 / 加入日历
- 这封邮件是不是“真正的会议邀请”,而不只是通知文本
RSVP 为什么要落回 chatter
do_accept()、do_decline() 会在 event 上 message_post()。
这说明 Odoo 不把 RSVP 当纯内部状态,而是把它视作协作事件的一部分。
好处是:
- 组织者在 chatter 中能看到接受 / 拒绝记录
- 后续审计和回看更清楚
- 会议协作不会变成黑箱字段变化
最容易踩的误区
误区一:只改 partner_ids,不管 attendee 语义
名单变了,不代表邀请状态、邮件上下文和 token 逻辑自动都对了。
误区二:把邀请邮件当普通 mass mail
会议邀请最重要的是可操作性,ICS 与 attendee 上下文比花哨正文更关键。
误区三:忽视 organizer 的状态默认值
很多“状态怎么不对”问题,往往就卡在组织者语义没想清楚。
排错顺序
如果用户说“为什么对方收不到正确邀请”或“RSVP 不回写”,建议查:
- 对应
calendar.attendee是否真实创建出来 - attendee 上是否有 email、mail_tz、access_token
- 邀请模板是否基于 attendee 模型正确渲染
- ICS 附件是否正常生成并附加
- accept / decline 动作是否真的落回 event chatter
一句话记忆
Odoo 把参会人单独建成
calendar.attendee,是因为会议协作需要的不是名单,而是每个人各自的邀请上下文。
DISCUSSION
评论区