很多人把考勤系统理解成“check in / check out 存进数据库就完事”。可企业真正关心的是:这些打卡怎么进工资、怎么与请假冲突处理、什么时候禁止改历史。enterprise/hr_work_entry_attendance/models/hr_attendance.py 和 hr_version.py 的答案很明确:打卡只是原材料,真正有业务价值的是 work entry。
一、关闭打卡时就可能直接生成 work entry
hr_attendance.py 里的 _create_work_entries() 会在出勤关闭后,检查员工是否存在与当前期间重叠的合同版本,再调用合同版本的 _get_work_entries_values()。如果当前打卡落在已生成区间里,系统不会等月底批处理,而是立刻补出 work entry。
这意味着企业版不是把考勤和工时拆成两个互不相干的系统,而是在“出勤已闭合”的那一刻就尝试补齐工资世界需要的记录。
二、写入 attendance 不只是改一条记录,而是可能触发整段重算
write() 里先找与 attendance 关联、状态为 validated 的 work entry;只要存在,就直接抛错,禁止修改。若允许修改,系统会根据员工和日期搜索 work entries,再调用 hr.work.entry.regeneration.wizard 重新生成对应 slot。
这个设计的重点不是“严”,而是“不要让工资已确认的数据被出勤界面反向污染”。一旦工时记录已验证,attendance 就不再只是前台输入,而是 payroll 的上游凭证。
三、加班不是额外一列时长,而是单独的 interval
hr_version.py 里的 _get_overtime_intervals() 会按员工、日期分组读取 hr.attendance.overtime.line;_get_real_attendance_work_entry_vals() 又会把普通 attendance interval 与 overtime interval 区分开来,分别映射到普通出勤和加班 work entry type。
这背后的好处是:系统不会把“多出来的两个小时”粗暴叠加到普通出勤里,而是给它单独类型、单独时间段、单独后续处理空间。报表、薪资规则、审批口径因此都更稳定。
四、删除 attendance 也不是删掉就完,而是要看下游是否还能自洽
_unlink_except_validated_work_entries() 会在删除前再次阻断所有已验证工时;真正 unlink() 时,还会在 hr.work.entry 的错误检查上下文里做清理,必要时归档最后一条 attendance 关联的 work entries。也就是说,删除历史打卡前,系统先问的不是“数据库能不能删”,而是“删了以后工时世界会不会失真”。
五、实战注意事项
- 把 validated 当红线:一旦工资或工时确认,历史 attendance 就不该再被随意改。
- 让加班有独立规则:既然源码把 overtime 拆成 interval,就别在工资规则里再用模糊差值补算。
- 批量导考勤要考虑重算成本:
write()的 regenerate 逻辑说明,大量回填历史打卡会触发整段 work entry 处理。 - 请假与出勤冲突要从 interval 层面看:不要只盯总小时数,真正起作用的是时间区间拼接。
新手误区
- 误以为 attendance 就是工资数据本身。
- 误以为 validated 后还能从打卡页面小修小补。
- 误以为加班只是一列加班小时数。
- 误以为删打卡只影响考勤报表,不影响 payroll。
主要源码参考
enterprise/hr_work_entry_attendance/models/hr_attendance.pyenterprise/hr_work_entry_attendance/models/hr_version.py
DISCUSSION
评论区