人力资源

Odoo 招聘问卷为什么不是发封邮件那么简单:partner 补建、answer 复用与 applicant 消息留痕讲透

很多团队把招聘问卷理解成“给候选人发一个 survey 链接”。Odoo 的 hr_recruitment_survey 源码处理得更细:发送前要先补齐 partner、已有答卷可能复用或重发、问卷动作和邮件正文还要回写到 applicant chatter,打印时也优先挑最近已完成的那份答卷。

人力资源
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 4 阅读

先说结论

Odoo 的招聘问卷,不是“发封邮件让候选人填一下”。

源码真正处理的是一条小而完整的业务链:

  1. 候选人如果还没有 partner_id,先补一个联系人
  2. 同一岗位问卷如果已经有答卷,决定是打印现有结果还是重发邀请
  3. 发送动作不只发邮件,还会把操作写进 applicant chatter
  4. 问卷和候选人的关系不是临时 URL,而是 response_ids 这条结构化连接

所以它管理的不是“邮件动作”,而是候选人与问卷答题过程之间的可持续关系


为什么 action_send_survey() 第一步是补 partner_id

hr_recruitment_survey.models.hr_applicant 里,action_send_survey() 先检查:

  • applicant 有没有 partner_id

如果没有,它不会直接硬发,而是:

  • 要求至少有 partner_name
  • 再用姓名、邮箱、电话创建一个 res.partner

这一步特别有代表性。

因为 Odoo 并不把候选人问卷视为“一次匿名链接投递”。它更想把这件事绑定到可通信、可跟踪、可在后续复用的联系人对象上。

没有 partner_id,很多后续能力都会变弱:

  • 邀请目标不稳定
  • 历史答卷不好归集
  • 邮件与 chatter 之间难以形成完整记录

所以先补 partner,不是为了表单好看,而是在给整条招聘沟通链打地基。


response_ids 说明问卷不是外部附件,而是 applicant 的一部分

模型上,hr.applicant 被扩展了:

  • survey_id:来自 job_id.survey_id
  • response_ids:候选人的问卷答卷集合

这就决定了一个很重要的事实:

候选人的问卷不是独立漂浮在 survey 模块里,而是被挂回招聘对象本身。

这意味着招聘负责人以后看 applicant,不只是看邮件、阶段、面试,还能看:

  • 这人是否被发过问卷
  • 发的是哪一份
  • 回了几次
  • 哪一份已完成

换句话说,Odoo 在这里做的是把“测评”变成招聘对象的延伸事实,而不是一个外链工具。


为什么打印问卷优先取“最近完成”的答卷

action_print_survey() 的逻辑非常值得注意。

它会先:

  • response_ids 里筛出当前 survey_id 相关答卷
  • create_date 倒序排序

然后分三种情况:

  1. 没答卷:打印空白 survey form
  2. 有已完成答卷:打印最近完成的那份
  3. 没完成但已有答卷:打印最近一份进行中的答卷

这背后的业务选择很清楚:

打印不是为了展示“曾经发过几次”,而是优先拿出最可用、最接近最终结果的那一份。

这和很多系统单纯“总是打印最新记录”不一样。Odoo 在这里把“已完成”看成比“最新创建”更重要的优先级。


_get_done_partners_emails():为什么重发不是重新发给所有人

在 survey invite 向导扩展里,_get_done_partners_emails() 会检查:

  • applicant 在当前 survey 下是否已有 response_ids
  • 如果存在,并且 existing_mode == 'resend'
  • 就把 applicant 的 partner 归入 done/resend 的逻辑里

这说明 Odoo 对“重复发送”并不是完全放开。

它知道招聘问卷里最常见的几种情况:

  • 候选人已经答过了
  • 候选人答到一半
  • HR 想催一次或补发一次

所以 resend 不只是“再点一遍发送按钮”,而是在已有 answer 语义上继续推进,而不是把历史状态抹掉重来。


action_invite():真正建立的是 applicant 与 answer 的绑定

action_invite() 的关键,不在于发邮件,而在于它会先判断:

  • applicant 在当前 survey 下有没有答卷

如果没有,就通过 survey._create_answer(...) 创建 answer,并把结果写回:

  • applicant.response_ids

这一步很关键。

因为如果系统只是临时发一个 survey 链接,后面 applicant 记录和答卷之间就会很松散;而现在 Odoo 先把 answer 建出来再发,意味着:

  • 这份问卷从一开始就是“属于这个 applicant 的答卷”
  • 后续邮件、查看、打印、重发,都围着同一条业务主线在走

所以问卷邀请的本质不是 email send,而是先创建可追踪答卷,再把入口发出去


为什么发完邮件还要 message_post()

向导里还有两处 chatter 留痕:

1)action_invite() 里记录“问卷已发给谁”

它会生成带 HTML link 的提示,回写到 applicant chatter。

2)_send_mail() 里把实际邮件正文也 message_post() 到 applicant

也就是说,Odoo 不是只关心“邮件是否被 SMTP 发走”,而是要让招聘负责人在 applicant 页面里看到:

  • 发过哪份问卷
  • 发给了谁
  • 邮件大致内容是什么

这对团队协作非常重要。因为招聘流程不一定由一个人跟到底,后续接手的人如果只能去邮件系统查历史,会非常痛苦。

而 chatter 留痕把问卷沟通纳入了招聘主记录。


为什么这套设计比“Survey + 链接”更稳

很多公司会说:

  • Survey 模块本来就能发链接
  • 招聘模块本来就有 applicant
  • 为什么还要多做一层集成?

答案就在源码里:

这层集成解决的是身份、历史和上下文一致性

没有它,你通常会遇到这几类问题:

  • 候选人邮箱变动后,旧答卷不好追
  • 同一个 applicant 的问卷和招聘沟通割裂
  • 招聘负责人看不到是否已经发过/回过
  • 打印结果时,不知道该拿空白卷、草稿还是最终卷

Odoo 在这里补的不是一个按钮,而是一整套问卷在招聘流程中的归档方式。


实施时最容易踩的坑

误区一:把 partner 看成可有可无

如果你跳过 partner,只靠邮箱字段发问卷,后面答卷和 applicant 的关系会弱很多。

误区二:重发等于重建答卷

源码里不是这么干的。已有答卷时,系统会尽量延续已有 answer 语义。

误区三:邮件发出就算完成

不够。message_post() 才让团队后续看得懂这件事发生过什么。

误区四:打印总是取最新记录

Odoo 更看重“最近已完成”的答卷,因为那才最接近可评审结果。


我会怎么跟招聘团队解释

如果招聘负责人问:“这不就是给候选人发个表单吗?”

我会说:

不是。Odoo 在做的是把问卷答题过程挂回 applicant 本身,让这份测评以后还能被查、被重发、被打印、被团队接力理解。


一句话记忆

Odoo 招聘问卷不是发链接,而是先把 applicant、partner、answer 和 chatter 串起来,再让邮件成为其中一个动作。

DISCUSSION

评论区

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