先说结论
Odoo 的 Time Off 里,有一个非常容易被低估的问题:
哪些“没在上班”的时间,仍然应该被当成会继续累计年假的合格时间?
很多企业制度其实都在处理这件事,只是平时不会这样说。
例如:
- 培训算不算持续服务时间?
- 带薪病假会不会影响年假累计?
- 无薪假是不是应该中断累计?
- 按工时累计时,哪些时间应该算进 worked hours?
Odoo 的源码没有把这些问题藏在备注里,而是拆成了几层模型:
time_type = other | leaveelligible_for_accrual_rateis_based_on_worked_timefrequency = worked_hours
这说明系统真正管理的是:
请假类型如何影响“累计资格”和“累计基数”。
Worked Time 和 Absence 不是界面文案,而是两种完全不同的会计语义
在 hr.leave.type 里,time_type 只有两个值:
other:Worked Timeleave:Absence
这个命名很妙,因为它不是问“员工在不在岗”,而是在问:
这段时间在累计制度里,应该更像工作时间,还是更像缺勤时间?
例如培训、陪审、某些组织安排的外部活动,业务上虽然人不在工位,但可能仍然算持续服务。
所以 Odoo 没有粗暴地把所有 Time Off 都当缺勤。
有些 Time Off 在累计语义上更接近 Worked Time。
elligible_for_accrual_rate 为什么是核心字段
这个字段名字虽然写得有点别扭,但语义很清楚:
这种 time off type,会不会在累计计算里被当成“可计入累计比例”的时间?
源码里还有一个很重要的约束:
- 如果
time_type = other,那它必须是elligible_for_accrual_rate = True
也就是说,官方认为“Worked Time 类型的 time off”天然应该进入累计资格。
这很合理。
如果一个假别本来就是“工作时间性质”,那它不该再被拿来削减 accrual rate。
反过来,leave 类型就可以按政策决定:
- 某些 absence 仍然算入累计资格
- 某些 absence 不算
于是制度差异就被显式建模了。
is_based_on_worked_time 问的不是“有没有上班”,而是“累计基数按什么取”
hr.leave.accrual.plan 里的 is_based_on_worked_time 很容易被误解成“按考勤模块开没开”。
其实它在问:
这套累计计划,是按完整累计周期给额度,还是按实际合格工时比例给额度?
源码 help 甚至直接提醒:
- 如果按 worked time,通常会排除某些不应进入累计基数的 time off
accrued_gain_time = start时,is_based_on_worked_time会被强制关掉
这背后逻辑很强:
如果额度是在周期开始就先给你,系统就不可能同时说“我要根据这个周期里后来发生的实际工时再决定给多少”。
所以:
- 周期开始先给额度
- 按已发生工时比例累计
这两个语义在源码里是互斥的。
worked_hours 频率为什么不是普通 hourly 的别名
hr_holidays_attendance 给 hr.leave.accrual.level.frequency 增加了一个新值:
worked_hours
它不是普通 hourly 的换皮。
源码还专门限制:
- 如果
accrued_gain_time = start,就不能使用worked_hours
这说明 worked_hours 的语义是:
按已经发生且被认可的工时,持续累积。
而不是“系统时钟每小时给一点额度”。
这对按小时工、兼职、排班浮动岗位尤其重要。
你可以把它理解成:
hourly:按自然时间刻度运行worked_hours:按合格工作投入运行
两者看起来都和小时有关,但业务含义完全不同。
Odoo 是怎么扣除“不应计入累计”的时间的
从 hr.leave.allocation 的逻辑可以看出,系统在计算 worked-time-based accrual 时,会分别统计:
- 应计入累计比例的 time off
- 不应计入累计比例的 time off
- 实际 worked hours
然后再把这些量折进 accrual amount。
这就是为什么某些企业会出现这种业务现象:
- 培训一天,不影响年假累计
- 带薪陪产假,可能部分保留累计资格
- 无薪长假,则明显拉低累计结果
源码不是靠 HR 手工判断,而是靠 leave type 的时间语义来区分。
为什么“带薪”不一定等于“继续累计”
这是很多业务方的直觉误区。
他们会说:
- 这类假是 paid
- 那当然应该继续累计年假
但源码里的判断维度并不是“付不付薪”本身,而是:
time_typeelligible_for_accrual_rate- accrual plan 是否
is_based_on_worked_time
也就是说,发薪语义和累计语义不是同一层规则。
某种假可以是 paid,但在 accrual policy 上仍可能被部分排除; 某种组织性活动虽然不是常规出勤,却可能作为 Worked Time 保留累计资格。
这是建模上非常成熟的一点:
工资政策、出勤政策、年假累计政策被拆开处理,而不是绑死。
这对实施最重要的启发是什么
1)不要只问“这是什么假”
更该问的是:
- 它在累计上属于 Worked Time 还是 Absence?
- 它是否 eligible for accrual rate?
2)不要把“按工时累计”理解成接 Attendance 秒数
源码关注的是“合格工时”,不是裸打卡秒数。
3)不要用 paid/unpaid 代替全部政策判断
paid/unpaid 很重要,但不足以表达 accrual eligibility。
4)制度不清时,先画资格矩阵
我通常会先让 HR 列一个表:
- 假别名称
- 是否视为 Worked Time
- 是否影响 accrual rate
- 是否参与 worked-hours-based accumulation
这个表一出来,Odoo 配置思路就会清晰很多。
我会怎么总结给业务方听
如果业务方问:“为什么有些假不影响年假累计,有些却会影响?”
我会说:
因为 Odoo 不是按‘有没有请假’来算累计,而是按‘这段时间在制度上算不算合格服务时间’来算。
这才是源码真正表达的业务语言。
一句话记忆
Odoo 通过 Worked Time / Absence、accrual eligibility 和 worked-hours-based accrual,把“哪些非出勤时间仍算合格服务时间”这件事显式建模了。
DISCUSSION
评论区