[IMP] hr_holidays: improve days calculation based on worked time

Prior to this commit accrual levels based on worked time did not
properly compute the number of days when the period prorata was taken
into account.

TaskId-2924364

closes odoo/odoo#99183

X-original-commit: d9000d3d883ab6fa8325692f1ac5249d6822046a
Signed-off-by: William Braeckman (wbr) <wbr@odoo.com>
This commit is contained in:
William Braeckman
2022-08-30 08:19:06 +02:00
parent 6beb52caea
commit 498b9476d3
2 changed files with 29 additions and 20 deletions
@@ -379,12 +379,19 @@ class HolidaysAllocation(models.Model):
self.ensure_one()
if level.is_based_on_worked_time:
start_dt = datetime.combine(start_date, datetime.min.time())
end_dt = datetime.combine(end_date, datetime.max.time())
end_dt = datetime.combine(end_date, datetime.min.time())
worked = self.employee_id._get_work_days_data_batch(start_dt, end_dt, calendar=self.employee_id.resource_calendar_id)\
[self.employee_id.id]['hours']
if start_period != start_date or end_period != end_date:
start_dt = datetime.combine(start_period, datetime.min.time())
end_dt = datetime.combine(end_period, datetime.min.time())
planned_worked = self.employee_id._get_work_days_data_batch(start_dt, end_dt, calendar=self.employee_id.resource_calendar_id)\
[self.employee_id.id]['hours']
else:
planned_worked = worked
left = self.employee_id.sudo()._get_leave_days_data_batch(start_dt, end_dt,
domain=[('time_type', '=', 'leave')])[self.employee_id.id]['hours']
work_entry_prorata = worked / (left + worked) if worked else 0
work_entry_prorata = worked / (left + planned_worked) if (left + planned_worked) else 0
added_value = work_entry_prorata * level.added_value
else:
added_value = level.added_value
@@ -392,7 +399,7 @@ class HolidaysAllocation(models.Model):
if level.added_value_type == 'hours':
added_value = added_value / (self.employee_id.sudo().resource_id.calendar_id.hours_per_day or HOURS_PER_DAY)
period_prorata = 1
if start_period != start_date or end_period != end_date:
if (start_period != start_date or end_period != end_date) and not level.is_based_on_worked_time:
period_days = (end_period - start_period)
call_days = (end_date - start_date)
period_prorata = min(1, call_days / period_days) if period_days else 1
@@ -285,24 +285,26 @@ class TestAccrualAllocations(TestHrHolidaysCommon):
# 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': [
(0, 0, {
'name': '%s_%d' % ('40 Hours', index),
'hour_from': 8,
'hour_to': 12,
'dayofweek': str(index),
'day_period': 'morning'
}, {
'name': '%s_%d' % ('40 Hours', index),
'hour_from': 13,
'hour_to': 18,
'dayofweek': str(index),
'day_period': 'afternoon'
}) for index in range(5)
],
'attendance_ids': attendances,
})
self.employee_emp.resource_calendar_id = calendar_emp.id
@@ -377,7 +379,7 @@ class TestAccrualAllocations(TestHrHolidaysCommon):
# 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.42857, 4, 'There should be 3.42857 days allocated.')
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')
@@ -386,7 +388,7 @@ class TestAccrualAllocations(TestHrHolidaysCommon):
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.42857, 4, 'There should be 8.42857 days allocated.')
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):