[FIX] hr_holidays : wrong number of days for multilevel accrual plan

Steps to reproduce:
- Create an accrual plan and an allocation request such as described
in the added test and validate the allocation request
- Run the scheduled action for accrual plan computation
- Check the number of allocated days

Expected behavior:
The first level should bring 6 days, the second 3 days and the last
1 day for a total of 10 days

Current behavior:
The 3 level give non integer values closed to the expected for a
total of 9,... days.

Explanation:
When transiting between two levels the nextcall was the last day
of the current level to have the right level, this creates an
inconsistency as the end_date of the _process_accrual_plan_level
level should go up to the first day of the next level to have the
full number of days expected. To solve the issue we change the
level selection so that if the date is the first day of the
level it is still in the previous level. We also modify the places
where the nextcall was modified when transiting level so that there
is no more one day delta anymore.

opw-2868297

closes odoo/odoo#96665

X-original-commit: 5d055130c97c5d8367c2b71fc617f588c5e15741
Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
mafo-odoo
2022-07-27 04:53:37 +02:00
parent c9d7f8b0ed
commit 4c263e0423
2 changed files with 51 additions and 4 deletions
@@ -357,7 +357,7 @@ class HolidaysAllocation(models.Model):
current_level = False
current_level_idx = -1
for idx, level in enumerate(level_ids):
if date >= self.date_from + get_timedelta(level.start_count, level.start_type):
if date > self.date_from + get_timedelta(level.start_count, level.start_type):
current_level = level
current_level_idx = idx
# If transition_mode is set to `immediately` or we are currently on the first level
@@ -421,7 +421,7 @@ class HolidaysAllocation(models.Model):
allocation.nextcall = first_level._get_next_date(allocation.lastcall)
if len(level_ids) > 1:
second_level_start_date = allocation.date_from + get_timedelta(level_ids[1].start_count, level_ids[1].start_type)
allocation.nextcall = min(second_level_start_date - relativedelta(days=1), allocation.nextcall)
allocation.nextcall = min(second_level_start_date, allocation.nextcall)
allocation._message_log(body=first_allocation)
days_added_per_level = defaultdict(lambda: 0)
while allocation.nextcall <= date_to:
@@ -435,7 +435,7 @@ class HolidaysAllocation(models.Model):
# Also prorate this accrual in the event that we are passing from one level to another
if current_level_idx < (len(level_ids) - 1) and allocation.accrual_plan_id.transition_mode == 'immediately':
next_level = level_ids[current_level_idx + 1]
current_level_last_date = allocation.date_from + get_timedelta(next_level.start_count, next_level.start_type) - relativedelta(days=1)
current_level_last_date = allocation.date_from + get_timedelta(next_level.start_count, next_level.start_type)
if allocation.nextcall != current_level_last_date:
nextcall = min(nextcall, current_level_last_date)
@@ -639,7 +639,54 @@ class TestAccrualAllocations(TestHrHolidaysCommon):
allocation.action_validate()
with freeze_time('2022-1-10'):
allocation._update_accrual()
self.assertAlmostEqual(allocation.number_of_days, 30.79, 2, "Invalid number of days")
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()
self.assertEqual(allocation.number_of_days, 10)
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