人力资源

Odoo 请假为什么不是“有假就能提”:审批层级、负余额和重叠申请的三道闸

很多团队把 Time Off 理解成余额够不够的问题。Odoo 源码里真正决定请假能不能走通的,至少还有三道闸:审批层级、是否允许负余额、以及重叠申请能不能叠在一起。

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

先说结论

很多用户会把请假理解成一句话:

余额够,就能提;余额不够,就不能提。

但 Odoo 的 hr_holidays 源码显示,真正决定一张请假单能不能成立的,往往至少有三道闸:

  • 审批闸:这类假走经理批、HR 批,还是双重审批
  • 额度闸:是否允许负余额,以及能透支到什么程度
  • 重叠闸:同一个人、同一时间段的请假能不能叠加

所以请假不是简单减法,而是一套制度校验。


第一层:为什么审批不是统一一条线

hr.leave.type 里,源码定义了 leave_validation_type

常见值包括:

  • no_validation
  • manager
  • hr
  • both

这意味着 Odoo 默认就承认:不同假种,审批权不一定在同一层。

比如:

  • 有些假可以自动批准
  • 有些只需要直属经理
  • 有些必须 HR 复核
  • 有些要经理先批,再到 HR 二批

这对现实企业非常重要,因为年假、病假、调休、无薪假,本来就不该一把尺子量到底。


双重审批为什么不是“多点一步流程”而已

源码里的 _check_double_validation_rules() 很值得看。

它会检查:

  • 当前用户是不是 Time Off Officer / Manager
  • 第一批的时候,是不是该员工的 leave manager
  • 第二批的时候,有没有更高层的审批权限

也就是说,双重审批不是表面上“点两次 Approve”。

它真正表达的是:

第一层和第二层批准,不只是状态变化,而是不同角色的业务责任。

这和很多项目里的误区正好相反:有些团队以为二次审批只是为了“更严格”,实际上它是在明确责任边界。


第二层:为什么允许负余额后,系统也不会无限放行

很多人看到 allows_negative,会误以为只要打开了,员工就能一直请到负数。

源码不是这样干的。

hr.leave.type 里,Odoo 同时还有:

  • allows_negative
  • max_allowed_negative

而在 hr.leave._check_validity() 里,它会用 allocation 数据判断:

  • 有没有有效 allocation
  • 当前 virtual_remaining_leaves 是否低于负余额上限

也就是说,允许负余额的真正含义不是“随便透支”,而是:

制度允许在明确阈值内透支。

这很符合 HR 现实:

  • 新员工年假先借用几天
  • 调休先休后补
  • 特殊情况允许小幅透支

但企业通常不希望它失控,所以 Odoo 用 max_allowed_negative 把边界钉死了。


为什么没有 allocation 时,哪怕允许负余额也不一定能过

源码里有一个很容易被忽略但很关键的判断:

如果 max_leaves 根本不存在,系统会报:

  • 你没有任何 allocation
  • 请先申请 allocation 再提交请假

这说明 Odoo 不是简单看“剩余额度能不能小于 0”,而是先问一个更根本的问题:

这类假有没有被制度性地发给过你。

所以负余额不是“无中生有”,而是“在既有制度类型下的可控透支”。


第三层:为什么重叠请假不会被默默合并

_compute_dashboard_warning_message() 里,Odoo 会搜索同员工、同时间段、状态未取消/拒绝的其他请假。

并且只要假种配置了 allow_request_on_top = False,就会把重叠记录列出来。

这很有意思,因为它告诉你:

  • Odoo 默认认为请假时间段是一种占用
  • 同一时间的占用,通常不应随便叠加
  • 但某些特殊假种可以允许叠加

比如现实里可能出现:

  • 公司统一停工日
  • 特殊福利假与别的制度并存
  • 某些技术性类型不应阻塞正常 Time Off

所以 Odoo 没有一刀切,而是把是否允许“盖在上面”交给假种配置。


allow_request_on_top 解决的不是 UI 提示,而是制度例外

很多人会把这个字段理解成“避免前端弹警告”。

其实不是。

它的设计意义更像是:

有些时间占用是互斥的,有些可以共存。

如果一家公司把所有假种都设成不可叠加,结果往往是:

  • 公共假期、补偿性休假、特殊技术假全混在一起互相打架

但如果全部都允许叠加,又会让同一时段同时出现多张逻辑上冲突的申请。

所以这不是一个“友好提示开关”,而是制度语义开关。


为什么请假创建后还要再次 _check_validity()

create() 里,Odoo 建完 hr.leave 后会立刻调用 _check_validity()

这说明系统的思路是:

  • 先形成记录
  • 再用统一逻辑校验它是否真成立

而不是完全依赖表单 onchange。

这对实施很重要,因为你以后无论是:

  • 前端提交
  • 批量导入
  • 自动化脚本
  • 自定义 API

最终都得过同一套有效性约束。

也就是说,真正的业务边界在模型层,不在界面层。


为什么已经开始的请假更难改

write() 里还有一层保护:

如果普通用户去改一个已经开始的请假,而且他不是相应经理,就会被拦下。

这很合理,因为假一旦开始,影响往往已经外溢到:

  • 排班
  • 出勤
  • Work Entry
  • 审批责任

所以系统不鼓励员工在事后随便回头改历史。


这套设计的现实价值

1. 它把“有没有假”与“谁来批”分开了

余额问题不等于审批问题。

2. 它把“能不能透支”做成制度边界,而不是人为口头规则

允许负余额可以,但上限必须清楚。

3. 它把时间冲突也纳入了请假模型

不是等后面出错再补救,而是在申请阶段就尽量提示和拦截。


最容易犯的误解

最常见的误解是:

“只要给员工 allocation,这个假种就已经配好了。”

其实还不够。

你还得一起看:

  • leave_validation_type
  • allows_negative
  • max_allowed_negative
  • allow_request_on_top
  • 是否已有其他重叠请假

这些一起决定了 Time Off 是“能申请”,还是“看起来能填,实际上过不了”。


一句话记忆

Odoo 请假不是只看余额,而是同时过审批、额度和时间冲突三道闸;负余额也不是无限透支,而是受上限约束的制度例外。

DISCUSSION

评论区

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