Skip to content
Snippets Groups Projects
Commit 57ff5142 authored by Thomas Lefebvre (thle)'s avatar Thomas Lefebvre (thle)
Browse files

[FIX] hr_work_entry_holidays: ensure the consistency of leaves


Steps to reproduce:
-------------------
- install `hr_work_entry_holidays` module
- create an employee and add two contracts:
    - from 2023-01-01 to 2023-06-30 with a full time (5/5) resource
      which is expired
    - from 2023-07-01 to 2023-12-31 with a partial time (4/5) resource
      which is running
      (doesn't work on Wednesday)
- create an allocation with 10 days
- with the employee, create two leaves:
    - 3 days during the first semester with one Wednesday
    - 3 days during the second semester with one Wednesday

Issue:
------
Leave duration is based on the current contract.
If we change the type of leave by modifying its days/hours unit,
we will get inconsistencies between days and hours for leave taken
in a period belonging to another contract.

Cause:
------
Expired contracts are not taken into account
when calculating the number of days and hours.

Solution:
---------
Add `close` state to contract search.

opw-3419380

closes odoo/odoo#132097

X-original-commit: 249f8c83
Signed-off-by: default avatarSofie Gvaladze (sgv) <sgv@odoo.com>
Signed-off-by: default avatarThomas Lefebvre (thle) <thle@odoo.com>
parent 01bf1a6e
No related branches found
No related tags found
No related merge requests found
......@@ -238,7 +238,7 @@ Contracts:
if employee_id:
employee = self.env['hr.employee'].browse(employee_id)
# Use sudo otherwise base users can't compute number of days
contracts = employee.sudo()._get_contracts(date_from, date_to, states=['open'])
contracts = employee.sudo()._get_contracts(date_from, date_to, states=['open', 'close'])
contracts |= employee.sudo()._get_incoming_contracts(date_from, date_to)
calendar = contracts[:1].resource_calendar_id if contracts else None # Note: if len(contracts)>1, the leave creation will crash because of unicity constaint
# We force the company in the domain as we are more than likely in a compute_sudo
......@@ -253,7 +253,7 @@ Contracts:
def _get_calendar(self):
self.ensure_one()
if self.date_from and self.date_to:
contracts = self.employee_id.sudo()._get_contracts(self.date_from, self.date_to, states=['open'])
contracts = self.employee_id.sudo()._get_contracts(self.date_from, self.date_to, states=['open', 'close'])
contracts |= self.employee_id.sudo()._get_incoming_contracts(self.date_from, self.date_to)
contract_calendar = contracts[:1].resource_calendar_id if contracts else None
return contract_calendar or self.employee_id.resource_calendar_id or self.env.company.resource_calendar_id
......
# # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, date
from datetime import datetime, date, time
from odoo.exceptions import ValidationError
from odoo.tests import tagged
from odoo.addons.hr_work_entry_holidays.tests.common import TestWorkEntryHolidaysBase
......@@ -170,3 +170,105 @@ class TestWorkEntryHolidaysMultiContract(TestWorkEntryHolidaysBase):
second_leave = leaves.filtered(lambda l: l.date_from.day == 16 and l.date_to.day == 30)
self.assertEqual(second_leave.state, 'validate')
self.assertEqual(second_leave.number_of_days, 11)
def test_contract_traceability_calculate_nbr_leave(self):
"""
The goal is to test the traceability of contracts in the past,
i.e. to check that expired contracts are taken into account
to ensure the consistency of leaves (number of days/hours) in the past.
"""
calendar_full, calendar_partial = self.env['resource.calendar'].create([
{
'name': 'Full time (5/5)',
},
{
'name': 'Partial time (4/5)',
'attendance_ids': [
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Monday Evening', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Tuesday Evening', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
# Does not work on Wednesdays
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Thursday Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Friday Evening', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
]
},
])
employee = self.env['hr.employee'].create({
'name': 'Employee',
'resource_calendar_id': calendar_partial.id,
})
self.env['hr.contract'].create([
{
'name': 'Full time (5/5)',
'employee_id': employee.id,
'date_start': datetime.strptime('2023-01-01', '%Y-%m-%d').date(),
'date_end': datetime.strptime('2023-06-30', '%Y-%m-%d').date(),
'resource_calendar_id': calendar_full.id,
'wage': 1000.0,
'state': 'close', # Old contract
'date_generated_from': datetime.strptime('2023-01-01', '%Y-%m-%d'),
'date_generated_to': datetime.strptime('2023-01-01', '%Y-%m-%d'),
},
{
'name': 'Partial time (4/5)',
'employee_id': employee.id,
'date_start': datetime.strptime('2023-07-01', '%Y-%m-%d').date(),
'date_end': datetime.strptime('2023-12-31', '%Y-%m-%d').date(),
'resource_calendar_id': calendar_partial.id,
'wage': 1000.0,
'state': 'open', # Current contract
'date_generated_from': datetime.strptime('2023-07-01', '%Y-%m-%d'),
'date_generated_to': datetime.strptime('2023-07-01', '%Y-%m-%d'),
},
])
leave_type = self.env['hr.leave.type'].create({
'name': 'Leave Type',
'time_type': 'leave',
'requires_allocation': 'yes',
'leave_validation_type': 'hr',
'request_unit': 'day',
})
self.env['hr.leave.allocation'].create({
'name': 'Allocation',
'employee_id': employee.id,
'holiday_status_id': leave_type.id,
'number_of_days': 10,
'state': 'confirm',
'date_from': datetime.strptime('2023-01-01', '%Y-%m-%d').date(),
'date_to': datetime.strptime('2023-12-31', '%Y-%m-%d').date(),
}).action_validate()
leave_during_full_time, leave_during_partial_time = self.env['hr.leave'].create([
{
'employee_id': employee.id,
'holiday_status_id': leave_type.id,
'number_of_days': 3,
'date_from': datetime.combine(date(2023, 1, 3), time.min), # Tuesday
'date_to': datetime.combine(date(2023, 1, 5), time.max), # Thursday
},
{
'employee_id': employee.id,
'holiday_status_id': leave_type.id,
'number_of_days': 2,
'date_from': datetime.combine(date(2023, 12, 5), time.min), # Tuesday
'date_to': datetime.combine(date(2023, 12, 7), time.max), # Thursday
},
])
self.assertEqual(leave_during_full_time.number_of_days_display, 3)
self.assertEqual(leave_during_partial_time.number_of_days_display, 2)
self.assertEqual(leave_during_full_time.number_of_hours_display, 24)
self.assertEqual(leave_during_partial_time.number_of_hours_display, 16)
# Simulate the unit change days/hours of the time off type
(leave_during_full_time + leave_during_partial_time)._compute_number_of_days()
self.assertEqual(leave_during_full_time.number_of_days_display, 3)
self.assertEqual(leave_during_partial_time.number_of_days_display, 2)
(leave_during_full_time + leave_during_partial_time)._compute_number_of_hours_display()
self.assertEqual(leave_during_full_time.number_of_hours_display, 24)
self.assertEqual(leave_during_partial_time.number_of_hours_display, 16)
# Check after leave approval
(leave_during_full_time + leave_during_partial_time).action_approve()
(leave_during_full_time + leave_during_partial_time)._compute_number_of_hours_display()
self.assertEqual(leave_during_full_time.number_of_hours_display, 24)
self.assertEqual(leave_during_partial_time.number_of_hours_display, 16)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment