人力资源

Odoo 一个人换合同、换工时后,Work Entry 到底按哪份算:多合同、多版本与生成边界全拆开讲

很多团队以为 work entry 只要按工资期批量生成就行。但 Odoo 在多合同和多版本场景下,实际上会按合同起止、版本区间、排班切换以及请假覆盖边界把结果拆开。理解这件事,才能看懂为什么同一个月会被切成两套工时。

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

先说结论

在 Odoo 里,Work Entry 从来不是“这个员工这个月有一条工时总账”那么粗。

源码真正做的是:

  • 先找出员工在这个区间内有效的 contract version
  • 再按每个 version 的合同日期、版本日期、日历/排班去生成工时
  • 如果期间有请假,再让 leave 去覆盖或替换对应 work entries
  • 如果月中换了合同、换了排班、从固定班变弹性班,系统就会把同一个工资期切成几段

所以很多人看到“同一个月为什么前半段 8 小时、后半段 3 小时”时,以为系统坏了。

其实这是合同时间线在工资工时里的自然投影


为什么 work entry 不是挂在 employee 上直接一把算完

hr.work.entry 看起来是员工级对象,但生成逻辑真正依赖的是 hr.version

这非常关键。

因为员工主对象只回答“这个人是谁”,而 work entry 需要回答:

  • 这一天适用哪份合同
  • 这一天适用哪个日历
  • 这一天是不是已经切进未来版本
  • 这一天的 work entry type 应该是什么

也就是说,work entry 需要时间切片后的雇佣事实,而不是一条静态员工档案。


contract_date_startcontract_date_enddate_version 的边界都要算进去

之前很多人只盯合同起止日期,但源码里 version 还同时有:

  • contract_date_start
  • contract_date_end
  • date_version

这些字段共同决定 version 的有效区间。

换句话说,不是“有合同就生效”,而是要同时满足:

  • 这份 version 已经到了它的版本生效时间
  • 它的合同日期区间覆盖当前生成窗口

因此月中调整合同,或者提前录入未来生效版本,都会影响 work entry 的切分方式。


多合同场景下,为什么同一个月天然会被拆成两半甚至更多

hr_work_entry 的测试里有非常典型的用例:

  • 9 月 1 日到 15 日:一个日历 / 一份合同
  • 9 月 16 日到 30 日:换成另一份日历 / 另一份合同

系统生成结果会明显分成两个区间:

  • 前半月按第一份 version 的日历时长生成
  • 后半月按第二份 version 的日历时长生成

而且这里不只是“合同名变了”,连每天生成几条、每条多少小时、周末算不算工作都可能不同。

测试甚至覆盖了这些切换:

  • flexible → standard
  • standard → flexible
  • fully flexible → standard
  • standard → fully flexible

说明 Odoo 真正在意的不是“有没有续约”,而是:

续约后适用的工作时间制度有没有变化。


为什么弹性班和标准班切换后,work entry 条数都会变

测试里很直观:

  • 标准班那一半,没有周末工时,常见是每天 8 小时
  • 弹性班或 fully flexible 那一半,可能周末也会生成,且时长可能按另一套逻辑,比如每天 3 小时

这代表 Work Entry 的职责并不是“忠实抄合同名称”,而是要把合同绑定的可结算工作时间结构落地。

所以续签合同时只改工资不改日历,和同时改日历,对 payroll side 的影响完全不同。


请假一旦跨合同边界,为什么更容易让人看不懂

hr_work_entry_holidays 的多合同测试也说明了一个关键点:

  • 请假发生在第二份合同期间
  • 生成 work entries 后,只过滤第二份 contract 对应的记录
  • 最终工作工时和请假工时分别在这份合同下统计

这说明 Leave 并不会悬空地覆盖“员工整个月”,而是覆盖它所落入的有效 version 区间

所以如果一个请假刚好跨:

  • 合同结束日
  • 新合同开始日
  • version 切换日

那最终结果就可能出现:

  • 一部分由旧合同语义解释
  • 一部分由新合同语义解释
  • leave work entry 只覆盖相应区间的原工作条目

这也是为什么多合同请假问题永远比单合同复杂。


为什么很多改动后都需要 regenerate,而不是手补几条工时

Work Entry 的生成逻辑跟 leave 覆盖逻辑一样,都高度依赖时间区间和 version 边界。

这意味着一旦你改了这些东西:

  • 合同起止
  • 版本日期
  • 资源日历
  • 请假批准状态

旧结果很可能已经不再可信。

源码之所以大量采用重建思路,是因为这里最重要的是区间一致性

手工补一条,看似改对了当日,但常常会把:

  • 相邻区间
  • 覆盖关系
  • payroll validation 前后的状态

一起弄乱。

所以这类问题我通常建议:

  1. 先定位当前版本边界
  2. 再看合同与日历变化
  3. 最后统一 regenerate

而不是直接在 work entry 表面修一条。


这对 payroll/HR 实施最大的提醒是什么

1)“续签合同”不是纯 HR 动作

只要合同带着日历和版本边界进入系统,它就会直接影响 payroll 输入层。

2)未来生效版本不是无害草稿

即使今天还没生效,它也已经在影响后续区间的切分逻辑。

3)请假问题不能只看 leave 表

尤其多合同场景,必须一起看 version、contract window 和生成后的 work entries。

4)先问“哪份 version 负责这一天”

这是排查所有 work entry 边界问题最快的入口。


我会给业务方的一句话解释

如果业务方问:“为什么同一个工资月会被系统拆成两套工时?”

我会说:

因为 Odoo 发薪前看的不是‘这个月有没有上班’,而是‘这个月每一天到底归哪份合同版本负责’。

只要这个问题的答案发生变化,work entry 就一定会分段。


一句话记忆

Odoo 的 work entry 是按合同版本时间线生成的;月中换合同、换日历、换请假状态,都会把同一工资期切成不同的责任区间。

DISCUSSION

评论区

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