[FIX] hr_holidays: accross two years accrual plans period fail:

Steps to reproduce:
- Create Accrual plan to add 3 days with frequency Yearly on 1 of
January and days lost at the end of the year
- Submit an Allocation request for this accrual plan starting more than
2 years ago
- Run scheduled action "Accrual Time: Updates the number of time off"

Current behavior:
Allocation request calculation time off computes 0 hours

Expected behavior:
Allocation request calculation time off computes 3 hours for the
period between the first of last year and the first of this year

Explanation:
With this modification there are two changes:

1) When the attribution day is the first of january, the nextcall
set in the _end_of_the_year_accrual is the first day of next
year meaning that no days are going to be computed for the last
year period. We change that so that if the first of the year is
the attribution day then the first of the year is the nextcall and
the last call is computed accordingly.

2) If the period to compute the attributed days is acrros two years
the begining of the period is not the first of the year anymore but
the usual lastcall, this has an impact particularly on based on
worked time accrual plans.

opw-2948216

closes odoo/odoo#99728

X-original-commit: 88ba0e68c7878ac4b45cd7103bfecf9cfb567d4f
Signed-off-by: Fockedey Martin (mafo) <mafo@odoo.com>
Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
mafo-odoo
2022-09-08 01:08:08 +02:00
parent 0f2e34acc8
commit 11713bfb1d
2 changed files with 50 additions and 25 deletions
@@ -330,17 +330,21 @@ class HolidaysAllocation(models.Model):
current_level = allocation._get_current_accrual_plan_level_id(first_day_this_year)[0]
if not current_level:
continue
lastcall = current_level._get_previous_date(first_day_this_year)
nextcall = current_level._get_next_date(first_day_this_year)
if current_level.action_with_unused_accruals == 'lost':
if lastcall == first_day_this_year:
lastcall = current_level._get_previous_date(first_day_this_year - relativedelta(days=1))
nextcall = first_day_this_year
# Allocations are lost but number_of_days should not be lower than leaves_taken
allocation.write({'number_of_days': allocation.leaves_taken, 'lastcall': first_day_this_year, 'nextcall': nextcall})
allocation.write({'number_of_days': allocation.leaves_taken, 'lastcall': lastcall, 'nextcall': nextcall})
elif current_level.action_with_unused_accruals == 'postponed' and current_level.postpone_max_days:
# Make sure the period was ran until the last day of last year
if allocation.nextcall:
allocation.nextcall = last_day_last_year
allocation._process_accrual_plans(last_day_last_year, True)
number_of_days = min(allocation.number_of_days - allocation.leaves_taken, current_level.postpone_max_days) + allocation.leaves_taken
allocation.write({'number_of_days': number_of_days, 'lastcall': first_day_this_year, 'nextcall': nextcall})
allocation.write({'number_of_days': number_of_days, 'lastcall': lastcall, 'nextcall': nextcall})
def _get_current_accrual_plan_level_id(self, date, level_ids=False):
"""
@@ -444,33 +448,24 @@ class HolidaysAllocation(models.Model):
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)
# We have to check for end of year actions if it is within our period
# since we can create retroactive allocations.
if allocation.lastcall.year < allocation.nextcall.year and\
current_level.action_with_unused_accruals == 'postponed' and\
current_level.postpone_max_days > 0:
# Compute number of days kept
allocation_days = allocation.number_of_days - allocation.leaves_taken
allowed_to_keep = max(0, current_level.postpone_max_days - allocation_days)
number_of_days = min(allocation_days, current_level.postpone_max_days)
allocation.number_of_days = number_of_days + allocation.leaves_taken
total_gained_days = sum(days_added_per_level.values())
days_added_per_level.clear()
days_added_per_level[current_level] = min(total_gained_days, allowed_to_keep)
gained_days = allocation._process_accrual_plan_level(
current_level, period_start, allocation.lastcall, period_end, allocation.nextcall)
days_added_per_level[current_level] += gained_days
if current_level.maximum_leave > 0 and sum(days_added_per_level.values()) > current_level.maximum_leave:
days_added_per_level[current_level] -= sum(days_added_per_level.values()) - current_level.maximum_leave
# We have to check for end of year actions if it is within our period
# since we can create retroactive allocations.
if allocation.lastcall.year < allocation.nextcall.year and\
(current_level.action_with_unused_accruals == 'lost' or\
current_level.postpone_max_days > 0):
after_period_gains = allocation._process_accrual_plan_level(
current_level, period_start, allocation.nextcall + relativedelta(day=1, month=1),
period_end, allocation.nextcall)
if current_level.action_with_unused_accruals == 'postponed':
# Compute number of days kept
allocation_days = allocation.number_of_days - allocation.leaves_taken
allowed_to_keep = max(0, current_level.postpone_max_days - allocation_days)
number_of_days = min(allocation_days, current_level.postpone_max_days)
allocation.number_of_days = number_of_days + allocation.leaves_taken
total_gained_days = sum(days_added_per_level.values())
days_added_per_level.clear()
days_added_per_level[current_level] = min(total_gained_days - after_period_gains, allowed_to_keep)
else:
allocation.number_of_days = allocation.leaves_taken
days_added_per_level.clear()
days_added_per_level[current_level] += after_period_gains
allocation.lastcall = allocation.nextcall
allocation.nextcall = nextcall
@@ -534,7 +534,7 @@ class TestAccrualAllocations(TestHrHolidaysCommon):
accrual_cron.lastcall = datetime.date(2021, 9, 1)
with freeze_time('2022-01-01'):
allocation._update_accrual()
self.assertEqual(allocation.number_of_days, 0, 'There number of days should be reset')
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
@@ -733,6 +733,36 @@ class TestAccrualAllocations(TestHrHolidaysCommon):
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):