先说结论
Odoo 的自动签退不是一个“到下班时间就把 check_out 写死”的定时器。
/home/ubuntu/odoo-temp/addons/hr_attendance/models/hr_attendance.py 里的 _cron_auto_check_out() 说明,官方真正做的是:
- 先找还没签退的 open attendance
- 再把
check_in转成员工自己的时区 - 再结合当天已累积工时、日历排班段、公司容忍度计算应该签到哪里
- 最后把
out_mode标成auto_check_out
所以它解决的问题不是“系统替你打卡”,而是:
当员工忘了签退时,系统如何尽量保守地补一刀,同时别把工时算得更离谱。
第一层:自动签退只处理“还开着的单”
入口条件很明确:check_out = False,并且公司开启 auto_check_out。
这意味着自动签退不是重新改写所有历史记录,而是专门处理那些“开口还没闭上”的 attendance。
所以如果你的问题是:
- 已经有人手工签退了但时间不对
- 历史记录重叠了
- 同一天多段班次算错
自动签退本身不是第一修复器。
第二层:为什么源码要先把时间转到员工时区
_cron_auto_check_out() 里专门定义 check_in_tz(attendance),把 check_in 转到员工时区。
这一步不是细节,而是整个逻辑能不能成立的前提。
因为“今天工作了多久、是不是已经跨天、当前排班属于周几”,都必须站在 员工本地日历 上判断。
如果你直接拿 UTC 或服务器时区判断:
- 跨国团队会错日
- 夜班会错班
- 前一天和今天的累计工时会串掉
第三层:为什么它还要看“今天之前已经有多少工时”
代码里会汇总该员工当天之前已完成 attendance 的 worked_hours,再把当前 open attendance 的时长加进去。
这是因为自动签退不是只看“这一条开了多久”,而是要看:
- 这一天理论上应该工作多少
- 前面已经工作了多少
- 当前这段最多还能留下多少
这也是为什么它更像“补录上限控制”,而不是“把 check_out 写到排班终点”。
第四层:排班段和容忍度为什么比固定下班时间更重要
源码会根据员工所在版本/日历去找当天有效的 attendance schedule,再叠加公司 auto_check_out_tolerance。
这说明官方知道现实排班不是一个固定 18:00:
- 有两段班
- 有两周轮班
- 有弹性时长
- 同一个人换合同后日历会变
所以“自动签退时间”并不是一个绝对时刻,而是:
理论工作时长 + 公司允许的超出容忍度。
第五层:为什么有时候系统会签到 23:59:59
代码里有一种兜底情况:当天找不到合适的班段上限时,会把 check_out 放到当天结束前。
这看起来很怪,但背后其实是风险最小化:
- 至少别让 open attendance 无限拖到未来
- 先把跨天污染切断
- 后续再让 HR 或主管补修真实时间
换句话说,23:59:59 不是说“员工真干到午夜”,而是系统在说:
- 我不知道最合理的签退点
- 但我知道不能继续开着
第六层:out_mode = auto_check_out 的价值在于可追溯
自动签退不仅改时间,还会把来源写成 auto_check_out。
这非常重要,因为后续你要区分:
- 员工自己签退
- kiosk 签退
- systray 签退
- 系统补签退
否则大家会把所有 check_out 当成同一可信度,后面就很难解释为什么这条记录看起来“像猜的”。
最容易踩的 4 个坑
1)把自动签退当排班引擎
它是补救机制,不是排班真相来源。
2)忽略时区
这会导致跨天和日累计工时一起错。
3)只看当前 open attendance,不看当天已完成工时
结果就是自动签退把一天总工时补超。
4)看到 23:59:59 就判定系统坏了
很多时候那是兜底止血,而不是最终业务结论。
排错顺序
- 先确认记录是不是 open attendance
- 看公司是否开启自动签退及容忍度
- 查员工时区和当天本地日期
- 查当天前面已累计的工时
- 再看资源日历、轮班周类型和弹性工时配置
- 最后才判断这次 auto check-out 是否合理
最后一句话
Odoo 自动签退的本质是“修补忘签退”,不是“代替员工精确下班”。把它当补救机制来配置和排错,你才不会对它期待错位。
DISCUSSION
评论区