From 498b9476d3c0f1385c6587d98e5acbbb8e158f8d Mon Sep 17 00:00:00 2001 From: William Braeckman Date: Tue, 19 Jul 2022 08:18:32 +0000 Subject: [PATCH] [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) --- .../hr_holidays/models/hr_leave_allocation.py | 13 +++++-- .../tests/test_accrual_allocations.py | 36 ++++++++++--------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index f7dca522c9b..ecffa3e9cc5 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -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 diff --git a/addons/hr_holidays/tests/test_accrual_allocations.py b/addons/hr_holidays/tests/test_accrual_allocations.py index 03caaf855c2..ac1539b3493 100644 --- a/addons/hr_holidays/tests/test_accrual_allocations.py +++ b/addons/hr_holidays/tests/test_accrual_allocations.py @@ -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):