# Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import date, datetime from freezegun import freeze_time from odoo import Command from odoo.tests import new_test_user, Form from odoo.tests.common import tagged, TransactionCase @tagged('hr_attendance_overtime_ruleset') class TestHrAttendanceOvertime(TransactionCase): """ Tests for overtime """ @classmethod def setUpClass(cls): super().setUpClass() cls.ruleset = cls.env['hr.attendance.overtime.ruleset'].create({ 'name': 'Ruleset schedule quantity', 'rule_ids': [ Command.create({ 'name': 'Rule schedule quantity', 'base_off': 'quantity', 'quantity_period': 'day', 'expected_hours': 8, 'paid': True, 'amount_rate': 150, }), Command.create({ 'name': 'Rule schedule quantity', 'base_off': 'quantity', 'quantity_period': 'day', 'expected_hours': 10, 'paid': True, 'amount_rate': 200, }), Command.create({ 'name': 'Rule schedule quantity', 'base_off': 'quantity', 'quantity_period': 'week', 'expected_hours': 40, 'paid': True, 'amount_rate': 150, }), ], }) cls.company = cls.env['res.company'].create({ 'name': 'SweatChipChop Inc.', 'attendance_overtime_validation': 'no_validation', }) cls.company.resource_calendar_id = cls.env.company.resource_calendar_id = cls.env['resource.calendar'].create({ 'name': 'Standard 40 hours/week (No Lunch)', 'company_id': cls.env.company.id, 'hours_per_day': 7.6, 'full_time_required_hours': 38, 'attendance_ids': [ (5, 0, 0), # Clear existing attendances (0, 0, {'name': 'Monday', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 16, 'day_period': 'morning'}), (0, 0, {'name': 'Tuesday', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 16, 'day_period': 'morning'}), (0, 0, {'name': 'Wednesday', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 16, 'day_period': 'morning'}), (0, 0, {'name': 'Thursday', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 16, 'day_period': 'morning'}), (0, 0, {'name': 'Friday', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 16, 'day_period': 'morning'}), ], }) cls.user = new_test_user(cls.env, login='fru', groups='base.group_user,hr_attendance.group_hr_attendance_manager', company_id=cls.company.id).with_company(cls.company) cls.employee = cls.env['hr.employee'].create({ 'name': "Marie-Edouard De La Court", 'user_id': cls.user.id, 'company_id': cls.company.id, 'tz': 'UTC', 'date_version': date(2020, 1, 1), 'contract_date_start': date(2020, 1, 1), 'resource_calendar_id': cls.company.resource_calendar_id.id, 'ruleset_id': cls.ruleset.id }) def test_daily_overtime_8_hours_rule(self): with freeze_time("2021-01-04"): # Attendance: 10 hours (8 expected + 2 overtime at 150%) 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, 18, 0) }) self.assertAlmostEqual(attendance.employee_id.total_overtime, 2, 2, msg="2 hours overtime at 150% should yield 2 hours total overtime") def test_daily_overtime_10_hours_rule(self): """ Test daily overtime for the 10-hour rule """ with freeze_time("2021-01-04"): # Attendance: 12 hours (10 expected + 2 overtime at 200%) 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, 20, 0) }) self.assertAlmostEqual(attendance.employee_id.total_overtime, 4.0, 2, msg="2 hours overtime at 200% should yield 4 hours total overtime") def test_no_overtime(self): """ Test no overtime when working expected hours or less """ with freeze_time("2021-01-04"): # Attendance: 8 hours (exactly 8 expected, no overtime) 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, 16, 0) }) self.assertAlmostEqual(attendance.employee_id.total_overtime, 0.0, 2, msg="No overtime should be recorded for 8 hours or less") def test_weekly_overtime(self): """ Test weekly overtime for the 40-hour rule """ with freeze_time("2021-01-04"): # Week: Mon-Fri, 10 hours/day = 50 hours total (10h daily OT + 8h weekly OT = 18h total) attendance_vals = [ { 'employee_id': self.employee.id, 'check_in': datetime(2021, 1, day, 8, 0), 'check_out': datetime(2021, 1, day, 18, 0) } for day in range(4, 9) # Monday to Friday ] self.env['hr.attendance'].create(attendance_vals) self.assertAlmostEqual(self.employee.total_overtime, 18, 2, msg="He should work from 8-16h so each day he did 2 hours of overtime") def test_multiple_attendances_same_day(self): """ Test multiple attendances in one day """ with freeze_time("2021-01-04"): # Two attendances: 6 hours + 6 hours = 12 hours (10 expected + 2 overtime at 200%) self.env['hr.attendance'].create([ { 'employee_id': self.employee.id, 'check_in': datetime(2021, 1, 4, 8, 0), 'check_out': datetime(2021, 1, 4, 14, 0) }, { 'employee_id': self.employee.id, 'check_in': datetime(2021, 1, 4, 14, 0), 'check_out': datetime(2021, 1, 4, 20, 0) } ]) self.assertAlmostEqual(self.employee.total_overtime, 4.0, 2, msg="2 hours overtime at 200% should yield 4 hours total overtime") def test_partial_week(self): """ Test partial week with overtime """ with freeze_time("2021-01-04"): # Week: Mon-Wed, 12 hours/day = 36 hours total (no weekly overtime, daily overtime applies) [ self.env['hr.attendance'].create({ 'employee_id': self.employee.id, 'check_in': datetime(2021, 1, day, 8, 0), 'check_out': datetime(2021, 1, day, 20, 0) }) for day in range(4, 7) # Monday to Wednesday ] self.assertAlmostEqual(self.employee.total_overtime, 12.0, 2, msg="3 days of 2 hours overtime at 200% should yield 12 hours total overtime") def test_access_ruleset_on_employee(self): """ Test the access rights of the ruleset on the employee Only the employee admin should be able to see and change the ruleset on the employee """ user = new_test_user(self.env, login='usr', groups='hr.group_hr_user', company_id=self.company.id).with_company(self.company) employee = self.env['hr.employee'].with_company(self.company).create({'name': "Employee Test"}) with Form(employee.with_user(user)) as employee_form: self.assertFalse("ruleset_id" in employee_form._view['fields']) # HR Mangers should be able to see the ruleset on the employee user.group_ids |= self.env.ref('hr.group_hr_manager') # fix le truc chelou de pas pouvoir ecrire la surrement les access rule with Form(employee.with_user(user)) as employee_form: self.assertTrue("ruleset_id" in employee_form._view['fields']) employee_form.record.ruleset_id = self.ruleset.id def test_is_manager_with_overtime(self): """ Test the computation of is_manager with overtime """ user = new_test_user(self.env, login='usr', groups='hr_attendance.group_hr_attendance_officer', company_id=self.company.id).with_company(self.company) self.employee.attendance_manager_id = user.id attendance = self.env['hr.attendance'].with_company(self.company).create({ 'employee_id': self.employee.id, 'check_in': datetime(2021, 1, 4, 8, 0), 'check_out': datetime(2021, 1, 4, 20, 0) }) self.assertTrue(attendance.with_user(user).linked_overtime_ids.is_manager)