企业 薪资模拟

Odoo 企业版薪资模拟为什么不是“先算个税前工资”而已:simulation offer、payslip 试算与临时记录回收讲透

基于 hr_contract_salary_payroll 源码,讲清薪资模拟如何借助 simulation offer、savepoint 下的 payslip 试算与定时清理,给候选人稳定预览。

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

很多团队以为薪资模拟就是“输入预算,换算一个 gross/net 数字”。但 enterprise/hr_contract_salary_payroll/models/hr_contract_salary_offer.pyhr_version.py 展示的其实是一套受控试算流程:前台改预算,后台临时构造 version,再在 savepoint 里生成模拟 payslip,最后把试算记录当作一次性工件回收掉。

一、simulation offer 是独立对象,不是普通 offer 上多几个字段

default_get() 会在 default_is_simulation_offer 场景下单独生成 access_token,说明模拟 offer 从一开始就被当成可分享、可访问、但应与正式 offer 区分的对象。_onchange_simulation_employee_id() 则把当前版本的工资、工时日历、薪资结构带进来,确保模拟不是凭空计算,而是建立在真实员工版本之上。

这一步解决的是“模拟口径漂移”问题。只要结构类型、resource calendar 和当前 version 不一致,前台看到的成本就会与后续正式合同脱节。

二、真正的核心不是工资换算,而是 savepoint 下的 payslip 试算

_compute_salary()flush_all(),再在 savepoint 中调用 version._generate_salary_simulation_payslip()。后者在 hr_version.py 里明确写了注释:不要在 rollback savepoint 之外调用。这句警告非常关键,因为 Odoo 这里不是为了“创建一张真的工资单”,而是为了借 payroll 规则引擎跑出 BASIC、NET、benefits、雇主成本这些结果。

源码甚至会处理小时工:_generate_salary_simulation_payslip() 在 hourly wage 合同下手工生成 worked_days_line_ids。这说明模拟结果不是静态公式,而是尽量复用正式薪资规则,连工时口径都要兼容。

三、前台预算变化,其实在驱动多层回算

_compute_monthly_wage() 会根据 final_yearly_costs 通过 version 的 _get_gross_from_employer_costs() 反推工资;_compute_salary() 再把 gross、net、monthly/yearly benefits、yearly/monthly employer cost 一起整理出来。也就是说,候选人改一个预算值,后台同时在做:

  1. 雇主成本到 gross 的换算;
  2. gross 到 payslip line 的工资规则展开;
  3. 福利成本的月度/年度聚合;
  4. 全职/非全职状态判断。

所以它不是“显示一个计算器结果”,而是“在不真正发工资的前提下,借 payroll 引擎做一次受控演练”。

四、临时记录如果不回收,模拟系统迟早会脏

action_cron_remove_simulation_offers() 会清理超过一个月的 simulation offers。这说明官方默认就承认:模拟记录会堆积,而且它们并不适合作为长期业务凭证保存。

实战里最容易忽略这点。很多企业把模拟当作沟通草稿,几轮谈薪下来会留下大量历史试算。如果不清理,后面用户会分不清哪些是正式 offer、哪些只是谈判过程中的临时版本。

五、落地时最需要防的不是“算错”,而是“口径不一致”

  • 结构类型必须跟真实 payroll 结构一致:否则 BASIC/NET 只是在算另一套规则。
  • resource calendar 要跟合同版本一致:尤其小时工和部分工时,工时口径直接影响模拟 payslip。
  • 不要把模拟结果当正式承诺:它依赖当前 payroll 规则和上下文,规则一变结果也会变。
  • 定期清理历史模拟:让 HR、候选人和经理看到的都是仍有业务价值的版本。

新手误区

  • 误以为 gross/net 可以直接用前端公式算完,不必进 payroll。
  • 误以为 simulation offer 和正式 offer 只是状态不同。
  • 误以为小时工场景下模拟不需要 worked days。
  • 误以为模拟记录可以无限保留,不会影响日常操作。

主要源码参考

  • enterprise/hr_contract_salary_payroll/models/hr_contract_salary_offer.py
  • enterprise/hr_contract_salary_payroll/models/hr_version.py

DISCUSSION

评论区

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