From 416a1cc08988a92fbf8cd7c0fd27ce7fbaddd0cf Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Fri, 3 Jan 2020 16:01:28 +0000 Subject: [PATCH] [IMP] hr_holidays: Correctly compute number_of_hours_display Purpose ======= Impacted versions: 13.0 Steps to reproduce: - Add employee - allocate paid leave - set leave in hours - Employee working hours is Mo: 8:30-12:30 Tu-Fr: 8:30-12:30 and 13:00-17:30 (total of 38h per week). - Employee requests on week of leave - Total hours in request is 38. Manager approves. Current behavior: Before approval the total hours in the request is 38. The manager approves and the total hours change to 40. Total remaining hours is also -40. Expected behavior: After approval the requested hours should not change and remain 38. The total remaining hours should also be -38. Taskid: 2166964 Closes: #41848 X-original-commit: b89565b93d8e6905d4cc3fe9d3de8ddecb43acd4 --- addons/hr_holidays/models/hr_leave.py | 19 +++++- .../hr_holidays/tests/test_leave_requests.py | 62 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 789c7aa85f1..ed6bbc3f667 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -473,8 +473,23 @@ class HolidaysRequest(models.Model): for holiday in self: calendar = holiday._get_calendar() if holiday.date_from and holiday.date_to: - number_of_hours = holiday._get_number_of_days(holiday.date_from, holiday.date_to, holiday.employee_id.id)['hours'] - holiday.number_of_hours_display = number_of_hours or (holiday.number_of_days * HOURS_PER_DAY) + # Take attendances into account, in case the leave validated + # Otherwise, this will result into number_of_hours = 0 + # and number_of_hours_display = 0 or (#day * calendar.hours_per_day), + # which could be wrong if the employee doesn't work the same number + # hours each day + if holiday.state == 'validate': + start_dt = holiday.date_from + end_dt = holiday.date_to + if not start_dt.tzinfo: + start_dt = start_dt.replace(tzinfo=UTC) + if not end_dt.tzinfo: + end_dt = end_dt.replace(tzinfo=UTC) + intervals = calendar._attendance_intervals(start_dt, end_dt, holiday.employee_id) + number_of_hours = sum((stop - start).total_seconds() / 3600 for start, stop, dummy in intervals) + else: + number_of_hours = holiday._get_number_of_days(holiday.date_from, holiday.date_to, holiday.employee_id.id)['hours'] + holiday.number_of_hours_display = number_of_hours or (holiday.number_of_days * (calendar.hours_per_day or HOURS_PER_DAY)) else: holiday.number_of_hours_display = 0 diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py index c4414640fe2..49e9d340f68 100644 --- a/addons/hr_holidays/tests/test_leave_requests.py +++ b/addons/hr_holidays/tests/test_leave_requests.py @@ -250,3 +250,65 @@ class TestLeaveRequests(TestHrHolidaysCommon): leave._onchange_request_parameters() self.assertEqual(leave.date_from, datetime(2019, 5, 5, 20, 0, 0), "It should have been localized before saving in UTC") self.assertEqual(leave.date_to, datetime(2019, 5, 6, 5, 0, 0), "It should have been localized before saving in UTC") + + def test_number_of_hours_display(self): + calendar = self.env['resource.calendar'].create({ + 'name': 'Monday Morning Else Full Time 38h/week', + 'hours_per_day': 7.6, + 'attendance_ids': [ + (0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8.5, 'hour_to': 12.5, 'day_period': 'morning'}), + (0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8.5, 'hour_to': 12.5, 'day_period': 'morning'}), + (0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17.5, 'day_period': 'afternoon'}), + (0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8.5, 'hour_to': 12.5, 'day_period': 'morning'}), + (0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17.5, 'day_period': 'afternoon'}), + (0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8.5, 'hour_to': 12.5, 'day_period': 'morning'}), + (0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17.5, 'day_period': 'afternoon'}), + (0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8.5, 'hour_to': 12.5, 'day_period': 'morning'}), + (0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17.5, 'day_period': 'afternoon'}) + ], + }) + employee = self.employee_emp + employee.resource_calendar_id = calendar + self.env.user.company_id.resource_calendar_id = calendar + leave_type = self.env['hr.leave.type'].create({ + 'name': 'Paid Time Off', + 'request_unit': 'hour', + 'leave_validation_type': 'both', + }) + allocation = self.env['hr.leave.allocation'].create({ + 'name': '20 days allocation', + 'holiday_status_id': leave_type.id, + 'number_of_days': 20, + 'employee_id': employee.id, + }) + allocation.action_approve() + + leave1 = self.env['hr.leave'].create({ + 'name': 'Holiday 1 week', + 'employee_id': employee.id, + 'holiday_status_id': leave_type.id, + 'date_from': fields.Datetime.from_string('2019-12-23 06:00:00'), + 'date_to': fields.Datetime.from_string('2019-12-27 20:00:00'), + 'number_of_days': 5, + }) + + self.assertEqual(leave1.number_of_hours_display, 38) + leave1.action_approve() + self.assertEqual(leave1.number_of_hours_display, 38) + leave1.action_validate() + self.assertEqual(leave1.number_of_hours_display, 38) + + leave2 = self.env['hr.leave'].create({ + 'name': 'Holiday 1 Day', + 'employee_id': employee.id, + 'holiday_status_id': leave_type.id, + 'date_from': fields.Datetime.from_string('2019-12-30 06:00:00'), + 'date_to': fields.Datetime.from_string('2019-12-30 13:00:00'), + 'number_of_days': 1, + }) + + self.assertEqual(leave2.number_of_hours_display, 4) + leave2.action_approve() + self.assertEqual(leave2.number_of_hours_display, 4) + leave2.action_validate() + self.assertEqual(leave2.number_of_hours_display, 4)