[FIX] hr_attendance: account for lunch break in overtime calculation

Remove lunch hours from work intervals so that overtime is not calculated when it's not due
tests adapted to take into account lunch period

task 3217799

closes odoo/odoo#120455

X-original-commit: 97c052432a25a1461de23fd8ab0c73ffe9b084ff
Related: odoo/enterprise#40677
Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
dasz
2023-05-04 08:56:12 +02:00
parent 38aaaeb27e
commit 1b8b35e216
2 changed files with 35 additions and 9 deletions
+7 -1
View File
@@ -251,7 +251,13 @@ class HrAttendance(models.Model):
pre_work_time += (min(planned_start_dt, local_check_out) - local_check_in).total_seconds() / 3600.0
# Interval inside the working hours -> Considered as working time
if local_check_in <= planned_end_dt and local_check_out >= planned_start_dt:
work_duration += (min(planned_end_dt, local_check_out) - max(planned_start_dt, local_check_in)).total_seconds() / 3600.0
start_dt = max(planned_start_dt, local_check_in)
stop_dt = min(planned_end_dt, local_check_out)
work_duration += (stop_dt - start_dt).total_seconds() / 3600.0
# remove lunch time from work duration
lunch_intervals = calendar._attendance_intervals_batch(start_dt, stop_dt, emp.resource_id, lunch=True)
work_duration -= sum((i[1] - i[0]).total_seconds() / 3600.0 for i in lunch_intervals[emp.resource_id.id])
# There is an overtime at the end of the day
if local_check_out > planned_end_dt:
post_work_time += (local_check_out - max(planned_end_dt, local_check_in)).total_seconds() / 3600.0
@@ -28,6 +28,7 @@ class TestHrAttendanceOvertime(TransactionCase):
cls.other_employee = cls.env['hr.employee'].create({
'name': 'Yolanda',
'company_id': cls.company.id,
'tz': 'UTC',
})
cls.jpn_employee = cls.env['hr.employee'].create({
'name': 'Sacha',
@@ -119,7 +120,7 @@ class TestHrAttendanceOvertime(TransactionCase):
self.env['hr.attendance'].create({
'employee_id': self.employee.id,
'check_in': datetime(2021, 1, 4, 7, 0),
'check_out': datetime(2021, 1, 4, 16, 0)
'check_out': datetime(2021, 1, 4, 17, 0)
})
self.assertEqual(self.employee.total_overtime, 12)
@@ -127,33 +128,34 @@ class TestHrAttendanceOvertime(TransactionCase):
self.assertEqual(self.employee.total_overtime, 1)
def test_overtime_change_employee(self):
attendance = self.env['hr.attendance'].create({
Attendance = self.env['hr.attendance']
attendance = Attendance.create({
'employee_id': self.employee.id,
'check_in': datetime(2021, 1, 4, 7, 0),
'check_out': datetime(2021, 1, 4, 18, 0)
})
self.assertEqual(self.employee.total_overtime, 3)
self.assertEqual(self.employee.total_overtime, 2)
self.assertEqual(self.other_employee.total_overtime, 0)
attendance.employee_id = self.other_employee.id
self.assertEqual(self.other_employee.total_overtime, 3)
self.assertEqual(self.other_employee.total_overtime, 2)
self.assertEqual(self.employee.total_overtime, 0)
def test_overtime_far_timezones(self):
# Since dates have to be stored in utc these are the tokyo timezone times for 7-12 / 13-18 (UTC+9)
self.env['hr.attendance'].create({
'employee_id': self.jpn_employee.id,
# Since dates have to be stored in utc these are the tokyo timezone times for 7-18 (UTC+9)
'check_in': datetime(2021, 1, 3, 22, 0),
'check_out': datetime(2021, 1, 4, 9, 0),
})
# Same but for alaskan times (UTC-10)
self.env['hr.attendance'].create({
'employee_id': self.honolulu_employee.id,
# Same but for alaskan times (UTC-10)
'check_in': datetime(2021, 1, 4, 17, 0),
'check_out': datetime(2021, 1, 5, 4, 0),
})
self.assertEqual(self.jpn_employee.total_overtime, 3)
self.assertEqual(self.honolulu_employee.total_overtime, 3)
self.assertEqual(self.jpn_employee.total_overtime, 2)
self.assertEqual(self.honolulu_employee.total_overtime, 2)
def test_overtime_unclosed(self):
attendance = self.env['hr.attendance'].create({
@@ -264,3 +266,21 @@ class TestHrAttendanceOvertime(TransactionCase):
overtime = self.env['hr.attendance.overtime'].search([('employee_id', '=', self.employee.id)])
self.assertFalse(overtime, 'Overtime entry should be unlinked since both overtime cancel each other.')
def test_overtime_lunch(self):
attendance = self.env['hr.attendance'].create({
'employee_id': self.employee.id,
'check_in': datetime(2021, 1, 4, 8, 0),
'check_out': datetime(2021, 1, 4, 17, 0),
})
self.assertEqual(self.employee.total_overtime, 0, 'There should be no overtime since the employee worked through the lunch period.')
# check that no overtime is created when employee starts and finishes 1 hour earlier but works through lunch period
attendance.check_in = datetime(2021, 1, 4, 7, 0)
attendance.check_out = datetime(2021, 1, 4, 16, 0)
self.assertEqual(self.employee.total_overtime, 0, 'There should be no overtime since the employee worked through the lunch period.')
# same but for 1 hour later
attendance.check_in = datetime(2021, 1, 4, 9, 0)
attendance.check_out = datetime(2021, 1, 4, 18, 0)
self.assertEqual(self.employee.total_overtime, 0, 'There should be no overtime since the employee worked through the lunch period.')