先说结论
在 Odoo 里,“有余额”并不等于“这段时间就能请假”。
/home/ubuntu/odoo-temp/addons/hr_holidays/models/hr_leave_mandatory_day.py 和 hr_leave.py 说明,官方专门保留了一道独立边界:
- 这天是不是 Mandatory Day
- 这名员工是否落在受限范围内
- 这段请假对应的日历和岗位是否被卡住
所以请假真正的判断顺序不是只有:
- 有无余额
- 审批人同不同意
还包括一个更硬的问题:
系统是否允许你在这段业务关键期提出这张假单。
第一层:Mandatory Day 不是节假日表,而是“禁止请假的业务窗口”
hr.leave.mandatory.day 模型很小,但信息非常集中:
start_dateend_datecompany_idresource_calendar_iddepartment_idsjob_ids
这说明 Mandatory Day 的本质不是“法定假日定义”,而是企业自己声明的一段不可自由请假的窗口。
它可以表达的典型场景包括:
- 年终盘点周
- 门店大促期间
- 项目上线冻结窗口
- 学校开学周
- 工厂换线关键期
官方甚至允许它按:
- 公司
- 工时日历
- 部门
- 岗位
分别生效,这已经远比“全公司一刀切禁假”更细。
第二层:Mandatory Day 是按员工上下文动态判断,不是静态勾选
_compute_has_mandatory_day() 里,Odoo 并不是简单查“日期有没有落在 mandatory day 里”。
它还会结合:
- 员工的部门
- 员工的资源日历
- 假种所属公司
- 员工通过
_get_mandatory_days()能拿到的候选 mandatory day
然后再过滤出真正适用当前员工的记录。
也就是说,同一天在系统里可能既是 Mandatory Day,又不是 Mandatory Day,关键看对谁而言。
这种设计很现实,因为企业里的“不能请假”从来都不是纯日期逻辑,而常常是组织范围逻辑。
第三层:普通员工会被拦,Officer/Manager 则保留干预空间
真正的硬限制出现在 _check_validity():
- 如果普通用户提交的请假命中了 mandatory day
- 会直接抛
ValidationError - 提示你不允许在 Mandatory Day 请假
但这里有一个很重要的边界:
- 不是所有角色都一样被锁死
- 代码判断的是
group_hr_holidays_user
这意味着官方承认现实里会有“业务冻结窗口”,但也承认 HR/管理层必须保留例外处理能力。
所以 Mandatory Day 不是绝对技术壁垒,更像:
对普通员工默认关闭,对管理角色保留兜底。
第四层:它和工作日历、版本时间线天然耦合
Mandatory Day 真正有意思的地方,在于它不是独立悬空的。
同一个 hr.leave 在计算过程中还要考虑:
resource_calendar_id- 员工当前版本能否跨多个版本
- 不同版本是否用了不同工作日历
_check_contracts() 甚至明确限制:
- 一张假单不能跨多个使用不同 working schedule 的版本
这说明 Odoo 的思路不是“先申请假,再事后算日历”,而是:
- 请假一开始就必须落在可解释的工时边界里
- Mandatory Day 只是其中一层限制
- 版本 / 日历一致性是另一层限制
两者叠加后,才是最终能不能请的结果。
第五层:它和 Public Holiday 也不是同一件事
很多实施会把 Mandatory Day 和 Public Holiday 混为一谈。
源码告诉你,它们是两回事:
- Public Holiday 影响请假时长计算与是否需要扣额度
- Mandatory Day 影响这段时间是否允许普通员工发起请假
举个典型例子:
- 春节法定假期:更像 Public Holiday
- 年度大促最后三天不得休假:更像 Mandatory Day
前者回答“这天算不算工时/假时长”,后者回答“这张申请能不能提”。
这两个问题的业务语义完全不同。
第六层:Mandatory Day 其实是在保护排班可执行性
如果只从员工体验看,Mandatory Day 很像“系统不让请假”。
但从源码设计看,它真正保护的是:
- 某些岗位在关键窗口的人手底线
- 排班和服务承诺的可执行性
- 不同工作日历下的可用人力
所以它不是为了“管控更严格”,而是为了避免组织在关键期被零散合法假单掏空。
这也是为什么模型里会出现:
resource_calendar_iddepartment_idsjob_ids
因为官方关心的是哪类人、在哪套工时规则下、在什么窗口不能随意休假。
实施时最容易踩的 4 个坑
1. 把 Mandatory Day 当法定节假日
它们的语义完全不同。
2. 忽略岗位和部门范围
这样会把本应局部受限的规则变成全员误伤。
3. 只看余额,不看 schedule 边界
余额足够也可能因为版本或日历边界过不去。
4. 让管理层也完全无法例外处理
源码本身就给 Officer 保留了干预空间,别把业务弹性设计没了。
最后一句话
Odoo 的 Mandatory Day 本质不是“又一层审批”,而是把企业关键窗口、组织范围和工作日历边界提前塞进请假校验里。
DISCUSSION
评论区