1273 lines
62 KiB
Python
1273 lines
62 KiB
Python
# -*- coding: utf-8 -*-
|
|
# 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 tagged
|
|
from odoo.tools import float_compare
|
|
from odoo.exceptions import ValidationError
|
|
|
|
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestAccrualAllocations(TestHrHolidaysCommon):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestAccrualAllocations, cls).setUpClass()
|
|
cls.leave_type = cls.env['hr.leave.type'].create({
|
|
'name': 'Paid Time Off',
|
|
'time_type': 'leave',
|
|
'requires_allocation': 'yes',
|
|
'allocation_validation_type': 'officer',
|
|
})
|
|
|
|
def setAllocationCreateDate(self, allocation_id, date):
|
|
""" This method is a hack in order to be able to define/redefine the create_date
|
|
of the allocations.
|
|
This is done in SQL because ORM does not allow to write onto the create_date field.
|
|
"""
|
|
self.env.cr.execute("""
|
|
UPDATE
|
|
hr_leave_allocation
|
|
SET create_date = '%s'
|
|
WHERE id = %s
|
|
""" % (date, allocation_id))
|
|
|
|
def test_accrual_unlink(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
})
|
|
|
|
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',
|
|
})
|
|
|
|
with self.assertRaises(ValidationError):
|
|
accrual_plan.unlink()
|
|
|
|
allocation.unlink()
|
|
accrual_plan.unlink()
|
|
|
|
def test_frequency_daily(self):
|
|
with freeze_time(datetime.date(2017, 12, 5)):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 10000
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
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.')
|
|
|
|
with freeze_time(tomorrow):
|
|
allocation._update_accrual()
|
|
nextcall = datetime.date.today() + relativedelta(days=1)
|
|
self.assertEqual(allocation.number_of_days, 1, 'There should be 1 day allocated.')
|
|
self.assertEqual(allocation.nextcall, nextcall, 'The next call date of the cron should be in 2 days.')
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 1, 'There should be only 1 day allocated.')
|
|
|
|
def test_frequency_weekly(self):
|
|
with freeze_time(datetime.date(2017, 12, 5)):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 10000
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2021-09-03',
|
|
})
|
|
with freeze_time(datetime.date.today() + relativedelta(days=2)):
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
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()
|
|
nextWeek = allocation.date_from + relativedelta(days=1, weekday=0)
|
|
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet. The accrual starts tomorrow.')
|
|
|
|
with freeze_time(nextWeek):
|
|
allocation._update_accrual()
|
|
nextWeek = datetime.date.today() + relativedelta(days=1, weekday=0)
|
|
#Prorated
|
|
self.assertAlmostEqual(allocation.number_of_days, 0.2857, 4, 'There should be 0.2857 day allocated.')
|
|
self.assertEqual(allocation.nextcall, nextWeek, 'The next call date of the cron should be in 2 weeks')
|
|
|
|
with freeze_time(nextWeek):
|
|
allocation._update_accrual()
|
|
nextWeek = datetime.date.today() + relativedelta(days=1, weekday=0)
|
|
self.assertAlmostEqual(allocation.number_of_days, 1.2857, 4, 'There should be 1.2857 day allocated.')
|
|
self.assertEqual(allocation.nextcall, nextWeek, 'The next call date of the cron should be in 2 weeks')
|
|
|
|
def test_frequency_bimonthly(self):
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'bimonthly',
|
|
'first_day': 1,
|
|
'second_day': 15,
|
|
'maximum_leave': 10000,
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2021-09-03',
|
|
})
|
|
self.setAllocationCreateDate(allocation.id, '2021-09-01 00:00:00')
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
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()
|
|
next_date = datetime.date(2021, 9, 15)
|
|
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet. The accrual starts tomorrow.')
|
|
|
|
with freeze_time(next_date):
|
|
next_date = datetime.date(2021, 10, 1)
|
|
allocation._update_accrual()
|
|
#Prorated
|
|
self.assertAlmostEqual(allocation.number_of_days, 0.7857, 4, 'There should be 0.7857 day allocated.')
|
|
self.assertEqual(allocation.nextcall, next_date, 'The next call date of the cron should be October 1st')
|
|
|
|
with freeze_time(next_date):
|
|
allocation._update_accrual()
|
|
#Not Prorated
|
|
self.assertAlmostEqual(allocation.number_of_days, 1.7857, 4, 'There should be 1.7857 day allocated.')
|
|
|
|
def test_frequency_monthly(self):
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'maximum_leave': 10000
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2021-08-31',
|
|
})
|
|
self.setAllocationCreateDate(allocation.id, '2021-09-01 00:00:00')
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
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()
|
|
next_date = datetime.date(2021, 10, 1)
|
|
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet. The accrual starts tomorrow.')
|
|
|
|
with freeze_time(next_date):
|
|
next_date = datetime.date(2021, 11, 1)
|
|
allocation._update_accrual()
|
|
# Prorata = 1 since a whole month passed
|
|
self.assertEqual(allocation.number_of_days, 1, 'There should be 1 day allocated.')
|
|
self.assertEqual(allocation.nextcall, next_date, 'The next call date of the cron should be November 1st')
|
|
|
|
def test_frequency_biyearly(self):
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'biyearly',
|
|
'maximum_leave': 10000,
|
|
})],
|
|
})
|
|
#this sets up an accrual on the 1st of January and the 1st of July
|
|
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',
|
|
})
|
|
self.setAllocationCreateDate(allocation.id, '2021-09-01 00:00:00')
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
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()
|
|
next_date = datetime.date(2022, 1, 1)
|
|
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet. The accrual starts tomorrow.')
|
|
|
|
with freeze_time(next_date):
|
|
next_date = datetime.date(2022, 7, 1)
|
|
allocation._update_accrual()
|
|
# Prorated
|
|
self.assertAlmostEqual(allocation.number_of_days, 0.6576, 4, 'There should be 0.6576 day allocated.')
|
|
self.assertEqual(allocation.nextcall, next_date, 'The next call date of the cron should be July 1st')
|
|
|
|
with freeze_time(next_date):
|
|
allocation._update_accrual()
|
|
# Not Prorated
|
|
self.assertAlmostEqual(allocation.number_of_days, 1.6576, 4, 'There should be 1.6576 day allocated.')
|
|
|
|
def test_frequency_yearly(self):
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'yearly',
|
|
'maximum_leave': 10000,
|
|
})],
|
|
})
|
|
#this sets up an accrual on the 1st of January
|
|
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',
|
|
})
|
|
self.setAllocationCreateDate(allocation.id, '2021-09-01 00:00:00')
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
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()
|
|
next_date = datetime.date(2022, 1, 1)
|
|
self.assertEqual(allocation.number_of_days, 0, 'There should be no days allocated yet. The accrual starts tomorrow.')
|
|
|
|
with freeze_time(next_date):
|
|
next_date = datetime.date(2023, 1, 1)
|
|
allocation._update_accrual()
|
|
self.assertAlmostEqual(allocation.number_of_days, 0.3315, 4, 'There should be 0.3315 day allocated.')
|
|
self.assertEqual(allocation.nextcall, next_date, 'The next call date of the cron should be January 1st 2023')
|
|
|
|
with freeze_time(next_date):
|
|
allocation._update_accrual()
|
|
self.assertAlmostEqual(allocation.number_of_days, 1.3315, 4, 'There should be 1.3315 day allocated.')
|
|
|
|
def test_check_gain(self):
|
|
# 2 accruals, one based on worked time, one not
|
|
# check gain
|
|
with freeze_time('2021-08-30'):
|
|
attendances = []
|
|
for index in range(5):
|
|
attendances.append((0, 0, {
|
|
'name': '%s_%d' % ('40 Hours', index),
|
|
'hour_from': 8,
|
|
'hour_to': 12,
|
|
'dayofweek': str(index),
|
|
'day_period': 'morning'
|
|
}))
|
|
attendances.append((0, 0, {
|
|
'name': '%s_%d' % ('40 Hours', index),
|
|
'hour_from': 13,
|
|
'hour_to': 17,
|
|
'dayofweek': str(index),
|
|
'day_period': 'afternoon'
|
|
}))
|
|
calendar_emp = self.env['resource.calendar'].create({
|
|
'name': '40 Hours',
|
|
'tz': self.employee_emp.tz,
|
|
'attendance_ids': attendances,
|
|
})
|
|
self.employee_emp.resource_calendar_id = calendar_emp.id
|
|
|
|
accrual_plan_not_based_on_worked_time = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 5,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 10000,
|
|
})],
|
|
})
|
|
accrual_plan_based_on_worked_time = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 5,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 10000,
|
|
'is_based_on_worked_time': True,
|
|
})],
|
|
})
|
|
allocation_not_worked_time = 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_not_based_on_worked_time.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'number_of_days': 0,
|
|
'allocation_type': 'accrual',
|
|
'state': 'confirm',
|
|
})
|
|
allocation_worked_time = 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_based_on_worked_time.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'number_of_days': 0,
|
|
'allocation_type': 'accrual',
|
|
'state': 'confirm',
|
|
})
|
|
(allocation_not_worked_time | allocation_worked_time).action_validate()
|
|
self.setAllocationCreateDate(allocation_not_worked_time.id, '2021-08-01 00:00:00')
|
|
self.setAllocationCreateDate(allocation_worked_time.id, '2021-08-01 00:00:00')
|
|
holiday_type = self.env['hr.leave.type'].create({
|
|
'name': 'Paid Time Off',
|
|
'requires_allocation': 'no',
|
|
'responsible_id': self.user_hrmanager_id,
|
|
'time_type': 'leave',
|
|
})
|
|
leave = self.env['hr.leave'].create({
|
|
'name': 'leave',
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': holiday_type.id,
|
|
'date_from': '2021-09-02 00:00:00',
|
|
'date_to': '2021-09-02 23:59:59',
|
|
})
|
|
leave.action_validate()
|
|
self.assertFalse(allocation_not_worked_time.nextcall, 'There should be no nextcall set on the allocation.')
|
|
self.assertFalse(allocation_worked_time.nextcall, 'There should be no nextcall set on the allocation.')
|
|
self.assertEqual(allocation_not_worked_time.number_of_days, 0, 'There should be no days allocated yet.')
|
|
self.assertEqual(allocation_worked_time.number_of_days, 0, 'There should be no days allocated yet.')
|
|
|
|
next_date = datetime.date(2021, 9, 6)
|
|
with freeze_time(next_date):
|
|
# next_date = datetime.date(2021, 9, 13)
|
|
self.env['hr.leave.allocation']._update_accrual()
|
|
# Prorated
|
|
self.assertAlmostEqual(allocation_not_worked_time.number_of_days, 4.2857, 4, 'There should be 4.2857 days allocated.')
|
|
# 3.75 -> starts 1 day after allocation date -> 31/08-3/09 => 4 days - 1 days time off => (3 / 4) * 5 days
|
|
# ^ result without prorata
|
|
# Prorated
|
|
self.assertAlmostEqual(allocation_worked_time.number_of_days, 3, 4, 'There should be 3 days allocated.')
|
|
self.assertEqual(allocation_not_worked_time.nextcall, datetime.date(2021, 9, 13), 'The next call date of the cron should be the September 13th')
|
|
self.assertEqual(allocation_worked_time.nextcall, datetime.date(2021, 9, 13), 'The next call date of the cron should be the September 13th')
|
|
|
|
with freeze_time(next_date + relativedelta(days=7)):
|
|
next_date = datetime.date(2021, 9, 20)
|
|
self.env['hr.leave.allocation']._update_accrual()
|
|
self.assertAlmostEqual(allocation_not_worked_time.number_of_days, 9.2857, 4, 'There should be 9.2857 days allocated.')
|
|
self.assertEqual(allocation_not_worked_time.nextcall, next_date, 'The next call date of the cron should be September 20th')
|
|
self.assertAlmostEqual(allocation_worked_time.number_of_days, 8, 4, 'There should be 8 days allocated.')
|
|
self.assertEqual(allocation_worked_time.nextcall, next_date, 'The next call date of the cron should be September 20th')
|
|
|
|
def test_check_max_value(self):
|
|
with freeze_time(datetime.date(2017, 12, 5)):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 1,
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
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.')
|
|
|
|
with freeze_time(tomorrow):
|
|
allocation._update_accrual()
|
|
nextcall = datetime.date.today() + relativedelta(days=1)
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 1, 'There should be only 1 day allocated.')
|
|
|
|
with freeze_time(nextcall):
|
|
allocation._update_accrual()
|
|
nextcall = datetime.date.today() + relativedelta(days=1)
|
|
#The maximum value is 1 so this shouldn't change anything
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 1, 'There should be only 1 day allocated.')
|
|
|
|
def test_check_max_value_hours(self):
|
|
with freeze_time(datetime.date(2017, 12, 5)):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'hours',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 4,
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
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.')
|
|
|
|
with freeze_time(tomorrow):
|
|
allocation._update_accrual()
|
|
nextcall = datetime.date.today() + relativedelta(days=10)
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 0.125, 'There should be only 0.125 days allocated.')
|
|
|
|
with freeze_time(nextcall):
|
|
allocation._update_accrual()
|
|
nextcall = datetime.date.today() + relativedelta(days=1)
|
|
#The maximum value is 1 so this shouldn't change anything
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 0.5, 'There should be only 0.5 days allocated.')
|
|
|
|
def test_accrual_transition_immediately(self):
|
|
with freeze_time(datetime.date(2017, 12, 5)):
|
|
#1 accrual with 2 levels and level transition immediately
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'transition_mode': 'immediately',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 1,
|
|
}), (0, 0, {
|
|
'start_count': 10,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 1,
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
next_date = datetime.date.today() + relativedelta(days=11)
|
|
second_level = self.env['hr.leave.accrual.level'].search([('accrual_plan_id', '=', accrual_plan.id), ('start_count', '=', 10)])
|
|
self.assertEqual(allocation._get_current_accrual_plan_level_id(next_date)[0], second_level, 'The second level should be selected')
|
|
|
|
def test_accrual_transition_after_period(self):
|
|
with freeze_time(datetime.date(2017, 12, 5)):
|
|
# 1 accrual with 2 levels and level transition after
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'transition_mode': 'end_of_accrual',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 1,
|
|
}), (0, 0, {
|
|
'start_count': 10,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'maximum_leave': 1,
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
next_date = datetime.date.today() + relativedelta(days=11)
|
|
second_level = self.env['hr.leave.accrual.level'].search([('accrual_plan_id', '=', accrual_plan.id), ('start_count', '=', 10)])
|
|
self.assertEqual(allocation._get_current_accrual_plan_level_id(next_date)[0], second_level, 'The second level should be selected')
|
|
|
|
def test_unused_accrual_lost(self):
|
|
#1 accrual with 2 levels and level transition immediately
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 1,
|
|
'action_with_unused_accruals': 'lost',
|
|
})],
|
|
})
|
|
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': 10,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
|
|
# Reset the cron's lastcall
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2021, 9, 1)
|
|
with freeze_time('2022-01-01'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 1, 'The number of days should be reset')
|
|
|
|
def test_unused_accrual_postponed(self):
|
|
# 1 accrual with 2 levels and level transition after
|
|
# This also tests retroactivity
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 25,
|
|
'action_with_unused_accruals': 'postponed',
|
|
})],
|
|
})
|
|
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': 10,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
|
|
# Reset the cron's lastcall
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2021, 9, 1)
|
|
with freeze_time('2022-01-01'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 25, 'The maximum number of days should be reached and kept.')
|
|
|
|
with freeze_time('2021-01-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 2,
|
|
'added_value_type': 'days',
|
|
'frequency': 'yearly',
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 10,
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
|
|
# Reset the cron's lastcall
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2021, 1, 1)
|
|
with freeze_time('2023-01-26'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 4, 'The maximum number of days should be reached and kept.')
|
|
|
|
def test_accrual_over_years(self):
|
|
hr_leave_allocation = self.env['hr.leave.allocation']
|
|
with freeze_time('2023-11-20'):
|
|
accrual_plan_postponed = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'first_day': 20,
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 10,
|
|
})],
|
|
})
|
|
accrual_plan_lost = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'first_day': 20,
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'lost',
|
|
})],
|
|
})
|
|
allocation_postponed = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual allocation for employee (postponed)',
|
|
'accrual_plan_id': accrual_plan_postponed.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'number_of_days': 0,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation_lost = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual allocation for employee (lost)',
|
|
'accrual_plan_id': accrual_plan_lost.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'number_of_days': 0,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation_postponed.action_confirm()
|
|
allocation_postponed.action_validate()
|
|
allocation_lost.action_confirm()
|
|
allocation_lost.action_validate()
|
|
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2023, 11, 20)
|
|
|
|
with freeze_time('2023-12-21'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(allocation_postponed.number_of_days, 1)
|
|
self.assertEqual(allocation_lost.number_of_days, 1)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2023, 12, 20))
|
|
self.assertEqual(allocation_lost.lastcall, datetime.date(2023, 12, 20))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2024, 1, 20))
|
|
self.assertEqual(allocation_lost.nextcall, datetime.date(2024, 1, 20))
|
|
|
|
with freeze_time('2024-1-5'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(float_compare(allocation_postponed.number_of_days, 1.39, 2), 0)
|
|
self.assertEqual(allocation_lost.number_of_days, 0)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2024, 1, 1))
|
|
self.assertEqual(allocation_lost.lastcall, datetime.date(2023, 12, 20))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2024, 1, 20))
|
|
self.assertEqual(allocation_lost.nextcall, datetime.date(2024, 1, 20))
|
|
|
|
with freeze_time('2024-1-21'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(allocation_postponed.number_of_days, 2)
|
|
self.assertEqual(allocation_lost.number_of_days, 1)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2024, 1, 20))
|
|
self.assertEqual(allocation_lost.lastcall, datetime.date(2024, 1, 20))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2024, 2, 20))
|
|
self.assertEqual(allocation_lost.nextcall, datetime.date(2024, 2, 20))
|
|
|
|
def test_accrual_over_multiple_years(self):
|
|
hr_leave_allocation = self.env['hr.leave.allocation']
|
|
with freeze_time('2023-1-1'):
|
|
accrual_plan_postponed = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'first_day': 20,
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 10,
|
|
})],
|
|
})
|
|
allocation_postponed = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual allocation for employee (postponed)',
|
|
'accrual_plan_id': accrual_plan_postponed.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'number_of_days': 0,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation_postponed.action_confirm()
|
|
allocation_postponed.action_validate()
|
|
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2023, 1, 1)
|
|
|
|
with freeze_time('2026-1-1'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(float_compare(allocation_postponed.number_of_days, 10.39, 2), 0)
|
|
# 2023 --> 2024: 0 + 12 = 12
|
|
# 12 ==> 10 (postpone_max_days)
|
|
# 2024 --> 2025: 10 + 12 = 22
|
|
# 22 ==> 10 (postpone_max_days)
|
|
# 2025 --> 2026: 10 + 12 = 22
|
|
# 22 ==> 10 (postpone_max_days)
|
|
# 1st January 2026: 10 + part between 2025-12-20 and 2026-1-1 = 10.39
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 1, 1))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 1, 20))
|
|
|
|
with freeze_time('2026-1-10'):
|
|
hr_leave_allocation._update_accrual()
|
|
# Nothing changes
|
|
self.assertEqual(float_compare(allocation_postponed.number_of_days, 10.39, 2), 0)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 1, 1))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 1, 20))
|
|
with freeze_time('2026-1-20'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(float_compare(allocation_postponed.number_of_days, 11, 2), 0)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 1, 20))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 2, 20))
|
|
with freeze_time('2026-2-20'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(float_compare(allocation_postponed.number_of_days, 12, 2), 0)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 2, 20))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 3, 20))
|
|
|
|
def test_accrual_over_multiple_years_first_of_month(self):
|
|
hr_leave_allocation = self.env['hr.leave.allocation']
|
|
with freeze_time('2023-1-1'):
|
|
accrual_plan_postponed = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'first_day': 1,
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 10,
|
|
})],
|
|
})
|
|
allocation_postponed = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual allocation for employee (postponed)',
|
|
'accrual_plan_id': accrual_plan_postponed.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'number_of_days': 0,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation_postponed.action_confirm()
|
|
allocation_postponed.action_validate()
|
|
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2023, 1, 1)
|
|
|
|
with freeze_time('2026-1-1'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(allocation_postponed.number_of_days, 11)
|
|
# 2023 --> 2024: 0 + 12 = 12
|
|
# 12 ==> 10 (postpone_max_days)
|
|
# 2024 --> 2025: 10 + 12 = 22
|
|
# 22 ==> 10 (postpone_max_days)
|
|
# 2025 --> 2026: 10 + 12 = 22
|
|
# 22 ==> 10 (postpone_max_days)
|
|
# 1st January 2026: 10 + 1 = 11
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 1, 1))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 2, 1))
|
|
|
|
with freeze_time('2026-1-10'):
|
|
hr_leave_allocation._update_accrual()
|
|
# Nothing changes
|
|
self.assertEqual(allocation_postponed.number_of_days, 11)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 1, 1))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 2, 1))
|
|
with freeze_time('2026-2-20'):
|
|
hr_leave_allocation._update_accrual()
|
|
self.assertEqual(allocation_postponed.number_of_days, 12)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2026, 2, 1))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2026, 3, 1))
|
|
|
|
def test_unused_accrual_postponed_limit(self):
|
|
# 1 accrual with 2 levels and level transition after
|
|
# This also tests retroactivity
|
|
with freeze_time('2021-09-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 25,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 15,
|
|
})],
|
|
})
|
|
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': 10,
|
|
'allocation_type': 'accrual',
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
|
|
# Reset the cron's lastcall
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2021, 9, 1)
|
|
with freeze_time('2022-01-01'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 16, 'The maximum number of days should be reached and kept.')
|
|
|
|
with freeze_time('2021-01-01'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 15,
|
|
'added_value_type': 'days',
|
|
'frequency': 'yearly',
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 7,
|
|
})],
|
|
})
|
|
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_confirm()
|
|
allocation.action_validate()
|
|
|
|
# Reset the cron's lastcall
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2021, 1, 1)
|
|
with freeze_time('2023-01-26'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 22, 'The maximum number of days should be reached and kept.')
|
|
|
|
def test_unused_accrual_postponed_limit_without_nextcall(self):
|
|
with freeze_time('2023-12-25'):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 10,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'first_day': 25,
|
|
'maximum_leave': 0,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'postpone_max_days': 50,
|
|
})],
|
|
})
|
|
allocation_postponed = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual allocation for employee (postponed)',
|
|
'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_postponed.action_confirm()
|
|
allocation_postponed.action_validate()
|
|
|
|
accrual_cron = self.env['ir.cron'].sudo().env.ref('hr_holidays.hr_leave_allocation_cron_accrual')
|
|
accrual_cron.lastcall = datetime.date(2023, 12, 25)
|
|
|
|
with freeze_time('2024-2-10'):
|
|
# allocation_postponed.nextcall = False
|
|
self.env['hr.leave.allocation']._update_accrual()
|
|
self.assertEqual(allocation_postponed.number_of_days, 10)
|
|
self.assertEqual(allocation_postponed.lastcall, datetime.date(2024, 1, 25))
|
|
self.assertEqual(allocation_postponed.nextcall, datetime.date(2024, 2, 25))
|
|
|
|
def test_accrual_skipped_period(self):
|
|
# Test that when an allocation is made in the past and the second level is technically reached
|
|
# that the first level is not skipped completely.
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 15,
|
|
'added_value_type': 'days',
|
|
'frequency': 'biyearly',
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
}), (0, 0, {
|
|
'start_count': 4,
|
|
'start_type': 'month',
|
|
'added_value': 10,
|
|
'added_value_type': 'days',
|
|
'frequency': 'biyearly',
|
|
'maximum_leave': 500,
|
|
'action_with_unused_accruals': 'postponed',
|
|
})],
|
|
})
|
|
allocation = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Allocation - Test',
|
|
'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',
|
|
'date_from': datetime.date(2020, 8, 16),
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
with freeze_time('2022-1-10'):
|
|
allocation._update_accrual()
|
|
self.assertAlmostEqual(allocation.number_of_days, 30.82, 2, "Invalid number of days")
|
|
|
|
def test_three_levels_accrual(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 2,
|
|
'start_type': 'month',
|
|
'added_value': 3,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'maximum_leave': 3,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'first_day': 31,
|
|
}), (0, 0, {
|
|
'start_count': 3,
|
|
'start_type': 'month',
|
|
'added_value': 6,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'maximum_leave': 6,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'first_day': 31,
|
|
}), (0, 0, {
|
|
'start_count': 4,
|
|
'start_type': 'month',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'maximum_leave': 100,
|
|
'action_with_unused_accruals': 'postponed',
|
|
'first_day': 31,
|
|
})],
|
|
})
|
|
allocation = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Allocation - Test',
|
|
'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',
|
|
'date_from': datetime.date(2022, 1, 31),
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
with freeze_time('2022-7-20'):
|
|
allocation._update_accrual()
|
|
# The first level gives 3 days
|
|
# The second level could give 6 days but since the first level was already giving
|
|
# 3 days, the second level gives 3 days to reach the second level's limit.
|
|
# The third level gives 1 day since it only counts for one iteration.
|
|
self.assertEqual(allocation.number_of_days, 7)
|
|
|
|
def test_accrual_lost_previous_days(self):
|
|
# Test that when an allocation with two levels is made and that the first level has it's action
|
|
# with unused accruals set as lost that the days are effectively lost
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [
|
|
(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'maximum_leave': 12,
|
|
'action_with_unused_accruals': 'lost',
|
|
}),
|
|
(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'year',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'monthly',
|
|
'maximum_leave': 12,
|
|
'action_with_unused_accruals': 'lost',
|
|
}),
|
|
],
|
|
})
|
|
allocation = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Allocation - Test',
|
|
'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',
|
|
'date_from': datetime.date(2021, 1, 1),
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
with freeze_time('2022-4-4'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 4, "Invalid number of days")
|
|
|
|
def test_accrual_lost_first_january(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [
|
|
(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 3,
|
|
'added_value_type': 'days',
|
|
'frequency': 'yearly',
|
|
'maximum_leave': 12,
|
|
'action_with_unused_accruals': 'lost',
|
|
})
|
|
],
|
|
})
|
|
allocation = self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Allocation - Test',
|
|
'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',
|
|
'date_from': datetime.date(2019, 1, 1),
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
with freeze_time('2022-4-1'):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 3, "Invalid number of days")
|
|
|
|
def test_accrual_maximum_leaves(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 5,
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2021-09-03',
|
|
})
|
|
|
|
with freeze_time(datetime.date(2021, 10, 3)):
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
allocation._update_accrual()
|
|
|
|
self.assertEqual(allocation.number_of_days, 5, "Should accrue maximum 5 days")
|
|
|
|
def test_accrual_maximum_leaves_no_limit(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 1,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'daily',
|
|
'maximum_leave': 0,
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2021-09-03',
|
|
})
|
|
|
|
with freeze_time(datetime.date(2021, 10, 3)):
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
allocation._update_accrual()
|
|
|
|
self.assertEqual(allocation.number_of_days, 29, "No limits for accrued days")
|
|
|
|
def test_accrual_leaves_taken_maximum(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'days',
|
|
'frequency': 'weekly',
|
|
'week_day': 'mon',
|
|
'maximum_leave': 5,
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2022-01-01',
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
|
|
with freeze_time(datetime.date(2022, 3, 2)):
|
|
allocation._update_accrual()
|
|
|
|
self.assertEqual(allocation.number_of_days, 5, "Maximum of 5 days accrued")
|
|
|
|
leave = self.env['hr.leave'].create({
|
|
'name': 'leave',
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'date_from': '2022-03-07 00:00:00',
|
|
'date_to': '2022-03-11 23:59:59',
|
|
})
|
|
leave.action_validate()
|
|
|
|
with freeze_time(datetime.date(2022, 6, 1)):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 10, "Should accrue 5 additional days")
|
|
|
|
def test_accrual_leaves_taken_maximum_hours(self):
|
|
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
|
'name': 'Accrual Plan For Test',
|
|
'level_ids': [(0, 0, {
|
|
'start_count': 0,
|
|
'start_type': 'day',
|
|
'added_value': 1,
|
|
'added_value_type': 'hours',
|
|
'frequency': 'weekly',
|
|
'week_day': 'mon',
|
|
'maximum_leave': 10,
|
|
})],
|
|
})
|
|
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',
|
|
'date_from': '2022-01-01',
|
|
})
|
|
allocation.action_confirm()
|
|
allocation.action_validate()
|
|
|
|
with freeze_time(datetime.date(2022, 4, 1)):
|
|
allocation._update_accrual()
|
|
|
|
self.assertEqual(allocation.number_of_days, 10 / self.hours_per_day, "Maximum of 10 hours accrued")
|
|
|
|
leave = self.env['hr.leave'].create({
|
|
'name': 'leave',
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.leave_type.id,
|
|
'date_from': '2022-03-07 00:00:00',
|
|
'date_to': '2022-03-07 23:59:59'
|
|
})
|
|
leave.action_validate()
|
|
|
|
with freeze_time(datetime.date(2022, 6, 1)):
|
|
allocation._update_accrual()
|
|
self.assertEqual(allocation.number_of_days, 18 / self.hours_per_day, "Should accrue 8 additional hours")
|