271 lines
12 KiB
Python
271 lines
12 KiB
Python
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from datetime import datetime
|
||
|
|
||
|
from odoo.tests import new_test_user
|
||
|
from odoo.tests.common import TransactionCase, tagged
|
||
|
|
||
|
from odoo.exceptions import AccessError, ValidationError
|
||
|
|
||
|
from freezegun import freeze_time
|
||
|
import time
|
||
|
|
||
|
@tagged('post_install', '-at_install', 'holidays_attendance')
|
||
|
class TestHolidaysOvertime(TransactionCase):
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super().setUpClass()
|
||
|
cls.company = cls.env['res.company'].create({
|
||
|
'name': 'SweatChipChop Inc.',
|
||
|
'hr_attendance_overtime': True,
|
||
|
'overtime_start_date': datetime(2021, 1, 1),
|
||
|
})
|
||
|
cls.user = new_test_user(cls.env, login='user', groups='base.group_user', company_id=cls.company.id).with_company(cls.company)
|
||
|
cls.user_manager = new_test_user(cls.env, login='manager', groups='base.group_user,hr_holidays.group_hr_holidays_user,hr_attendance.group_hr_attendance_manager', company_id=cls.company.id).with_company(cls.company)
|
||
|
|
||
|
cls.manager = cls.env['hr.employee'].create({
|
||
|
'name': 'Dominique',
|
||
|
'user_id': cls.user_manager.id,
|
||
|
'company_id': cls.company.id,
|
||
|
})
|
||
|
cls.employee = cls.env['hr.employee'].create({
|
||
|
'name': 'Barnabé',
|
||
|
'user_id': cls.user.id,
|
||
|
'parent_id': cls.manager.id,
|
||
|
'company_id': cls.company.id,
|
||
|
})
|
||
|
|
||
|
cls.leave_type_no_alloc = cls.env['hr.leave.type'].create({
|
||
|
'name': 'Overtime Compensation No Allocation',
|
||
|
'company_id': cls.company.id,
|
||
|
'requires_allocation': 'no',
|
||
|
'overtime_deductible': True,
|
||
|
})
|
||
|
cls.leave_type_employee_allocation = cls.env['hr.leave.type'].create({
|
||
|
'name': 'Overtime Compensation Employee Allocation',
|
||
|
'company_id': cls.company.id,
|
||
|
'requires_allocation': 'yes',
|
||
|
'employee_requests': 'yes',
|
||
|
'allocation_validation_type': 'officer',
|
||
|
'overtime_deductible': True,
|
||
|
})
|
||
|
|
||
|
def new_attendance(self, check_in, check_out=False):
|
||
|
return self.env['hr.attendance'].sudo().create({
|
||
|
'employee_id': self.employee.id,
|
||
|
'check_in': check_in,
|
||
|
'check_out': check_out,
|
||
|
})
|
||
|
|
||
|
def test_deduct_button_visibility(self):
|
||
|
with self.with_user('user'):
|
||
|
self.assertFalse(self.user.request_overtime, 'Button should not be visible')
|
||
|
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 18))
|
||
|
self.assertEqual(self.user.total_overtime, 10, 'Should have 10 hours of overtime')
|
||
|
self.assertTrue(self.user.request_overtime, 'Button should be visible')
|
||
|
|
||
|
def test_check_overtime(self):
|
||
|
with self.with_user('user'):
|
||
|
self.assertEqual(self.user.total_overtime, 0, 'No overtime')
|
||
|
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
self.env['hr.leave'].create({
|
||
|
'name': 'no overtime',
|
||
|
'employee_id': self.employee.id,
|
||
|
'holiday_status_id': self.leave_type_no_alloc.id,
|
||
|
'request_date_from': datetime(2021, 1, 4),
|
||
|
'request_date_to': datetime(2021, 1, 4),
|
||
|
'state': 'draft',
|
||
|
})
|
||
|
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||
|
self.assertEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime')
|
||
|
leave = self.env['hr.leave'].create({
|
||
|
'name': 'no overtime',
|
||
|
'employee_id': self.employee.id,
|
||
|
'holiday_status_id': self.leave_type_no_alloc.id,
|
||
|
'request_date_from': datetime(2021, 1, 4),
|
||
|
'request_date_to': datetime(2021, 1, 4),
|
||
|
})
|
||
|
|
||
|
# The employee doesn't have the right to read the overtime from the leave
|
||
|
overtime = leave.sudo().overtime_id.with_user(self.user)
|
||
|
|
||
|
# An employee cannot delete an overtime adjustment
|
||
|
with self.assertRaises(AccessError), self.cr.savepoint():
|
||
|
overtime.unlink()
|
||
|
|
||
|
# ... nor change its duration
|
||
|
with self.assertRaises(AccessError), self.cr.savepoint():
|
||
|
overtime.duration = 8
|
||
|
|
||
|
def test_leave_adjust_overtime(self):
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||
|
self.assertEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime')
|
||
|
|
||
|
leave = self.env['hr.leave'].create({
|
||
|
'name': 'no overtime',
|
||
|
'employee_id': self.employee.id,
|
||
|
'holiday_status_id': self.leave_type_no_alloc.id,
|
||
|
'request_date_from': datetime(2021, 1, 4),
|
||
|
'request_date_to': datetime(2021, 1, 4),
|
||
|
})
|
||
|
|
||
|
self.assertTrue(leave.overtime_id.adjustment, "An adjustment overtime should be created")
|
||
|
self.assertEqual(leave.overtime_id.duration, -8)
|
||
|
|
||
|
self.assertEqual(self.employee.total_overtime, 0)
|
||
|
|
||
|
leave.action_refuse()
|
||
|
self.assertFalse(leave.overtime_id.exists(), "Overtime should be deleted")
|
||
|
self.assertEqual(self.employee.total_overtime, 8)
|
||
|
|
||
|
leave.action_draft()
|
||
|
self.assertTrue(leave.overtime_id.exists(), "Overtime should be created")
|
||
|
self.assertEqual(self.employee.total_overtime, 0)
|
||
|
|
||
|
overtime = leave.overtime_id
|
||
|
leave.unlink()
|
||
|
self.assertFalse(overtime.exists(), "Overtime should be deleted along with the leave")
|
||
|
self.assertEqual(self.employee.total_overtime, 8)
|
||
|
|
||
|
def test_leave_check_overtime_write(self):
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 3, 8), check_out=datetime(2021, 1, 3, 16))
|
||
|
self.assertEqual(self.employee.total_overtime, 16)
|
||
|
|
||
|
leave = self.env['hr.leave'].create({
|
||
|
'name': 'no overtime',
|
||
|
'employee_id': self.employee.id,
|
||
|
'holiday_status_id': self.leave_type_no_alloc.id,
|
||
|
'request_date_from': '2021-1-4',
|
||
|
'request_date_to': '2021-1-4',
|
||
|
})
|
||
|
self.assertEqual(self.employee.total_overtime, 8)
|
||
|
|
||
|
leave.date_to = datetime(2021, 1, 5)
|
||
|
self.assertEqual(self.employee.total_overtime, 0)
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
leave.date_to = datetime(2021, 1, 6)
|
||
|
|
||
|
leave.date_to = datetime(2021, 1, 4)
|
||
|
self.assertEqual(self.employee.total_overtime, 8)
|
||
|
|
||
|
def test_employee_create_allocation(self):
|
||
|
with self.with_user('user'):
|
||
|
self.assertEqual(self.employee.total_overtime, 0)
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
self.env['hr.leave.allocation'].create({
|
||
|
'name': 'test allocation',
|
||
|
'holiday_status_id': self.leave_type_employee_allocation.id,
|
||
|
'employee_id': self.employee.id,
|
||
|
'number_of_days': 1,
|
||
|
'state': 'confirm',
|
||
|
'date_from': time.strftime('%Y-1-1'),
|
||
|
'date_to': time.strftime('%Y-12-31'),
|
||
|
})
|
||
|
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||
|
self.assertAlmostEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime')
|
||
|
|
||
|
self.env['hr.leave.allocation'].sudo().create({
|
||
|
'name': 'test allocation',
|
||
|
'holiday_status_id': self.leave_type_employee_allocation.id,
|
||
|
'employee_id': self.employee.id,
|
||
|
'number_of_days': 1,
|
||
|
'state': 'confirm',
|
||
|
'date_from': time.strftime('%Y-1-1'),
|
||
|
'date_to': time.strftime('%Y-12-31'),
|
||
|
})
|
||
|
self.assertEqual(self.employee.total_overtime, 0)
|
||
|
|
||
|
leave_type = self.env['hr.leave.type'].sudo().create({
|
||
|
'name': 'Overtime Compensation Employee Allocation',
|
||
|
'company_id': self.company.id,
|
||
|
'requires_allocation': 'yes',
|
||
|
'employee_requests': 'yes',
|
||
|
'allocation_validation_type': 'officer',
|
||
|
'overtime_deductible': False,
|
||
|
})
|
||
|
|
||
|
# User can request another allocation even without overtime
|
||
|
self.env['hr.leave.allocation'].create({
|
||
|
'name': 'test allocation',
|
||
|
'holiday_status_id': leave_type.id,
|
||
|
'employee_id': self.employee.id,
|
||
|
'number_of_days': 1,
|
||
|
'state': 'confirm',
|
||
|
'date_from': time.strftime('%Y-1-1'),
|
||
|
'date_to': time.strftime('%Y-12-31'),
|
||
|
})
|
||
|
|
||
|
def test_allocation_check_overtime_write(self):
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 3, 8), check_out=datetime(2021, 1, 3, 16))
|
||
|
self.assertEqual(self.employee.total_overtime, 16, 'Should have 16 hours of overtime')
|
||
|
|
||
|
alloc = self.env['hr.leave.allocation'].create({
|
||
|
'name': 'test allocation',
|
||
|
'holiday_status_id': self.leave_type_employee_allocation.id,
|
||
|
'employee_id': self.employee.id,
|
||
|
'number_of_days': 1,
|
||
|
'state': 'confirm',
|
||
|
'date_from': time.strftime('%Y-1-1'),
|
||
|
'date_to': time.strftime('%Y-12-31'),
|
||
|
})
|
||
|
self.assertEqual(self.employee.total_overtime, 8)
|
||
|
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
alloc.number_of_days = 3
|
||
|
|
||
|
alloc.number_of_days = 2
|
||
|
self.assertEqual(self.employee.total_overtime, 0)
|
||
|
|
||
|
@freeze_time('2022-1-1')
|
||
|
def test_leave_check_cancel(self):
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||
|
self.new_attendance(check_in=datetime(2021, 1, 3, 8), check_out=datetime(2021, 1, 3, 16))
|
||
|
self.assertEqual(self.employee.total_overtime, 16)
|
||
|
|
||
|
leave = self.env['hr.leave'].create({
|
||
|
'name': 'no overtime',
|
||
|
'employee_id': self.employee.id,
|
||
|
'holiday_status_id': self.leave_type_no_alloc.id,
|
||
|
'request_date_from': '2022-1-6',
|
||
|
'request_date_to': '2022-1-6',
|
||
|
})
|
||
|
leave.with_user(self.user_manager).action_validate()
|
||
|
self.assertEqual(self.employee.total_overtime, 8)
|
||
|
|
||
|
self.assertTrue(leave.with_user(self.user).can_cancel)
|
||
|
self.env['hr.holidays.cancel.leave'].with_user(self.user).with_context(default_leave_id=leave.id) \
|
||
|
.new({'reason': 'Test remove holiday'}) \
|
||
|
.action_cancel_leave()
|
||
|
self.assertFalse(leave.overtime_id.exists())
|
||
|
|
||
|
def test_public_leave_overtime(self):
|
||
|
leave = self.env['resource.calendar.leaves'].create([{
|
||
|
'name': 'Public Holiday',
|
||
|
'date_from': datetime(2022, 5, 5, 6),
|
||
|
'date_to': datetime(2022, 5, 5, 18),
|
||
|
}])
|
||
|
|
||
|
leave.company_id.write({
|
||
|
'hr_attendance_overtime': True,
|
||
|
'overtime_start_date': datetime(2021, 1, 1),
|
||
|
})
|
||
|
self.assertNotEqual(leave.company_id, self.employee.company_id)
|
||
|
self.manager.company_id = leave.company_id.id
|
||
|
|
||
|
for emp in [self.employee, self.manager]:
|
||
|
self.env['hr.attendance'].create({
|
||
|
'employee_id': emp.id,
|
||
|
'check_in': datetime(2022, 5, 5, 8),
|
||
|
'check_out': datetime(2022, 5, 5, 17),
|
||
|
})
|
||
|
|
||
|
self.assertEqual(self.employee.total_overtime, 0, "Should have 0 hours of overtime as the public holiday doesn't impact his company")
|
||
|
self.assertEqual(self.manager.total_overtime, 8, 'Should have 8 hours of overtime (there is one hour of lunch)')
|