mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-26 21:52:02 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -2,3 +2,4 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_holidays_overtime
|
||||
from . import test_accrual_allocations
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
from freezegun import freeze_time
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo.tests import Form, tagged
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'accruals')
|
||||
class TestAccrualAllocationsAttendance(TestHrHolidaysCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestAccrualAllocationsAttendance, cls).setUpClass()
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'hr',
|
||||
})
|
||||
|
||||
def test_frequency_hourly_attendance(self):
|
||||
with freeze_time("2017-12-05"):
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
||||
'is_based_on_worked_time': True,
|
||||
'can_be_carryover': True,
|
||||
'level_ids': [(0, 0, {
|
||||
'milestone_date': 'after',
|
||||
'start_count': 1,
|
||||
'start_type': 'day',
|
||||
'added_value': 1,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'worked_hours',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 10000,
|
||||
'action_with_unused_accruals': 'all',
|
||||
})],
|
||||
})
|
||||
allocation = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
||||
'name': 'Accrual allocation for employee',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'number_of_days': 0,
|
||||
'allocation_type': 'accrual',
|
||||
})
|
||||
allocation.action_approve()
|
||||
self.assertFalse(allocation.nextcall, 'There should be no nextcall set on the allocation.')
|
||||
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet.')
|
||||
allocation._update_accrual()
|
||||
tomorrow = datetime.date.today() + relativedelta(days=2)
|
||||
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet. The accrual starts tomorrow.')
|
||||
|
||||
self.env['hr.attendance'].create({
|
||||
'employee_id': self.employee_emp.id,
|
||||
'check_in': datetime.datetime(2017, 12, 6, 8, 0, 0),
|
||||
'check_out': datetime.datetime(2017, 12, 6, 13, 22, 0),
|
||||
})
|
||||
|
||||
with freeze_time(tomorrow):
|
||||
allocation._update_accrual()
|
||||
nextcall = datetime.date.today() + relativedelta(days=1)
|
||||
self.assertAlmostEqual(allocation.number_of_days, 4.37, places=2)
|
||||
self.assertEqual(allocation.nextcall, nextcall, 'The next call date of the cron should be in 2 days.')
|
||||
allocation._update_accrual()
|
||||
self.assertAlmostEqual(allocation.number_of_days, 4.37, places=2)
|
||||
|
||||
def test_accrual_allocation_based_on_attendance(self):
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].create({
|
||||
'name': 'Accrual Plan For Test',
|
||||
'is_based_on_worked_time': False,
|
||||
'accrued_gain_time': 'end',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'year_start',
|
||||
'level_ids': [(0, 0, {
|
||||
'milestone_date': 'after',
|
||||
'start_count': 1,
|
||||
'added_value': 1,
|
||||
'added_value_type': 'hour',
|
||||
'frequency': 'worked_hours',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 100,
|
||||
'action_with_unused_accruals': 'all',
|
||||
})],
|
||||
})
|
||||
self.env['hr.attendance'].create({
|
||||
'employee_id': self.employee_emp.id,
|
||||
'check_in': datetime.datetime(2024, 4, 1, 8, 0, 0),
|
||||
'check_out': datetime.datetime(2024, 4, 1, 17, 0, 0),
|
||||
})
|
||||
with Form(self.env['hr.leave.allocation'].with_user(self.user_hrmanager)) as allocation_form:
|
||||
allocation_form.allocation_type = 'accrual'
|
||||
allocation_form.employee_id = self.employee_emp
|
||||
allocation_form.accrual_plan_id = accrual_plan
|
||||
allocation_form.holiday_status_id = self.leave_type
|
||||
allocation_form.date_from = datetime.date(2024, 3, 20)
|
||||
allocation_form.name = 'Accrual allocation for employee'
|
||||
self.assertEqual(allocation_form.number_of_hours_display, 8.0)
|
||||
allocation_form.date_from = datetime.date(2024, 3, 25)
|
||||
allocation_form.name = 'Accrual allocation for employee'
|
||||
self.assertEqual(allocation_form.number_of_hours_display, 8.0)
|
||||
|
||||
def test_accrual_allocation_with_overlapping_attendance(self):
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].create({
|
||||
'name': 'Accrual Plan For Test',
|
||||
'is_based_on_worked_time': True,
|
||||
'accrued_gain_time': 'end',
|
||||
'carryover_date': 'year_start',
|
||||
'level_ids': [(0, 0, {
|
||||
'start_count': 0,
|
||||
'added_value': 1,
|
||||
'added_value_type': 'hour',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 100,
|
||||
'frequency': 'worked_hours'
|
||||
})],
|
||||
})
|
||||
with freeze_time("2024-4-1"):
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'name': 'Accrual allocation for employee',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'number_of_days': 0,
|
||||
'allocation_type': 'accrual',
|
||||
})
|
||||
allocation.action_approve()
|
||||
|
||||
self.env['hr.attendance'].create({
|
||||
'employee_id': self.employee_emp.id,
|
||||
'check_in': datetime.datetime(2024, 4, 1, 22, 0, 0),
|
||||
'check_out': datetime.datetime(2024, 4, 2, 7, 0, 0),
|
||||
})
|
||||
|
||||
with freeze_time(datetime.datetime(2024, 4, 2, 20, 0, 0)):
|
||||
# Only counts the part of the attendance on the 01/04/2024: 2 hours
|
||||
allocation._update_accrual()
|
||||
self.assertEqual(allocation.number_of_days, 0.25) # 2 / 8 = 0.25
|
||||
|
||||
with freeze_time(datetime.datetime(2024, 4, 3, 20, 0, 0)):
|
||||
# Counts the whole attendance: 9 hours
|
||||
allocation._update_accrual()
|
||||
self.assertEqual(allocation.number_of_days, 1.125) # 9 / 8 = 1.125
|
||||
|
||||
def test_accrual_allocation_with_overlapping_attendance_timezone(self):
|
||||
self.employee_emp.tz = 'Asia/Tokyo'
|
||||
self.employee_emp.resource_calendar_id.tz = 'Asia/Tokyo'
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].create({
|
||||
'name': 'Accrual Plan For Test',
|
||||
'is_based_on_worked_time': True,
|
||||
'accrued_gain_time': 'end',
|
||||
'carryover_date': 'year_start',
|
||||
'level_ids': [(0, 0, {
|
||||
'start_count': 0,
|
||||
'added_value': 1,
|
||||
'added_value_type': 'hour',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 100,
|
||||
'frequency': 'worked_hours'
|
||||
})],
|
||||
})
|
||||
with freeze_time("2024-4-1"):
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'name': 'Accrual allocation for employee',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'number_of_days': 0,
|
||||
'allocation_type': 'accrual',
|
||||
})
|
||||
allocation.action_approve()
|
||||
|
||||
self.env['hr.attendance'].create({
|
||||
'employee_id': self.employee_emp.id,
|
||||
'check_in': datetime.datetime(2024, 4, 1, 22, 0, 0), # In Tokyo: 2024/04/02, 7h
|
||||
'check_out': datetime.datetime(2024, 4, 2, 7, 0, 0), # In Tokyo: 2024/04/02, 16h
|
||||
})
|
||||
|
||||
with freeze_time(datetime.datetime(2024, 4, 2, 20, 0, 0)):
|
||||
# Only counts the part of the attendance on the 01/04/2024 UTC: 2 hours
|
||||
allocation._update_accrual()
|
||||
self.assertEqual(allocation.number_of_days, 0.25) # 2 / 8 = 0.25
|
||||
|
||||
with freeze_time(datetime.datetime(2024, 4, 3, 20, 0, 0)):
|
||||
# Counts the whole attendance: 9 hours - 1h of lunchtime = 8h
|
||||
allocation._update_accrual()
|
||||
self.assertEqual(allocation.number_of_days, 1.0) # 8 / 8 = 1.0
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests import new_test_user
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
from odoo.exceptions import AccessError, ValidationError
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from freezegun import freeze_time
|
||||
import time
|
||||
|
|
@ -18,11 +19,9 @@ class TestHolidaysOvertime(TransactionCase):
|
|||
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,hr_attendance.group_hr_attendance', 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', company_id=cls.company.id).with_company(cls.company)
|
||||
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',
|
||||
|
|
@ -39,69 +38,92 @@ class TestHolidaysOvertime(TransactionCase):
|
|||
cls.leave_type_no_alloc = cls.env['hr.leave.type'].create({
|
||||
'name': 'Overtime Compensation No Allocation',
|
||||
'company_id': cls.company.id,
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
'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',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': True,
|
||||
'allocation_validation_type': 'hr',
|
||||
'overtime_deductible': True,
|
||||
})
|
||||
cls.regular_leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Regular Leave Type',
|
||||
'company_id': cls.company.id,
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.ruleset = cls.env['hr.attendance.overtime.ruleset'].create({
|
||||
'name': 'Ruleset schedule quantity',
|
||||
'rule_ids': [
|
||||
Command.create({
|
||||
'name': 'Rule schedule quantity',
|
||||
'base_off': 'quantity',
|
||||
'expected_hours_from_contract': True,
|
||||
'quantity_period': 'day',
|
||||
'compensable_as_leave': True,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
cls.ruleset_with_timing_rule = cls.env['hr.attendance.overtime.ruleset'].create({
|
||||
'name': 'Ruleset schedule quantity',
|
||||
'rule_ids': [
|
||||
Command.create({
|
||||
'name': 'Rule schedule quantity',
|
||||
'base_off': 'quantity',
|
||||
'expected_hours_from_contract': True,
|
||||
'quantity_period': 'day',
|
||||
'compensable_as_leave': True,
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Rule employee is off',
|
||||
'base_off': 'timing',
|
||||
'timing_type': 'leave',
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
cls.employee.ruleset_id = cls.ruleset
|
||||
cls.employee.version_ids.sorted('date_version')[0].date_version = datetime(2020, 1, 1).date()
|
||||
|
||||
cls.manager.ruleset_id = cls.ruleset
|
||||
cls.manager.version_ids.sorted('date_version')[0].date_version = datetime(2020, 1, 1).date()
|
||||
|
||||
def new_attendance(self, check_in, check_out=False):
|
||||
return self.env['hr.attendance'].create({
|
||||
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 _check_deductible(self, expected_hours):
|
||||
ded = self.employee._get_deductible_employee_overtime()
|
||||
self.assertAlmostEqual(ded[self.employee], expected_hours, 5)
|
||||
|
||||
def test_check_overtime(self):
|
||||
with self.with_user('user'):
|
||||
self.assertEqual(self.user.total_overtime, 0, 'No overtime')
|
||||
self.assertEqual(self.employee.total_overtime, 0, 'No overtime')
|
||||
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].create({
|
||||
'name': 'no overtime',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type_no_alloc.id,
|
||||
'number_of_days': 1,
|
||||
'date_from': datetime(2021, 1, 4),
|
||||
'date_to': datetime(2021, 1, 5),
|
||||
'state': 'draft',
|
||||
'request_date_from': datetime(2021, 1, 4),
|
||||
'request_date_to': datetime(2021, 1, 4),
|
||||
'state': 'confirm',
|
||||
})
|
||||
|
||||
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,
|
||||
'number_of_days': 1,
|
||||
'date_from': datetime(2021, 1, 4),
|
||||
'date_to': datetime(2021, 1, 5),
|
||||
})
|
||||
|
||||
# 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
|
||||
overtime_leave_data = self.leave_type_no_alloc.get_allocation_data(self.employee)
|
||||
self.assertEqual(overtime_leave_data[self.employee][0][1]['virtual_remaining_leaves'], 8.0)
|
||||
# `employee_company` must be present to avoid traceback when opening the Time Off Type
|
||||
self.assertTrue(overtime_leave_data[self.employee][0][1].get('employee_company'))
|
||||
|
||||
def test_leave_adjust_overtime(self):
|
||||
self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
|
||||
|
|
@ -111,28 +133,13 @@ class TestHolidaysOvertime(TransactionCase):
|
|||
'name': 'no overtime',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type_no_alloc.id,
|
||||
'number_of_days': 1,
|
||||
'date_from': datetime(2021, 1, 4),
|
||||
'date_to': datetime(2021, 1, 5),
|
||||
'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)
|
||||
|
||||
self._check_deductible(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.assertFalse(leave.overtime_id.exists(), "Overtime should not be created")
|
||||
self.assertEqual(self.employee.total_overtime, 8)
|
||||
|
||||
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)
|
||||
self._check_deductible(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))
|
||||
|
|
@ -143,93 +150,91 @@ class TestHolidaysOvertime(TransactionCase):
|
|||
'name': 'no overtime',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type_no_alloc.id,
|
||||
'number_of_days': 1,
|
||||
'date_from': datetime(2021, 1, 4),
|
||||
'date_to': datetime(2021, 1, 5),
|
||||
'request_date_from': '2021-01-04',
|
||||
'request_date_to': '2021-01-04',
|
||||
})
|
||||
self.assertEqual(self.employee.total_overtime, 8)
|
||||
|
||||
leave.date_to = datetime(2021, 1, 6)
|
||||
self.assertEqual(self.employee.total_overtime, 0)
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
leave.date_to = datetime(2021, 1, 7)
|
||||
self._check_deductible(8)
|
||||
|
||||
leave.date_to = datetime(2021, 1, 5)
|
||||
self.assertEqual(self.employee.total_overtime, 8)
|
||||
self._check_deductible(0)
|
||||
with self.assertRaises(ValidationError):
|
||||
leave.date_to = datetime(2021, 1, 6)
|
||||
|
||||
leave.date_to = datetime(2021, 1, 4)
|
||||
self._check_deductible(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():
|
||||
with self.assertRaises(ValidationError):
|
||||
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': 'draft',
|
||||
'date_from': time.strftime('%Y-1-1'),
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-01-01'),
|
||||
'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')
|
||||
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
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': 'draft',
|
||||
'date_from': time.strftime('%Y-1-1'),
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-01-01'),
|
||||
'date_to': time.strftime('%Y-12-31'),
|
||||
})
|
||||
allocation.action_confirm()
|
||||
self.assertEqual(self.employee.total_overtime, 0)
|
||||
self._check_deductible(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',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': True,
|
||||
'allocation_validation_type': 'hr',
|
||||
'overtime_deductible': False,
|
||||
})
|
||||
|
||||
# User can request another allocation even without overtime
|
||||
allocation2 = self.env['hr.leave.allocation'].create({
|
||||
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': 'draft',
|
||||
'date_from': time.strftime('%Y-1-1'),
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-01-01'),
|
||||
'date_to': time.strftime('%Y-12-31'),
|
||||
})
|
||||
allocation2.action_confirm()
|
||||
|
||||
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')
|
||||
self._check_deductible(16)
|
||||
|
||||
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': 'draft',
|
||||
'date_from': time.strftime('%Y-1-1'),
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-01-01'),
|
||||
'date_to': time.strftime('%Y-12-31'),
|
||||
})
|
||||
self.assertEqual(self.employee.total_overtime, 8)
|
||||
self._check_deductible(8)
|
||||
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
with self.assertRaises(ValidationError):
|
||||
alloc.number_of_days = 3
|
||||
|
||||
alloc.number_of_days = 2
|
||||
self.assertEqual(self.employee.total_overtime, 0)
|
||||
self._check_deductible(0)
|
||||
|
||||
@freeze_time('2022-1-1')
|
||||
@freeze_time('2022-01-01')
|
||||
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))
|
||||
|
|
@ -239,39 +244,220 @@ class TestHolidaysOvertime(TransactionCase):
|
|||
'name': 'no overtime',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type_no_alloc.id,
|
||||
'number_of_days': 1,
|
||||
'date_from': datetime(2022, 1, 6),
|
||||
'date_to': datetime(2022, 1, 6),
|
||||
'request_date_from': '2022-01-06',
|
||||
'request_date_to': '2022-01-06',
|
||||
})
|
||||
leave.with_user(self.user_manager).action_validate()
|
||||
self.assertEqual(self.employee.total_overtime, 8)
|
||||
leave.with_user(self.user_manager).action_approve()
|
||||
self._check_deductible(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())
|
||||
self._check_deductible(16)
|
||||
|
||||
def test_public_leave_overtime(self):
|
||||
leave = self.env['resource.calendar.leaves'].create([{
|
||||
def test_public_leave_overtime_with_timing_rule(self):
|
||||
(self.employee.version_ids + self.manager.version_ids).ruleset_id = self.ruleset_with_timing_rule
|
||||
self.manager.company_id = self.env.company
|
||||
leave = self.env['resource.calendar.leaves'].with_company(self.manager.company_id).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),
|
||||
'attendance_overtime_validation': 'no_validation',
|
||||
})
|
||||
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, 16),
|
||||
'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")
|
||||
self.assertEqual(self.employee.total_overtime, 0, 'Should have 0 hours of overtime')
|
||||
self.assertEqual(self.manager.total_overtime, 9, "Should have 9 hours of overtime")
|
||||
|
||||
def test_public_leave_overtime_without_timing_rule(self):
|
||||
self.manager.company_id = self.env.company
|
||||
leave = self.env['resource.calendar.leaves'].with_company(self.manager.company_id).create([{
|
||||
'name': 'Public Holiday',
|
||||
'date_from': datetime(2022, 5, 5, 6),
|
||||
'date_to': datetime(2022, 5, 5, 18),
|
||||
}])
|
||||
|
||||
leave.company_id.write({
|
||||
'attendance_overtime_validation': 'no_validation',
|
||||
})
|
||||
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')
|
||||
self.assertEqual(self.manager.total_overtime, 9, "Should have 9 hours of overtime (because of the quantity rule)")
|
||||
|
||||
def test_worked_leave_type_overtime(self):
|
||||
""" Test that an attendance during a worked time off doesn't count as overtime. """
|
||||
calendar = self.env['resource.calendar'].create({'name': 'Calendar'})
|
||||
self.env['hr.version'].create({
|
||||
'date_version': datetime(2021, 1, 1),
|
||||
'contract_date_start': datetime(2021, 1, 1),
|
||||
'contract_date_end': datetime(2021, 12, 31),
|
||||
'name': 'Contract 2021',
|
||||
'resource_calendar_id': calendar.id,
|
||||
'wage': 5000.0,
|
||||
'employee_id': self.employee.id,
|
||||
})
|
||||
|
||||
leave_type_worked = self.env['hr.leave.type'].create({
|
||||
'name': 'Worked Leave Type',
|
||||
'company_id': self.company.id,
|
||||
'requires_allocation': False,
|
||||
'overtime_deductible': False,
|
||||
'time_type': 'other',
|
||||
})
|
||||
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'no overtime',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type_worked.id,
|
||||
'request_date_from': datetime(2021, 1, 5),
|
||||
'request_date_to': datetime(2021, 1, 5),
|
||||
})
|
||||
leave._action_validate()
|
||||
|
||||
att = self.env['hr.attendance'].create({
|
||||
'employee_id': self.employee.id,
|
||||
'check_in': datetime(2021, 1, 5, 8),
|
||||
'check_out': datetime(2021, 1, 5, 16),
|
||||
})
|
||||
|
||||
self.assertEqual(att.overtime_hours, 0)
|
||||
self.assertEqual(att.worked_hours, 7)
|
||||
|
||||
self.assertEqual(self.employee.total_overtime, 0, 'Should have 0 hours of overtime')
|
||||
|
||||
def test_overtime_approval_after_refusal(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_approve()
|
||||
self._check_deductible(8)
|
||||
|
||||
leave.with_user(self.user_manager).action_refuse()
|
||||
self._check_deductible(16)
|
||||
|
||||
leave.with_user(self.user_manager).action_approve(check_state=False)
|
||||
self._check_deductible(8)
|
||||
|
||||
def test_get_overtime_data_by_employee(self):
|
||||
# Even if employee has not overtime, it should still appear in return
|
||||
# value
|
||||
expected_overtime_data = {
|
||||
'compensable_overtime': 0,
|
||||
'not_compensable_overtime': 0,
|
||||
'unspent_compensable_overtime': 0,
|
||||
}
|
||||
overtime_data = self.employee.get_overtime_data_by_employee()
|
||||
self.assertEqual(
|
||||
overtime_data[self.employee.id],
|
||||
expected_overtime_data,
|
||||
"get_overtime_data_by_employee() did not return an empty overtime_data",
|
||||
)
|
||||
|
||||
# These attendances will create some extra hours that is deductible as
|
||||
# time off
|
||||
self.new_attendance(
|
||||
check_in=datetime(2021, 1, 1, 8), check_out=datetime(2021, 1, 1, 20)
|
||||
)
|
||||
self.new_attendance(
|
||||
check_in=datetime(2021, 1, 2, 4), check_out=datetime(2021, 1, 2, 20)
|
||||
)
|
||||
self.new_attendance(
|
||||
check_in=datetime(2021, 2, 2, 4), check_out=datetime(2021, 2, 2, 18)
|
||||
)
|
||||
|
||||
# The extra hours from the next attendances will not be deductible as
|
||||
# time off. Affects compensable_overtime's value.
|
||||
not_compensable_ruleset = self.env[
|
||||
'hr.attendance.overtime.ruleset'
|
||||
].create(
|
||||
{
|
||||
'name': 'Ruleset schedule quantity',
|
||||
'rule_ids': [
|
||||
Command.create(
|
||||
{
|
||||
'name': 'Extra Mile',
|
||||
'base_off': 'quantity',
|
||||
'expected_hours_from_contract': True,
|
||||
'quantity_period': 'day',
|
||||
'compensable_as_leave': False,
|
||||
}
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
self.employee.ruleset_id = not_compensable_ruleset
|
||||
|
||||
# Creates extra hours, but won't be usable as time off
|
||||
# Affects not_compensable_overtime's value.
|
||||
self.new_attendance(
|
||||
check_in=datetime(2021, 3, 3, 5), check_out=datetime(2021, 3, 3, 20)
|
||||
)
|
||||
|
||||
# Use some of the overtime as a day off (8 hours)
|
||||
# Affects unspent_compensable_time's value
|
||||
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_approve()
|
||||
|
||||
expected_overtime_data = {
|
||||
'compensable_overtime': 24.0,
|
||||
'not_compensable_overtime': 6.0,
|
||||
'unspent_compensable_overtime': 16.0,
|
||||
}
|
||||
overtime_data = self.employee.get_overtime_data_by_employee()
|
||||
self.assertEqual(
|
||||
overtime_data[self.employee.id],
|
||||
expected_overtime_data,
|
||||
"get_overtime_data_by_employee() did not return the expected values",
|
||||
)
|
||||
|
||||
def test_overtime_update_after_leave(self):
|
||||
self.employee.ruleset_id = self.ruleset_with_timing_rule
|
||||
|
||||
self.new_attendance(check_in=datetime(2026, 1, 13, 8), check_out=datetime(2026, 1, 13, 16))
|
||||
self.assertEqual(self.employee.total_overtime, 0, 'Should have 0 hours of overtime')
|
||||
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'Vacation Yippie',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.regular_leave_type.id,
|
||||
'request_date_from': datetime(2026, 1, 13),
|
||||
'request_date_to': datetime(2026, 1, 13),
|
||||
})
|
||||
self.assertEqual(self.employee.total_overtime, 0, 'Should have 0 hours of overtime as the leave has not been approved yet.')
|
||||
|
||||
leave.action_approve()
|
||||
self.assertEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime as the leave has been approved.')
|
||||
|
||||
leave.action_refuse()
|
||||
self.assertEqual(self.employee.total_overtime, 0, 'Should have 0 hours of overtime as the leave has been refused.')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue