先说结论
很多团队想给项目流程加短信通知时,第一反应是:
- 任务到了某列
- 系统就给客户发条短信
听起来没问题,但 /home/ubuntu/odoo-temp/addons/project_sms/models/project_task.py 和 project_task_type.py 的实现其实比这个克制得多。
真正触发短信,至少要同时满足:
- 任务有
partner_id - 当前任务阶段
stage_id绑定了sms_template_id - 该任务不是模板任务
is_template = False
而且触发点也只有两个:
- 任务创建后
- 任务写入并发生
stage_id变化后
这说明 Odoo 的设计重点不是“尽量多发”,而是:
只有在具备明确客户对象和明确阶段模板时,才做可解释的自动通知。
第一层:为什么“有客户”是硬前提
短信和内部 chatter 不一样。
chatter 可以是内部协作噪音,短信不行。短信一旦出去,就是外部触达。
所以 project_sms 在 _send_sms() 里第一件事就是看:
task.partner_id是否存在
没有客户对象,就不发。
这背后的意义是:
- 阶段变化很多
- 但不是每个阶段变化都值得外发通知
- 只有和某个明确客户主体绑定时,才有业务解释
这是一道很重要的刹车。
否则你会很快把内部项目流转误发成客户骚扰。
第二层:为什么模板写在任务阶段,而不是项目上
project_sms/models/project_task_type.py 给 project.task.type 扩展了:
sms_template_id
这意味着短信规则不是“项目只要开短信就全发”,而是:
- 某个阶段可以发
- 某个阶段不发
- 发什么内容由阶段模板决定
这个设计比项目级总开关精细得多。
因为真实项目里,客户真正关心的往往是少数关键节点:
- 已受理
- 已完成
- 待客户确认
- 计划变更
而不是每一次内部挪列。
把模板挂在阶段上,本质上是在说:
- 不是所有状态变化都值得通知
- 只有有业务意义的阶段,才应该外发
第三层:为什么模板任务不能发
实现里还有一个很关键的判断:
not task.is_template
也就是说,模板任务即使阶段上有 SMS 模板,也不会发短信。
这个限制非常必要。
因为模板任务的本质是:
- 未来项目的蓝本
- 结构设计资产
- 不是正在对外执行的真实任务
如果模板任务也能发短信,就会产生非常离谱的问题:
- 配模板时误触发通知
- 复制模板项目时外部收到测试短信
- 培训环境里把客户轰炸一遍
所以 Odoo 在这里做了正确的防呆:
- 模板是模板
- 真实任务才是通知对象
第四层:为什么创建任务也可能发短信
不少人只盯着 write(stage_id),但这里在 create() 后也会 _send_sms()。
这意味着:
- 如果任务一创建就落在某个带 SMS 模板的阶段
- 并且已有 partner
- 也可能立即发送短信
所以排错时别只查“谁拖动了看板”,还要查:
- 创建默认阶段是什么
- 初始化值有没有直接命中带模板的阶段
- 是否在导入 / 批量建任务时顺带触发了通知
这一步特别适合做上线前防呆。
第五层:实施中最常见的误区
误区一:把短信当阶段日志
短信不是状态日志。它是外部触达,应该极少而准。
误区二:阶段模板写得像内部术语
客户看不懂“已进入 UAT”“已转 L2”“已完成内部验收”。
既然阶段短信是外发,就该用客户语言写。
误区三:忽略创建时触发
很多“为什么刚建任务客户就收到短信”的事故,根因都在这里。
误区四:模板项目也拿来测试通知
虽然系统已经做了 is_template 保护,但实施时仍要分清真实项目与模板项目的测试边界。
推荐的配置原则
如果你真要用项目阶段短信,我建议只保留极少数节点:
- 新建已受理
- 已完成待确认
- 关键延期或变更通知
并且每条模板都要回答三个问题:
- 这条短信对客户真的有用吗?
- 客户读完后知道下一步该做什么吗?
- 如果这条短信一天发很多次,会不会变成骚扰?
只要第三个问题让你犹豫,这条短信大概率就不该自动发。
最后一句
project_sms 的实现之所以好,不是因为它能发短信,而是因为它知道什么时候不该发。
- 没客户,不发
- 没阶段模板,不发
- 是模板任务,不发
- 只有真正的阶段事件,才发
自动化最难的从来不是触发,而是克制。Odoo 在这件事上是清醒的。
DISCUSSION
评论区