In hr_holidays, the duration field is editable but doesn't bring any added value. The field is errorprone and not editable if you are not HR Officer. It should be the same in every case. The duration field should be read only on the dashboard and time off creation screens. task-2901951 closes odoo/odoo#95534 Related: odoo/enterprise#29352 Signed-off-by: Kevin Baptiste <kba@odoo.com>
803 lines
38 KiB
Python
803 lines
38 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import datetime, date
|
|
import time
|
|
from dateutil.relativedelta import relativedelta
|
|
from freezegun import freeze_time
|
|
from pytz import timezone, UTC
|
|
|
|
from odoo import fields
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tools import mute_logger
|
|
from odoo.tests.common import Form
|
|
from odoo.tests import tagged
|
|
|
|
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
|
|
|
@tagged('leave_requests')
|
|
class TestLeaveRequests(TestHrHolidaysCommon):
|
|
|
|
def _check_holidays_status(self, holiday_status, ml, lt, rl, vrl):
|
|
self.assertEqual(holiday_status.max_leaves, ml,
|
|
'hr_holidays: wrong type days computation')
|
|
self.assertEqual(holiday_status.leaves_taken, lt,
|
|
'hr_holidays: wrong type days computation')
|
|
self.assertEqual(holiday_status.remaining_leaves, rl,
|
|
'hr_holidays: wrong type days computation')
|
|
self.assertEqual(holiday_status.virtual_remaining_leaves, vrl,
|
|
'hr_holidays: wrong type days computation')
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestLeaveRequests, cls).setUpClass()
|
|
|
|
# Make sure we have the rights to create, validate and delete the leaves, leave types and allocations
|
|
LeaveType = cls.env['hr.leave.type'].with_user(cls.user_hrmanager_id).with_context(tracking_disable=True)
|
|
|
|
cls.holidays_type_1 = LeaveType.create({
|
|
'name': 'NotLimitedHR',
|
|
'requires_allocation': 'no',
|
|
'leave_validation_type': 'hr',
|
|
})
|
|
cls.holidays_type_2 = LeaveType.create({
|
|
'name': 'Limited',
|
|
'requires_allocation': 'yes',
|
|
'employee_requests': 'yes',
|
|
'leave_validation_type': 'hr',
|
|
})
|
|
cls.holidays_type_3 = LeaveType.create({
|
|
'name': 'TimeNotLimited',
|
|
'requires_allocation': 'no',
|
|
'leave_validation_type': 'manager',
|
|
})
|
|
|
|
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')
|
|
|
|
@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
|
|
of the employees.
|
|
This is done in SQL because ORM does not allow to write onto the create_date field.
|
|
"""
|
|
cls.env.cr.execute("""
|
|
UPDATE
|
|
hr_employee
|
|
SET create_date = '%s'
|
|
WHERE id = %s
|
|
""" % (newdate, _id))
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_overlapping_requests(self):
|
|
""" Employee cannot create a new leave request at the same time, avoid interlapping """
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Hol11',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': (datetime.today() - relativedelta(days=1)),
|
|
'date_to': datetime.today(),
|
|
'number_of_days': 1,
|
|
})
|
|
|
|
with self.assertRaises(ValidationError):
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Hol21',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': (datetime.today() - relativedelta(days=1)),
|
|
'date_to': datetime.today(),
|
|
'number_of_days': 1,
|
|
})
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_limited_type_no_days(self):
|
|
""" Employee creates a leave request in a limited category but has not enough days left """
|
|
|
|
with self.assertRaises(ValidationError):
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Hol22',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': (datetime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=2)),
|
|
'number_of_days': 1,
|
|
})
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_limited_type_days_left(self):
|
|
""" Employee creates a leave request in a limited category and has enough days left """
|
|
with freeze_time('2022-01-05'):
|
|
aloc1_user_group = self.env['hr.leave.allocation'].with_user(self.user_hruser_id).create({
|
|
'name': 'Days for limited category',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'number_of_days': 2,
|
|
'state': 'confirm',
|
|
'date_from': time.strftime('%Y-1-1'),
|
|
'date_to': time.strftime('%Y-12-31'),
|
|
})
|
|
aloc1_user_group.action_validate()
|
|
|
|
holiday_status = self.holidays_type_2.with_user(self.user_employee_id)
|
|
self._check_holidays_status(holiday_status, 2.0, 0.0, 2.0, 2.0)
|
|
|
|
hol = self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Hol11',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': (datetime.today() - relativedelta(days=2)),
|
|
'date_to': datetime.today(),
|
|
'number_of_days': 2,
|
|
})
|
|
|
|
holiday_status.invalidate_model()
|
|
self._check_holidays_status(holiday_status, 2.0, 0.0, 2.0, 0.0)
|
|
|
|
hol.with_user(self.user_hrmanager_id).action_approve()
|
|
|
|
holiday_status.invalidate_model(['max_leaves'])
|
|
self._check_holidays_status(holiday_status, 2.0, 2.0, 0.0, 0.0)
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_accrual_validity_time_valid(self):
|
|
""" Employee ask leave during a valid validity time """
|
|
|
|
self.env['hr.leave.allocation'].with_user(self.user_hrmanager_id).create({
|
|
'name': 'Sick Time Off',
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'employee_id': self.employee_emp.id,
|
|
'date_from': fields.Datetime.from_string('2017-01-01 00:00:00'),
|
|
'date_to': fields.Datetime.from_string('2017-06-01 00:00:00'),
|
|
'number_of_days': 10,
|
|
'state': 'validate',
|
|
})
|
|
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Valid time period',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': fields.Datetime.from_string('2017-03-03 06:00:00'),
|
|
'date_to': fields.Datetime.from_string('2017-03-11 19:00:00'),
|
|
'number_of_days': 1,
|
|
})
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_accrual_validity_time_not_valid(self):
|
|
""" Employee ask leave when there's no valid allocation """
|
|
with self.assertRaises(ValidationError):
|
|
self.env['hr.leave'].with_user(self.user_employee).create({
|
|
'name': 'Sick Time Off',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': fields.Datetime.from_string('2017-07-03 06:00:00'),
|
|
'date_to': fields.Datetime.from_string('2017-07-11 19:00:00'),
|
|
'number_of_days': 1,
|
|
})
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_department_leave(self):
|
|
""" Create a department leave """
|
|
self.employee_hrmanager.write({'department_id': self.hr_dept.id})
|
|
self.assertFalse(self.env['hr.leave'].search([('employee_id', 'in', self.hr_dept.member_ids.ids)]))
|
|
leave_form = Form(self.env['hr.leave'].with_user(self.user_hrmanager), view='hr_holidays.hr_leave_view_form_manager')
|
|
leave_form.holiday_type = 'department'
|
|
leave_form.department_id = self.hr_dept
|
|
leave_form.holiday_status_id = self.holidays_type_1
|
|
leave_form.request_date_from = date(2019, 5, 6)
|
|
leave_form.request_date_to = date(2019, 5, 6)
|
|
leave = leave_form.save()
|
|
leave.action_approve()
|
|
member_ids = self.hr_dept.member_ids.ids
|
|
self.assertEqual(self.env['hr.leave'].search_count([('employee_id', 'in', member_ids)]), len(member_ids), "Leave should be created for members of department")
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_allocation_request(self):
|
|
""" Create an allocation request """
|
|
# employee should be set to current user
|
|
allocation_form = Form(self.env['hr.leave.allocation'].with_user(self.user_employee))
|
|
allocation_form.name = 'New Allocation Request'
|
|
allocation_form.holiday_status_id = self.holidays_type_2
|
|
allocation_form.date_from = date(2019, 5, 6)
|
|
allocation_form.date_to = date(2019, 5, 6)
|
|
allocation = allocation_form.save()
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_employee_is_absent(self):
|
|
""" Only the concerned employee should be considered absent """
|
|
user_employee_leave = self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Hol11',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': (fields.Datetime.now() - relativedelta(days=1)),
|
|
'date_to': fields.Datetime.now() + relativedelta(days=1),
|
|
'number_of_days': 2,
|
|
})
|
|
(self.employee_emp | self.employee_hrmanager).mapped('is_absent') # compute in batch
|
|
self.assertFalse(self.employee_emp.is_absent, "He should not be considered absent")
|
|
self.assertFalse(self.employee_hrmanager.is_absent, "He should not be considered absent")
|
|
|
|
user_employee_leave.sudo().write({
|
|
'state': 'validate',
|
|
})
|
|
(self.employee_emp | self.employee_hrmanager)._compute_leave_status()
|
|
self.assertTrue(self.employee_emp.is_absent, "He should be considered absent")
|
|
self.assertFalse(self.employee_hrmanager.is_absent, "He should not be considered absent")
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_timezone_employee_leave_request(self):
|
|
""" Create a leave request for an employee in another timezone """
|
|
self.employee_emp.tz = 'NZ' # GMT+12
|
|
leave = self.env['hr.leave'].new({
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'request_unit_hours': True,
|
|
'request_date_from': date(2019, 5, 6),
|
|
'request_date_to': date(2019, 5, 6),
|
|
'request_hour_from': '8', # 8:00 AM in the employee's timezone
|
|
'request_hour_to': '17', # 5:00 PM in the employee's timezone
|
|
})
|
|
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")
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_timezone_company_leave_request(self):
|
|
""" Create a leave request for a company in another timezone """
|
|
company = self.env['res.company'].create({'name': "Hergé"})
|
|
company.resource_calendar_id.tz = 'NZ' # GMT+12
|
|
leave = self.env['hr.leave'].new({
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'request_unit_hours': True,
|
|
'holiday_type': 'company',
|
|
'mode_company_id': company.id,
|
|
'request_date_from': date(2019, 5, 6),
|
|
'request_date_to': date(2019, 5, 6),
|
|
'request_hour_from': '8', # 8:00 AM in the company's timezone
|
|
'request_hour_to': '17', # 5:00 PM in the company's timezone
|
|
})
|
|
self.assertEqual(leave.date_from, datetime(2019, 5, 6, 6, 0, 0), "It should have been localized in the Employee timezone")
|
|
self.assertEqual(leave.date_to, datetime(2019, 5, 6, 15, 0, 0), "It should have been localized in the Employee timezone")
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_timezone_company_validated(self):
|
|
""" Create a leave request for a company in another timezone and validate it """
|
|
self.env.user.tz = 'NZ' # GMT+12
|
|
company = self.env['res.company'].create({'name': "Hergé"})
|
|
employee = self.env['hr.employee'].create({'name': "Remi", 'company_id': company.id})
|
|
leave_form = Form(self.env['hr.leave'], view='hr_holidays.hr_leave_view_form_manager')
|
|
leave_form.holiday_type = 'company'
|
|
leave_form.mode_company_id = company
|
|
leave_form.holiday_status_id = self.holidays_type_1
|
|
leave_form.request_date_from = date(2019, 5, 6)
|
|
leave_form.request_date_to = date(2019, 5, 6)
|
|
# TODO: The test is wrong by modifying `date_from` and `date_to`, which are invisible
|
|
# It should edit only `request_date_from` and `request_date_to` instead
|
|
# And there is really a bug. Using the web client, when you put your PC in Auckland timezone,
|
|
# and the admin preferences in Auckland Timezone
|
|
# and create a time off for the current day, the computation is completely wrong
|
|
# and compute the date to before the date from *-)
|
|
# For instance, for a time-off from 06/16/2022 to 06/16/2022 (1 day) it computes
|
|
# 06/16/2022 08:00:00 as date_from and 06/15/2022 17:00:00 as date_to
|
|
# Bug reported to the rd-fun-vidange channel to the dev who introduced the bug
|
|
# https://discord.com/channels/678381219515465750/687337760452902925/986918361768263710
|
|
leave_form._view['modifiers']['date_from']['invisible'] = False
|
|
leave_form._view['modifiers']['date_to']['invisible'] = False
|
|
leave_form.date_from = datetime(2019, 5, 6, 0, 0, 0)
|
|
leave_form.date_to = datetime(2019, 5, 6, 23, 59, 59)
|
|
leave = leave_form.save()
|
|
leave.state = 'confirm'
|
|
leave.action_validate()
|
|
employee_leave = self.env['hr.leave'].search([('employee_id', '=', employee.id)])
|
|
self.assertEqual(
|
|
employee_leave.request_date_from, date(2019, 5, 5),
|
|
"Timezone should be be adapted on the employee leave"
|
|
)
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_timezone_department_leave_request(self):
|
|
""" Create a leave request for a department in another timezone """
|
|
company = self.env['res.company'].create({'name': "Hergé"})
|
|
company.resource_calendar_id.tz = 'NZ' # GMT+12
|
|
department = self.env['hr.department'].create({'name': "Museum", 'company_id': company.id})
|
|
leave = self.env['hr.leave'].new({
|
|
'employee_id': self.employee_emp.id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'request_unit_hours': True,
|
|
'holiday_type': 'department',
|
|
'department_id': department.id,
|
|
'request_date_from': date(2019, 5, 6),
|
|
'request_date_to': date(2019, 5, 6),
|
|
'request_hour_from': '8', # 8:00 AM in the department's timezone
|
|
'request_hour_to': '17', # 5:00 PM in the department's timezone
|
|
})
|
|
self.assertEqual(leave.date_from, datetime(2019, 5, 6, 6, 0, 0), "It should have been localized in the Employee timezone")
|
|
self.assertEqual(leave.date_to, datetime(2019, 5, 6, 15, 0, 0), "It should have been localized in the Employee timezone")
|
|
|
|
def test_number_of_hours_display(self):
|
|
# Test that the field number_of_hours_dispay doesn't change
|
|
# after time off validation, as it takes the attendances
|
|
# minus the resource leaves to compute that field.
|
|
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,
|
|
'state': 'confirm',
|
|
'date_from': time.strftime('2018-1-1'),
|
|
'date_to': time.strftime('%Y-1-1'),
|
|
})
|
|
allocation.action_validate()
|
|
|
|
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)
|
|
|
|
def test_number_of_hours_display_global_leave(self):
|
|
# Check that the field number_of_hours_display
|
|
# takes the global leaves into account, even
|
|
# after validation
|
|
calendar = self.env['resource.calendar'].create({
|
|
'name': 'Classic 40h/week',
|
|
'hours_per_day': 8.0,
|
|
'attendance_ids': [
|
|
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
|
(0, 0, {'name': 'Monday Afternoon', '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 Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
|
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
|
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
|
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
|
(0, 0, {'name': 'Thursday Afternoon', '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 Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
|
|
],
|
|
'global_leave_ids': [(0, 0, {
|
|
'name': 'Christmas Leave',
|
|
'date_from': fields.Datetime.from_string('2019-12-25 00:00:00'),
|
|
'date_to': fields.Datetime.from_string('2019-12-26 23:59:59'),
|
|
'resource_id': False,
|
|
'time_type': 'leave',
|
|
})]
|
|
})
|
|
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': 'Sick',
|
|
'request_unit': 'hour',
|
|
'leave_validation_type': 'both',
|
|
'requires_allocation': 'no',
|
|
})
|
|
leave1 = self.env['hr.leave'].create({
|
|
'name': 'Sick 1 week during christmas snif',
|
|
'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, 24)
|
|
leave1.action_approve()
|
|
self.assertEqual(leave1.number_of_hours_display, 24)
|
|
leave1.action_validate()
|
|
self.assertEqual(leave1.number_of_hours_display, 24)
|
|
|
|
def _test_leave_with_tz(self, tz, local_date_from, local_date_to, number_of_days):
|
|
self.user_employee.tz = tz
|
|
tz = timezone(tz)
|
|
|
|
# Mimic what is done by the calendar widget when clicking on a day. It
|
|
# will take the local datetime from 0:00 to 23:59
|
|
values = {
|
|
'date_from': local_date_from,
|
|
'date_to': local_date_to, # note that this can be the next day in UTC
|
|
}
|
|
values.update(self.env['hr.leave'].with_user(self.user_employee_id)._default_get_request_parameters(values))
|
|
|
|
# Dates should be local to the user
|
|
self.assertEqual(values['request_date_from'], local_date_from.date())
|
|
self.assertEqual(values['request_date_to'], local_date_to.date())
|
|
|
|
values.update({
|
|
'name': 'Test',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
})
|
|
leave = self.env['hr.leave'].with_user(self.user_employee_id).new(values)
|
|
self.assertEqual(leave.number_of_days, number_of_days)
|
|
|
|
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
|
def test_leave_defaults_with_timezones(self):
|
|
""" Make sure that leaves start with correct defaults for non-UTC timezones """
|
|
timezones_to_test = ('UTC', 'Pacific/Midway', 'US/Pacific', 'Asia/Taipei', 'Pacific/Kiritimati') # UTC, UTC -11, UTC -8, UTC +8, UTC +14
|
|
|
|
# January 2020
|
|
# Su Mo Tu We Th Fr Sa
|
|
# 1 2 3 4
|
|
# 5 6 7 8 9 10 11
|
|
# 12 13 14 15 16 17 18
|
|
# 19 20 21 22 23 24 25
|
|
# 26 27 28 29 30 31
|
|
local_date_from = datetime(2020, 1, 1, 0, 0, 0)
|
|
local_date_to = datetime(2020, 1, 1, 23, 59, 59)
|
|
for tz in timezones_to_test:
|
|
self._test_leave_with_tz(tz, local_date_from, local_date_to, 1)
|
|
|
|
# We, Th, Fr, Mo, Tu, We => 6 days
|
|
local_date_from = datetime(2020, 1, 1, 0, 0, 0)
|
|
local_date_to = datetime(2020, 1, 8, 23, 59, 59)
|
|
for tz in timezones_to_test:
|
|
self._test_leave_with_tz(tz, local_date_from, local_date_to, 6)#TODO JUD check why this fails
|
|
|
|
def test_expired_allocation(self):
|
|
allocation = self.env['hr.leave.allocation'].create({
|
|
'name': 'Expired Allocation',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'number_of_days': 20,
|
|
'state': 'confirm',
|
|
'date_from': '2020-01-01',
|
|
'date_to': '2020-12-31',
|
|
})
|
|
allocation.action_validate()
|
|
|
|
with self.assertRaises(ValidationError):
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': '2021-09-01',
|
|
'date_to': '2021-09-02',
|
|
'number_of_days': 1,
|
|
})
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': '2020-09-01',
|
|
'date_to': '2020-09-02',
|
|
'number_of_days': 1,
|
|
})
|
|
|
|
def test_no_days_expired(self):
|
|
# First expired allocation
|
|
allocation1 = self.env['hr.leave.allocation'].create({
|
|
'name': 'Expired Allocation',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'number_of_days': 20,
|
|
'state': 'confirm',
|
|
'date_from': '2020-01-01',
|
|
'date_to': '2020-12-31',
|
|
})
|
|
allocation2 = self.env['hr.leave.allocation'].create({
|
|
'name': 'Expired Allocation',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'number_of_days': 3,
|
|
'state': 'confirm',
|
|
'date_from': '2021-01-01',
|
|
'date_to': '2021-12-31',
|
|
})
|
|
allocation1.action_validate()
|
|
allocation2.action_validate()
|
|
# Try creating a request that could be validated if allocation1 was still valid
|
|
with self.assertRaises(ValidationError):
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': '2021-09-06',
|
|
'date_to': '2021-09-10',
|
|
'number_of_days': 5,
|
|
})
|
|
# This time we have enough days
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': '2021-09-06',
|
|
'date_to': '2021-09-08',
|
|
'number_of_days': 3,
|
|
})
|
|
|
|
def test_company_leaves(self):
|
|
# First expired allocation
|
|
allocation = self.env['hr.leave.allocation'].create({
|
|
'name': 'Allocation',
|
|
'holiday_type': 'company',
|
|
'mode_company_id': self.env.company.id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'number_of_days': 20,
|
|
'state': 'confirm',
|
|
'date_from': '2021-01-01',
|
|
})
|
|
allocation.action_validate()
|
|
|
|
req1_form = Form(self.env['hr.leave'].sudo())
|
|
req1_form.employee_ids.add(self.employee_emp)
|
|
req1_form.employee_ids.add(self.employee_hrmanager)
|
|
req1_form.holiday_status_id = self.holidays_type_1
|
|
req1_form.request_date_from = fields.Date.to_date('2021-12-06')
|
|
req1_form.request_date_to = fields.Date.to_date('2021-12-08')
|
|
|
|
self.assertEqual(req1_form.number_of_days_display, 3)
|
|
req1_form.save().action_approve()
|
|
|
|
req2_form = Form(self.env['hr.leave'].sudo())
|
|
req2_form.employee_ids.add(self.employee_hruser)
|
|
req2_form.holiday_status_id = self.holidays_type_1
|
|
req2_form.request_date_from = fields.Date.to_date('2021-12-06')
|
|
req2_form.request_date_to = fields.Date.to_date('2021-12-08')
|
|
|
|
self.assertEqual(req2_form.number_of_days_display, 3)
|
|
|
|
def test_leave_with_public_holiday_other_company(self):
|
|
other_company = self.env['res.company'].create({
|
|
'name': 'Test Company',
|
|
})
|
|
# Create a public holiday for the second company
|
|
p_leave = self.env['resource.calendar.leaves'].create({
|
|
'date_from': datetime(2022, 3, 11),
|
|
'date_to': datetime(2022, 3, 11, 23, 59, 59),
|
|
})
|
|
p_leave.company_id = other_company
|
|
|
|
leave = 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_1.id,
|
|
'date_from': datetime(2022, 3, 11),
|
|
'date_to': datetime(2022, 3, 11, 23, 59, 59),
|
|
})
|
|
self.assertEqual(leave.number_of_days, 1)
|
|
|
|
def test_several_allocations(self):
|
|
allocation_vals = {
|
|
'name': 'Allocation',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'number_of_days': 5,
|
|
'state': 'confirm',
|
|
'date_from': '2022-01-01',
|
|
'date_to': '2022-12-31',
|
|
}
|
|
allocation1 = self.env['hr.leave.allocation'].create(allocation_vals)
|
|
allocation2 = self.env['hr.leave.allocation'].create(allocation_vals)
|
|
|
|
allocation1.action_validate()
|
|
allocation2.action_validate()
|
|
|
|
# Neither allocation has enough days
|
|
with self.assertRaises(ValidationError):
|
|
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': '2022-01-01',
|
|
'date_to': '2022-01-15',
|
|
'number_of_days': 10,
|
|
})
|
|
|
|
allocation_vals.update({'number_of_days': 10})
|
|
allocation3 = self.env['hr.leave.allocation'].create(allocation_vals)
|
|
allocation3.action_validate()
|
|
|
|
# The 3rd allocation has enough days, so it should be picked
|
|
holiday = self.env['hr.leave'].with_user(self.user_employee_id).create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'date_from': '2022-01-01',
|
|
'date_to': '2022-01-15',
|
|
'number_of_days': 10,
|
|
})
|
|
holiday.sudo().action_validate()
|
|
|
|
self.assertEqual(allocation1.leaves_taken, 0.0, 'As this allocation does not have enough days, it should not be affected')
|
|
self.assertEqual(allocation2.leaves_taken, 0.0, 'As this allocation does not have enough days, it should not be affected')
|
|
self.assertEqual(allocation3.leaves_taken, 10.0, 'As this allocation has enough days, the leave days should be taken')
|
|
|
|
def test_several_allocations_split(self):
|
|
Allocation = self.env['hr.leave.allocation']
|
|
allocation_vals = {
|
|
'name': 'Allocation',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
'state': 'confirm',
|
|
'date_from': '2022-01-01',
|
|
'date_to': '2022-12-31',
|
|
}
|
|
Leave = self.env['hr.leave'].with_user(self.user_employee_id).sudo()
|
|
leave_vals = {
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_2.id,
|
|
}
|
|
|
|
for unit in ['hour', 'day']:
|
|
self.holidays_type_2.request_unit = unit
|
|
|
|
allocation_vals.update({'number_of_days': 4})
|
|
allocation_4days = Allocation.create(allocation_vals)
|
|
allocation_vals.update({'number_of_days': 1})
|
|
allocation_1day = Allocation.create(allocation_vals)
|
|
allocations = (allocation_4days + allocation_1day)
|
|
allocations.action_validate()
|
|
|
|
leave_vals.update({
|
|
'date_from': '2022-01-03 00:00:00',
|
|
'date_to': '2022-01-06 23:59:59',
|
|
'number_of_days': 4,
|
|
})
|
|
leave_draft = Leave.create(leave_vals)
|
|
leave_draft.action_refuse()
|
|
leave_vals.update({
|
|
'date_from': '2022-01-03 00:00:00',
|
|
'date_to': '2022-01-06 23:59:59',
|
|
'number_of_days': 4,
|
|
})
|
|
leave_4days = Leave.create(leave_vals)
|
|
leave_vals.update({
|
|
'date_from': '2022-01-07 00:00:00',
|
|
'date_to': '2022-01-07 23:59:59',
|
|
'number_of_days': 1,
|
|
})
|
|
leave_1day = Leave.create(leave_vals)
|
|
leaves = (leave_4days + leave_1day)
|
|
leaves.action_approve()
|
|
|
|
self.assertEqual(allocation_4days.leaves_taken, leave_4days['number_of_%ss_display' % unit], 'As 4 days were available in this allocation, they should have been taken')
|
|
self.assertEqual(allocation_1day.leaves_taken, leave_1day['number_of_%ss_display' % unit], 'As no days were available in previous allocation, they should have been taken in this one')
|
|
leaves.action_refuse()
|
|
allocations.action_refuse()
|
|
|
|
def test_time_off_recovery_on_create(self):
|
|
time_off = self.env['hr.leave'].create([
|
|
{
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': '2021-12-06 00:00:00',
|
|
'date_to': '2021-12-10 23:59:59',
|
|
},
|
|
{
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_hruser_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': '2021-12-06 00:00:00',
|
|
'date_to': '2021-12-10 23:59:59',
|
|
}
|
|
])
|
|
self.assertEqual(time_off[0].number_of_days, 5)
|
|
self.assertEqual(time_off[1].number_of_days, 5)
|
|
self.env['resource.calendar.leaves'].create({
|
|
'name': 'Global Time Off',
|
|
'date_from': '2021-12-07 00:00:00',
|
|
'date_to': '2021-12-07 23:59:59',
|
|
})
|
|
self.assertEqual(time_off[0].number_of_days, 4)
|
|
self.assertEqual(time_off[1].number_of_days, 4)
|
|
|
|
def test_time_off_recovery_on_write(self):
|
|
global_time_off = self.env['resource.calendar.leaves'].create({
|
|
'name': 'Global Time Off',
|
|
'date_from': '2021-12-07 00:00:00',
|
|
'date_to': '2021-12-07 23:59:59',
|
|
})
|
|
|
|
time_off_1 = self.env['hr.leave'].create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': '2021-12-06 00:00:00',
|
|
'date_to': '2021-12-10 23:59:59',
|
|
})
|
|
self.assertEqual(time_off_1.number_of_days, 4)
|
|
|
|
time_off_2 = self.env['hr.leave'].create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': '2021-12-13 00:00:00',
|
|
'date_to': '2021-12-17 23:59:59',
|
|
})
|
|
self.assertEqual(time_off_2.number_of_days, 5)
|
|
|
|
# adding 1 day to the global time off
|
|
global_time_off.write({
|
|
'date_to': '2021-12-08 23:59:59',
|
|
})
|
|
self.assertEqual(time_off_1.number_of_days, 3)
|
|
|
|
# moving the global time off to the next week
|
|
global_time_off.write({
|
|
'date_from': '2021-12-15 00:00:00',
|
|
'date_to': '2021-12-15 23:59:59',
|
|
})
|
|
self.assertEqual(time_off_1.number_of_days, 2)
|
|
self.assertEqual(time_off_2.number_of_days, 4)
|
|
|
|
def test_time_off_recovery_on_unlink(self):
|
|
global_time_off = self.env['resource.calendar.leaves'].create({
|
|
'name': 'Global Time Off',
|
|
'date_from': '2021-12-07 00:00:00',
|
|
'date_to': '2021-12-07 23:59:59',
|
|
})
|
|
time_off = self.env['hr.leave'].create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': '2021-12-06 00:00:00',
|
|
'date_to': '2021-12-10 23:59:59',
|
|
})
|
|
self.assertEqual(time_off.number_of_days, 4)
|
|
global_time_off.unlink()
|
|
self.assertEqual(time_off.number_of_days, 3)
|
|
|
|
def test_time_off_auto_cancel(self):
|
|
time_off = self.env['hr.leave'].create({
|
|
'name': 'Holiday Request',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_type_1.id,
|
|
'date_from': '2021-11-15 00:00:00',
|
|
'date_to': '2021-11-19 23:59:59',
|
|
})
|
|
self.env['resource.calendar.leaves'].create({
|
|
'name': 'Global Time Off',
|
|
'date_from': '2021-11-15 00:00:00',
|
|
'date_to': '2021-11-19 23:59:59',
|
|
})
|
|
self.assertEqual(time_off.active, False)
|