工资单接考勤,最容易被误解成一句话:在工资期里把打卡加总一下就好。但 /home/ubuntu/odoo-temp/enterprise/hr_payroll_attendance 明显不是这么做的。官方真正关心的是:哪些打卡属于哪张工资单、跨日跨时区怎么归属、加班又该以什么形式进入薪资计算。
参考入口:
enterprise/hr_payroll_attendance/models/hr_payslip.pyenterprise/hr_payroll_attendance/models/hr_payslip_worked_days.pyenterprise/hr_payroll_attendance/tests/test_payslip_attendance.pyenterprise/hr_payroll_attendance/tests/test_payslip_overtime.py
一、工资单匹配考勤不是简单按完整落入区间过滤
_get_attendance_by_payslip() 的域条件是:
- 员工相同;
check_in <= date_to;check_out >= date_from。
也就是说,只要打卡区间与工资期有重叠,就先纳入候选。然后官方再按 check_in:day 聚合,并根据 slip 的 date_from <= check_in.date() <= date_to 来决定归属。
这说明它不是只接受“完全落在工资期内”的记录,而是先找相交考勤,再按日期粒度切归属。
二、为什么测试特别强调时区
test_get_attendance_from_payslip_with_timezone 很关键。相同的打卡,在 Europe/Brussels 上下文里可能落到不同日期边界。换句话说,工资单归属不是纯数据库 UTC 比较,而是用户看见的业务日期也会影响结果。
这正是薪资系统最容易踩坑的地方:夜班、跨午夜、跨国团队,只要你忽略时区,某些 attendance 就会被分到错误工资期。
三、attendance_count 是解释入口,不只是一个统计数字
_compute_attendance_count() 和 action_open_attendances() 做的事情很朴素:把工资单对应到具体考勤列表,并给 HR 一个可钻取入口。这个设计非常务实,因为薪资对账时最怕“总额不对但找不到底层记录”。
也就是说,attendance_count 的真正价值是把工资结果回链到原始打卡事实。
四、overtime 进 worked days,但不能被当成半天假
HrPayslipWorkedDays._is_half_day() 明确排除了 work_entry_type_overtime。这行代码很短,但业务含义非常重:加班是额外工时,不应套用请假/缺勤那套半天语义。
测试里还覆盖了:
- 工资期外的 overtime 不进本次工资单;
- 负向 overtime 不一定生成行;
- 需要审批的加班,在
action_approve_overtime()之前不会进入 worked days。
所以 overtime 不是“只要出现就进薪资”,而是受工资期、审批状态和工时语义共同约束。
五、实战注意事项
- 先确认员工
version_id.work_entry_source是否真的是attendance,否则工资单根本不会走这套逻辑。 - 发现某条打卡没进工资期时,先查时区边界,再查审批状态,最后再看重叠区间。
- 不要把 overtime worked day 当成休假型行去复用半天算法。
六、结论
hr_payroll_attendance 解决的不是“把打卡算进工资”这么简单,而是把工资期、业务日期、考勤事实和 overtime 审批串成一条可追溯的链路。只有把这些边界守住,工资单才不会在跨时区与异常加班场景里出错。
DISCUSSION
评论区