[FIX] hr_holidays: fix banner and days count

In hr_holidays, the banner disappears when the number of days taken is more than the days allocated.
Also, the count of the days is based on all existing allocations.

This commit fixes the banner by allowing to display negative days remaining when there are more days taken than days allocated.
It also fixes the count of the days by couting days only for active allocations.

task-2983188

closes odoo/odoo#100452

X-original-commit: efb351f86a3645ad292fdaaa6b223ff80096b63c
Signed-off-by: Kevin Baptiste <kba@odoo.com>
Signed-off-by: phwa-odoo <phwa@odoo.com>
This commit is contained in:
Philippe Wauthy
2022-09-18 18:52:42 +02:00
parent 8b6f1a195a
commit 68c76f479b
2 changed files with 132 additions and 30 deletions
+15 -3
View File
@@ -229,7 +229,7 @@ class HolidaysType(models.Model):
('holiday_status_id', 'in', self.ids)
])
allocations = self.env['hr.leave.allocation'].search([
allocations = self.env['hr.leave.allocation'].with_context(active_test=False).search([
('employee_id', 'in', employee_ids),
('state', 'in', ['validate']),
('holiday_status_id', 'in', self.ids),
@@ -328,6 +328,8 @@ class HolidaysType(models.Model):
leave_unit = 'hours'
if holiday_status_id.requires_allocation != 'no':
for available_allocation in sorted_available_allocations:
if available_allocation.date_to and available_allocation.date_to < leave.date_from.date():
continue
virtual_remaining_leaves = (available_allocation.number_of_days if leave_unit == 'days' else available_allocation.number_of_hours_display) - allocations_days_consumed[employee_id][holiday_status_id][available_allocation]['virtual_leaves_taken']
max_leaves = min(virtual_remaining_leaves, leave_duration)
days_consumed[available_allocation]['virtual_leaves_taken'] += max_leaves
@@ -337,7 +339,6 @@ class HolidaysType(models.Model):
if leave_duration > 0:
# There are not enough allocation for the number of leaves
days_consumed[False]['virtual_remaining_leaves'] -= leave_duration
return allocations_days_consumed
else:
days_consumed[False]['virtual_leaves_taken'] += leave_duration
if leave.state == 'validate':
@@ -353,7 +354,10 @@ class HolidaysType(models.Model):
fields.datetime.combine(date, time.max) + timedelta(days=5*365),
self.env['hr.leave'])])
search_date = date
allocations_of_that_type = intervals._items[0][2].filtered(lambda a: a.date_to and a.state == 'validate' and a.date_to >= date)
closest_allocations = self.env['hr.leave.allocation']
for interval in intervals._items:
closest_allocations |= interval[2]
allocations_of_that_type = closest_allocations.filtered(lambda a: a.date_to and a.state == 'validate' and a.date_to >= date)
allocations_sorted = sorted(allocations_of_that_type, key=lambda a: a.date_to)
allocation_closest = allocations_sorted[0] if allocations_sorted else False
allocations_days_consumed[employee_id][holiday_status_id][False]['closest_allocation_to_expire'] = allocation_closest
@@ -361,6 +365,8 @@ class HolidaysType(models.Model):
if future_allocation_interval[0].date() > search_date:
continue
for allocation in future_allocation_interval[2]:
if not allocation.active:
continue
days_consumed = allocations_days_consumed[employee_id][holiday_status_id][allocation]
if future_allocation_interval[1] != fields.datetime.combine(date, time.max) + timedelta(days=5*365):
# Compute the remaining number of days/hours in the allocation only if it has an end date
@@ -404,6 +410,9 @@ class HolidaysType(models.Model):
} for employee_id in employee_ids
}
if not date:
date = fields.Date.to_date(self.env.context.get('default_date_from')) or fields.Date.context_today(self)
allocations_days_consumed = self._get_employees_days_per_allocation(employee_ids, date)
leave_keys = ['max_leaves', 'leaves_taken', 'remaining_leaves', 'virtual_remaining_leaves', 'virtual_leaves_taken']
@@ -412,6 +421,8 @@ class HolidaysType(models.Model):
for holiday_status_id in allocations_days_consumed[employee_id]:
for allocation in allocations_days_consumed[employee_id][holiday_status_id]:
if allocation:
if allocation.date_to and (allocation.date_to < date or allocation.date_from > date):
continue
for leave_key in leave_keys:
result[employee_id][holiday_status_id if isinstance(holiday_status_id, int) else holiday_status_id.id][leave_key] += allocations_days_consumed[employee_id][holiday_status_id][allocation][leave_key]
else:
@@ -419,6 +430,7 @@ class HolidaysType(models.Model):
for leave_key in leave_keys:
if allocations_days_consumed[employee_id][holiday_status_id][False].get(leave_key):
result[employee_id][holiday_status_id if isinstance(holiday_status_id, int) else holiday_status_id.id][leave_key] = allocations_days_consumed[employee_id][holiday_status_id][False][leave_key]
return result
@api.model
+117 -27
View File
@@ -62,6 +62,20 @@ class TestLeaveRequests(TestHrHolidaysCommon):
cls.set_employee_create_date(cls.employee_emp_id, '2010-02-03 00:00:00')
cls.set_employee_create_date(cls.employee_hruser_id, '2010-02-03 00:00:00')
def _check_holidays_count(self, holidays_count_result, ml, lt, rl, vrl, vlt, closest_allocation):
self.assertEqual(
holidays_count_result,
{
'closest_allocation_to_expire': closest_allocation,
'max_leaves': ml,
'leaves_taken': lt,
'remaining_leaves': rl,
'virtual_remaining_leaves': vrl,
'virtual_leaves_taken': vlt,
}
)
@classmethod
def set_employee_create_date(cls, _id, newdate):
""" This method is a hack in order to be able to define/redefine the create_date
@@ -799,16 +813,9 @@ class TestLeaveRequests(TestHrHolidaysCommon):
'number_of_days': 3,
})
self.assertEqual(
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id])[self.employee_emp_id][self.holidays_type_2.id],
{
'closest_allocation_to_expire': allocation,
'max_leaves': 5,
'leaves_taken': 0,
'remaining_leaves': 5,
'virtual_remaining_leaves': 2,
'virtual_leaves_taken': 3,
}
ml=5, lt=0, rl=5, vrl=2, vlt=3, closest_allocation=allocation,
)
self.holidays_type_2.requires_allocation = 'no'
@@ -823,32 +830,115 @@ class TestLeaveRequests(TestHrHolidaysCommon):
# The 5 allocation days are not consumed anymore
# virtual_remaining_leaves reflect the total number of leave days taken
self.assertEqual(
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id])[self.employee_emp_id][self.holidays_type_2.id],
{
'closest_allocation_to_expire': allocation,
'max_leaves': 5,
'leaves_taken': 0,
'remaining_leaves': 5,
'virtual_remaining_leaves': 5,
'virtual_leaves_taken': 6,
}
ml=5, lt=0, rl=5, vrl=5, vlt=6, closest_allocation=allocation,
)
leave1.with_user(self.user_hrmanager_id).action_approve()
leave2.with_user(self.user_hrmanager_id).action_approve()
# leaves_taken and virtual_leaves_taken reflect the total number of leave days taken
self.assertEqual(
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id])[self.employee_emp_id][self.holidays_type_2.id],
{
'closest_allocation_to_expire': allocation,
'max_leaves': 5,
'leaves_taken': 6,
'remaining_leaves': 5,
'virtual_remaining_leaves': 5,
'virtual_leaves_taken': 6,
}
ml=5, lt=6, rl=5, vrl=5, vlt=6, closest_allocation=allocation,
)
def test_archived_allocation(self):
with freeze_time('2022-09-15'):
allocation_2021 = self.env['hr.leave.allocation'].create({
'name': 'Annual Time Off 2021',
'employee_id': self.employee_emp_id,
'holiday_status_id': self.holidays_type_2.id,
'number_of_days': 10,
'state': 'confirm',
'date_from': '2021-06-01',
'date_to': '2021-12-31',
})
allocation_2022 = self.env['hr.leave.allocation'].create({
'name': 'Annual Time Off 2022',
'employee_id': self.employee_emp_id,
'holiday_status_id': self.holidays_type_2.id,
'number_of_days': 20,
'state': 'confirm',
'date_from': '2022-01-01',
'date_to': '2022-12-31',
})
allocation_2021.action_validate()
allocation_2022.action_validate()
# Leave taken in 2021
leave_2021 = self.env['hr.leave'].with_user(self.user_employee_id).create({
'name': 'Holiday Request',
'holiday_type': 'employee',
'employee_id': self.employee_emp.id,
'holiday_status_id': self.holidays_type_2.id,
'date_from': datetime(2021, 8, 9, 0, 0, 0),
'date_to': datetime(2021, 8, 13, 23, 59, 59),
})
leave_2021.with_user(self.user_hrmanager_id).action_approve()
# The holidays count only takes into account the valid allocations at that date
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id], date=date(2021, 12, 1))[self.employee_emp_id][self.holidays_type_2.id],
ml=10, lt=5, rl=5, vrl=5, vlt=5, closest_allocation=allocation_2021,
)
# Virtual remaining leave is equal to 1 because there is only one day remaining in the allocation based on its validity
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id], date=date(2021, 12, 31))[self.employee_emp_id][self.holidays_type_2.id],
ml=10, lt=5, rl=5, vrl=1, vlt=5, closest_allocation=allocation_2021,
)
leave_2022 = self.env['hr.leave'].with_user(self.user_employee_id).create({
'name': 'Holiday Request',
'holiday_type': 'employee',
'employee_id': self.employee_emp.id,
'holiday_status_id': self.holidays_type_2.id,
'date_from': datetime(2022, 8, 9, 0, 0, 0),
'date_to': datetime(2022, 8, 13, 23, 59, 59),
})
leave_2022.with_user(self.user_hrmanager_id).action_approve()
# The holidays count in 2022 is not affected by the first leave taken in 2021
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id])[self.employee_emp_id][self.holidays_type_2.id],
ml=20, lt=4, rl=16, vrl=16, vlt=4, closest_allocation=allocation_2022,
)
# The holidays count in 2021 is not affected by the leave taken in 2022
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id], date=date(2021, 12, 1))[self.employee_emp_id][self.holidays_type_2.id],
ml=10, lt=5, rl=5, vrl=5, vlt=5, closest_allocation=allocation_2021,
)
allocation_2021.active = False
# If the allocation is archived, the leaves taken are still counted on this allocation
# but the max leaves and remaining leaves are not counted anymore
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id], date=date(2021, 12, 1))[self.employee_emp_id][self.holidays_type_2.id],
ml=0, lt=5, rl=0, vrl=0, vlt=5, closest_allocation=allocation_2021,
)
# The holidays count in 2022 is not affected by the archived allocation in 2021
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id])[self.employee_emp_id][self.holidays_type_2.id],
ml=20, lt=4, rl=16, vrl=16, vlt=4, closest_allocation=allocation_2022,
)
allocation_2021.active = True
# The holidays count in 2021 is back to what it was when the allocation was active
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id], date=date(2021, 12, 1))[self.employee_emp_id][self.holidays_type_2.id],
ml=10, lt=5, rl=5, vrl=5, vlt=5, closest_allocation=allocation_2021,
)
# The holidays count in 2022 is still not affected by the allocation in 2021
self._check_holidays_count(
self.holidays_type_2.get_employees_days([self.employee_emp_id])[self.employee_emp_id][self.holidays_type_2.id],
ml=20, lt=4, rl=16, vrl=16, vlt=4, closest_allocation=allocation_2022,
)
def test_cancel_leave(self):