企业 项目预测

Odoo 企业版项目预测容量为什么不是“allocated hours 减请假小时”:planning_holidays、resource calendar 与 forecast 汇总口径讲透

基于 planning_holidays 与 project_forecast 源码,讲清员工请假、公共假日与半天假如何回写 planning slot 的 allocated_hours,并进一步改变项目预测总工时。

企业 项目
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

很多实施把项目预测容量理解成一个很粗的公式:计划工时 = 排班小时,员工请假时再手动减掉。但 Odoo 企业版不是这样设计的。

在企业版里,请假和公共假日并不是“报表层扣一笔”,而是会直接进入 planning.slot 的底层工时计算,让 slot 自己的 allocated_hours 发生变化;而项目预测页面、项目看板上的 forecast 小时,本质上只是把这些已经被重算过的 slot 再聚合一遍。

这条链的关键代码主要在:

  • enterprise/planning_holidays/models/resource_calendar_leave.py
  • enterprise/planning_holidays/models/resource_calendar.py
  • enterprise/planning_holidays/models/planning_slot.py
  • enterprise/project_forecast/models/project_project.py

一、为什么请假不是“最后展示时减掉”

planning_holidays.models.resource_calendar_leave.ResourceCalendarLeaves 里最关键的方法是:

  • _process_shifts_domain()
  • _recompute_shifts_in_leave_periods()

它的思路不是去改 project 报表,而是先找出在请假区间内、且受该资源或全局假日影响的 planning slots,然后把这些 slot 加入 allocated_hours 的重算队列。

也就是说:

  1. 新建请假 / 公共假日;
  2. 系统圈出受影响的 shifts;
  3. 这些 shifts 的 allocated_hours 重新计算;
  4. 项目预测再读取这些被重算后的 slot。

这就是为什么你会看到:项目 forecast 的数字变化,往往不是 project 模块自己扣的,而是 planning 层先变了。

二、公共假日和员工个人请假,作用层级并不一样

_process_shifts_domain() 很值得细看。

它把假期分成两类:

  • resource_id 的:员工/资源级请假;
  • 没有 resource_id 的:全局公共假日。

因此它生成 domain 时,会同时考虑两种覆盖方式:

  • 某个具体资源在某时间段不可用;
  • 整个公司/日历层在某时间段不可用。

这意味着项目经理常见的一个误解是错的:

“我只给员工批了假,不会影响 forecast 模板本身。”

不一定。只要该 slot 绑定了资源,而且时间段被覆盖,allocated_hours 就要重算;如果是公共假日,则可能影响更广的一批 slots。

三、半天假为什么不是简单砍成 4 小时

planning_holidays.models.resource_calendar.ResourceCalendarInherit._handle_flexible_leave_interval() 专门处理灵活假期与半天假。

这里不是写死“半天 = 4 小时”,而是按请假的请求粒度去改区间边界:

  • 上午半天:从当天 00:00 起算到中午;
  • 下午半天:从中午开始到当天结束;
  • 不是半天假时,再交给上层逻辑处理。

这说明 Odoo 的判断口径是日历区间边界,不是写死一个经验工时值。真正扣掉多少小时,仍然依赖该员工/资源日历里的工作时间结构。

所以同样叫“半天假”,在不同工作日历下:

  • 可能不是整齐的 4 小时;
  • 也可能影响上午/下午不同班段;
  • 对跨时区或弹性班表资源,结果更不应手算。

四、slot 上到底会看到什么变化

planning_holidays.models.planning_slot.PlanningSlot 里有两个常被忽略的信号:

  • leave_warning
  • is_absent

_compute_leave_warning() 会调用 hr.leave._get_leave_interval()_get_leave_warning(),把请假提示直接挂到 shift 上。也就是说,系统不仅改工时,还会在 UI 上告诉你:这个人这段时间其实在请假。

但更关键的是,真正影响容量的并不是 warning,而是前面提到的 allocated_hours 重算。

换句话说:

  • leave_warning 是给人看的;
  • allocated_hours 是给容量模型和聚合结果看的。

很多人只盯 warning,不盯工时字段本身,所以才会误以为“系统只是提示冲突,没有真的改预测口径”。

五、项目 forecast 为什么会跟着变

project_forecast.models.project_project.ProjectProject._compute_total_forecast_time() 非常直接:

  • planning.slotproject_id_read_group
  • 聚合 allocated_hours:sum
  • 再写入 total_forecast_time

这说明项目页面上的 forecast 总工时,本质不是另一套算法,而是:

项目 = 该项目下所有 planning slots 的 allocated_hours 汇总

因此只要 leave / holiday 机制把 slot 的 allocated_hours 改掉,项目 forecast 就会同步变化。

这也是为什么有时用户会困惑:

  • 我没改 project;
  • 我只是批了个假;
  • 为什么项目预测小时数变了?

答案是:因为 project 读的是 planning 的结果,不是 project 自己保存了一份独立容量值。

六、真正该关注的是“工时口径是否已经进 slot”

实施里要避免三类错误判断。

1. 以为批准请假后,只会影响 HR,不会影响项目容量

错。resource.calendar.leaves 的 create / write / unlink 都会触发 _recompute_shifts_in_leave_periods(),只要时间段命中,slot 工时就会被重算。

2. 以为 forecast 页面上的小时数可以和原始 slot 时长简单对比

也错。slot 的显示时长、开始结束时间,与最终被聚合的 allocated_hours 并不总是同一个数字。公共假日、半天假、资源日历都会改变后者。

3. 以为半天假就是“人工减 0.5 天”

依旧不对。企业版按区间边界和资源日历去算,不是按管理口语去算。

七、实战建议

  • 做项目预测实施时,优先核对员工的 resource.calendar,不要只测 project 视图。
  • 如果客户常抱怨 forecast 不准,先检查是否启用了公共假日、半天假和弹性日历,而不是先怀疑项目模块。
  • 自定义报表时,尽量直接读取 planning.slot.allocated_hours 的最终值,不要自己用开始结束时间重算一次。
  • 遇到“批假后项目 forecast 变了”的工单,先解释这是设计预期,不是异常数据漂移。

八、结论

Odoo 企业版的项目容量预测,核心不是“项目模块里有个减法公式”,而是请假和公共假日先改 planning slot 的可计入工时,再由 project forecast 做聚合。所以真正决定结果的,不是你在项目里看到了多少小时,而是底层 allocated_hours 在资源日历与假期规则作用后,还剩多少可分配容量。

DISCUSSION

评论区

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