先说结论
很多人配置工作日历时,脑子里只有一个问题:
- 周一到周五几点上班,几点下班?
但 Odoo 的 resource_calendar 源码告诉你,工作日历远不只是时间表。
它其实是很多 HR 计算的底座,至少会影响:
- 一周到底算几小时
- 一天平均算几小时
- 是否全职
- 请假按天还是按小时怎么换算
- 双周制排班怎么表达
- 弹性工时下“工作时段”到底如何理解
- 公司公共休假怎么传播到具体日历
所以如果把 calendar 只看成“上下班时间模板”,后面很多 HR 结果都会解释不清。
为什么 schedule_type 是关键,不只是一个显示选项
源码里 resource.calendar 有:
schedule_typeflexiblefully_fixedflexible_hours
而 flexible_hours 其实就是根据 schedule_type 算出来的。
这说明 Odoo 一开始就在区分两种完全不同的制度:
Fully Fixed
系统明确知道:
- 哪天上班
- 什么时候开始
- 什么时候结束
- 午休怎么切分
Flexible
系统更关心的是:
- 一周该工作多少小时
- 一天上限如何理解
- 时间分布可以更弹性
这不是“界面多一个开关”,而是 工时制度本身不同。
为什么双周制不是“多建两张周表”这么简单
two_weeks_calendar 打开后,源码会把出勤拆成:
attendance_ids_1st_weekattendance_ids_2nd_week
并且 _check_attendance_ids() 会分别校验第一周和第二周有没有重叠。
同时,_get_hours_per_week() 和 _get_days_per_week() 也会把两周总量再除以 2,算出平均每周值。
这意味着 Odoo 对双周制的理解不是:
把奇数周、偶数周随便堆在一起。
而是:
先承认这是一个两周循环,再把统计口径重新折算回“平均每周”。
这对 HR 很重要,因为请假、工时比例、FTE 这些口径,最终还是经常要落回“每周多少小时”。
hours_per_day 和 hours_per_week 为什么不是手工文本说明
源码里这两个字段都是计算字段。
hours_per_week来自出勤区间累加hours_per_day来自“每周工时 / 每周工作日数”
并且双周制时会自动做平均。
这说明 Odoo 很在意一件事:
工时口径必须能被系统可靠计算,而不是靠 HR 自己脑补。
因为后面很多地方都要用它:
- 请假天数和小时换算
- Part-time 比例
- 工资期间统计
- 弹性工时校正
FTE 比例为什么长在 calendar 上
源码里还有:
full_time_required_hourswork_time_rateis_fulltime
work_time_rate 的计算很直接:
- 当前 calendar 的
hours_per_week - 除以公司的
full_time_required_hours
所以 Odoo 的全职/兼职判断,本质上不是“岗位名字里写着兼职”,而是:
这套工作制度相对于公司标准全职工时,占了多少比例。
这比手工标签靠谱得多。
比如:
- 公司标准 40 小时
- 某员工日历 30 小时
- 那就是 75% work time rate
为什么公共休假会从公司日历复制下来
_compute_global_leave_ids() 会把公司默认日历上的 global_leave_ids 复制到具体日历。
这背后有个很典型的 HR 思路:
- 公司层面的法定节假日、集体停工
- 应该成为各个工作日历的默认基线
- 但具体日历仍保留单独调整空间
也就是说,Odoo 不是把“公共休假”当成纯公司公告,而是当成会进入工时计算的正式输入。
弹性工时为什么最容易被误解
很多人以为 flexible = 系统不知道什么时候上班,所以就没法算工时。
源码不是这么做的。
在 resource.resource 里:
- 没有日历时,资源甚至会被视作 fully flexible
_get_flexible_resource_valid_work_intervals()会按天和按周计算可用工时_format_leave()还会在请假时把hours_per_day、hours_per_week扣减掉_get_flexible_resource_work_hours()会在“天上限 + 周上限”里计算真正可计入的工时
这说明弹性工时不是“不算”。
而是:
系统不强依赖固定上下班时刻,但仍然要守住每天/每周的制度边界。
这很适合现实里的顾问、销售、创意岗位、远程团队。
为什么弹性工时下,请假和异常日判断也不同
_get_unusual_days() 在 flexible 和 fixed calendar 上走的是不同逻辑。
- 固定工时:看当天有没有 work intervals
- 弹性工时:更看 leave intervals 是否占用了这一天
这个差异很值得注意,因为它说明 Odoo 并没有拿固定排班那套硬套到弹性员工身上。
如果硬套,会出现很多荒唐结果:
- 明明制度允许弹性,却总被系统判成排班外
- 请假天数和工作时长经常不对
源码在这里明显做了制度差异化处理。
这套设计的现实价值
1. 同一家公司能表达不同工时制度
固定班、双周制、弹性工时,不必混成一个模板。
2. 请假、出勤和 FTE 的口径更一致
因为它们都回到 calendar 的小时/天/周计算。
3. 兼职与全职不再只靠人工标签判断
而是基于制度工时比例自动算出来。
实施时最容易踩的坑
坑一:把弹性工时当成“没有规则”
实际上只是规则从具体时刻,转成了日/周工时边界。
坑二:双周制只在界面上拆两周,不管统计口径
这样后面天数、工时、FTE 全会偏。
坑三:只配考勤时间,不校验 hours_per_day 和 hours_per_week
很多“请假换算不对”的根源,其实在 calendar。
一句话记忆
Odoo 的工作日历不是上下班模板,而是 HR 制度引擎:双周制、弹性工时、公共休假、小时/天换算和 FTE 比例都从这里往外长。
DISCUSSION
评论区