先说结论
Odoo 的 Presence 从来不是一个“谁在线谁不在线”的聊天小功能。
在 /home/ubuntu/odoo-temp/addons/hr_presence/models/hr_employee.py 里,官方真正做的是一套在岗推断机制:
- 先判断员工现在是不是理论上应该在岗
- 再看今天有没有 IP 活动
- 再看今天有没有 邮件发送活动
- 最后允许 HR 手动覆盖
所以它的目标不是回答“人是不是开着电脑”,而是回答:
这名员工在当前工作时段里,是否有足够证据被视为 present。
第一层:Presence 先依赖“现在是不是工作时间”
_compute_presence_state() 里最关键的不是 IP,也不是 email,而是 working_now_list。
这意味着 Presence 不是全天候判定的。只有当员工此刻落在自己的工作时间里,系统才会认真区分:
presentabsentout_of_working_hour
这三个状态的业务含义完全不同。
很多实施会误把 absent 当成“今天没来上班”。源码并不是这么说的。
更准确地讲:
out_of_working_hour:现在本来就不该上班absent:现在应该在岗,但系统没抓到足够的活动信号present:现在应该在岗,并且有足够信号证明他活跃
所以 Presence 是一个时段内判断,不是日终考勤结论。
第二层:IP 检查看的不是登录记录,而是“今天是否从受控网段出现过”
_check_presence() 会先把全公司员工当天的 Presence 辅助标记重置掉:
email_sent = Falseip_connected = Falsemanually_set_present = Falsemanually_set_presence = False
然后才重新计算。
如果公司启用了 hr_presence_control_ip,系统会去查 res.users.log,并只看:
- 当前员工关联用户
ip不为空create_date在今天 0 点之后
/home/ubuntu/odoo-temp/addons/hr_presence/models/res_users_log.py 还专门给 res.users.log 扩了 ip 字段,说明官方是把“今天是否从某些办公网段活跃过”当成 Presence 证据之一。
这一步的重点不是精确定位,而是一个比较务实的判断:
今天在受控 IP 列表里出现过,就更像是在岗。
所以它更像“办公网络信号”,不是“物理门禁信号”。
第三层:邮件阈值不是沟通统计,而是活跃代理指标
如果公司启用了 hr_presence_control_email,系统还会统计员工今天发出的邮件数。
源码看的是:
mail.message- 作者是该员工
user_id.partner_id - 时间区间在今天 0 点到现在
- 数量达到
hr_presence_control_email_amount
这一步很有意思。
Odoo 并没有把“发邮件多”理解成 KPI,而只是把它当成一个活跃代理信号:
- 发过足够数量的业务邮件
- 说明今天至少在系统里有工作动作
- 因而可用来支持
present判断
它不是为了评价绩效,而是为了降低“明明在办公却被标成 absent”的误判率。
第四层:手动改状态不是备注,而是强制覆盖
action_set_present() / action_set_absent() 最值得注意的地方,是它们并不是简单写一个展示字段。
HR Manager 手动处理后,系统会写:
manually_set_presentmanually_set_presencehr_presence_state_display
随后 _compute_presence_state() 里第一优先级就会检查:
- 如果
manually_set_presence为真 - 直接采用
hr_presence_state_display
这说明手动 Presence 不是“给界面加个小标签”,而是覆盖自动判定结果。
也就是说,官方非常清楚自动信号会有噪声,所以给了 HR 一个可解释、可纠偏的人工兜底入口。
第五层:Presence 和缺勤处理之间还有动作链
源码里 get_presence_server_action_data() 会准备一组服务器动作,包含:
- 标记 present
- 标记 absent
- 写日志
- 发送短信
- 发起 Time Off
再看 action_open_leave_request(),你会发现 Presence 不是停在“看板颜色”。
它还能把“非计划缺勤”推进到后续动作:
- 单人时打开
hr.leave - 多人时打开批量缺勤向导
- 默认原因就是
Unplanned Absence
这一步很关键,因为它说明 Presence 不是最终事实,而是运营入口:
看见异常在岗状态后,HR 可以顺着它继续补日志、催沟通、补请假。
第六层:为什么官方每天都先清零再重算
很多人第一次看到 _check_presence() 里那段重置代码,会觉得“这不是会丢历史吗”。
其实 Presence 本来就不是历史归档对象。
官方在这里强调的是:
- Presence 是当日即时状态
- 不是长期稽核明细
- 真正的历史凭证仍在邮件、日志、请假、考勤等其他对象里
因此每天先清零再重算,反而能避免昨天的信号污染今天的判断。
这也解释了为什么 res.company 里只保留了一个 hr_presence_last_compute_date:
- 用来确认当天是否已重算
- 而不是保存一整套 Presence 历史账本
实施时最容易踩的 4 个坑
1. 把 Presence 当考勤
Presence 只能说“现在像不像在岗”,不能直接替代 Attendance 或 Payroll。
2. 把 IP 证据当绝对事实
员工用 VPN、移动网络、远程桌面时,IP 证据会失真。
3. 把邮件阈值当绩效考核
它只是 Presence 信号,不是绩效分。
4. 忘了手动覆盖优先级最高
如果 HR 频繁手改状态,却没有配套流程,就会让自动判定失去可信度。
最后一句话
Odoo Presence 的本质不是“在线灯”,而是把工作时段、系统活动和人工纠偏拼成一张当日“是否在岗”的推断图。
DISCUSSION
评论区