如果把薪资套餐看成“候选人在网页上随手勾福利”,你会低估这个模块的约束力度。enterprise/hr_contract_salary/models/hr_contract_salary_offer.py、hr_version.py 和 hr_contract_salary_benefit.py 共同说明:Odoo 企业版真正管理的,是“哪些字段允许被候选人改、改了以后如何反映总成本、需要补哪些资料、最终签哪份模板”。它不是一张动态表单,而是一条被严格收口的报价链路。
一、入口先从 version 出发,不是从表单出发
hr_contract_salary_offer.py 里的 default_get() 会优先从 hr.version、hr.applicant 和当前合同上下文回填数据;_compute_offer_values_from_template() 再把 final_yearly_costs、岗位、部门、公司等核心值从合同模板带入。这意味着候选人看到的页面,并不是一个空白配置器,而是一个已经被岗位模板、公司和合同版本预先收紧的 offer。
业务上这很重要:企业希望候选人“选择”,但不希望候选人“发明一套新的薪酬结构”。如果岗位模板已经限定结构、公司和部门,前台体验才不会把后端工资结构冲散。
二、benefit 不是随便暴露字段,而是先过白名单/黑名单
hr_version.py 的 _get_benefit_fields() 会先按字段类型筛,再扣掉 _benefit_black_list() 里的敏感字段,例如魔法列、生成区间、签署时工资等;只有允许的字段才会被当作可配置 benefit。默认 _benefit_white_list() 为空,说明“允许开放”本来就是保守策略,而不是默认全开。
然后 _compute_final_yearly_costs() 会根据工资、结构类型和 benefit 对应字段重算 employer cost。也就是说,候选人每改一次选项,本质上都在触发一个“允许字段集合内的成本重估”。
新手最容易犯的错,是把自定义字段、Studio 字段甚至内部控制字段直接挂进套餐页,以为“前台看得到就能选”。源码的白名单/黑名单思路恰恰说明:套餐页应该只暴露能被解释、能被核算、能被审批的字段。
三、资料请求是 benefit 的一部分,不是签约后的补录
hr_contract_salary_benefit.py 的 _compute_requested_documents() 会把 benefit 绑定的 requested_documents_field_ids 汇总成字符串,并调用 _set_requested_documents_as_required() 去把对应个人信息项标成必填。也就是说,某些福利一旦被选中,候选人需要补哪些资料,是配置阶段就确定的。
这背后的设计非常务实:企业不是在 offer 签完后再发现“还缺车辆信息”“还缺保险材料”,而是在候选人选择福利时就同步抬高资料门槛。这样 HR 能把资料收集和薪资谈判放在同一流程里完成。
四、签署模板并不是附件,它与 signatory 配置一起组成终点
hr_contract_salary_offer.py 里有 _compute_sign_template_id()、_copy_contract_template_signatories()、_compute_sign_template_signatories_ids() 等逻辑,hr_version.py 也会计算签署模板和签署人数。说明薪资套餐不是孤立的薪酬试算器,而是通向签署请求的前置阶段。
因此,候选人前面选的 benefit、模板里预设的 signatory、岗位对应的合同模板,最终都会影响“发出去的是哪份 offer、谁需要签、签署是否需要半签状态”。这也是为什么 action_send_by_email() 和 _cron_update_state() 被放在同一模型上:发送、有效期、拒绝、过期,都是 offer 生命周期的一部分。
五、实战落地时最该盯的 4 个点
- 岗位模板先定边界:先在
hr.version和合同模板上定好结构、岗位、公司、签署模板,再谈套餐页。 - benefit 字段只暴露可核算项:能解释成本、能映射工资、能生成所需资料,才值得开放。
- 资料项跟着福利走:不要把文档收集放到签约后补洞,配置 benefit 时就绑好
requested_documents_field_ids。 - 把过期和拒绝当成正常状态:
action_refuse_offer()、_cron_update_state()说明 offer 失败是常态,不要只设计“成功签约”一条路径。
新手误区
- 误以为薪资套餐页面是“前端自由配置器”,忽略后端 version 的控制。
- 误以为所有工资相关字段都能开放给候选人修改。
- 误以为资料收集和签署是后续动作,不必在 offer 模型里考虑。
- 误以为发出邮件就算流程结束,没有处理过期、拒绝和二次沟通。
主要源码参考
enterprise/hr_contract_salary/models/hr_contract_salary_offer.pyenterprise/hr_contract_salary/models/hr_version.pyenterprise/hr_contract_salary/models/hr_contract_salary_benefit.py
DISCUSSION
评论区