结论先行
很多公司把“录用流程”理解成招聘模块里改一次阶段,再发一份 PDF。企业版的实现更谨慎:候选人还是 hr.applicant 时,签署对象是签署请求;真正入职后,签署历史才挂到 hr.employee / hr.version。这不是绕,而是典型的对象迁移链。
第一层:入口或表面动作
hr_recruitment_sign 在 applicant 上先加了 sign_request_count 和 open_applicant_sign_requests()。它并不把签署请求硬写在 applicant 上,而是通过 sign.request.item 反查 partner,说明招聘侧承认:签署记录的事实所有者是签署模块,招聘只是消费视图。更关键的是 _get_employee_create_vals(),当 applicant 真正转员工时,它会把该候选人 partner 对应的 sign_request_ids 一并塞进 employee 创建值。这一步就是 从“候选人签过什么”迁移到“员工继承哪些签署资产”。
第二层:真正的业务护栏
发起签署时,hr.recruitment.sign.document.wizard.validate_signature() 也不是简单套个模板。它先根据模板上有几个 role,决定是“只让 applicant 签”还是“applicant + 负责人双签”;随后批量创建 sign.request,复制附件、订阅抄送对象,并在 applicant 上发 chatter 留痕。换句话说,招聘模块负责收集上下文和业务语义,真正的签名动作与访问 token 仍然交给 sign 模块。
第三层:状态落点与边界
入职后的边界在 hr_sign 更明显。hr.version 上有 sign_request_ids 与 open_sign_requests(),并且 _unlink_if_sign_request_canceled() 禁止删除仍关联有效签署请求的合同版本。企业版这里其实是在说:签署完成的合同不只是招聘附件,而是 HR 主数据的一部分。所以合同版本能打开 sign 请求,但不能随意删掉,避免员工档案和签署证据脱节。
为什么这套设计更稳
这条链之所以稳,是因为三个对象各守一段边界:applicant 负责招聘上下文,sign.request 负责签署状态机与 token,employee/version 负责入职后的长期归档。如果你在 applicant 阶段就直接创建 employee,只为了让合同挂到员工上,后面 offer 被拒、重新签版、双签人变更都会很难收口。
实战启示
自定义时的建议也很直接:不要跳过 wizard 直接写 sign.request,否则 role 选择、responsible 双签、附件复制和 chatter 留痕都容易丢;也不要在员工创建后重新“复制一份”签署请求,应该像源码那样迁移引用。招聘到签署再到入职,真正要守住的不是按钮顺序,而是对象身份何时切换。
DISCUSSION
评论区