mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-25 11:31:59 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_access_rights
|
||||
|
|
@ -14,8 +13,25 @@ from . import test_leave_requests
|
|||
from . import test_out_of_office
|
||||
from . import test_company_leave
|
||||
from . import test_res_partner
|
||||
from . import test_stress_days
|
||||
from . import test_resource_calendar
|
||||
from . import test_mandatory_days
|
||||
from . import test_global_leaves
|
||||
from . import test_uninstall
|
||||
from . import test_holidays_calendar
|
||||
from . import test_holidays_mail
|
||||
from . import test_negative
|
||||
from . import test_past_accruals
|
||||
from . import test_allocations
|
||||
from . import test_multicompany
|
||||
from . import test_timeoff_event
|
||||
from . import test_working_hours
|
||||
from . import test_dashboard
|
||||
from . import test_expiring_leaves
|
||||
from . import test_hr_departure_wizard
|
||||
from . import test_time_off_card_tour
|
||||
from . import test_hr_leave_type_tour
|
||||
from . import test_time_off_graph_view_tour
|
||||
from . import test_leave_type_data
|
||||
from . import test_multi_contract
|
||||
from . import test_time_off_allocation_tour
|
||||
from . import test_flexible_resource_calendar
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import Command, fields
|
||||
from odoo.tests import common, Form
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.fields import Datetime
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestHrHolidaysCommon(common.TransactionCase):
|
||||
|
|
@ -13,6 +17,26 @@ class TestHrHolidaysCommon(common.TransactionCase):
|
|||
cls.env.user.tz = 'Europe/Brussels'
|
||||
cls.env.user.company_id.resource_calendar_id.tz = "Europe/Brussels"
|
||||
|
||||
cls.company = cls.env['res.company'].create({'name': 'Test company'})
|
||||
cls.external_company = cls.env['res.company'].create({'name': 'External Test company'})
|
||||
|
||||
cls.env.user.company_id = cls.company
|
||||
|
||||
# The available time off types are the ones whose:
|
||||
# 1. Company is one of the selected companies.
|
||||
# 2. Company is false but whose country is one the countries of the selected companies.
|
||||
# 3. Company is false and country is false
|
||||
# Thus, a time off type is defined to be available for `Test company`
|
||||
# For example, the tour 'time_off_request_calendar_view' would succeed (false positive) without this leave type.
|
||||
# However, the tour won't create a time-off request (as expected)because no time-off type is available to be selected on the leave
|
||||
# This would cause the test case that uses the tour to fail.
|
||||
cls.env['hr.leave.type'].create({
|
||||
'name': 'Test Leave Type',
|
||||
'requires_allocation': False,
|
||||
'request_unit': 'day',
|
||||
'company_id': cls.company.id,
|
||||
})
|
||||
|
||||
# Test users to use through the various tests
|
||||
cls.user_hruser = mail_new_test_user(cls.env, login='armande', groups='base.group_user,hr_holidays.group_hr_holidays_user')
|
||||
cls.user_hruser_id = cls.user_hruser.id
|
||||
|
|
@ -21,9 +45,12 @@ class TestHrHolidaysCommon(common.TransactionCase):
|
|||
cls.user_hrmanager_id = cls.user_hrmanager.id
|
||||
cls.user_hrmanager.tz = 'Europe/Brussels'
|
||||
|
||||
cls.user_employee = mail_new_test_user(cls.env, login='david', groups='base.group_user')
|
||||
cls.user_responsible = mail_new_test_user(cls.env, login='Titus', groups='base.group_user,hr_holidays.group_hr_holidays_responsible')
|
||||
cls.user_responsible_id = cls.user_responsible.id
|
||||
cls.user_employee = mail_new_test_user(cls.env, login='enguerran', password='enguerran', groups='base.group_user')
|
||||
cls.user_employee_id = cls.user_employee.id
|
||||
|
||||
cls.external_user_employee = mail_new_test_user(cls.env, login='external', password='external', groups='base.group_user')
|
||||
cls.external_user_employee_id = cls.external_user_employee.id
|
||||
# Hr Data
|
||||
Department = cls.env['hr.department'].with_context(tracking_disable=True)
|
||||
|
||||
|
|
@ -34,13 +61,28 @@ class TestHrHolidaysCommon(common.TransactionCase):
|
|||
'name': 'Research and devlopment',
|
||||
})
|
||||
|
||||
cls.employee_responsible = cls.env['hr.employee'].create({
|
||||
'name': 'David Employee',
|
||||
'user_id': cls.user_responsible_id,
|
||||
'department_id': cls.rd_dept.id,
|
||||
})
|
||||
|
||||
cls.employee_emp = cls.env['hr.employee'].create({
|
||||
'name': 'David Employee',
|
||||
'user_id': cls.user_employee_id,
|
||||
'leave_manager_id': cls.user_responsible_id,
|
||||
'department_id': cls.rd_dept.id,
|
||||
'company_id': cls.company.id,
|
||||
})
|
||||
cls.employee_emp_id = cls.employee_emp.id
|
||||
|
||||
cls.employee_external = cls.env['hr.employee'].create({
|
||||
'name': 'external Employee',
|
||||
'user_id': cls.external_user_employee_id,
|
||||
'company_id': cls.external_company.id,
|
||||
})
|
||||
cls.external_employee_id = cls.employee_external.id
|
||||
|
||||
cls.employee_hruser = cls.env['hr.employee'].create({
|
||||
'name': 'Armande HrUser',
|
||||
'user_id': cls.user_hruser_id,
|
||||
|
|
@ -58,3 +100,110 @@ class TestHrHolidaysCommon(common.TransactionCase):
|
|||
|
||||
cls.rd_dept.write({'manager_id': cls.employee_hruser_id})
|
||||
cls.hours_per_day = cls.employee_emp.resource_id.calendar_id.hours_per_day or 8
|
||||
|
||||
def assert_remaining_leaves_equal(self, leave_type, value, employee, date=None, digits=None):
|
||||
allocation_data = leave_type.get_allocation_data(employee, date)
|
||||
if not date:
|
||||
date = fields.Date.today()
|
||||
if digits:
|
||||
self.assertAlmostEqual(allocation_data[employee][0][1]['remaining_leaves'], value,
|
||||
digits, f"Remaining leaves for date '{date}' are incorrect.")
|
||||
else:
|
||||
self.assertEqual(allocation_data[employee][0][1]['remaining_leaves'],
|
||||
value, f"Remaining leaves for date '{date}' are incorrect.")
|
||||
|
||||
def _create_form_test_accrual_allocation(self, leave_type, date_from, employee, accrual_plan, date_to=None, creator_user=None):
|
||||
allocation = self.env['hr.leave.allocation']
|
||||
if creator_user:
|
||||
allocation = allocation.with_user(creator_user)
|
||||
with Form(allocation, 'hr_holidays.hr_leave_allocation_view_form_manager') as form:
|
||||
form.name = 'Test accrual allocation'
|
||||
form.allocation_type = 'accrual'
|
||||
form.accrual_plan_id = accrual_plan
|
||||
form.employee_id = employee
|
||||
form.holiday_status_id = leave_type
|
||||
form.date_from = date_from
|
||||
if date_to:
|
||||
form.date_to = date_to
|
||||
return form.record
|
||||
|
||||
|
||||
class TestHolidayContract(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Legal Leaves',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
'responsible_ids': [Command.link(cls.env.ref('base.user_admin').id)],
|
||||
})
|
||||
cls.env.ref('base.user_admin').notification_type = 'inbox'
|
||||
|
||||
cls.dep_rd = cls.env['hr.department'].create({
|
||||
'name': 'Research & Development - Test',
|
||||
})
|
||||
|
||||
# I create a new employee "Jules"
|
||||
cls.jules_emp = cls.env['hr.employee'].create({
|
||||
'name': 'Jules',
|
||||
'sex': 'male',
|
||||
'birthday': '1984-05-01',
|
||||
'country_id': cls.env.ref('base.be').id,
|
||||
'department_id': cls.dep_rd.id,
|
||||
})
|
||||
|
||||
cls.calendar_35h = cls.env['resource.calendar'].create({
|
||||
'name': '35h calendar',
|
||||
'attendance_ids': [
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Monday Evening', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Lunch', 'dayofweek': '1', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Tuesday Evening', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Lunch', 'dayofweek': '2', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Wednesday Evening', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Lunch', 'dayofweek': '3', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Thursday Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Friday Lunch', 'dayofweek': '4', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Friday Evening', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'})
|
||||
]
|
||||
})
|
||||
cls.calendar_40h = cls.env['resource.calendar'].create({'name': 'Default calendar'})
|
||||
|
||||
# This contract ends at the 15th of the month
|
||||
cls.jules_emp.version_id.write({ # Fixed term contract
|
||||
'contract_date_end': datetime.strptime('2015-11-15', '%Y-%m-%d'),
|
||||
'contract_date_start': datetime.strptime('2015-01-01', '%Y-%m-%d'),
|
||||
'date_version': datetime.strptime('2015-01-01', '%Y-%m-%d'),
|
||||
'name': 'First CDD Contract for Jules',
|
||||
'resource_calendar_id': cls.calendar_40h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
cls.contract_cdd = cls.jules_emp.version_id
|
||||
|
||||
# This contract starts the next day
|
||||
cls.contract_cdi = cls.jules_emp.create_version({
|
||||
'date_version': datetime.strptime('2015-11-16', '%Y-%m-%d'),
|
||||
'contract_date_start': datetime.strptime('2015-11-16', '%Y-%m-%d'),
|
||||
'contract_date_end': False,
|
||||
'name': 'Contract for Jules',
|
||||
'resource_calendar_id': cls.calendar_35h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def create_leave(cls, date_from=None, date_to=None, name="", employee_id=False):
|
||||
return cls.env['hr.leave'].create({
|
||||
'name': name or 'Holiday!!!',
|
||||
'employee_id': employee_id or cls.richard_emp.id,
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
'request_date_to': date_to or Datetime.today(),
|
||||
'request_date_from': date_from or Datetime.today(),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
import unittest
|
||||
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import tests
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
from odoo.tools import date_utils
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
|
|
@ -19,51 +22,47 @@ class TestHrHolidaysAccessRightsCommon(TestHrHolidaysCommon):
|
|||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Unlimited',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
cls.rd_dept.manager_id = False
|
||||
cls.hr_dept.manager_id = False
|
||||
cls.employee_emp.parent_id = False
|
||||
|
||||
leave_day = date_utils.start_of(date.today() + relativedelta(days=30), 'week')
|
||||
|
||||
cls.employee_leave = cls.env['hr.leave'].with_user(cls.user_employee_id).create({
|
||||
'name': 'Test',
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
'department_id': cls.employee_emp.department_id.id,
|
||||
'employee_id': cls.employee_emp.id,
|
||||
'date_from': datetime.now() + relativedelta(days=30),
|
||||
'date_to': datetime.now() + relativedelta(days=31),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': leave_day,
|
||||
'request_date_to': leave_day,
|
||||
})
|
||||
|
||||
cls.lt_no_validation = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = no_validation',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.lt_validation_hr = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = HR',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.lt_validation_manager = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = manager',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.lt_validation_both = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = both',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.draft_status = [
|
||||
cls.lt_validation_hr,
|
||||
cls.lt_validation_manager,
|
||||
cls.lt_validation_both
|
||||
]
|
||||
|
||||
cls.confirm_status = [
|
||||
cls.lt_no_validation,
|
||||
cls.lt_validation_hr,
|
||||
|
|
@ -72,314 +71,16 @@ class TestHrHolidaysAccessRightsCommon(TestHrHolidaysCommon):
|
|||
]
|
||||
|
||||
# Here we only test access rights, prevent any conflict with
|
||||
# existing stress days - they are tested someplace else.
|
||||
cls.env['hr.leave.stress.day'].search([]).unlink()
|
||||
# existing mandatory days - they are tested someplace else.
|
||||
cls.env['hr.leave.mandatory.day'].search([]).unlink()
|
||||
|
||||
def request_leave(self, user_id, date_from, number_of_days, values=None):
|
||||
def request_leave(self, user_id, request_date_from, number_of_days, values=None):
|
||||
values = dict(values or {}, **{
|
||||
'date_from': date_from,
|
||||
'request_date_from': date_from,
|
||||
'date_to': date_from + relativedelta(days=number_of_days),
|
||||
'request_date_to': date_from + relativedelta(days=number_of_days),
|
||||
'number_of_days': number_of_days,
|
||||
'request_date_from': request_date_from,
|
||||
'request_date_to': request_date_from + relativedelta(days=number_of_days - 1),
|
||||
})
|
||||
return self.env['hr.leave'].with_user(user_id).create(values)
|
||||
|
||||
|
||||
@tests.tagged('access_rights', 'access_rights_states')
|
||||
class TestAcessRightsStates(TestHrHolidaysAccessRightsCommon):
|
||||
# ******************************************************
|
||||
# Action draft
|
||||
# ******************************************************
|
||||
|
||||
def test_draft_status(self):
|
||||
"""
|
||||
We should only be able to draft a leave that is
|
||||
in confirm or refuse state
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Ranoi',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.action_draft()
|
||||
|
||||
values = {
|
||||
'name': 'Ranoi',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=20 + i), 1, values)
|
||||
# the state has to be set to draft in a write because it is initialized to confirm if it has validation
|
||||
leave.write({'state': 'draft'})
|
||||
with self.assertRaises(UserError):
|
||||
leave.action_draft()
|
||||
|
||||
def test_base_user_draft_his_leave(self):
|
||||
"""
|
||||
Should be able to draft his own leave
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_employee.id).action_draft()
|
||||
|
||||
def test_base_user_draft_other_employee_leave(self):
|
||||
"""
|
||||
Should not be able to draft the leave of someone else
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_employee.id).action_draft()
|
||||
|
||||
def test_base_user_draft_other_employee_leave_and_is_leave_manager_id(self):
|
||||
"""
|
||||
Should not be able to draft the leave of someone else
|
||||
even when being the leave manager id for this person
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
self.employee_hruser.write({'leave_manager_id': self.user_employee.id})
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_employee.id).action_draft()
|
||||
|
||||
def test_base_user_draft_self_and_is_leave_manager_id(self):
|
||||
"""
|
||||
Should be able to draft his own leave
|
||||
even when being leave manager id
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
self.employee_emp.write({'leave_manager_id': self.user_employee.id})
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_employee.id).action_draft()
|
||||
|
||||
def test_base_user_draft_refused_leave(self):
|
||||
"""
|
||||
Should not be able to draft a refused leave
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.action_refuse()
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_employee.id).action_draft()
|
||||
|
||||
def test_base_user_draft_current_leave(self):
|
||||
"""
|
||||
Should not be able to draft a passed leave
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=-20 + i), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_employee.id).action_draft()
|
||||
|
||||
def test_holiday_user_draft_his_leave(self):
|
||||
"""
|
||||
Should be able to draft his own leave
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_hruser.id).action_draft()
|
||||
|
||||
def test_holiday_user_draft_other_employee_leave(self):
|
||||
"""
|
||||
Should not be able to draft other employee leave
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_hruser.id).action_draft()
|
||||
|
||||
def test_holiday_user_draft_other_employee_leave_and_is_leave_manager_id(self):
|
||||
"""
|
||||
Should not be able to draft other employee leave
|
||||
even if he is the leave manager id
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
self.employee_emp.write({'leave_manager_id': self.user_hruser.id})
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_hruser.id).action_draft()
|
||||
|
||||
def test_holiday_user_draft_self_and_is_manager_id(self):
|
||||
"""
|
||||
Should be able to draft his own leave
|
||||
even if he is leave manager id
|
||||
whatever the holiday_status_id
|
||||
"""
|
||||
self.employee_hruser.write({'leave_manager_id': self.user_hruser.id})
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_hruser.id).action_draft()
|
||||
|
||||
def test_holiday_user_draft_refused_leave(self):
|
||||
"""
|
||||
Should not be able to draft a refused leave
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.action_refuse()
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_hruser.id).action_draft()
|
||||
|
||||
def test_holiday_user_draft_current_leave(self):
|
||||
"""
|
||||
Should not be able to draft a passed leave
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=-20 + i), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_hruser.id).action_draft()
|
||||
|
||||
def test_holiday_manager_draft_his_leave(self):
|
||||
"""
|
||||
The holiday manager should be able to do everything
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hrmanager.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_hrmanager.id).action_draft()
|
||||
|
||||
def test_holiday_manager_draft_other_employee_leave(self):
|
||||
"""
|
||||
The holiday manager should be able to do everything
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_hrmanager.id).action_draft()
|
||||
|
||||
def test_holiday_manager_draft_other_employee_leave_and_is_leave_manager_id(self):
|
||||
"""
|
||||
The holiday manager should be able to do everything
|
||||
"""
|
||||
self.employee_hruser.write({'leave_manager_id': self.user_hrmanager.id})
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_hrmanager.id).action_draft()
|
||||
|
||||
def test_holiday_manager_draft_self_and_is_manager_id(self):
|
||||
"""
|
||||
The holiday manager should be able to do everything
|
||||
"""
|
||||
self.employee_hrmanager.write({'leave_manager_id': self.user_hrmanager.id})
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hrmanager.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.with_user(self.user_hrmanager.id).action_draft()
|
||||
|
||||
def test_holiday_manager_draft_refused_leave(self):
|
||||
"""
|
||||
The holiday manager should be able to do everything
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=5 + i), 1, values)
|
||||
leave.action_refuse()
|
||||
leave.with_user(self.user_hrmanager.id).action_draft()
|
||||
|
||||
def test_holiday_manager_draft_current_leave(self):
|
||||
"""
|
||||
The holiday manager should be able to do everything
|
||||
"""
|
||||
for i, status in enumerate(self.draft_status):
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': status.id,
|
||||
}
|
||||
leave = self.request_leave(1, datetime.today() + relativedelta(days=-20 + i), 1, values)
|
||||
leave.with_user(self.user_hrmanager.id).action_draft()
|
||||
|
||||
@tests.tagged('access_rights', 'access_rights_create')
|
||||
class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -390,7 +91,7 @@ class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
|||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
self.request_leave(self.user_employee_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
self.request_leave(self.user_employee_id, date.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_base_user_create_other(self):
|
||||
|
|
@ -401,21 +102,9 @@ class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
with self.assertRaises(AccessError):
|
||||
self.request_leave(self.user_employee_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
self.request_leave(self.user_employee_id, date.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_base_user_create_batch(self):
|
||||
""" A simple user cannot create a leave in bacth mode (by company, by department, by tag)"""
|
||||
values = {
|
||||
'name': 'Hol10',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': 1,
|
||||
}
|
||||
with self.assertRaises(AccessError):
|
||||
self.request_leave(self.user_employee_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
# hr_holidays.group_hr_holidays_user
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -426,7 +115,7 @@ class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
|||
'employee_id': self.employee_hruser_id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
self.request_leave(self.user_hruser_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
self.request_leave(self.user_hruser_id, date.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_holidays_user_create_other(self):
|
||||
|
|
@ -436,7 +125,7 @@ class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
|||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
self.request_leave(self.user_hruser_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
self.request_leave(self.user_hruser_id, date.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
# hr_holidays.group_hr_holidays_manager
|
||||
|
||||
|
|
@ -448,7 +137,7 @@ class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
|||
'employee_id': self.employee_hrmanager_id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
self.request_leave(self.user_hrmanager_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
self.request_leave(self.user_hrmanager_id, date.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_holidays_manager_create_other(self):
|
||||
|
|
@ -458,18 +147,7 @@ class TestAccessRightsCreate(TestHrHolidaysAccessRightsCommon):
|
|||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
self.request_leave(self.user_hrmanager_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_holidays_manager_create_batch(self):
|
||||
""" A holidays manager can create a leave in bacth mode (by company, by department, by tag)"""
|
||||
values = {
|
||||
'name': 'Hol10',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': 1,
|
||||
}
|
||||
self.request_leave(self.user_hrmanager_id, datetime.today() + relativedelta(days=5), 1, values)
|
||||
self.request_leave(self.user_hrmanager_id, date.today() + relativedelta(days=5), 1, values)
|
||||
|
||||
|
||||
@tests.tagged('access_rights', 'access_rights_read')
|
||||
|
|
@ -484,11 +162,10 @@ class TestAccessRightsRead(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
'department_id': self.employee_hruser.department_id.id,
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'date_from': datetime.now(),
|
||||
'date_to': datetime.now() + relativedelta(days=1),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': date.today(),
|
||||
'request_date_to': date.today() + relativedelta(days=1),
|
||||
})
|
||||
with self.assertRaises(AccessError), self.cr.savepoint():
|
||||
with self.assertRaises(AccessError):
|
||||
res = other_leave.with_user(self.user_employee_id).read(['number_of_days', 'state', 'name'])
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -499,11 +176,10 @@ class TestAccessRightsRead(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
'department_id': self.employee_hruser.department_id.id,
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'date_from': datetime.now(),
|
||||
'date_to': datetime.now() + relativedelta(days=1),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': date.today(),
|
||||
'request_date_to': date.today() + relativedelta(days=1),
|
||||
})
|
||||
with self.assertRaises(AccessError), self.cr.savepoint():
|
||||
with self.assertRaises(AccessError):
|
||||
other_leave.invalidate_model(['name'])
|
||||
name = other_leave.with_user(self.user_employee_id).name
|
||||
|
||||
|
|
@ -531,9 +207,8 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
'department_id': self.employee_hruser.department_id.id,
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'date_from': datetime.now(),
|
||||
'date_to': datetime.now() + relativedelta(days=1),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': date.today(),
|
||||
'request_date_to': date.today() + relativedelta(days=1),
|
||||
})
|
||||
with self.assertRaises(AccessError):
|
||||
other_leave.with_user(self.user_employee_id).write({'name': 'Crocodile Dundee is my man'})
|
||||
|
|
@ -547,9 +222,8 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
'name': 'Hol10',
|
||||
'employee_id': self.employee_hruser_id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'date_from': (datetime.today() - relativedelta(days=1)),
|
||||
'date_to': datetime.today(),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': (date.today() - relativedelta(days=1)),
|
||||
'request_date_to': date.today(),
|
||||
})
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -598,13 +272,13 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_leave_hr_to_validate_by_manager(self):
|
||||
""" Manager validate its own leaves """
|
||||
leave_start = date_utils.start_of(date.today() + relativedelta(days=15), 'week')
|
||||
manager_leave = self.env['hr.leave'].with_user(self.user_hrmanager_id).create({
|
||||
'name': 'Hol manager',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_hrmanager_id,
|
||||
'date_from': (datetime.today() + relativedelta(days=15)),
|
||||
'date_to': (datetime.today() + relativedelta(days=16)),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': leave_start,
|
||||
'request_date_to': leave_start + relativedelta(days=1),
|
||||
})
|
||||
self.assertEqual(manager_leave.state, 'confirm')
|
||||
manager_leave.action_approve()
|
||||
|
|
@ -626,8 +300,8 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'confirm',
|
||||
}
|
||||
hr_leave = self.request_leave(self.user_hruser_id, datetime.now() + relativedelta(days=2), 1, values)
|
||||
with self.assertRaises(AccessError):
|
||||
hr_leave = self.request_leave(self.user_hruser_id, date_utils.start_of(date.today() + relativedelta(days=7), 'week'), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
hr_leave.with_user(self.user_employee_id).action_approve()
|
||||
self.employee_hruser.write({'leave_manager_id': self.user_employee_id})
|
||||
hr_leave.with_user(self.user_employee_id).action_approve()
|
||||
|
|
@ -644,7 +318,7 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'confirm',
|
||||
}
|
||||
hr_leave = self.request_leave(self.user_hruser_id, datetime.now() + relativedelta(days=2), 1, values)
|
||||
hr_leave = self.request_leave(self.user_hruser_id, date_utils.start_of(date.today() + relativedelta(days=7), 'week'), 1, values)
|
||||
hr_leave.with_user(self.user_hruser_id).action_approve()
|
||||
|
||||
# ----------------------------------------
|
||||
|
|
@ -661,18 +335,18 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
'state': 'confirm',
|
||||
}
|
||||
self.employee_hrmanager.leave_manager_id = self.env['res.users'].browse(1)
|
||||
hr_leave = self.request_leave(self.user_hruser_id, datetime.now() + relativedelta(days=6), 1, values)
|
||||
leave_date = date_utils.start_of(date.today() + relativedelta(days=7), 'week')
|
||||
hr_leave = self.request_leave(self.user_hruser_id, leave_date, 1, values)
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
with self.assertRaises(UserError):
|
||||
hr_leave.with_user(self.user_employee_id).action_approve()
|
||||
self.employee_hrmanager.leave_manager_id = self.user_responsible
|
||||
hr_leave.with_user(self.user_responsible_id).action_approve()
|
||||
|
||||
self.employee_hrmanager.leave_manager_id = self.user_hruser
|
||||
with self.assertRaises(UserError):
|
||||
hr_leave.with_user(self.user_employee_id).action_approve()
|
||||
hr_leave.with_user(self.user_hruser_id).action_approve()
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
hr_leave.with_user(self.user_employee_id).action_validate()
|
||||
hr_leave.with_user(self.user_hruser_id).action_validate()
|
||||
|
||||
# hr_holidays.group_hr_holidays_manager
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -684,9 +358,9 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'confirm',
|
||||
}
|
||||
hr_leave = self.request_leave(self.user_hrmanager_id, datetime.now() + relativedelta(days=4), 1, values).with_user(self.user_hrmanager_id)
|
||||
leave_start = date_utils.start_of(date.today() + relativedelta(days=15), 'week')
|
||||
hr_leave = self.request_leave(self.user_hrmanager_id, leave_start, 1, values).with_user(self.user_hrmanager_id)
|
||||
hr_leave.action_approve()
|
||||
hr_leave.action_validate()
|
||||
|
||||
# ----------------------------------------
|
||||
# State = Refuse
|
||||
|
|
@ -702,6 +376,32 @@ class TestAccessRightsWrite(TestHrHolidaysAccessRightsCommon):
|
|||
|
||||
# TODO Can refuse
|
||||
|
||||
# hr_holidays.group_hr_holidays_responsible
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
@freeze_time('2026-01-23 10:00:00')
|
||||
def test_holiday_responsible_refuse_leave(self):
|
||||
"""
|
||||
The holiday responsible should be able to accept and refuse correct type leaves of users they are responsible for
|
||||
"""
|
||||
respo_user = self.user_responsible
|
||||
self.employee_emp.leave_manager_id = respo_user
|
||||
|
||||
for validatation_type in ['manager', 'both']:
|
||||
self.leave_type.write({'leave_validation_type': validatation_type})
|
||||
values = {
|
||||
'name': 'Random Time Off',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'confirm',
|
||||
}
|
||||
leave = self.request_leave(self.user_employee, date.today(), 1, values)
|
||||
leave.with_user(respo_user).action_refuse()
|
||||
# Check that refusing after first approval also works
|
||||
leave = self.request_leave(self.user_employee, date.today(), 1, values)
|
||||
leave.with_user(respo_user).action_approve()
|
||||
leave.with_user(respo_user).action_refuse()
|
||||
|
||||
# ----------------------------------------
|
||||
# State = Cancel
|
||||
# ----------------------------------------
|
||||
|
|
@ -722,54 +422,40 @@ class TestAccessRightsUnlink(TestHrHolidaysAccessRightsCommon):
|
|||
|
||||
# base.group_user
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_leave_unlink_draft_by_user(self):
|
||||
""" A simple user may delete its leave in draft state in the future"""
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'draft',
|
||||
}
|
||||
leave = self.request_leave(self.user_employee_id, datetime.now() + relativedelta(days=5), 1, values)
|
||||
leave.with_user(self.user_employee.id).unlink()
|
||||
|
||||
def test_leave_unlink_confirm_by_user(self):
|
||||
""" A simple user may delete its leave in confirm state in the future"""
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'name': 'Random Time Off',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'confirm',
|
||||
}
|
||||
leave = self.request_leave(self.user_employee_id, datetime.now() + relativedelta(days=5), 1, values)
|
||||
leave = self.request_leave(self.user_employee_id, date.today() + relativedelta(days=6), 1, values)
|
||||
leave.with_user(self.user_employee.id).unlink()
|
||||
|
||||
def test_leave_unlink_confirm_in_past_by_user(self):
|
||||
""" A simple user cannot delete past leaves, but can delete today's leave"""
|
||||
""" A simple user cannot delete its leave in the past"""
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'name': 'Random Time Off',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'state': 'confirm',
|
||||
}
|
||||
with freeze_time('2024-5-23 13:00:00'):
|
||||
other_leave = self.request_leave(self.user_employee_id, datetime.now() + relativedelta(hours=-4), 1, values)
|
||||
other_leave.with_user(self.user_employee.id).unlink()
|
||||
leave = self.request_leave(self.user_employee_id, datetime.now() + relativedelta(days=-4), 1, values)
|
||||
with self.assertRaises(UserError), self.cr.savepoint():
|
||||
leave.with_user(self.user_employee.id).unlink()
|
||||
leave = self.request_leave(self.user_employee_id, date.today() + relativedelta(days=-4), 1, values)
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_employee.id).unlink()
|
||||
|
||||
def test_leave_unlink_validate_by_user(self):
|
||||
""" A simple user cannot delete its leave in validate state"""
|
||||
values = {
|
||||
'name': 'Random Leave',
|
||||
'name': 'Random Time Off',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
}
|
||||
leave = self.request_leave(self.user_employee_id, datetime.now() + relativedelta(days=5), 1, values)
|
||||
leave = self.request_leave(self.user_employee_id, date.today() + relativedelta(days=6), 1, values)
|
||||
leave.with_user(self.user_hrmanager_id).write({'state': 'validate'})
|
||||
with self.assertRaises(UserError), self.cr.savepoint():
|
||||
with self.assertRaises(UserError):
|
||||
leave.with_user(self.user_employee.id).unlink()
|
||||
|
||||
class TestMultiCompany(TestHrHolidaysCommon):
|
||||
|
|
@ -784,8 +470,9 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
'name': 'Unlimited - Company New',
|
||||
'company_id': cls.new_company.id,
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
cls.employee_emp.company_id = cls.new_company
|
||||
cls.rd_dept.manager_id = False
|
||||
cls.hr_dept.manager_id = False
|
||||
|
||||
|
|
@ -794,9 +481,8 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
'holiday_status_id': cls.leave_type.id,
|
||||
'department_id': cls.employee_emp.department_id.id,
|
||||
'employee_id': cls.employee_emp.id,
|
||||
'date_from': datetime.now(),
|
||||
'date_to': datetime.now() + relativedelta(days=1),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': date.today(),
|
||||
'request_date_to': date.today() + relativedelta(days=1),
|
||||
})
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -806,7 +492,7 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
with self.assertRaises(AccessError):
|
||||
employee_leave.name
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
with self.assertRaises(UserError):
|
||||
employee_leave.action_approve()
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -816,7 +502,7 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
with self.assertRaises(AccessError):
|
||||
employee_leave_hruser.name
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
with self.assertRaises(UserError):
|
||||
employee_leave_hruser.action_approve()
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
|
|
@ -826,11 +512,12 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
with self.assertRaises(AccessError):
|
||||
employee_leave_hrmanager.name
|
||||
|
||||
with self.assertRaises(AccessError):
|
||||
with self.assertRaises(UserError):
|
||||
employee_leave_hrmanager.action_approve()
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_leave_access_no_company_user(self):
|
||||
self.employee_emp.company_id = self.user_employee.company_id
|
||||
self.leave_type.write({'company_id': False})
|
||||
employee_leave = self.employee_leave.with_user(self.user_employee)
|
||||
|
||||
|
|
@ -839,8 +526,10 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
employee_leave.action_approve()
|
||||
self.assertEqual(employee_leave.state, 'confirm')
|
||||
|
||||
@unittest.skip
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_leave_access_no_company_officer(self):
|
||||
self.employee_emp.company_id = self.user_employee.company_id
|
||||
self.leave_type.write({'company_id': False})
|
||||
employee_leave_hruser = self.employee_leave.with_user(self.user_hruser)
|
||||
|
||||
|
|
@ -848,8 +537,10 @@ class TestMultiCompany(TestHrHolidaysCommon):
|
|||
employee_leave_hruser.action_approve()
|
||||
self.assertEqual(employee_leave_hruser.state, 'validate')
|
||||
|
||||
@unittest.skip
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_leave_access_no_company_manager(self):
|
||||
self.employee_emp.company_id = self.user_employee.company_id
|
||||
self.leave_type.write({'company_id': False})
|
||||
employee_leave_hrmanager = self.employee_leave.with_user(self.user_hrmanager)
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import tests
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
import time
|
||||
|
|
@ -17,25 +16,25 @@ class TestAllocationRights(TestHrHolidaysCommon):
|
|||
cls.employee_emp.parent_id = False
|
||||
cls.employee_emp.leave_manager_id = False
|
||||
|
||||
cls.lt_no_allocation = cls.env['hr.leave.type'].create({
|
||||
cls.lt_validation_hr = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = HR',
|
||||
'allocation_validation_type': 'officer',
|
||||
'requires_allocation': 'no',
|
||||
'employee_requests': 'yes',
|
||||
'allocation_validation_type': 'hr',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': True,
|
||||
})
|
||||
|
||||
cls.lt_validation_manager = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = manager',
|
||||
'allocation_validation_type': 'officer',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'yes',
|
||||
'allocation_validation_type': 'manager',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': True,
|
||||
})
|
||||
|
||||
cls.lt_allocation_no_validation = cls.env['hr.leave.type'].create({
|
||||
'name': 'Validation = user',
|
||||
'allocation_validation_type': 'no',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'yes',
|
||||
'allocation_validation_type': 'no_validation',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': True,
|
||||
})
|
||||
|
||||
def request_allocation(self, user, values={}):
|
||||
|
|
@ -58,7 +57,7 @@ class TestAccessRightsSimpleUser(TestAllocationRights):
|
|||
}
|
||||
allocation = self.request_allocation(self.user_employee.id, values)
|
||||
with self.assertRaises(UserError):
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
|
||||
def test_simple_user_request_allocation_no_validation(self):
|
||||
""" A simple user can request and automatically validate an allocation with no validation """
|
||||
|
|
@ -85,11 +84,7 @@ class TestAccessRightsSimpleUser(TestAllocationRights):
|
|||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_employee.id, values)
|
||||
self.assertEqual(allocation.state, 'draft')
|
||||
allocation.action_confirm()
|
||||
self.assertEqual(allocation.state, 'confirm', "It should be confirmed")
|
||||
allocation.action_draft()
|
||||
self.assertEqual(allocation.state, 'draft', "It should have been reset to draft")
|
||||
self.assertEqual(allocation.state, 'confirm', "The allocation should be in 'confirm' state")
|
||||
|
||||
|
||||
class TestAccessRightsEmployeeManager(TestAllocationRights):
|
||||
|
|
@ -118,8 +113,7 @@ class TestAccessRightsEmployeeManager(TestAllocationRights):
|
|||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_employee.id, values)
|
||||
allocation.action_confirm()
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
self.assertEqual(allocation.state, 'validate', "The allocation should be validated")
|
||||
|
||||
def test_manager_refuse_request_allocation(self):
|
||||
|
|
@ -129,20 +123,9 @@ class TestAccessRightsEmployeeManager(TestAllocationRights):
|
|||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_employee.id, values)
|
||||
allocation.action_confirm()
|
||||
allocation.action_refuse()
|
||||
self.assertEqual(allocation.state, 'refuse', "The allocation should be validated")
|
||||
|
||||
def test_manager_batch_allocation(self):
|
||||
""" A manager cannot create batch allocation """
|
||||
values = {
|
||||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.user_employee.company_id.id,
|
||||
}
|
||||
with self.assertRaises(AccessError):
|
||||
self.request_allocation(self.user_employee.id, values)
|
||||
|
||||
def test_manager_approve_own(self):
|
||||
""" A manager cannot approve his own allocation """
|
||||
values = {
|
||||
|
|
@ -151,31 +134,21 @@ class TestAccessRightsEmployeeManager(TestAllocationRights):
|
|||
}
|
||||
allocation = self.request_allocation(self.user_employee.id, values)
|
||||
with self.assertRaises(UserError):
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
|
||||
|
||||
class TestAccessRightsHolidayUser(TestAllocationRights):
|
||||
|
||||
def test_holiday_user_request_allocation(self):
|
||||
""" A holiday user can request and approve an allocation for any employee """
|
||||
""" A holiday user can request and approve an allocation for any internal employee """
|
||||
values = {
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
'holiday_status_id': self.lt_validation_hr.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_hruser.id, values)
|
||||
allocation.action_confirm()
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
self.assertEqual(allocation.state, 'validate', "It should have been validated")
|
||||
|
||||
def test_holiday_user_batch_allocation(self):
|
||||
""" A holiday user cannot create a batch allocation """
|
||||
values = {
|
||||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.user_employee.company_id.id,
|
||||
}
|
||||
with self.assertRaises(AccessError):
|
||||
self.request_allocation(self.user_hruser.id, values)
|
||||
|
||||
def test_holiday_user_cannot_approve_own(self):
|
||||
""" A holiday user cannot approve his own allocation """
|
||||
values = {
|
||||
|
|
@ -183,9 +156,27 @@ class TestAccessRightsHolidayUser(TestAllocationRights):
|
|||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_hruser.id, values)
|
||||
allocation.action_confirm()
|
||||
with self.assertRaises(UserError):
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
|
||||
def test_holiday_user_cannot_approve_external_company(self):
|
||||
"""A holidy user can validate but not approve allocations for employees in external company"""
|
||||
|
||||
self.user_hruser.write({
|
||||
'company_ids': [(6, 0, [self.company.id, self.external_company.id])],
|
||||
})
|
||||
|
||||
values = {
|
||||
'employee_id': self.employee_external.id,
|
||||
'holiday_status_id': self.lt_validation_hr.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_hruser.id, values).with_company(self.external_company.id)
|
||||
self.assertEqual(allocation.can_validate, True)
|
||||
self.assertEqual(allocation.can_approve, False)
|
||||
|
||||
self.assertEqual(allocation.state, 'confirm')
|
||||
allocation.action_approve()
|
||||
self.assertEqual(allocation.state, 'validate')
|
||||
|
||||
|
||||
class TestAccessRightsHolidayManager(TestAllocationRights):
|
||||
|
|
@ -197,8 +188,7 @@ class TestAccessRightsHolidayManager(TestAllocationRights):
|
|||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_hrmanager.id, values)
|
||||
allocation.action_confirm()
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
self.assertEqual(allocation.state, 'validate', "It should have been validated")
|
||||
|
||||
def test_holiday_manager_refuse_validated(self):
|
||||
|
|
@ -208,8 +198,7 @@ class TestAccessRightsHolidayManager(TestAllocationRights):
|
|||
'holiday_status_id': self.lt_validation_manager.id,
|
||||
}
|
||||
allocation = self.request_allocation(self.user_hrmanager.id, values)
|
||||
allocation.action_confirm()
|
||||
allocation.action_validate()
|
||||
allocation.action_approve()
|
||||
self.assertEqual(allocation.state, 'validate', "It should have been validated")
|
||||
allocation.action_refuse()
|
||||
self.assertEqual(allocation.state, 'refuse', "It should have been refused")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,656 @@
|
|||
from datetime import date, timedelta
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.fields import Date, Datetime
|
||||
from odoo.tests import Form, tagged, users
|
||||
from odoo.tools import format_date
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
@tagged('allocation')
|
||||
class TestAllocations(TestHrHolidaysCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestAllocations, cls).setUpClass()
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Time Off with no validation for approval',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'no_validation',
|
||||
})
|
||||
cls.department = cls.env['hr.department'].create({
|
||||
'name': 'Test Department',
|
||||
})
|
||||
cls.category_tag = cls.env['hr.employee.category'].create({
|
||||
'name': 'Test category'
|
||||
})
|
||||
cls.employee = cls.env['hr.employee'].create({
|
||||
'name': 'My Employee',
|
||||
'company_id': cls.company.id,
|
||||
'department_id': cls.department.id,
|
||||
'category_ids': [(4, cls.category_tag.id)],
|
||||
})
|
||||
|
||||
cls.leave_type_paid = cls.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'no_validation',
|
||||
})
|
||||
|
||||
cls.calendar_35h = cls.env['resource.calendar'].create({
|
||||
'name': 'Calendar - 35H',
|
||||
'company_id': cls.company.id,
|
||||
'attendance_ids': [(5, 0, 0),
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Lunch', 'dayofweek': '1', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Lunch', 'dayofweek': '2', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Lunch', 'dayofweek': '3', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Friday Lunch', 'dayofweek': '4', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'})
|
||||
]
|
||||
})
|
||||
|
||||
def test_allocation_whole_company(self):
|
||||
company_allocation = self.env['hr.leave.allocation.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'duration': 2,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
company_allocation.action_generate_allocations()
|
||||
|
||||
num_of_allocations = self.env['hr.leave.allocation'].search_count([('employee_id', '=', self.employee.id)])
|
||||
self.assertEqual(num_of_allocations, 1)
|
||||
|
||||
def test_allocation_multi_employee(self):
|
||||
employee_allocation = self.env['hr.leave.allocation.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'allocation_mode': 'employee',
|
||||
'employee_ids': [(4, self.employee.id), (4, self.employee_emp.id)],
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'duration': 2,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
employee_allocation.action_generate_allocations()
|
||||
|
||||
num_of_allocations = self.env['hr.leave.allocation'].search_count([('employee_id', '=', self.employee.id)])
|
||||
self.assertEqual(num_of_allocations, 1)
|
||||
|
||||
def test_allocation_department(self):
|
||||
department_allocation = self.env['hr.leave.allocation.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'allocation_mode': 'department',
|
||||
'department_id': self.department.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'duration': 2,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
department_allocation.action_generate_allocations()
|
||||
|
||||
num_of_allocations = self.env['hr.leave.allocation'].search_count([('employee_id', '=', self.employee.id)])
|
||||
self.assertEqual(num_of_allocations, 1)
|
||||
|
||||
@users('Titus')
|
||||
def test_create_group_allocation_without_hr_right(self):
|
||||
employee_1, employee_2 = self.env['hr.employee'].sudo().create([
|
||||
{
|
||||
'name': 'Emp1',
|
||||
'leave_manager_id': self.user_responsible_id,
|
||||
}, {
|
||||
'name': 'Emp2',
|
||||
'leave_manager_id': self.user_responsible_id,
|
||||
},
|
||||
])
|
||||
allocation_wizard = self.env['hr.leave.allocation.generate.multi.wizard'].create({
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'date_from': date(2019, 5, 6),
|
||||
'date_to': date(2019, 5, 6),
|
||||
'employee_ids': (employee_1 + employee_2).ids,
|
||||
'duration': 2,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
allocation_wizard.action_generate_allocations()
|
||||
|
||||
def test_allocation_category(self):
|
||||
category_allocation = self.env['hr.leave.allocation.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'allocation_mode': 'category',
|
||||
'category_id': self.category_tag.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'duration': 2,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
category_allocation.action_generate_allocations()
|
||||
|
||||
num_of_allocations = self.env['hr.leave.allocation'].search_count([('employee_id', '=', self.employee.id)])
|
||||
self.assertEqual(num_of_allocations, 1)
|
||||
|
||||
def test_allocation_request_day(self):
|
||||
self.leave_type.write({
|
||||
'name': 'Custom Time Off Test',
|
||||
'allocation_validation_type': 'hr'
|
||||
})
|
||||
|
||||
employee_allocation = self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
with Form(employee_allocation.with_context(is_employee_allocation=True), 'hr_holidays.hr_leave_allocation_view_form_dashboard') as allocation:
|
||||
allocation.number_of_days_display = 10
|
||||
employee_allocation = allocation.save()
|
||||
|
||||
self.assertEqual(employee_allocation.name, "Custom Time Off Test (10.0 day(s))")
|
||||
|
||||
def test_allocation_request_half_days(self):
|
||||
self.leave_type.write({
|
||||
'name': 'Custom Time Off Test',
|
||||
'allocation_validation_type': 'hr'
|
||||
})
|
||||
|
||||
employee_allocation = self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'allocation_type': 'regular',
|
||||
'type_request_unit': 'half_day',
|
||||
})
|
||||
|
||||
with Form(employee_allocation.with_context(is_employee_allocation=True), 'hr_holidays.hr_leave_allocation_view_form_dashboard') as allocation:
|
||||
allocation.number_of_days_display = 10
|
||||
employee_allocation = allocation.save()
|
||||
|
||||
self.assertEqual(employee_allocation.name, "Custom Time Off Test (10.0 day(s))")
|
||||
|
||||
def change_allocation_type_day(self):
|
||||
self.leave_type.write({
|
||||
'name': 'Custom Time Off Test',
|
||||
'allocation_validation_type': 'hr'
|
||||
})
|
||||
|
||||
employee_allocation = self.env['hr.leave.allocation'].create({
|
||||
'holiday_type': 'employee',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
with Form(employee_allocation.with_context(is_employee_allocation=True), 'hr_holidays.hr_leave_allocation_view_form_dashboard') as allocation:
|
||||
allocation.allocation_type = 'extra'
|
||||
allocation.allocation_type = 'regular'
|
||||
employee_allocation = allocation.save()
|
||||
|
||||
self.assertEqual(employee_allocation.number_of_days, 1.0)
|
||||
|
||||
def test_allocation_type_hours_with_resource_calendar(self):
|
||||
self.leave_type.request_unit = 'hour'
|
||||
self.employee.resource_calendar_id = self.calendar_35h
|
||||
|
||||
hour_type_allocation = self.env['hr.leave.allocation.generate.multi.wizard'].create({
|
||||
'name': 'Hours Allocation',
|
||||
'allocation_mode': 'employee',
|
||||
'employee_ids': [(4, self.employee.id), (4, self.employee_emp.id)],
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'duration': 10,
|
||||
'allocation_type': 'regular',
|
||||
})
|
||||
|
||||
self.assertEqual(self.employee.resource_calendar_id.hours_per_day, 7.0)
|
||||
self.assertEqual(self.employee_emp.resource_calendar_id.hours_per_day, 8.0)
|
||||
|
||||
hour_type_allocation.action_generate_allocations()
|
||||
|
||||
# Find allocations created for individual employees
|
||||
employee_allocation = self.env['hr.leave.allocation'].search([
|
||||
('employee_id', '=', self.employee.id),
|
||||
])
|
||||
employee_emp_allocation = self.env['hr.leave.allocation'].search([
|
||||
('employee_id', '=', self.employee_emp.id),
|
||||
])
|
||||
|
||||
self.assertEqual(employee_allocation.number_of_hours_display, 10)
|
||||
self.assertEqual(employee_emp_allocation.number_of_hours_display, 10)
|
||||
|
||||
def change_allocation_type_hours(self):
|
||||
self.leave_type.write({
|
||||
'name': 'Custom Time Off Test',
|
||||
'allocation_validation_type': 'hr'
|
||||
})
|
||||
|
||||
employee_allocation = self.env['hr.leave.allocation'].create({
|
||||
'holiday_type': 'employee',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'allocation_type': 'regular',
|
||||
'type_request_unit': 'hour',
|
||||
})
|
||||
|
||||
with Form(employee_allocation.with_context(is_employee_allocation=True), 'hr_holidays.hr_leave_allocation_view_form_dashboard') as allocation:
|
||||
allocation.allocation_type = 'extra'
|
||||
allocation.allocation_type = 'regular'
|
||||
employee_allocation = allocation.save()
|
||||
|
||||
self.assertEqual(employee_allocation.number_of_days, 1.0)
|
||||
|
||||
def test_allowed_change_allocation(self):
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'name': 'Initial Allocation',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'number_of_days': 20,
|
||||
'employee_id': self.employee.id,
|
||||
'date_from': date(2024, 1, 1),
|
||||
})
|
||||
allocation.action_approve()
|
||||
|
||||
leave_request = self.env['hr.leave'].create({
|
||||
'name': 'Leave Request',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'request_date_from': date(2024, 1, 5),
|
||||
'request_date_to': date(2024, 1, 10),
|
||||
'employee_id': self.employee.id,
|
||||
})
|
||||
leave_request.action_approve()
|
||||
allocation.write({'number_of_days_display': 14, 'number_of_days': 14})
|
||||
self.assertEqual(allocation.number_of_days_display, 14)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
allocation.write({'number_of_days_display': 2, 'number_of_days': 2})
|
||||
|
||||
def test_disallowed_change_allocation_with_overlapping_allocations(self):
|
||||
# Creating the first allocation
|
||||
allocation_one = self.env['hr.leave.allocation'].create({
|
||||
'name': 'First Allocation',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'number_of_days': 5,
|
||||
'employee_id': self.employee.id,
|
||||
'date_from': date(2024, 1, 1),
|
||||
'date_to': date(2024, 1, 30),
|
||||
})
|
||||
allocation_one.action_approve()
|
||||
|
||||
# Creating the second overlapping allocation
|
||||
allocation_two = self.env['hr.leave.allocation'].create({
|
||||
'name': 'Second Half Allocation',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'number_of_days': 5,
|
||||
'employee_id': self.employee.id,
|
||||
'date_from': date(2024, 1, 20),
|
||||
'date_to': date(2024, 2, 20),
|
||||
})
|
||||
allocation_two.action_approve()
|
||||
|
||||
# Creating a leave request consuming days from both allocations
|
||||
leave_request = self.env['hr.leave'].create({
|
||||
'name': 'Leave Request Spanning Allocations',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'request_date_from': date(2024, 1, 25),
|
||||
'request_date_to': date(2024, 2, 5),
|
||||
'employee_id': self.employee.id,
|
||||
})
|
||||
leave_request.action_approve()
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
allocation_one.write({'number_of_days_display': 2, 'number_of_days': 2})
|
||||
|
||||
allocation_one.write({'number_of_days_display': 3, 'number_of_days': 3})
|
||||
|
||||
@users('admin')
|
||||
@freeze_time('2024-03-25')
|
||||
def test_allocation_dropdown_after_period(self):
|
||||
"""
|
||||
Test when having two allocations of the same type with different
|
||||
time range and submitting a request will the allocations be
|
||||
shown correctly in the dropdown menu or not
|
||||
:return:
|
||||
"""
|
||||
leave_type = self.env.ref('hr_holidays.leave_type_compensatory_days')
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'name': 'Alloc',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 3,
|
||||
'allocation_type': 'regular',
|
||||
'date_from': date(2024, 1, 1),
|
||||
'date_to': date(2024, 4, 30)
|
||||
})
|
||||
allocation.action_approve()
|
||||
|
||||
second_allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'name': 'Alloc2',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 9,
|
||||
'allocation_type': 'regular',
|
||||
'date_from': date(2024, 5, 1),
|
||||
'date_to': date(2024, 12, 31)
|
||||
})
|
||||
second_allocation.action_approve()
|
||||
|
||||
# _compute_leaves depends on the context that is getting cleared
|
||||
self.env['hr.leave.type'].invalidate_model(['max_leaves', 'leaves_taken', 'virtual_remaining_leaves'])
|
||||
result = self.env['hr.leave.type'].with_context(
|
||||
employee_id=self.employee.id,
|
||||
leave_date_from='2024-08-18 06:00:00', # for _compute_leaves
|
||||
default_date_from='2024-08-18 06:00:00',
|
||||
default_date_to='2024-08-18 15:00:00'
|
||||
).name_search(domain=[['id', '=', leave_type.id]])
|
||||
self.assertEqual(result[0][1], 'Compensatory Days (9 remaining out of 9 days)')
|
||||
|
||||
def test_allocation_hourly_leave_type(self):
|
||||
"""
|
||||
Make sure that the number of hours is correctly set on the allocation for an hourly leave type
|
||||
for an employee who works some other schedule than the default 8 hours per day.
|
||||
"""
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': 'My Employee',
|
||||
'company_id': self.company.id,
|
||||
'resource_calendar_id': self.calendar_35h.id,
|
||||
})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Hourly Leave Type',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'no_validation',
|
||||
'request_unit': 'hour',
|
||||
})
|
||||
|
||||
with Form(self.env['hr.leave.allocation'].with_user(self.user_hrmanager)) as allocation_form:
|
||||
allocation_form.allocation_type = 'regular'
|
||||
allocation_form.employee_id = employee
|
||||
allocation_form.holiday_status_id = leave_type
|
||||
allocation_form.number_of_hours_display = 10
|
||||
allocation = allocation_form.save()
|
||||
|
||||
self.assertEqual(allocation.number_of_hours_display, 10.0)
|
||||
|
||||
def test_automatic_allocation_type(self):
|
||||
"""
|
||||
Make sure that an allocation with an accrual plan imported will automatically set the allocation_type to 'accrual'
|
||||
"""
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Hourly Leave Type',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': 'yes',
|
||||
'allocation_validation_type': 'no_validation',
|
||||
'request_unit': 'hour',
|
||||
})
|
||||
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
||||
'name': 'Accrual Plan For Test',
|
||||
})
|
||||
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'name': 'Alloc with accrual plan',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
})
|
||||
|
||||
self.assertEqual(allocation.allocation_type, 'accrual')
|
||||
|
||||
allocation.update({
|
||||
'accrual_plan_id': False,
|
||||
})
|
||||
|
||||
self.assertEqual(allocation.allocation_type, 'regular')
|
||||
|
||||
def test_create_allocation_from_company_with_no_employee_for_current_user(self):
|
||||
"""
|
||||
This test makes sure that the allocation can be created if the current company doesn't have an employee
|
||||
linked to the loggedIn user.
|
||||
"""
|
||||
self.user_hrmanager.employee_id = False
|
||||
allocation_form = Form(self.env['hr.leave.allocation'].with_user(self.user_hrmanager))
|
||||
self.assertFalse(allocation_form.employee_id)
|
||||
allocation_form.employee_id = self.employee
|
||||
allocation_form.holiday_status_id = self.leave_type
|
||||
allocation = allocation_form.save()
|
||||
self.assertTrue(allocation)
|
||||
|
||||
def test_hr_leave_allocation_balance(self):
|
||||
"""
|
||||
This test makes sure that the time off balance showed on the time off management kanban card is correct
|
||||
"""
|
||||
leave_type = self.env.ref('hr_holidays.leave_type_compensatory_days')
|
||||
|
||||
invalid_allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'name': 'Alloc',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 5,
|
||||
'allocation_type': 'regular',
|
||||
'date_from': date(2024, 1, 1),
|
||||
'date_to': date(2024, 4, 30)
|
||||
})
|
||||
invalid_allocation.action_approve()
|
||||
|
||||
first_valid_allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'name': 'Alloc',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 10,
|
||||
'allocation_type': 'regular',
|
||||
'date_from': date(2024, 1, 1),
|
||||
'date_to': False
|
||||
})
|
||||
first_valid_allocation.action_approve()
|
||||
|
||||
second_valid_allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'name': 'Alloc',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 12,
|
||||
'allocation_type': 'regular',
|
||||
'date_from': date(2025, 1, 1),
|
||||
'date_to': date.today()
|
||||
})
|
||||
second_valid_allocation.action_approve()
|
||||
|
||||
leave = self.env['hr.leave'].create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': date(2025, 1, 1),
|
||||
'request_date_to': date(2025, 1, 10)
|
||||
})
|
||||
leave._action_validate()
|
||||
|
||||
self.assertEqual(leave.max_leaves, 22)
|
||||
self.assertEqual(leave.virtual_remaining_leaves, 14)
|
||||
|
||||
def test_allocation_request_with_date_from(self):
|
||||
allocation = self.env['hr.leave.allocation'].with_user(self.user_hrmanager)
|
||||
allocation_view = 'hr_holidays.hr_leave_allocation_view_form'
|
||||
with self.assertRaises(AssertionError):
|
||||
with Form(allocation, allocation_view) as allocation_form:
|
||||
allocation_form.holiday_status_id = self.leave_type
|
||||
allocation_form.date_from = False
|
||||
|
||||
with Form(allocation, allocation_view) as allocation_form:
|
||||
date_from = Date.today()
|
||||
allocation_form.holiday_status_id = self.leave_type
|
||||
allocation_form.date_from = date_from
|
||||
|
||||
self.assertEqual(allocation_form.date_from, date_from)
|
||||
self.assertEqual(
|
||||
allocation_form.name_validity,
|
||||
"%(allocation_name)s (from %(date_from)s to No Limit)" % {
|
||||
'allocation_name': allocation_form.name,
|
||||
'date_from': format_date(allocation.env, Date.context_today(allocation, Datetime.to_datetime(allocation_form.date_from))),
|
||||
},
|
||||
"The name_validity field was not set correctly."
|
||||
)
|
||||
|
||||
def test_leave_allocation_by_removing_employee(self):
|
||||
"""
|
||||
Test that creating a leave allocation and then removing the employee will
|
||||
not raise an error
|
||||
"""
|
||||
self.leave_type.request_unit = "hour"
|
||||
with self.assertRaises(AssertionError): # AssertionError raised by Form as employee is required
|
||||
with Form(self.env['hr.leave.allocation']) as allocation_form:
|
||||
allocation_form.allocation_type = "regular"
|
||||
allocation_form.holiday_status_id = self.leave_type
|
||||
allocation_form.number_of_hours_display = 10
|
||||
allocation_form.employee_id = self.env["hr.employee"]
|
||||
allocation_form.save()
|
||||
|
||||
def test_employee_holidays_archived_display(self):
|
||||
admin_user = self.env.ref('base.user_admin')
|
||||
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': 'test_employee',
|
||||
})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].with_user(admin_user)
|
||||
|
||||
holidays_type_1 = leave_type.create({
|
||||
'name': 'archived_holidays',
|
||||
'allocation_validation_type': 'no_validation',
|
||||
})
|
||||
|
||||
self.env['hr.leave.allocation'].create({
|
||||
'name': 'archived_holidays_allocation',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': holidays_type_1.id,
|
||||
'number_of_days': 10,
|
||||
'state': 'confirm',
|
||||
'date_from': '2022-01-01',
|
||||
})
|
||||
|
||||
self.assertEqual(employee.allocation_display, '10')
|
||||
|
||||
holidays_type_1.active = False
|
||||
employee._compute_allocation_remaining_display()
|
||||
|
||||
self.assertEqual(employee.allocation_display, '0')
|
||||
|
||||
def test_refuse_validated_allocation_with_leaves(self):
|
||||
"""
|
||||
Test that an allocation can be refused after being validated only if the existing leave's taken days can be
|
||||
handled by the other allocations
|
||||
"""
|
||||
|
||||
today = date.today()
|
||||
start_of_week = today - timedelta(days=today.weekday())
|
||||
|
||||
leave_employee = self.env['hr.employee'].create({
|
||||
'name': 'Test Employee',
|
||||
'user_id': self.env.uid,
|
||||
})
|
||||
|
||||
def _create_allocation(days):
|
||||
return self.env['hr.leave.allocation'].create({
|
||||
'name': f'{days} days Allocation',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'number_of_days': days,
|
||||
'employee_id': leave_employee.id,
|
||||
'date_from': start_of_week,
|
||||
})
|
||||
|
||||
allocation_5_days = _create_allocation(days=5)
|
||||
allocation_5_days.action_approve()
|
||||
self.assertEqual(allocation_5_days.state, 'validate')
|
||||
|
||||
# 4 Days leave - Can be only on the 5 days allocation
|
||||
leave_request = self.env['hr.leave'].create({
|
||||
'name': 'Leave Request',
|
||||
'holiday_status_id': self.leave_type_paid.id,
|
||||
'request_date_from': start_of_week,
|
||||
'request_date_to': start_of_week + timedelta(days=3),
|
||||
'employee_id': leave_employee.id,
|
||||
})
|
||||
leave_request.action_approve()
|
||||
|
||||
allocation_3_days = _create_allocation(days=3)
|
||||
allocation_3_days.date_to = start_of_week + timedelta(days=5)
|
||||
allocation_3_days.action_approve()
|
||||
self.assertEqual(allocation_3_days.state, 'validate')
|
||||
|
||||
# Can't Refuse 5 days allocation
|
||||
with self.assertRaises(ValidationError):
|
||||
allocation_5_days.action_refuse()
|
||||
self.assertEqual(allocation_5_days.state, 'validate')
|
||||
|
||||
# But can Refuse 3 days one
|
||||
allocation_3_days.action_refuse()
|
||||
self.assertEqual(allocation_3_days.state, 'refuse')
|
||||
allocation_3_days.state = 'confirm'
|
||||
allocation_3_days.action_approve()
|
||||
self.assertEqual(allocation_3_days.state, 'validate')
|
||||
|
||||
# 2 Days leave - Both allocations can be refused / but not at the same time
|
||||
leave_request.state = 'confirm'
|
||||
leave_request.request_date_to = start_of_week + timedelta(days=1)
|
||||
leave_request.action_approve()
|
||||
|
||||
allocation_5_days.action_refuse()
|
||||
self.assertEqual(allocation_5_days.state, 'refuse')
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
allocation_3_days.action_refuse()
|
||||
self.assertEqual(allocation_3_days.state, 'validate')
|
||||
|
||||
allocation_5_days.state = 'confirm'
|
||||
allocation_5_days.action_approve()
|
||||
self.assertEqual(allocation_5_days.state, 'validate')
|
||||
allocation_3_days.action_refuse()
|
||||
self.assertEqual(allocation_3_days.state, 'refuse')
|
||||
|
||||
def test_time_off_hours_start_date_attendance(self):
|
||||
"""
|
||||
When we set a date_from and/or a date_to on an attendance, it doesn't appear in global attendances anymore,
|
||||
causing the hours of this attendance to not be taken into account. If all attendances have a date_from and/or
|
||||
a date_to, the total hours_per_day will reach zero, which causes a division per zero when setting a time
|
||||
off based on hours. This test makes sure that we don't divide ever by zero, even in that case.
|
||||
"""
|
||||
calendar = self.env['resource.calendar'].create({
|
||||
'name': 'Standard Calendar',
|
||||
'two_weeks_calendar': False,
|
||||
})
|
||||
self.env['resource.calendar.attendance'].create({
|
||||
'name': 'Monday',
|
||||
'calendar_id': calendar.id,
|
||||
'dayofweek': '0', # Monday
|
||||
'hour_from': 8,
|
||||
'hour_to': 16,
|
||||
})
|
||||
self.leave_type.write({'request_unit': 'hour'})
|
||||
with Form(self.env['hr.leave.allocation'].with_user(self.user_hrmanager)) as allocation_form:
|
||||
allocation_form.allocation_type = 'regular'
|
||||
allocation_form.employee_id = self.employee
|
||||
allocation_form.holiday_status_id = self.leave_type
|
||||
allocation_form.number_of_hours_display = 7.2
|
||||
allocation = allocation_form.save()
|
||||
self.assertEqual(allocation.duration_display, '7.2 hours')
|
||||
|
||||
@freeze_time('2024-03-25')
|
||||
def test_allocation_count_date_previous_year(self):
|
||||
"""Checks that the allocation count is calculated correctly when an employee has an allocation starting during
|
||||
the prevous year, but which hasn't expired yet."""
|
||||
|
||||
self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'allocation_type': 'regular',
|
||||
'date_from': '2023-12-25'
|
||||
})
|
||||
self.assertEqual(1, self.leave_type.allocation_count)
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from datetime import date, datetime
|
||||
|
||||
from odoo.tests.common import Form
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import Form
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
||||
|
|
@ -14,8 +13,7 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Automatic Test',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': 'no',
|
||||
# Required for `request_unit_half` to be visible in the view
|
||||
'requires_allocation': False,
|
||||
'request_unit': 'half_day',
|
||||
})
|
||||
|
||||
|
|
@ -30,12 +28,11 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '0 Hours')
|
||||
leave = leave_form.record
|
||||
self.assertEqual(leave.number_of_days, 0)
|
||||
self.assertEqual(leave.number_of_hours, 0)
|
||||
|
||||
def test_single_attendance_on_morning_and_afternoon(self):
|
||||
calendar = self.env['resource.calendar'].create({
|
||||
|
|
@ -48,6 +45,13 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday lunch',
|
||||
'hour_from': 12,
|
||||
'hour_to': 13,
|
||||
'day_period': 'lunch',
|
||||
'dayofweek': '0',
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday afternoon',
|
||||
'hour_from': 13,
|
||||
|
|
@ -64,16 +68,19 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, .5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '4 Hours')
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, .5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 4)
|
||||
|
||||
leave_form.request_date_from_period = 'pm'
|
||||
leave_form.request_date_to_period = 'pm'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, .5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '4 Hours')
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, .5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 4)
|
||||
|
||||
def test_multiple_attendance_on_morning(self):
|
||||
calendar = self.env['resource.calendar'].create({
|
||||
|
|
@ -85,6 +92,7 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
'hour_to': 10,
|
||||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
'duration_days': 0.25,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday morning 2',
|
||||
|
|
@ -92,6 +100,14 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
'hour_to': 12.25,
|
||||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
'duration_days': 0.25,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday lunch',
|
||||
'hour_from': 12.25,
|
||||
'hour_to': 13,
|
||||
'day_period': 'lunch',
|
||||
'dayofweek': '0',
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday afternoon',
|
||||
|
|
@ -99,6 +115,7 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
'hour_to': 17,
|
||||
'day_period': 'afternoon',
|
||||
'dayofweek': '0',
|
||||
'duration_days': 0.5,
|
||||
})]
|
||||
})
|
||||
employee = self.employee_emp
|
||||
|
|
@ -108,16 +125,19 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, .5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '4 Hours')
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, .5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 4)
|
||||
|
||||
leave_form.request_date_from_period = 'pm'
|
||||
leave_form.request_date_to_period = 'pm'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, .5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '4 Hours')
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, .5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 4)
|
||||
|
||||
def test_attendance_on_morning(self):
|
||||
calendar = self.env['resource.calendar'].create({
|
||||
|
|
@ -137,18 +157,160 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
# Ask for morning
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0.5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '8 Hours')
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 1)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 8)
|
||||
|
||||
# Ask for afternoon
|
||||
leave_form.request_date_from_period = 'pm'
|
||||
leave_form.request_date_to_period = 'pm'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0.5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '8 Hours')
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 0)
|
||||
|
||||
def test_attendance_full_day(self):
|
||||
calendar = self.env["resource.calendar"].create({
|
||||
"name": "Full Days",
|
||||
"attendance_ids": [
|
||||
Command.clear(),
|
||||
Command.create({
|
||||
"name": "Monday",
|
||||
"hour_from": 8,
|
||||
"hour_to": 16,
|
||||
"day_period": "full_day",
|
||||
"dayofweek": "0",
|
||||
}),
|
||||
],
|
||||
})
|
||||
employee = self.employee_emp
|
||||
employee.resource_calendar_id = calendar
|
||||
with Form(
|
||||
self.env["hr.leave"].with_context(default_employee_id=employee.id)
|
||||
) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2) # Monday
|
||||
leave_form.request_date_to = date(2019, 9, 2) # Monday
|
||||
|
||||
# Ask for morning
|
||||
leave_form.request_date_from_period = "am"
|
||||
leave_form.request_date_to_period = "am"
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0.5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 4)
|
||||
# dates are checked in UTC that why -2
|
||||
self.assertEqual(leave_form.record.date_from, datetime(2019, 9, 2, 6, 0, 0))
|
||||
self.assertEqual(leave_form.record.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
|
||||
# Ask for afternoon
|
||||
leave_form.request_date_from_period = "pm"
|
||||
leave_form.request_date_to_period = "pm"
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0.5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 4)
|
||||
# dates are checked in UTC that why -2
|
||||
self.assertEqual(leave_form.record.date_from, datetime(2019, 9, 2, 10, 0, 0))
|
||||
self.assertEqual(leave_form.record.date_to, datetime(2019, 9, 2, 14, 0, 0))
|
||||
|
||||
def test_attendance_based_on_duration(self):
|
||||
calendar = self.env["resource.calendar"].create({
|
||||
"name": "Full Days",
|
||||
"duration_based": True,
|
||||
"attendance_ids": [
|
||||
Command.clear(),
|
||||
Command.create({
|
||||
"name": "Monday Morning",
|
||||
"duration_hours": 5, # hour_from: 7, hour_to: 12
|
||||
"day_period": "morning",
|
||||
"dayofweek": "0"}),
|
||||
Command.create({
|
||||
"name": "Monday Afternoon",
|
||||
"duration_hours": 3, # hour_from: 12, hour_to: 15
|
||||
"day_period": "afternoon",
|
||||
"dayofweek": "0"}),
|
||||
],
|
||||
})
|
||||
employee = self.employee_emp
|
||||
employee.resource_calendar_id = calendar
|
||||
with Form(
|
||||
self.env["hr.leave"].with_context(default_employee_id=employee.id)
|
||||
) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2) # Monday
|
||||
leave_form.request_date_to = date(2019, 9, 2) # Monday
|
||||
|
||||
# Ask for morning
|
||||
leave_form.request_date_from_period = "am"
|
||||
leave_form.request_date_to_period = "am"
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0.5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 5)
|
||||
# dates are checked in UTC that why -2
|
||||
self.assertEqual(leave_form.record.date_from, datetime(2019, 9, 2, 5, 0, 0))
|
||||
self.assertEqual(leave_form.record.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
|
||||
# Ask for afternoon
|
||||
leave_form.request_date_from_period = "pm"
|
||||
leave_form.request_date_to_period = "pm"
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0.5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 3)
|
||||
# dates are checked in UTC that why -2
|
||||
self.assertEqual(leave_form.record.date_from, datetime(2019, 9, 2, 10, 0, 0))
|
||||
self.assertEqual(leave_form.record.date_to, datetime(2019, 9, 2, 13, 0, 0))
|
||||
|
||||
def test_attendance_based_on_duration_full_day(self):
|
||||
calendar = self.env["resource.calendar"].create({
|
||||
"name": "Full Days",
|
||||
"duration_based": True,
|
||||
"attendance_ids": [
|
||||
Command.clear(),
|
||||
Command.create({
|
||||
"name": "Monday",
|
||||
"duration_hours": 6, # hour_from: 9, hour_to: 15
|
||||
"day_period": "full_day",
|
||||
"dayofweek": "0",
|
||||
}),
|
||||
],
|
||||
})
|
||||
employee = self.employee_emp
|
||||
employee.resource_calendar_id = calendar
|
||||
with Form(
|
||||
self.env["hr.leave"].with_context(default_employee_id=employee.id)
|
||||
) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = date(2019, 9, 2) # Monday
|
||||
leave_form.request_date_to = date(2019, 9, 2) # Monday
|
||||
|
||||
# Ask for morning
|
||||
leave_form.request_date_from_period = "am"
|
||||
leave_form.request_date_to_period = "am"
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0.5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 3)
|
||||
# dates are checked in UTC that why -2
|
||||
self.assertEqual(leave_form.record.date_from, datetime(2019, 9, 2, 7, 0, 0))
|
||||
self.assertEqual(leave_form.record.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
|
||||
# Ask for afternoon
|
||||
leave_form.request_date_from_period = "pm"
|
||||
leave_form.request_date_to_period = "pm"
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertEqual(leave_form.record.number_of_days, 0.5)
|
||||
self.assertEqual(leave_form.record.number_of_hours, 3)
|
||||
# dates are checked in UTC that why -2
|
||||
self.assertEqual(leave_form.record.date_from, datetime(2019, 9, 2, 10, 0, 0))
|
||||
self.assertEqual(leave_form.record.date_to, datetime(2019, 9, 2, 13, 0, 0))
|
||||
|
||||
def test_attendance_next_day(self):
|
||||
self.env.user.tz = 'Europe/Brussels'
|
||||
|
|
@ -171,14 +333,14 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
# does not work on mondays
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '0 Hours')
|
||||
self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0))
|
||||
self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
leave = leave_form.record
|
||||
self.assertEqual(leave.number_of_days, 0)
|
||||
self.assertEqual(leave.number_of_hours, 0)
|
||||
self.assertEqual(leave.date_from, datetime(2019, 9, 2, 6, 0, 0))
|
||||
self.assertEqual(leave.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
|
||||
def test_attendance_previous_day(self):
|
||||
self.env.user.tz = 'Europe/Brussels'
|
||||
|
|
@ -201,14 +363,14 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
# does not work on tuesdays
|
||||
leave_form.request_date_from = date(2019, 9, 3)
|
||||
leave_form.request_date_to = date(2019, 9, 3)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '0 Hours')
|
||||
self.assertEqual(leave_form.date_from, datetime(2019, 9, 3, 6, 0, 0))
|
||||
self.assertEqual(leave_form.date_to, datetime(2019, 9, 3, 10, 0, 0))
|
||||
leave = leave_form.record
|
||||
self.assertEqual(leave.number_of_days, 0)
|
||||
self.assertEqual(leave.number_of_hours, 0)
|
||||
self.assertEqual(leave.date_from, datetime(2019, 9, 3, 6, 0, 0))
|
||||
self.assertEqual(leave.date_to, datetime(2019, 9, 3, 10, 0, 0))
|
||||
|
||||
def test_2weeks_calendar(self):
|
||||
self.env.user.tz = 'Europe/Brussels'
|
||||
|
|
@ -223,6 +385,7 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
'week_type': '0',
|
||||
'duration_days': 0.5,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday morning even week',
|
||||
|
|
@ -231,6 +394,7 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
'week_type': '1',
|
||||
'duration_days': 0.25
|
||||
})]
|
||||
})
|
||||
employee = self.employee_emp
|
||||
|
|
@ -241,26 +405,28 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
# even week, works 2 hours
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0.5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '2 Hours')
|
||||
self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 8, 0, 0))
|
||||
self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
leave = leave_form.record
|
||||
self.assertEqual(leave.number_of_days, 0.25)
|
||||
self.assertEqual(leave.number_of_hours, 2)
|
||||
self.assertEqual(leave.date_from, datetime(2019, 9, 2, 8, 0, 0))
|
||||
self.assertEqual(leave.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
|
||||
with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
# odd week, works 4 hours
|
||||
leave_form.request_date_from = date(2019, 9, 9)
|
||||
leave_form.request_date_to = date(2019, 9, 9)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0.5)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '4 Hours')
|
||||
self.assertEqual(leave_form.date_from, datetime(2019, 9, 9, 6, 0, 0))
|
||||
self.assertEqual(leave_form.date_to, datetime(2019, 9, 9, 10, 0, 0))
|
||||
leave = leave_form.record
|
||||
self.assertEqual(leave.number_of_days, 0.5)
|
||||
self.assertEqual(leave.number_of_hours, 4)
|
||||
self.assertEqual(leave.date_from, datetime(2019, 9, 9, 6, 0, 0))
|
||||
self.assertEqual(leave.date_to, datetime(2019, 9, 9, 10, 0, 0))
|
||||
|
||||
def test_2weeks_calendar_next_week(self):
|
||||
self.env.user.tz = 'Europe/Brussels'
|
||||
|
|
@ -285,10 +451,11 @@ class TestAutomaticLeaveDates(TestHrHolidaysCommon):
|
|||
# even week, does not work
|
||||
leave_form.request_date_from = date(2019, 9, 2)
|
||||
leave_form.request_date_to = date(2019, 9, 2)
|
||||
leave_form.request_unit_half = True
|
||||
leave_form.request_date_from_period = 'am'
|
||||
leave_form.request_date_to_period = 'am'
|
||||
|
||||
self.assertEqual(leave_form.number_of_days_display, 0)
|
||||
self.assertEqual(leave_form.number_of_hours_text, '0 Hours')
|
||||
self.assertEqual(leave_form.date_from, datetime(2019, 9, 2, 6, 0, 0))
|
||||
self.assertEqual(leave_form.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
leave = leave_form.record
|
||||
self.assertEqual(leave.number_of_days, 0)
|
||||
self.assertEqual(leave.number_of_hours, 0)
|
||||
self.assertEqual(leave.date_from, datetime(2019, 9, 2, 6, 0, 0))
|
||||
self.assertEqual(leave.date_to, datetime(2019, 9, 2, 10, 0, 0))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta, MO, FR
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
|
@ -14,7 +13,7 @@ class TestChangeDepartment(TestHrHolidaysCommon):
|
|||
HolidayStatusManagerGroup = self.env['hr.leave.type'].with_user(self.user_hrmanager_id)
|
||||
self.holidays_status_1 = HolidayStatusManagerGroup.create({
|
||||
'name': 'NotLimitedHR',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
def create_holiday(name, start, end):
|
||||
|
|
@ -22,45 +21,51 @@ class TestChangeDepartment(TestHrHolidaysCommon):
|
|||
'name': name,
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_status_1.id,
|
||||
'date_from': (datetime.today() + relativedelta(days=start)).strftime('%Y-%m-%d %H:%M'),
|
||||
'date_to': datetime.today() + relativedelta(days=end),
|
||||
'number_of_days': end-start,
|
||||
'request_date_from': date.today() + relativedelta(weekday=(MO(2) if start > 0 else FR(-1))) + relativedelta(days=start),
|
||||
'request_date_to': date.today() + relativedelta(weekday=(MO(2) if start > 0 else FR(-1))) + relativedelta(days=end),
|
||||
})
|
||||
|
||||
# Non approved leave request change department
|
||||
self.employee_emp.department_id = self.rd_dept
|
||||
hol1_employee_group = create_holiday("hol1", 1, 2)
|
||||
hol1_employee_group = create_holiday("hol1", 1, 1)
|
||||
self.employee_emp.department_id = self.hr_dept
|
||||
self.assertEqual(hol1_employee_group.department_id, self.hr_dept, 'hr_holidays: non approved leave request should change department if employee change department')
|
||||
|
||||
# flushing is needed after approving because the hr.leave.department_id
|
||||
# is not yet recomputed
|
||||
|
||||
# Approved passed leave request change department
|
||||
self.employee_emp.department_id = self.hr_dept
|
||||
hol2_employee_group = create_holiday("hol2", -4, -3)
|
||||
hol2_employee_group = create_holiday("hol2", -1, -1)
|
||||
hol2_user_group = hol2_employee_group.with_user(self.user_hruser_id)
|
||||
hol2_user_group.action_approve()
|
||||
self.env.flush_all()
|
||||
self.employee_emp.department_id = self.rd_dept
|
||||
self.assertEqual(hol2_employee_group.department_id, self.hr_dept, 'hr_holidays: approved passed leave request should stay in previous department if employee change department')
|
||||
|
||||
# Approved futur leave request change department
|
||||
# Approved future leave request change department
|
||||
self.employee_emp.department_id = self.hr_dept
|
||||
hol22_employee_group = create_holiday("hol22", 3, 4)
|
||||
hol22_employee_group = create_holiday("hol22", 2, 2)
|
||||
hol22_user_group = hol22_employee_group.with_user(self.user_hruser_id)
|
||||
hol22_user_group.action_approve()
|
||||
self.env.flush_all()
|
||||
self.employee_emp.department_id = self.rd_dept
|
||||
self.assertEqual(hol22_employee_group.department_id, self.rd_dept, 'hr_holidays: approved futur leave request should change department if employee change department')
|
||||
self.assertEqual(hol22_employee_group.department_id, self.rd_dept, 'hr_holidays: approved future leave request should change department if employee change department')
|
||||
|
||||
# Refused passed leave request change department
|
||||
self.employee_emp.department_id = self.rd_dept
|
||||
hol3_employee_group = create_holiday("hol3", -6, -5)
|
||||
hol3_employee_group = create_holiday("hol3", -2, -2)
|
||||
hol3_user_group = hol3_employee_group.with_user(self.user_hruser_id)
|
||||
hol3_user_group.action_refuse()
|
||||
self.env.flush_all()
|
||||
self.employee_emp.department_id = self.hr_dept # Change department
|
||||
self.assertEqual(hol3_employee_group.department_id, self.rd_dept, 'hr_holidays: refused passed leave request should stay in previous department if employee change department')
|
||||
self.assertEqual(hol3_user_group.department_id, self.rd_dept, 'hr_holidays: refused passed leave request should stay in previous department if employee change department')
|
||||
|
||||
# Refused futur leave request change department
|
||||
# Refused future leave request change department
|
||||
self.employee_emp.department_id = self.rd_dept
|
||||
hol32_employee_group = create_holiday("hol32", 5, 6)
|
||||
hol32_employee_group = create_holiday("hol32", 10, 10)
|
||||
hol32_user_group = hol32_employee_group.with_user(self.user_hruser_id)
|
||||
hol32_user_group.action_refuse()
|
||||
self.env.flush_all()
|
||||
self.employee_emp.department_id = self.hr_dept # Change department
|
||||
self.assertEqual(hol32_employee_group.department_id, self.hr_dept, 'hr_holidays: refused futur leave request should change department if employee change department')
|
||||
self.assertEqual(hol32_employee_group.department_id, self.hr_dept, 'hr_holidays: refused future leave request should change department if employee change department')
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from datetime import date, datetime
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import TransactionCase, warmup
|
||||
|
||||
|
|
@ -20,9 +21,9 @@ class TestCompanyLeave(TransactionCase):
|
|||
|
||||
cls.bank_holiday = cls.env['hr.leave.type'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'responsible_id': cls.env.user.id,
|
||||
'responsible_ids': [Command.link(cls.env.ref('base.user_admin').id)],
|
||||
'company_id': cls.company.id,
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.paid_time_off = cls.env['hr.leave.type'].create({
|
||||
|
|
@ -30,7 +31,7 @@ class TestCompanyLeave(TransactionCase):
|
|||
'request_unit': 'day',
|
||||
'leave_validation_type': 'both',
|
||||
'company_id': cls.company.id,
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
cls.employee = cls.env['hr.employee'].create({
|
||||
|
|
@ -39,16 +40,7 @@ class TestCompanyLeave(TransactionCase):
|
|||
'tz': "Europe/Brussels",
|
||||
})
|
||||
|
||||
cls.paid_time_off_hours = cls.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off in Hours',
|
||||
'request_unit': 'hour',
|
||||
'leave_validation_type': 'no_validation',
|
||||
'company_id': cls.company.id,
|
||||
'time_type': 'other',
|
||||
'requires_allocation': 'yes',
|
||||
})
|
||||
|
||||
def test_leave_whole_company_01(self):
|
||||
def test_01_leave_whole_company(self):
|
||||
# TEST CASE 1: Leaves taken in days. Take a 3 days leave
|
||||
# Add a company leave on the second day.
|
||||
# Check that leave is split into 2.
|
||||
|
|
@ -58,50 +50,38 @@ class TestCompanyLeave(TransactionCase):
|
|||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.paid_time_off.id,
|
||||
'request_date_from': date(2020, 1, 7),
|
||||
'date_from': date(2020, 1, 7),
|
||||
'request_date_to': date(2020, 1, 9),
|
||||
'date_to': date(2020, 1, 9),
|
||||
'number_of_days': 3,
|
||||
})
|
||||
leave._compute_date_from_to()
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 8),
|
||||
'request_date_from': date(2020, 1, 8),
|
||||
'date_to': date(2020, 1, 8),
|
||||
'request_date_to': date(2020, 1, 8),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
|
||||
company_leave.action_validate()
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
all_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
self.assertEqual(len(all_leaves), 4)
|
||||
# Original Leave
|
||||
self.assertEqual(leave.state, 'refuse')
|
||||
# before leave
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 7, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 7, 16, 0))
|
||||
self.assertEqual(len(all_leaves), 3)
|
||||
# Before Time Off
|
||||
self.assertEqual(all_leaves[0].date_from, datetime(2020, 1, 7, 7, 0))
|
||||
self.assertEqual(all_leaves[0].date_to, datetime(2020, 1, 7, 16, 0))
|
||||
self.assertEqual(all_leaves[0].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[0].state, 'confirm')
|
||||
# After Time Off
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 9, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
self.assertEqual(all_leaves[1].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[1].state, 'confirm')
|
||||
# After leave
|
||||
self.assertEqual(all_leaves[2].date_from, datetime(2020, 1, 9, 7, 0))
|
||||
self.assertEqual(all_leaves[2].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
# Company Time Off
|
||||
self.assertEqual(all_leaves[2].date_from, datetime(2020, 1, 8, 7, 0))
|
||||
self.assertEqual(all_leaves[2].date_to, datetime(2020, 1, 8, 16, 0))
|
||||
self.assertEqual(all_leaves[2].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[2].state, 'confirm')
|
||||
# Company Leave
|
||||
self.assertEqual(all_leaves[3].date_from, datetime(2020, 1, 8, 7, 0))
|
||||
self.assertEqual(all_leaves[3].date_to, datetime(2020, 1, 8, 16, 0))
|
||||
self.assertEqual(all_leaves[3].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[3].state, 'validate')
|
||||
self.assertEqual(all_leaves[2].state, 'validate')
|
||||
|
||||
|
||||
def test_leave_whole_company_02(self):
|
||||
def test_02_leave_whole_company(self):
|
||||
# TEST CASE 2: Leaves taken in half-days. Take a 3 days leave
|
||||
# Add a company leave on the second day
|
||||
# Check that leave is split into 2
|
||||
|
|
@ -112,50 +92,41 @@ class TestCompanyLeave(TransactionCase):
|
|||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.paid_time_off.id,
|
||||
'request_date_from': date(2020, 1, 7),
|
||||
'date_from': date(2020, 1, 7),
|
||||
'request_date_to': date(2020, 1, 9),
|
||||
'date_to': date(2020, 1, 9),
|
||||
'number_of_days': 3,
|
||||
})
|
||||
leave._compute_date_from_to()
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 8),
|
||||
'request_date_from': date(2020, 1, 8),
|
||||
'date_to': date(2020, 1, 8),
|
||||
'request_date_to': date(2020, 1, 8),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
|
||||
company_leave.action_validate()
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
all_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
self.assertEqual(len(all_leaves), 4)
|
||||
# Original Leave
|
||||
self.assertEqual(leave.state, 'refuse')
|
||||
# before leave
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 7, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 7, 16, 0))
|
||||
self.assertEqual(len(all_leaves), 3)
|
||||
# Before Time Off
|
||||
self.assertEqual(all_leaves[0].date_from, datetime(2020, 1, 7, 7, 0))
|
||||
self.assertEqual(all_leaves[0].date_to, datetime(2020, 1, 7, 16, 0))
|
||||
self.assertEqual(all_leaves[0].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[0].state, 'confirm')
|
||||
# After Time Off
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 9, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
self.assertEqual(all_leaves[1].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[1].state, 'confirm')
|
||||
# After leave
|
||||
self.assertEqual(all_leaves[2].date_from, datetime(2020, 1, 9, 7, 0))
|
||||
self.assertEqual(all_leaves[2].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
# Company Time Off
|
||||
self.assertEqual(all_leaves[2].date_from, datetime(2020, 1, 8, 7, 0))
|
||||
self.assertEqual(all_leaves[2].date_to, datetime(2020, 1, 8, 16, 0))
|
||||
self.assertEqual(all_leaves[2].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[2].state, 'confirm')
|
||||
# Company Leave
|
||||
self.assertEqual(all_leaves[3].date_from, datetime(2020, 1, 8, 7, 0))
|
||||
self.assertEqual(all_leaves[3].date_to, datetime(2020, 1, 8, 16, 0))
|
||||
self.assertEqual(all_leaves[3].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[3].state, 'validate')
|
||||
self.assertEqual(all_leaves[2].state, 'validate')
|
||||
|
||||
def test_leave_whole_company_03(self):
|
||||
# TEST CASE 3: Leaves taken in half-days. Take a 0.5 days leave
|
||||
def test_03_leave_whole_company(self):
|
||||
# TEST CASE 3: Time Off taken in half-days. Take a 0.5 days leave
|
||||
# Add a company leave on the same day
|
||||
# Check that leave refused
|
||||
self.paid_time_off.request_unit = 'half_day'
|
||||
|
|
@ -164,43 +135,34 @@ class TestCompanyLeave(TransactionCase):
|
|||
'name': 'Hol11',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.paid_time_off.id,
|
||||
'date_from': date(2020, 1, 7),
|
||||
'request_date_from': date(2020, 1, 7),
|
||||
'date_to': date(2020, 1, 7),
|
||||
'request_date_to': date(2020, 1, 7),
|
||||
'number_of_days': 0.5,
|
||||
'request_unit_half': True,
|
||||
'request_date_from_period': 'am',
|
||||
|
||||
})
|
||||
leave._compute_date_from_to()
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 7),
|
||||
'request_date_from': date(2020, 1, 7),
|
||||
'date_to': date(2020, 1, 7),
|
||||
'request_date_to': date(2020, 1, 7),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
|
||||
company_leave.action_validate()
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
all_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
self.assertEqual(len(all_leaves), 2)
|
||||
# Original Leave
|
||||
# Original Time Off
|
||||
self.assertEqual(leave.state, 'refuse')
|
||||
# Company Leave
|
||||
# Company Time Off
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 7, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 7, 16, 0))
|
||||
self.assertEqual(all_leaves[1].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[1].state, 'validate')
|
||||
|
||||
def test_leave_whole_company_04(self):
|
||||
def test_04_leave_whole_company(self):
|
||||
# TEST CASE 4: Leaves taken in days. Take a 1 days leave
|
||||
# Add a company leave on the same day
|
||||
# Check that leave is refused
|
||||
|
|
@ -210,41 +172,34 @@ class TestCompanyLeave(TransactionCase):
|
|||
'name': 'Hol11',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.paid_time_off.id,
|
||||
'date_from': datetime.now(),
|
||||
'request_date_from': date(2020, 1, 9),
|
||||
'date_to': datetime.now(),
|
||||
'request_date_to': date(2020, 1, 9),
|
||||
'number_of_days': 1,
|
||||
|
||||
})
|
||||
leave._compute_date_from_to()
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 9),
|
||||
'request_date_from': date(2020, 1, 9),
|
||||
'date_to': date(2020, 1, 9),
|
||||
'request_date_to': date(2020, 1, 9),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
|
||||
company_leave.action_validate()
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
all_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
self.assertEqual(len(all_leaves), 2)
|
||||
# Original Leave
|
||||
# Original Time Off
|
||||
self.assertEqual(leave.state, 'refuse')
|
||||
# Company Leave
|
||||
# Company Time Off
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 9, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
self.assertEqual(all_leaves[1].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[1].state, 'validate')
|
||||
|
||||
def test_leave_whole_company_06(self):
|
||||
def test_06_leave_whole_company(self):
|
||||
# Test case 6: Leaves taken in days. But the employee
|
||||
# only works on Monday, Wednesday and Friday
|
||||
# Takes a time off for all the week (3 days), should be split
|
||||
|
|
@ -252,10 +207,13 @@ class TestCompanyLeave(TransactionCase):
|
|||
self.employee.resource_calendar_id.write({'attendance_ids': [
|
||||
(5, 0, 0),
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', '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 Lunch', 'dayofweek': '2', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', '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 Lunch', 'dayofweek': '4', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
|
||||
]})
|
||||
|
||||
|
|
@ -263,45 +221,36 @@ class TestCompanyLeave(TransactionCase):
|
|||
'name': 'Hol11',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.paid_time_off.id,
|
||||
'date_from': date(2020, 1, 6),
|
||||
'request_date_from': date(2020, 1, 6),
|
||||
'date_to': date(2020, 1, 10),
|
||||
'request_date_to': date(2020, 1, 10),
|
||||
'number_of_days': 3,
|
||||
})
|
||||
leave._compute_date_from_to()
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 10),
|
||||
'request_date_from': date(2020, 1, 10),
|
||||
'date_to': date(2020, 1, 10),
|
||||
'request_date_to': date(2020, 1, 10),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
company_leave.action_validate()
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
all_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
self.assertEqual(len(all_leaves), 3)
|
||||
# Original Leave
|
||||
self.assertEqual(leave.state, 'refuse')
|
||||
# before leave
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 6, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
self.assertEqual(all_leaves[1].number_of_days, 2)
|
||||
self.assertEqual(all_leaves[1].state, 'confirm')
|
||||
# Company Leave
|
||||
self.assertEqual(all_leaves[2].date_from, datetime(2020, 1, 10, 7, 0))
|
||||
self.assertEqual(all_leaves[2].date_to, datetime(2020, 1, 10, 16, 0))
|
||||
self.assertEqual(all_leaves[2].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[2].state, 'validate')
|
||||
self.assertEqual(len(all_leaves), 2)
|
||||
# Before Time Off
|
||||
self.assertEqual(all_leaves[0].date_from, datetime(2020, 1, 6, 7, 0))
|
||||
self.assertEqual(all_leaves[0].date_to, datetime(2020, 1, 9, 16, 0))
|
||||
self.assertEqual(all_leaves[0].number_of_days, 2)
|
||||
self.assertEqual(all_leaves[0].state, 'confirm')
|
||||
# Company Time Off
|
||||
self.assertEqual(all_leaves[1].date_from, datetime(2020, 1, 10, 7, 0))
|
||||
self.assertEqual(all_leaves[1].date_to, datetime(2020, 1, 10, 16, 0))
|
||||
self.assertEqual(all_leaves[1].number_of_days, 1)
|
||||
self.assertEqual(all_leaves[1].state, 'validate')
|
||||
|
||||
@warmup
|
||||
def test_leave_whole_company_07(self):
|
||||
def test_07_leave_whole_company(self):
|
||||
# Test Case 7: Try to create a bank holidays for a lot of
|
||||
# employees, and check the performances
|
||||
# 100 employees - 15 already on holidays that day
|
||||
|
|
@ -316,199 +265,23 @@ class TestCompanyLeave(TransactionCase):
|
|||
'employee_id': employee.id,
|
||||
'holiday_status_id': self.paid_time_off.id,
|
||||
'request_date_from': date(2020, 3, 29),
|
||||
'date_from': datetime(2020, 3, 29, 7, 0, 0),
|
||||
'request_date_to': date(2020, 4, 1),
|
||||
'date_to': datetime(2020, 4, 1, 19, 0, 0),
|
||||
'number_of_days': 3,
|
||||
} for employee in employees[0:15]])
|
||||
leaves._compute_date_from_to()
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 4, 1),
|
||||
'request_date_from': date(2020, 4, 1),
|
||||
'date_to': date(2020, 4, 1),
|
||||
'request_date_to': date(2020, 4, 1),
|
||||
'number_of_days': 1,
|
||||
'date_from': date(2020, 4, 2),
|
||||
'date_to': date(2020, 4, 2),
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
|
||||
with self.assertQueryCount(__system__=830): # 770 community
|
||||
with self.assertQueryCount(__system__=1856): # 770 community
|
||||
# Original query count: 1987
|
||||
# Without tracking/activity context keys: 5154
|
||||
company_leave.action_validate()
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
leaves = self.env['hr.leave'].search([('holiday_status_id', '=', self.bank_holiday.id)])
|
||||
self.assertEqual(len(leaves), 102)
|
||||
|
||||
def test_leave_whole_company_08(self):
|
||||
"""
|
||||
Give a company leave with employees on different schedules.
|
||||
"""
|
||||
# employee on different schedule
|
||||
calendar = self.env['resource.calendar'].create({
|
||||
'name': 'Different schedule',
|
||||
'attendance_ids': [(5, 0, 0),
|
||||
(0, 0, {
|
||||
'name': 'monday morning, earlier start',
|
||||
'hour_from': 7.5,
|
||||
'hour_to': 9.75,
|
||||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday morning, second attendance',
|
||||
'hour_from': 10,
|
||||
'hour_to': 12,
|
||||
'day_period': 'morning',
|
||||
'dayofweek': '0',
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'monday afternoon',
|
||||
'hour_from': 13,
|
||||
'hour_to': 17,
|
||||
'day_period': 'afternoon',
|
||||
'dayofweek': '0',
|
||||
}),
|
||||
]
|
||||
})
|
||||
self.employee.resource_calendar_id = calendar
|
||||
|
||||
# employee on default schedule
|
||||
employee2 = self.env['hr.employee'].create({
|
||||
'name': 'Employee2',
|
||||
'company_id': self.company.id,
|
||||
'tz': "Europe/Brussels",
|
||||
})
|
||||
|
||||
company_leave = self.env['hr.leave'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 6),
|
||||
'request_date_from': date(2020, 1, 6),
|
||||
'date_to': date(2020, 1, 6),
|
||||
'request_date_to': date(2020, 1, 6),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
company_leave.action_validate()
|
||||
|
||||
half_day_company_leave = self.env['hr.leave'].create({
|
||||
'name': 'Bank Holiday',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 13),
|
||||
'request_date_from': date(2020, 1, 13),
|
||||
'date_to': date(2020, 1, 13),
|
||||
'request_date_to': date(2020, 1, 13),
|
||||
'number_of_days': 0.5,
|
||||
'request_unit_half': True,
|
||||
'request_date_from_period': 'am',
|
||||
})
|
||||
half_day_company_leave._compute_date_from_to()
|
||||
half_day_company_leave.action_validate()
|
||||
|
||||
employee_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
self.assertEqual(employee_leaves[0].date_from, datetime(2020, 1, 6, 6, 30))
|
||||
self.assertEqual(employee_leaves[0].date_to, datetime(2020, 1, 6, 16, 0))
|
||||
self.assertEqual(employee_leaves[0].number_of_days, 1)
|
||||
self.assertEqual(employee_leaves[0].number_of_hours_display, 8.25)
|
||||
self.assertEqual(employee_leaves[1].date_from, datetime(2020, 1, 13, 6, 30))
|
||||
self.assertEqual(employee_leaves[1].date_to, datetime(2020, 1, 13, 11, 0))
|
||||
self.assertEqual(employee_leaves[1].number_of_days, 0.5)
|
||||
self.assertEqual(employee_leaves[1].number_of_hours_display, 4.25)
|
||||
|
||||
employee2_leaves = self.env['hr.leave'].search([('employee_id', '=', employee2.id)], order='id')
|
||||
self.assertEqual(employee2_leaves[0].date_from, datetime(2020, 1, 6, 7, 0))
|
||||
self.assertEqual(employee2_leaves[0].number_of_days, 1)
|
||||
self.assertEqual(employee2_leaves[1].date_from, datetime(2020, 1, 13, 7, 0))
|
||||
self.assertEqual(employee2_leaves[1].date_to, datetime(2020, 1, 13, 11, 0))
|
||||
self.assertEqual(employee2_leaves[1].number_of_days, 0.5)
|
||||
|
||||
def test_leave_whole_company_09(self):
|
||||
"""
|
||||
Check leaves given in half days and in hours for a company.
|
||||
"""
|
||||
half_day_leave = self.env['hr.leave'].create({
|
||||
'name': 'Bank Holiday (full day)',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 6),
|
||||
'request_date_from': date(2020, 1, 6),
|
||||
'date_to': date(2020, 1, 6),
|
||||
'request_date_to': date(2020, 1, 6),
|
||||
'number_of_days': 0.5,
|
||||
'request_unit_half': True,
|
||||
'request_date_from_period': 'am',
|
||||
})
|
||||
hours_leave = self.env['hr.leave'].create({
|
||||
'name': 'Bank Holiday (half day)',
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'holiday_status_id': self.bank_holiday.id,
|
||||
'date_from': date(2020, 1, 7),
|
||||
'request_date_from': date(2020, 1, 7),
|
||||
'date_to': date(2020, 1, 7),
|
||||
'request_date_to': date(2020, 1, 7),
|
||||
'request_unit_hours': True,
|
||||
'request_hour_from': '5.5',
|
||||
'request_hour_to': '9',
|
||||
})
|
||||
|
||||
half_day_leave._compute_date_from_to()
|
||||
half_day_leave.action_validate()
|
||||
hours_leave._compute_date_from_to()
|
||||
hours_leave.action_validate()
|
||||
|
||||
employee_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)], order='id')
|
||||
# half days leave
|
||||
self.assertEqual(employee_leaves[0].date_from, datetime(2020, 1, 6, 7, 0))
|
||||
self.assertEqual(employee_leaves[0].date_to, datetime(2020, 1, 6, 11, 0))
|
||||
self.assertEqual(employee_leaves[0].number_of_days, 0.5)
|
||||
# leave given in hours
|
||||
self.assertEqual(employee_leaves[1].date_from, datetime(2020, 1, 7, 3, 30))
|
||||
self.assertEqual(employee_leaves[1].date_to, datetime(2020, 1, 7, 7, 0))
|
||||
self.assertEqual(employee_leaves[1].number_of_hours_display, 1.0)
|
||||
|
||||
def test_leave_whole_company_10(self):
|
||||
"""
|
||||
Check leaves given in hours for a company,
|
||||
Making sure no leaves are given for 0 Hours / Week employee(i.e. Contractors billed for hours).
|
||||
"""
|
||||
employee_0_test_10, employee_1_test_10, employee_2_test_10 = self.env['hr.employee'].create([{
|
||||
'name': 'My Employee 0',
|
||||
'company_id': self.company.id,
|
||||
'tz': "Europe/Brussels",
|
||||
},{
|
||||
'name': 'My Employee 1',
|
||||
'company_id': self.company.id,
|
||||
'tz': "Europe/Brussels",
|
||||
},{
|
||||
'name': 'My Employee 2',
|
||||
'company_id': self.company.id,
|
||||
'tz': "Europe/Brussels",
|
||||
}])
|
||||
zero_hours_working_schedule = self.env['resource.calendar'].create({
|
||||
'name': 'Standard - Hours/Week',
|
||||
'hours_per_day': 0,
|
||||
'tz': "Europe/Brussels",
|
||||
})
|
||||
employee_0_test_10.resource_calendar_id = zero_hours_working_schedule
|
||||
self.env['hr.leave.allocation'].create({
|
||||
'name': 'Holiday (8 Hours)',
|
||||
'holiday_status_id': self.paid_time_off_hours.id,
|
||||
'holiday_type': 'company',
|
||||
'mode_company_id': self.company.id,
|
||||
'number_of_days': 1,
|
||||
})
|
||||
employee_leaves = self.env['hr.leave.allocation'].search([
|
||||
('name', '=', 'Holiday (8 Hours)'),
|
||||
('employee_id', 'in', [employee_0_test_10.id, employee_1_test_10.id, employee_2_test_10.id])])
|
||||
self.assertEqual(len(employee_leaves), 2)
|
||||
self.assertEqual(len(leaves), 101)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
from datetime import date, datetime
|
||||
from freezegun import freeze_time
|
||||
from datetime import datetime
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
class TestDashboard(TestHrHolidaysCommon):
|
||||
def test_dashboard_special_days(self):
|
||||
self.env.user = self.user_hrmanager
|
||||
self.uid = self.user_hrmanager.id
|
||||
employee = self.env.user.employee_id
|
||||
other_calendar = employee.company_id.resource_calendar_ids[1]
|
||||
other_calendar = self.env['resource.calendar'].sudo().create({
|
||||
'name': 'Other calendar',
|
||||
})
|
||||
|
||||
stress_day_vals = [
|
||||
mandatory_day_vals = [
|
||||
{
|
||||
'name': 'Super Event (employee schedule)',
|
||||
'company_id': employee.company_id.id,
|
||||
|
|
@ -32,7 +33,7 @@ class TestDashboard(TestHrHolidaysCommon):
|
|||
'resource_calendar_id': other_calendar.id,
|
||||
}
|
||||
]
|
||||
self.env['hr.leave.stress.day'].create(stress_day_vals)
|
||||
self.env['hr.leave.mandatory.day'].create(mandatory_day_vals)
|
||||
|
||||
public_holiday_vals = [
|
||||
{
|
||||
|
|
@ -55,38 +56,7 @@ class TestDashboard(TestHrHolidaysCommon):
|
|||
]
|
||||
self.env['resource.calendar.leaves'].create(public_holiday_vals)
|
||||
|
||||
dashboard_data = self.env['hr.employee'].get_special_days_data("2021/06/01", "2021/07/01")
|
||||
dashboard_data = self.env['hr.employee'].get_special_days_data("2021-06-01", "2021-07-01")
|
||||
|
||||
self.assertEqual({d["title"] for d in dashboard_data["stressDays"]}, {'Super Event (employee schedule)', 'Super Event (no schedule)'})
|
||||
self.assertEqual({d["title"] for d in dashboard_data["mandatoryDays"]}, {'Super Event (employee schedule)', 'Super Event (no schedule)'})
|
||||
self.assertEqual({d["title"] for d in dashboard_data["bankHolidays"]}, {'Public holiday (employee schedule)', 'Public holiday (no schedule)'})
|
||||
|
||||
def test_dashboard_max_near_accrual_validity_end(self):
|
||||
emp_id = self.employee_emp_id
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Test Time Off',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'no',
|
||||
'allocation_validation_type': 'no',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_id': self.user_hrmanager_id,
|
||||
})
|
||||
self.env['hr.leave.allocation'].create([{
|
||||
'employee_id': emp_id,
|
||||
'name': '10 days allocation',
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 10,
|
||||
'date_from': date(2024, 1, 1),
|
||||
'date_to': date(2024, 12, 30),
|
||||
}, {
|
||||
'employee_id': emp_id,
|
||||
'name': '2 days allocation starting later',
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 2,
|
||||
'date_from': date(2024, 2, 1),
|
||||
'date_to': date(2024, 12, 30),
|
||||
}])
|
||||
|
||||
with freeze_time('2024-12-27'):
|
||||
employee_max_leaves = leave_type.get_employees_days([emp_id])[emp_id][leave_type.id]['max_leaves']
|
||||
self.assertEqual(employee_max_leaves, 12, "All 12 leaves should be seen from the dashboard")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,812 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.addons.base.tests.common import HttpCase
|
||||
from odoo.tests.common import tagged
|
||||
from odoo.tests.common import users
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'carryover_expiring_leaves')
|
||||
class TestExpiringLeaves(HttpCase, TestHrHolidaysCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Test',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'no_validation',
|
||||
})
|
||||
cls.accrual_plan_with_accrual_validity = cls.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan With Accrual Validity',
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 1,
|
||||
'carryover_month': '4',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'start_count': 0,
|
||||
'start_type': 'day',
|
||||
'added_value': 3,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
'carryover_options': 'limited',
|
||||
'postpone_max_days': 5,
|
||||
'accrual_validity': True,
|
||||
'accrual_validity_count': 3,
|
||||
'accrual_validity_type': 'month',
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
@users('enguerran')
|
||||
def test_no_carried_over_leaves(self):
|
||||
"""
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 31/12 (end of the year).
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 10 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are lost (no leaves are carried over).
|
||||
|
||||
Create an accrual allocation with this plan and allocate it to the logged-in user.
|
||||
The employee will be accrued 10 days. The employee will use some of them. The carryover policy is set
|
||||
to None, so no leaves will be carriedover. The remaining days of the allocation will expire.
|
||||
"""
|
||||
number_of_accrued_days = 10
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 31,
|
||||
'carryover_month': '12',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'lost'
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': date(date.today().year, 1, 1),
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(date.today().year + 1, 12, 30)
|
||||
leave = self.env['hr.leave'].create({
|
||||
'employee_id': logged_in_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': target_date + relativedelta(month=12, day=1),
|
||||
'request_date_to': target_date + relativedelta(month=12, day=7)
|
||||
})
|
||||
|
||||
allocation_data = self.leave_type.get_allocation_data(
|
||||
allocation.employee_id, target_date)
|
||||
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
allocation._get_carryover_date(target_date).strftime('%m/%d/%Y'),
|
||||
"The expiration date should match the carryover date")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'],
|
||||
number_of_accrued_days - leave.number_of_days,
|
||||
"All the remaining days of the allocation will expire")
|
||||
|
||||
@users('enguerran')
|
||||
def test_carried_over_leaves_with_maximum(self):
|
||||
"""
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 31/12 (end of the year).
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 20 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are carried over with a maximum of 10.
|
||||
|
||||
Create an accrual allocation with this plan and allocate it to the logged-in user.
|
||||
The employee will be accrued 20 days. The employee will use some of them. The carryover
|
||||
policy is set to carryover with a maximum of 10, so at max 10 leaves will be carriedover.
|
||||
The remaining days of the allocation will expire.
|
||||
"""
|
||||
|
||||
number_of_accrued_days = 20
|
||||
carryover_limit = 10
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 31,
|
||||
'carryover_month': '12',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
'carryover_options': 'limited',
|
||||
'postpone_max_days': carryover_limit,
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': date(date.today().year, 1, 1),
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(date.today().year + 1, 12, 30)
|
||||
leave = self.env['hr.leave'].create({
|
||||
'employee_id': logged_in_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': target_date + relativedelta(month=12, day=1),
|
||||
'request_date_to': target_date + relativedelta(month=12, day=7)
|
||||
})
|
||||
allocation_data = self.leave_type.get_allocation_data(
|
||||
allocation.employee_id, target_date)
|
||||
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
allocation._get_carryover_date(target_date).strftime('%m/%d/%Y'),
|
||||
"The expiration date should match the carryover date")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'],
|
||||
number_of_accrued_days - leave.number_of_days - carryover_limit,
|
||||
"All the remaining days of the allocation will expire")
|
||||
|
||||
@users('enguerran')
|
||||
def test_allocation_with_max_carryover_and_expiring_allocation(self):
|
||||
"""
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 31/12 (end of the year).
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 20 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are carried over with a maximum of 10.
|
||||
|
||||
Create an accrual allocation with this plan:
|
||||
- Employee: logged-in user.
|
||||
- Start date: 01/01/2024
|
||||
- Carryover Policy: carryover with a maximum of 10
|
||||
|
||||
On 01/01/2025 The employee will be accrued 20 days.
|
||||
The employee will take 5 leaves using this allocation.
|
||||
- The carryover policy is set to carryover with a maximum of 10, so at max 10 days will be carriedover.
|
||||
- 5 days will expire.
|
||||
|
||||
Create a second accrual allocation:
|
||||
- The configuration is the same as the previous one except that carryover date is the start of the year
|
||||
and all unused accruals will be carriedover.
|
||||
- This allocation will expire on 31/12 next year. 20 days will expire when the allocation expires.
|
||||
Number of expiring leaves = number of not carriedover days from the first allocation +
|
||||
number of expiring leaves due to expiration of the second allocation
|
||||
= 5 + 20 = 25 days
|
||||
"""
|
||||
|
||||
number_of_accrued_days = 20
|
||||
carryover_limit = 10
|
||||
accrual_plan_1 = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 31,
|
||||
'carryover_month': '12',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
'carryover_options': 'limited',
|
||||
'postpone_max_days': carryover_limit,
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
accrual_plan_2 = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan With All Leaves Carried Over',
|
||||
'can_be_carryover': True,
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
with freeze_time("2024-01-01"):
|
||||
allocation_with_carryover = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2024-01-01',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan_1.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
leave = self.env['hr.leave'].create({
|
||||
'employee_id': logged_in_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': '2025-12-01',
|
||||
'request_date_to': '2025-12-05'
|
||||
})
|
||||
# The expiring allocation
|
||||
self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2024-01-01',
|
||||
'date_to': '2025-12-31',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan_2.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(2025, 12, 30)
|
||||
allocation_data = self.leave_type.get_allocation_data(logged_in_emp, target_date)
|
||||
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
allocation_with_carryover._get_carryover_date(target_date).strftime('%m/%d/%Y'),
|
||||
"The expiration date should match the carryover date")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'],
|
||||
(number_of_accrued_days - leave.number_of_days - carryover_limit) + number_of_accrued_days,
|
||||
"All the remaining days of the allocation will expire")
|
||||
|
||||
@users('enguerran')
|
||||
def test_expiring_allocation_without_carried_over_leaves(self):
|
||||
"""
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 31/12 (end of the year).
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 20 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are lost (no leaves are carried over).
|
||||
|
||||
Create an accrual allocation with this plan and allocate it to the logged-in user. This
|
||||
allocation will expire on 31/12 next year.
|
||||
The employee will be accrued 10 days. The carryover policy is set to None, so no leaves
|
||||
will be carriedover. The remaining days of the allocation will expire.
|
||||
|
||||
The number of expiring leaves should be 10 and not 20 (10 for the expiration of the allocation
|
||||
and 10 for the leaves being not carried over).
|
||||
"""
|
||||
|
||||
number_of_accrued_days = 10
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 31,
|
||||
'carryover_month': '12',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'lost',
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': date(date.today().year, 1, 1),
|
||||
'date_to': date(date.today().year + 1, 12, 31),
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(date.today().year + 1, 12, 30)
|
||||
allocation_data = self.leave_type.get_allocation_data(
|
||||
allocation.employee_id, target_date)
|
||||
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
allocation._get_carryover_date(target_date).strftime('%m/%d/%Y'),
|
||||
"The expiration date should match the carryover date")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'],
|
||||
number_of_accrued_days,
|
||||
"All the remaining days of the allocation will expire")
|
||||
|
||||
@users('enguerran')
|
||||
def test_expiration_date(self):
|
||||
"""
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 01/01.
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 10 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are carried over with a maximum of 5.
|
||||
|
||||
Create an accrual allocation with this plan and allocate it to the logged-in user.
|
||||
The employee will be accrued 10 days. The carryover policy is set to carryover with
|
||||
a maximum of 5, so only 5 leaves will be carriedover. The remaining days of the allocation
|
||||
will expire.
|
||||
|
||||
If the target date is 01/01/2025, then the expiration date should be 01/01/2026 because 5 of the days
|
||||
accrued on 01/01/2025 will expire on 01/01/2026.
|
||||
"""
|
||||
with freeze_time('2024-01-01'):
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'year_start',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': 10,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
'carryover_options': 'limited',
|
||||
'postpone_max_days': 5,
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': date(2024, 1, 1),
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(2025, 1, 1)
|
||||
allocation_data = self.leave_type.get_allocation_data(allocation.employee_id, target_date)
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
(target_date + relativedelta(years=1)).strftime('%m/%d/%Y'),
|
||||
"The expiration date should be the carryover date of the year that follows the target date's year")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'], 5)
|
||||
|
||||
@users('enguerran')
|
||||
def test_expiration_date_2(self):
|
||||
"""
|
||||
- Define an accrual plan:
|
||||
- Carryover date : 1st of September.
|
||||
- Carryover with a maximum of 5 days.
|
||||
- Define 2 allocations:
|
||||
- Both allocations will start on 01/01/2023.
|
||||
- Both allocations use the accrual plan defined above.
|
||||
- Both allocations accrue 3 days yearly.
|
||||
- The first allocation expires on the 1st of October.
|
||||
- The second allocation doesn't expire.
|
||||
|
||||
- On 01/01/2024, both allocations will accrue 3 days for the employee.
|
||||
- The expiration date should be 01/10/2024 because on 01/09/2024, 3 days will carryover for both allocations
|
||||
and no days will expire.
|
||||
"""
|
||||
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 1,
|
||||
'carryover_month': '9',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': 3,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
'carryover_options': 'limited',
|
||||
'postpone_max_days': 5,
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
with freeze_time("2023-01-01"):
|
||||
# Allocation 1
|
||||
self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2023-01-01',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
# Allocation 2
|
||||
self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2023-01-01',
|
||||
'date_to': '2024-10-01',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
with freeze_time("2024-01-01"):
|
||||
self.env['hr.leave.allocation'].with_user(self.user_hruser)._update_accrual()
|
||||
|
||||
target_date = date(2024, 1, 1)
|
||||
allocation_data = self.leave_type.get_allocation_data(logged_in_emp, target_date)
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
(target_date + relativedelta(month=10)).strftime('%m/%d/%Y'),
|
||||
"The expiration date should be the expiration date of the second allocation because no days will expire on carryover date")
|
||||
|
||||
@users('enguerran')
|
||||
def test_no_carried_over_leaves_for_flexible_resource(self):
|
||||
"""
|
||||
Identical test to test_no_carried_over_leaves but with a flexible resource calendar. The test aims to verify that
|
||||
the expiration date is correctly calculated even if attendance is not taken into account for the flexible resource.
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 31/12 (end of the year).
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 10 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are lost (no leaves are carried over).
|
||||
|
||||
Create an accrual allocation with this plan and allocate it to the logged-in user.
|
||||
The employee will be accrued 10 days. The employee will use some of them. The carryover policy is set
|
||||
to None, so no leaves will be carriedover. The remaining days of the allocation will expire.
|
||||
"""
|
||||
number_of_accrued_days = 10
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 31,
|
||||
'carryover_month': '12',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'lost'
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
self.flex_40h_calendar = self.env['resource.calendar'].sudo().create({
|
||||
'name': 'Flexible 40h/week',
|
||||
'tz': 'UTC',
|
||||
'hours_per_day': 8.0,
|
||||
'hours_per_week': 80.0,
|
||||
'full_time_required_hours': 80.0,
|
||||
'flexible_hours': True,
|
||||
})
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
logged_in_emp.resource_calendar_id = self.flex_40h_calendar
|
||||
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': date(date.today().year, 1, 1),
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(date.today().year + 1, 12, 30)
|
||||
leave = self.env['hr.leave'].create({
|
||||
'employee_id': logged_in_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': target_date + relativedelta(month=12, day=1),
|
||||
'request_date_to': target_date + relativedelta(month=12, day=7)
|
||||
})
|
||||
|
||||
allocation_data = self.leave_type.get_allocation_data(
|
||||
allocation.employee_id, target_date)
|
||||
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
allocation._get_carryover_date(target_date).strftime('%m/%d/%Y'),
|
||||
"The expiration date should match the carryover date")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'],
|
||||
number_of_accrued_days - leave.number_of_days,
|
||||
"All the remaining days of the allocation will expire")
|
||||
|
||||
@users('enguerran')
|
||||
def test_no_carried_over_leaves_for_fully_flexible_resource(self):
|
||||
"""
|
||||
/!\\ Fully Flexible Resource should not take leaves. However the test aims to verify that the expiration date
|
||||
is correctly calculated for the fully flexible resource.
|
||||
|
||||
The accrual plan:
|
||||
- Accrue at the end of period.
|
||||
- Carryover date : 31/12 (end of the year).
|
||||
Milestones:
|
||||
Milestone 1:
|
||||
- Start immediately.
|
||||
- Accrue 10 days.
|
||||
- Accrue days on 01/01 (start of the year).
|
||||
- Unused accruals are lost (no leaves are carried over).
|
||||
|
||||
Create an accrual allocation with this plan and allocate it to the logged-in user.
|
||||
The employee will be accrued 10 days. The employee will use some of them. The carryover policy is set
|
||||
to None, so no leaves will be carriedover. The remaining days of the allocation will expire.
|
||||
"""
|
||||
number_of_accrued_days = 10
|
||||
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'can_be_carryover': True,
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 31,
|
||||
'carryover_month': '12',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'creation',
|
||||
'start_type': 'day',
|
||||
'added_value': number_of_accrued_days,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'lost'
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
logged_in_emp.resource_calendar_id = None # Set as Fully flexible resource
|
||||
|
||||
allocation = self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': date(date.today().year, 1, 1),
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
target_date = date(date.today().year + 1, 12, 30)
|
||||
leave = self.env['hr.leave'].create({
|
||||
'employee_id': logged_in_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': target_date + relativedelta(month=12, day=1),
|
||||
'request_date_to': target_date + relativedelta(month=12, day=7)
|
||||
})
|
||||
|
||||
allocation_data = self.leave_type.get_allocation_data(
|
||||
allocation.employee_id, target_date)
|
||||
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
allocation._get_carryover_date(target_date).strftime('%m/%d/%Y'),
|
||||
"The expiration date should match the carryover date")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'],
|
||||
number_of_accrued_days - leave.number_of_days,
|
||||
"All the remaining days of the allocation will expire")
|
||||
|
||||
# Days between the target date and the expiration date (accrual_plan's carryover date)
|
||||
working_days_equivalent_needed = (allocation._get_carryover_date(target_date) - target_date).days + 1
|
||||
|
||||
# Assert the closest allocation duration (number of working days equivalent (8 hours/day) remaining before the allocation expires)
|
||||
self.assertEqual(round(allocation_data[logged_in_emp][0][1]['closest_allocation_duration']), working_days_equivalent_needed,
|
||||
"The closest allocation duration should be the number of working days equivalent (24 hours/day) remaining before the allocation expires")
|
||||
|
||||
@users('enguerran')
|
||||
def test_carried_over_days_expiration_date(self):
|
||||
"""
|
||||
This test case aims to assert that carried_over_days_expiration_date is taken into account when the
|
||||
expiration date is computed.
|
||||
- First accrual plan:
|
||||
- Carryover date : 1st of April.
|
||||
- Has 1 level:
|
||||
- Accrues 3 days yearly on the 1st of January.
|
||||
- Carryover with a maximum of 5 days.
|
||||
- Second accrual plan:
|
||||
Has the same definition as the one above except that carried over days are valid for 3 months.
|
||||
- Note: the following dates are in format dd/mm/YYYY
|
||||
- Define 2 allocations:
|
||||
- Both allocations will start on 01/01/2023.
|
||||
- One allocation uses the first accrual plan and the other uses the second accrual plan.
|
||||
- Both allocations accrue 3 days yearly.
|
||||
- The first allocation expires on the 1st of October.
|
||||
- The second allocation doesn't expire.
|
||||
- On 01/01/2024, both allocations will accrue 3 days for the employee.
|
||||
- If target date is 01/04/2024, then the expiration date should be 01/7/2024 because on 01/04/2024, 3 days will carryover for
|
||||
the second allocation and these 3 days will expire in 3 months.
|
||||
"""
|
||||
accrual_plan_without_accrual_validity = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).sudo().create({
|
||||
'name': 'Test Accrual Plan',
|
||||
'carryover_date': 'other',
|
||||
'carryover_day': 1,
|
||||
'carryover_month': '4',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'start_count': 0,
|
||||
'start_type': 'day',
|
||||
'added_value': 3,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'yearly_day': 1,
|
||||
'yearly_month': '1',
|
||||
'cap_accrued_time': False,
|
||||
'action_with_unused_accruals': 'all',
|
||||
'carryover_options': 'limited',
|
||||
'postpone_max_days': 5,
|
||||
})
|
||||
],
|
||||
})
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
with freeze_time("2023-01-01"):
|
||||
# Allocation 1
|
||||
self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2023-01-01',
|
||||
'date_to': '2024-10-01',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': accrual_plan_without_accrual_validity.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
# Allocation 2
|
||||
self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2023-01-01',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': self.accrual_plan_with_accrual_validity.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
with freeze_time("2024-04-01"):
|
||||
self.env['hr.leave.allocation'].with_user(self.user_hruser)._update_accrual()
|
||||
|
||||
target_date = date(2024, 4, 1)
|
||||
allocation_data = self.leave_type.get_allocation_data(logged_in_emp, target_date)
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
(target_date + relativedelta(month=7)).strftime('%m/%d/%Y'),
|
||||
"The expiration date should be the carried over days expiration date of allocation 3")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'], 3)
|
||||
|
||||
@users('enguerran')
|
||||
def test_carried_over_days_expiration_date_2(self):
|
||||
"""
|
||||
This tess case aims to assert that the number of expiring leaves on carried_over_days_expiration_date is
|
||||
computed properly
|
||||
- Define an accrual plan:
|
||||
- Carryover date : 1st of April.
|
||||
- Has 1 level:
|
||||
- Accrues 3 days yearly on the 1st of January.
|
||||
- Carryover with a maximum of 5 days.
|
||||
- Carried over days are valid for 3 months.
|
||||
- Note: the following dates are in format dd/mm/YYYY
|
||||
- Define an allocation:
|
||||
- The allocation will start on 01/01/2023.
|
||||
- The allocation uses the accrual plan defined above.
|
||||
- The allocation expires on the 1st of October.
|
||||
- On 01/01/2024, 3 days are accrued.
|
||||
- If target date is 01/05/2024, then the expiration date should be 01/7/2024.
|
||||
- On 01/04/2024, 3 days will carryover.
|
||||
- The employee taked 2 days as time off.
|
||||
- The number of expiring days on 01/07/2024 is 1 day.
|
||||
"""
|
||||
|
||||
logged_in_emp = self.env.user.employee_id
|
||||
with freeze_time("2023-01-01"):
|
||||
self.env['hr.leave.allocation'].sudo().create({
|
||||
'date_from': '2023-01-01',
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': self.accrual_plan_with_accrual_validity.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': logged_in_emp.id,
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
with freeze_time("2024-04-01"):
|
||||
self.env['hr.leave.allocation'].with_user(self.user_hruser)._update_accrual()
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'leave',
|
||||
'employee_id': logged_in_emp.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': '2024-04-03',
|
||||
'request_date_to': '2024-04-04',
|
||||
})
|
||||
leave.sudo().action_approve()
|
||||
|
||||
target_date = date(2024, 5, 1)
|
||||
allocation_data = self.leave_type.get_allocation_data(logged_in_emp, target_date)
|
||||
# Assert the date of expiration
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_expire'],
|
||||
(target_date + relativedelta(month=7)).strftime('%m/%d/%Y'),
|
||||
"The expiration date should be the carried over days expiration date of allocation 3")
|
||||
|
||||
# Assert the number of expiring leaves
|
||||
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'], 1)
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import pytz
|
||||
from datetime import datetime, date
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
UTC = pytz.timezone('UTC')
|
||||
|
||||
|
||||
class TestFlexibleResourceCalendar(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.calendar_40h_flex = cls.env['resource.calendar'].create({
|
||||
'name': 'Flexible 40h/week',
|
||||
'tz': 'UTC',
|
||||
'hours_per_day': 8.0,
|
||||
'flexible_hours': True,
|
||||
})
|
||||
cls.flex_resource, cls.fully_flex_resource = cls.env['resource.resource'].create([{
|
||||
'name': 'Flex',
|
||||
'tz': 'UTC',
|
||||
'calendar_id': cls.calendar_40h_flex.id,
|
||||
}, {
|
||||
'name': 'fully flex',
|
||||
'tz': 'UTC',
|
||||
'calendar_id': False,
|
||||
}])
|
||||
|
||||
cls.flex_employee, cls.fully_flex_employee = cls.env['hr.employee'].create([{
|
||||
'name': "flexible employee",
|
||||
'date_version': date(2025, 1, 1),
|
||||
'contract_date_start': date(2025, 1, 1),
|
||||
'wage': 10,
|
||||
'resource_calendar_id': cls.calendar_40h_flex.id,
|
||||
'resource_id': cls.flex_resource.id,
|
||||
}, {
|
||||
'name': "fully flexible employee",
|
||||
'date_version': date(2025, 1, 1),
|
||||
'contract_date_start': date(2025, 1, 1),
|
||||
'wage': 10,
|
||||
'resource_calendar_id': False,
|
||||
'resource_id': cls.fully_flex_resource.id,
|
||||
}])
|
||||
|
||||
def test_flexible_resource_work_intervals_with_leaves(self):
|
||||
self.env['resource.calendar.leaves'].create([{
|
||||
'resource_id': self.flex_resource.id,
|
||||
'date_from': datetime(2025, 7, 31, 8),
|
||||
'date_to': datetime(2025, 8, 1, 17),
|
||||
}, {
|
||||
'resource_id': self.fully_flex_resource.id,
|
||||
'date_from': datetime(2025, 7, 31, 8),
|
||||
'date_to': datetime(2025, 8, 1, 17),
|
||||
}])
|
||||
|
||||
custom_leave, half_day_leave = self.env['hr.leave.type'].create([{
|
||||
'name': 'Custom Leave',
|
||||
'requires_allocation': False,
|
||||
'request_unit': 'hour',
|
||||
}, {
|
||||
'name': 'Half day',
|
||||
'requires_allocation': False,
|
||||
'request_unit': 'half_day',
|
||||
}])
|
||||
|
||||
self.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True).create([{
|
||||
'name': 'Half 1',
|
||||
'holiday_status_id': half_day_leave.id,
|
||||
'employee_id': self.flex_employee.id,
|
||||
'request_date_from': date(2025, 7, 28),
|
||||
'request_date_to': date(2025, 7, 28),
|
||||
'request_date_from_period': 'am',
|
||||
'request_date_to_period': 'am',
|
||||
}, {
|
||||
'name': 'Half 2',
|
||||
'holiday_status_id': half_day_leave.id,
|
||||
'employee_id': self.flex_employee.id,
|
||||
'request_date_from': date(2025, 7, 30),
|
||||
'request_date_to': date(2025, 7, 30),
|
||||
'request_date_from_period': 'pm',
|
||||
'request_date_to_period': 'pm',
|
||||
}, {
|
||||
'name': 'Custom',
|
||||
'holiday_status_id': custom_leave.id,
|
||||
'employee_id': self.flex_employee.id,
|
||||
'request_date_from': date(2025, 7, 29),
|
||||
'request_date_to': date(2025, 7, 29),
|
||||
'request_hour_from': 11.0,
|
||||
'request_hour_to': 16.0,
|
||||
}, {
|
||||
'name': 'Half 1',
|
||||
'holiday_status_id': half_day_leave.id,
|
||||
'employee_id': self.fully_flex_employee.id,
|
||||
'request_date_from': date(2025, 7, 28),
|
||||
'request_date_to': date(2025, 7, 28),
|
||||
'request_date_from_period': 'am',
|
||||
'request_date_to_period': 'am',
|
||||
}, {
|
||||
'name': 'Half 2',
|
||||
'holiday_status_id': half_day_leave.id,
|
||||
'employee_id': self.fully_flex_employee.id,
|
||||
'request_date_from': date(2025, 7, 30),
|
||||
'request_date_to': date(2025, 7, 30),
|
||||
'request_date_from_period': 'pm',
|
||||
'request_date_to_period': 'pm',
|
||||
}, {
|
||||
'name': 'Custom',
|
||||
'holiday_status_id': custom_leave.id,
|
||||
'employee_id': self.fully_flex_employee.id,
|
||||
'request_date_from': date(2025, 7, 29),
|
||||
'request_date_to': date(2025, 7, 29),
|
||||
'request_hour_from': 11.0,
|
||||
'request_hour_to': 16.0,
|
||||
}]).action_approve()
|
||||
|
||||
start_dt = datetime(2025, 7, 28).astimezone(UTC)
|
||||
end_dt = datetime(2025, 8, 3, 17).astimezone(UTC)
|
||||
|
||||
resources = self.flex_resource | self.fully_flex_resource
|
||||
work_intervals, hours_per_day, hours_per_week = resources._get_flexible_resource_valid_work_intervals(start_dt, end_dt)
|
||||
|
||||
self.maxDiff = None
|
||||
for resource in resources:
|
||||
self.assertEqual(work_intervals[resource.id]._items, [
|
||||
(datetime(2025, 7, 28, 12, 0, tzinfo=UTC), datetime(2025, 7, 28, 23, 59, 59, 999999, tzinfo=UTC), self.env['resource.calendar.attendance']),
|
||||
(datetime(2025, 7, 29, 0, 0, tzinfo=UTC), datetime(2025, 7, 29, 11, 0, tzinfo=UTC), self.env['resource.calendar.attendance']),
|
||||
(datetime(2025, 7, 29, 16, 0, tzinfo=UTC), datetime(2025, 7, 29, 23, 59, 59, 999999, tzinfo=UTC), self.env['resource.calendar.attendance']),
|
||||
(datetime(2025, 7, 30, 0, 0, tzinfo=UTC), datetime(2025, 7, 30, 12, 0, tzinfo=UTC), self.env['resource.calendar.attendance']),
|
||||
(datetime(2025, 8, 2, 0, 0, tzinfo=UTC), datetime(2025, 8, 2, 23, 59, 59, 999999, tzinfo=UTC), self.env['resource.calendar.attendance']),
|
||||
(datetime(2025, 8, 3, 0, 0, tzinfo=UTC), datetime(2025, 8, 3, 17, 0, tzinfo=UTC), self.env['resource.calendar.attendance']),
|
||||
], "resource not available on 29, 31, 01, 28 morning and 30 afternoon. for other days, resource can do his hours at any moment of the day (from 00:00:00 to 23:59:59)")
|
||||
|
||||
self.assertDictEqual(hours_per_day[self.flex_resource.id], {
|
||||
date(2025, 7, 28): 4.0,
|
||||
date(2025, 7, 29): 3.0,
|
||||
date(2025, 7, 30): 4.0,
|
||||
date(2025, 7, 31): 0.0,
|
||||
date(2025, 8, 1): 0.0,
|
||||
date(2025, 8, 2): 8.0,
|
||||
date(2025, 8, 3): 8.0,
|
||||
}, "hours_per_day/2 for half days off, and hours_per_day - number_of_hoursfor custom time off")
|
||||
self.assertTrue(self.fully_flex_resource.id not in hours_per_day)
|
||||
|
||||
self.assertDictEqual(hours_per_week[self.flex_resource.id], {
|
||||
(2025, 31): 11.0,
|
||||
(2025, 32): 40.0,
|
||||
}, "week 31 (27/07 -> 02/08): 2 days off 31 & 01 (-16 hours), half day on 28 and 30 (-8 hours), 5 hours off on day 29 / hours = 40-(16+8+5) = 11 hours, no timeoff on week 32")
|
||||
self.assertTrue(self.fully_flex_resource.id not in hours_per_week)
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date, datetime, timedelta
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.exceptions import ValidationError
|
||||
from freezegun import freeze_time
|
||||
|
||||
|
|
@ -21,14 +22,19 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
'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 Lunch', 'dayofweek': '0', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(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 Lunch', 'dayofweek': '1', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(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 Lunch', 'dayofweek': '2', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(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 Lunch', 'dayofweek': '3', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(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 Lunch', 'dayofweek': '4', 'hour_from': 12, 'hour_to': 13, 'day_period': 'lunch'}),
|
||||
(0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
|
||||
]
|
||||
})
|
||||
|
|
@ -47,13 +53,13 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
})
|
||||
|
||||
cls.global_leave = cls.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Leave',
|
||||
'name': 'Global Time Off',
|
||||
'date_from': date(2022, 3, 7),
|
||||
'date_to': date(2022, 3, 7),
|
||||
})
|
||||
|
||||
cls.calendar_leave = cls.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Leave',
|
||||
'name': 'Global Time Off',
|
||||
'date_from': date(2022, 3, 8),
|
||||
'date_to': date(2022, 3, 8),
|
||||
'calendar_id': cls.calendar_1.id,
|
||||
|
|
@ -62,7 +68,7 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
def test_leave_on_global_leave(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Wrong Leave',
|
||||
'name': 'Wrong Time Off',
|
||||
'date_from': date(2022, 3, 7),
|
||||
'date_to': date(2022, 3, 7),
|
||||
'calendar_id': self.calendar_1.id,
|
||||
|
|
@ -70,72 +76,14 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Wrong Leave',
|
||||
'name': 'Wrong Time Off',
|
||||
'date_from': date(2022, 3, 7),
|
||||
'date_to': date(2022, 3, 7),
|
||||
})
|
||||
|
||||
def test_leave_on_deleted_global_leave(self):
|
||||
public_leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Public Time Off',
|
||||
'date_from': datetime(2024, 2, 20, 0, 0),
|
||||
'date_to': datetime(2024, 2, 22, 23, 59),
|
||||
'company_id': self.employee_emp.company_id.id,
|
||||
})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'no',
|
||||
'allocation_validation_type': 'no',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_id': self.user_hrmanager_id,
|
||||
})
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.employee_emp_id,
|
||||
'name': '2 days allocation',
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 2,
|
||||
'state': 'confirm',
|
||||
'date_from': date(2024, 2, 1),
|
||||
'date_to': date(2024, 2, 29),
|
||||
})
|
||||
allocation.action_validate()
|
||||
covered_leave_1 = self.env['hr.leave'].create({
|
||||
'name': 'Covered Leave',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'date_from': datetime(2024, 2, 19, 7, 0),
|
||||
'date_to': datetime(2024, 2, 20, 18, 0),
|
||||
})
|
||||
self.assertEqual(covered_leave_1.number_of_days, 1, 'The leave should have a duration of 1 day.')
|
||||
covered_leave_2 = self.env['hr.leave'].create({
|
||||
'name': 'Covered Leave',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'date_from': datetime(2024, 2, 21, 7, 0),
|
||||
'date_to': datetime(2024, 2, 21, 18, 0),
|
||||
})
|
||||
self.assertEqual(covered_leave_2.number_of_days, 0, 'The leave should have a duration of 0 days.')
|
||||
covered_leave_3 = self.env['hr.leave'].create({
|
||||
'name': 'Covered Leave',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'date_from': datetime(2024, 2, 22, 7, 0),
|
||||
'date_to': datetime(2024, 2, 23, 18, 0),
|
||||
})
|
||||
self.assertEqual(covered_leave_3.number_of_days, 1, 'The leave should have a duration of 1 day.')
|
||||
|
||||
public_leave.unlink()
|
||||
self.assertEqual(covered_leave_1.active, True, 'The partially covered leave should still be active.')
|
||||
self.assertEqual(covered_leave_1.number_of_days, 1, 'The leave should have a duration of 1 day.')
|
||||
self.assertEqual(covered_leave_2.active, False, 'The covered leave should be archived.')
|
||||
self.assertEqual(covered_leave_3.active, True, 'The partially covered leave should still be active.')
|
||||
self.assertEqual(covered_leave_3.number_of_days, 1, 'The leave should have a duration of 1 day.')
|
||||
|
||||
def test_leave_on_calendar_leave(self):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Correct Leave',
|
||||
'name': 'Correct Time Off',
|
||||
'date_from': date(2022, 3, 8),
|
||||
'date_to': date(2022, 3, 8),
|
||||
'calendar_id': self.calendar_2.id,
|
||||
|
|
@ -143,14 +91,14 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Wrong Leave',
|
||||
'name': 'Wrong Time Off',
|
||||
'date_from': date(2022, 3, 8),
|
||||
'date_to': date(2022, 3, 8),
|
||||
})
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Wrong Leave',
|
||||
'name': 'Wrong Time Off',
|
||||
'date_from': date(2022, 3, 8),
|
||||
'date_to': date(2022, 3, 8),
|
||||
'calendar_id': self.calendar_1.id,
|
||||
|
|
@ -184,6 +132,84 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
# The user in Europe/Brussels timezone see 4:30 and not 2:30 because he is in UTC +02:00.
|
||||
# The user in Asia/Kolkata timezone (determined via the browser) see 8:00 because he is in UTC +05:30
|
||||
|
||||
def test_global_leave_working_schedule_without_company(self):
|
||||
"""
|
||||
Check public holidays for a company apply to employees of this company
|
||||
when using a working schedule without a company.
|
||||
"""
|
||||
calendar_no_company = self.env['resource.calendar'].create({
|
||||
'name': 'Schedule without company',
|
||||
'company_id': False,
|
||||
})
|
||||
self.employee_emp.resource_calendar_id = calendar_no_company
|
||||
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Public Holiday',
|
||||
'date_from': datetime(2024, 1, 3, 0, 0),
|
||||
'date_to': datetime(2024, 1, 3, 23, 59),
|
||||
'calendar_id': calendar_no_company.id,
|
||||
'company_id': self.employee_emp.company_id.id,
|
||||
})
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'Time Off',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': date(2024, 1, 2),
|
||||
'request_date_to': date(2024, 1, 4),
|
||||
})
|
||||
|
||||
self.assertEqual(leave.number_of_days, 2, "Public holiday duration should not be included")
|
||||
|
||||
def test_global_leave_number_of_days_with_new(self):
|
||||
"""
|
||||
Check that leaves stored in memory (and not in the database)
|
||||
take into account global leaves.
|
||||
"""
|
||||
global_leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Time Off',
|
||||
'date_from': datetime(2024, 1, 3, 6, 0, 0),
|
||||
'date_to': datetime(2024, 1, 3, 19, 0, 0),
|
||||
'calendar_id': self.calendar_1.id,
|
||||
})
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
self.employee_emp.resource_calendar_id = self.calendar_1.id
|
||||
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'Test new leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': global_leave.date_from,
|
||||
'request_date_to': global_leave.date_to,
|
||||
})
|
||||
self.assertEqual(leave.number_of_days, 0, 'It is a global leave')
|
||||
|
||||
leave = self.env['hr.leave'].new({
|
||||
'name': 'Test new leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': global_leave.date_from,
|
||||
'request_date_to': global_leave.date_to,
|
||||
})
|
||||
self.assertEqual(leave.number_of_days, 0, 'It is a global leave')
|
||||
|
||||
leave = self.env['hr.leave'].new({
|
||||
'name': 'Test new leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': global_leave.date_from - timedelta(days=1),
|
||||
'request_date_to': global_leave.date_to + timedelta(days=1),
|
||||
})
|
||||
self.assertEqual(leave.number_of_days, 2, 'There is a global leave')
|
||||
|
||||
@freeze_time('2024-12-01')
|
||||
def test_global_leave_keeps_employee_resource_leave(self):
|
||||
"""
|
||||
|
|
@ -191,37 +217,35 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
if the employee's leave is not fully covered by the global leave, the employee's leave
|
||||
should still have resource leaves linked to it.
|
||||
"""
|
||||
employee = self.employee_emp
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'no',
|
||||
'allocation_validation_type': 'no',
|
||||
'request_unit': 'hour',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_id': self.user_hrmanager_id,
|
||||
})
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.employee_emp_id,
|
||||
'name': '5 days allocation',
|
||||
self.env['hr.leave.allocation'].create({
|
||||
'name': '20 days allocation',
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 5,
|
||||
'number_of_days': 20,
|
||||
'employee_id': employee.id,
|
||||
'state': 'confirm',
|
||||
'date_from': date(2024, 12, 1),
|
||||
'date_to': date(2024, 12, 30),
|
||||
})
|
||||
allocation.action_validate()
|
||||
}).action_approve()
|
||||
|
||||
partially_covered_leave = self.env['hr.leave'].create({
|
||||
'name': 'Covered Leave',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'name': 'Holiday 1 week',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'date_from': datetime(2024, 12, 3, 7, 0),
|
||||
'date_to': datetime(2024, 12, 5, 18, 0),
|
||||
'request_date_from': datetime(2024, 12, 3, 7, 0),
|
||||
'request_date_to': datetime(2024, 12, 5, 18, 0),
|
||||
})
|
||||
partially_covered_leave.action_validate()
|
||||
partially_covered_leave.action_approve()
|
||||
|
||||
global_leave = self.env['resource.calendar.leaves'].with_user(self.env.user).create({
|
||||
'name': 'Public holiday',
|
||||
'date_from': "2024-12-4 06:00:00",
|
||||
'date_to': "2024-12-4 23:00:00",
|
||||
'date_from': "2024-12-04 06:00:00",
|
||||
'date_to': "2024-12-04 23:00:00",
|
||||
'calendar_id': self.calendar_1.id,
|
||||
})
|
||||
|
||||
|
|
@ -230,3 +254,156 @@ class TestGlobalLeaves(TestHrHolidaysCommon):
|
|||
('holiday_id', '=', partially_covered_leave.id)
|
||||
])
|
||||
self.assertTrue(resource_leaves, 'Resource leaves linked to the employee leave should exist.')
|
||||
|
||||
@freeze_time('2025-05-11')
|
||||
def test_employee_leave_with_global_leave(self):
|
||||
"""
|
||||
When an employee's leave is created, if there are any public holidays within the leave period,
|
||||
the number of leave days is reduced accordingly.
|
||||
eg,.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
| Leave Requested | Leave State | Public Holiday days | # days leave remains |
|
||||
|---------------------------------------------------------------------------------|
|
||||
| 5 Days | confirm | 1 Days | 4 Days |
|
||||
|---------------------------------------------------------------------------------|
|
||||
| 4 Days | validate1 | 1 Days | 3 Days |
|
||||
|---------------------------------------------------------------------------------|
|
||||
| 3 Days | validate | 1 Days | 2 Days |
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
"""
|
||||
user_david = mail_new_test_user(self.env, login='david', groups='base.group_user')
|
||||
user_timeoff_officer_david = mail_new_test_user(self.env, login='timeoff_officer', groups='base.group_user')
|
||||
|
||||
employee_david = self.env['hr.employee'].create({
|
||||
'name': 'David Employee',
|
||||
'user_id': user_david.id,
|
||||
'leave_manager_id': user_timeoff_officer_david.id,
|
||||
'parent_id': self.employee_hruser.id,
|
||||
'department_id': self.rd_dept.id,
|
||||
'resource_calendar_id': self.calendar_1.id,
|
||||
})
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Sick Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'both',
|
||||
})
|
||||
|
||||
employee_leave = self.env['hr.leave'].create({
|
||||
'name': 'Holiday 5 days',
|
||||
'employee_id': employee_david.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': datetime(2025, 5, 12),
|
||||
'request_date_to': datetime(2025, 5, 16),
|
||||
})
|
||||
|
||||
self.env['resource.calendar.leaves'].with_user(self.user_hrmanager).create({
|
||||
'name': 'Public holiday day 1',
|
||||
'date_from': datetime(2025, 5, 13),
|
||||
'date_to': datetime(2025, 5, 13, 23, 59),
|
||||
'calendar_id': employee_david.resource_calendar_id.id,
|
||||
})
|
||||
|
||||
self.assertEqual(employee_leave.number_of_days, 4, 'Leave duration should be reduced because of public holiday day 1')
|
||||
|
||||
employee_leave.with_user(user_timeoff_officer_david).action_approve()
|
||||
self.env['resource.calendar.leaves'].with_user(self.user_hrmanager).create({
|
||||
'name': 'Public holiday day 2',
|
||||
'date_from': datetime(2025, 5, 14),
|
||||
'date_to': datetime(2025, 5, 14, 23, 59),
|
||||
'calendar_id': employee_david.resource_calendar_id.id,
|
||||
})
|
||||
self.assertEqual(employee_leave.number_of_days, 3, 'Leave duration should be reduced because of public holiday day 2')
|
||||
|
||||
employee_leave.with_user(self.user_hruser).action_approve()
|
||||
self.env['resource.calendar.leaves'].with_user(self.user_hrmanager).create({
|
||||
'name': 'Public holiday day 3',
|
||||
'date_from': datetime(2025, 5, 15),
|
||||
'date_to': datetime(2025, 5, 15, 23, 59),
|
||||
'calendar_id': employee_david.resource_calendar_id.id,
|
||||
})
|
||||
self.assertEqual(employee_leave.number_of_days, 2, 'Leave duration should be reduced because of public holiday day 3')
|
||||
|
||||
def test_multi_day_public_holidays_for_flexible_schedule(self):
|
||||
"""
|
||||
Test that _get_unusual_days return correct value for
|
||||
multi-day holidays in flexible schedules
|
||||
"""
|
||||
|
||||
flex_cal = self.env['resource.calendar'].create({
|
||||
'name': 'Flexible', 'tz': 'UTC', 'flexible_hours': True, 'hours_per_day': 8.0
|
||||
})
|
||||
|
||||
# tuesday to thursday
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': '3 day holiday', 'calendar_id': flex_cal.id,
|
||||
'date_from': datetime(2024, 3, 5), 'date_to': date(2024, 3, 7)
|
||||
})
|
||||
|
||||
# monday to saturday
|
||||
start = datetime(2024, 3, 4)
|
||||
end = datetime(2024, 3, 10)
|
||||
|
||||
flex_days = flex_cal._get_unusual_days(start, end)
|
||||
|
||||
expected = {
|
||||
'2024-03-04': False,
|
||||
'2024-03-05': True,
|
||||
'2024-03-06': True,
|
||||
'2024-03-07': True,
|
||||
'2024-03-08': False,
|
||||
'2024-03-09': False,
|
||||
'2024-03-10': False,
|
||||
}
|
||||
for day, value in expected.items():
|
||||
self.assertEqual(flex_days.get(day), value, f"Day {day} should be {'unusual' if value else 'normal'}")
|
||||
|
||||
def test_public_holidays_for_consecutive_allocations(self):
|
||||
employee = self.employee_emp
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': 'yes',
|
||||
})
|
||||
self.env['hr.leave.allocation'].create([
|
||||
{
|
||||
'name': '2025 allocation',
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 20,
|
||||
'employee_id': employee.id,
|
||||
'state': 'confirm',
|
||||
'date_from': date(2025, 1, 1),
|
||||
'date_to': date(2025, 12, 31),
|
||||
},
|
||||
{
|
||||
'name': '2026 allocation',
|
||||
'holiday_status_id': leave_type.id,
|
||||
'number_of_days': 20,
|
||||
'employee_id': employee.id,
|
||||
'state': 'confirm',
|
||||
'date_from': date(2026, 1, 1),
|
||||
'date_to': date(2026, 12, 31),
|
||||
}
|
||||
]).action_approve()
|
||||
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'Holiday 1 week',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': datetime(2025, 12, 8, 7, 0),
|
||||
'request_date_to': datetime(2026, 1, 3, 18, 0),
|
||||
})
|
||||
leave.action_approve()
|
||||
|
||||
self.assertEqual(leave.number_of_days, 20, "Number of days should be 20")
|
||||
|
||||
public_holiday = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Time Off',
|
||||
'date_from': datetime(2025, 12, 31, 23, 0, 0),
|
||||
'date_to': datetime(2026, 1, 1, 22, 59, 59),
|
||||
})
|
||||
|
||||
self.assertTrue(public_holiday)
|
||||
self.assertEqual(leave.number_of_days, 19, "Number of days should be 19 as one day has been granted back to the"
|
||||
"the employee for the public holiday")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo.addons.base.tests.common import HttpCase
|
||||
from odoo.tests.common import tagged
|
||||
|
|
@ -8,23 +8,96 @@ from odoo.tests.common import users
|
|||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
@tagged('post_install', '-at_install', 'holiday_calendar')
|
||||
class TestHolidaysCalendar(HttpCase, TestHrHolidaysCommon):
|
||||
|
||||
@users('admin')
|
||||
@users('enguerran')
|
||||
def test_hours_time_off_request_calendar_view(self):
|
||||
"""
|
||||
Testing the flow of clicking on a day, save the leave request directly
|
||||
and verify that the start/end time are correctly set
|
||||
and verify that the start/end time are correctly set.
|
||||
"""
|
||||
self.env.user.tz = 'UTC'
|
||||
calendar = self.env.user.employee_id.resource_calendar_id.attendance_ids
|
||||
expected_start_thursday = calendar[6].hour_from
|
||||
expected_end_thursday = calendar[7].hour_to
|
||||
first_day_of_year = date(date.today().year, 1, 1)
|
||||
days_to_thursday = (3 - first_day_of_year.weekday()) % 7
|
||||
first_thursday_of_year = first_day_of_year + timedelta(days=days_to_thursday)
|
||||
|
||||
self.start_tour('/', 'time_off_request_calendar_view', login='admin')
|
||||
leave = self.env['hr.leave'].new({
|
||||
'name': 'Reference Holiday',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': first_thursday_of_year,
|
||||
'request_date_to': first_thursday_of_year,
|
||||
})
|
||||
leave._compute_date_from_to()
|
||||
expected_leave_start = leave.date_from.hour
|
||||
expected_leave_end = leave.date_to.hour
|
||||
|
||||
last_leave = self.env['hr.leave'].search([('employee_id.id', '=', self.env.user.employee_id.id)]).sorted(lambda leave: leave.create_date)[-1]
|
||||
# Tour that takes a leave on the first thursday of the year.
|
||||
self.start_tour('/', 'time_off_request_calendar_view', login='enguerran')
|
||||
|
||||
last_leave = self.env['hr.leave'].search([('employee_id.id', '=', self.employee_emp.id)]).sorted(lambda leave: leave.create_date)[-1]
|
||||
self.assertEqual(last_leave.date_from.weekday(), 3, "It should be Thursday")
|
||||
self.assertEqual(last_leave.date_from.hour, expected_start_thursday, "Wrong start of the day")
|
||||
self.assertEqual(last_leave.date_to.hour, expected_end_thursday, "Wrong end of the day")
|
||||
self.assertEqual(last_leave.date_from.hour, expected_leave_start, "Wrong start of the day")
|
||||
self.assertEqual(last_leave.date_to.hour, expected_leave_end, "Wrong end of the day")
|
||||
|
||||
def test_timezone_calendar_event_single_day(self):
|
||||
"""
|
||||
Test that single-day time off requests have a single day display in calendar
|
||||
"""
|
||||
|
||||
leave_type, leave_type_half = self.env['hr.leave.type'].create([
|
||||
{
|
||||
'name': 'Test Leave Type',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'no_validation',
|
||||
'create_calendar_meeting': True,
|
||||
},
|
||||
{
|
||||
'name': 'Test Leave Type Half Day',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'no_validation',
|
||||
'create_calendar_meeting': True,
|
||||
'request_unit': 'half_day',
|
||||
},
|
||||
])
|
||||
|
||||
# case 1: full day in Los/Angeles tz
|
||||
|
||||
test_date = date(2025, 4, 22)
|
||||
self.employee_emp.user_id.tz = 'America/Los_Angeles'
|
||||
self.employee_emp.resource_calendar_id.tz = 'America/Los_Angeles'
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'Single Day Leave',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': test_date,
|
||||
'request_date_to': test_date,
|
||||
})
|
||||
|
||||
leave.action_approve()
|
||||
|
||||
self.assertEqual(leave.meeting_id.allday, True)
|
||||
self.assertEqual(leave.meeting_id.start_date, test_date,
|
||||
f"Meeting start date should be {test_date}")
|
||||
self.assertEqual(leave.meeting_id.stop_date, test_date,
|
||||
f"Meeting end date should be {test_date}")
|
||||
|
||||
# case 2: half day in Los/Angeles tz
|
||||
|
||||
test_date_half = date(2025, 4, 23)
|
||||
|
||||
leave_half = self.env['hr.leave'].create({
|
||||
'name': 'Half Day Leave LA',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': leave_type_half.id,
|
||||
'request_date_from': test_date_half,
|
||||
'request_date_to': test_date_half,
|
||||
'request_date_from_period': 'pm',
|
||||
'request_date_to_period': 'pm',
|
||||
})
|
||||
|
||||
leave_half.action_approve()
|
||||
|
||||
self.assertEqual(leave_half.meeting_id.allday, False)
|
||||
self.assertEqual(leave_half.meeting_id.start, leave_half.date_from)
|
||||
self.assertEqual(leave_half.meeting_id.stop, leave_half.date_to)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import datetime, date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
from psycopg2 import IntegrityError
|
||||
|
||||
from odoo import fields
|
||||
from odoo.exceptions import AccessError, ValidationError, UserError
|
||||
from odoo.tools import mute_logger, test_reports
|
||||
from odoo import Command
|
||||
from odoo.tools import date_utils, mute_logger, test_reports
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
|
@ -26,29 +25,29 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
HolidayStatusManagerGroup = HolidaysStatus.with_user(self.user_hrmanager_id)
|
||||
HolidayStatusManagerGroup.create({
|
||||
'name': 'WithMeetingType',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
self.holidays_status_hr = HolidayStatusManagerGroup.create({
|
||||
'name': 'NotLimitedHR',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'hr',
|
||||
})
|
||||
self.holidays_status_manager = HolidayStatusManagerGroup.create({
|
||||
'name': 'NotLimitedManager',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'manager',
|
||||
})
|
||||
|
||||
HolidaysEmployeeGroup = Requests.with_user(self.user_employee_id)
|
||||
|
||||
# Employee creates a leave request in a no-limit category hr manager only
|
||||
leave_date = date_utils.start_of((date.today() - relativedelta(days=1)), 'week')
|
||||
hol1_employee_group = HolidaysEmployeeGroup.create({
|
||||
'name': 'Hol11',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_status_hr.id,
|
||||
'date_from': (datetime.today() - relativedelta(days=1)),
|
||||
'date_to': datetime.today(),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': leave_date,
|
||||
'request_date_to': leave_date,
|
||||
})
|
||||
hol1_user_group = hol1_employee_group.with_user(self.user_hruser_id)
|
||||
hol1_manager_group = hol1_employee_group.with_user(self.user_hrmanager_id)
|
||||
|
|
@ -59,13 +58,13 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
self.assertEqual(hol1_manager_group.state, 'validate', 'hr_holidays: validated leave request should be in validate state')
|
||||
|
||||
# Employee creates a leave request in a no-limit category department manager only
|
||||
leave_date = date_utils.start_of(date.today() + relativedelta(days=11), 'week')
|
||||
hol12_employee_group = HolidaysEmployeeGroup.create({
|
||||
'name': 'Hol12',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_status_manager.id,
|
||||
'date_from': (datetime.today() + relativedelta(days=12)),
|
||||
'date_to': (datetime.today() + relativedelta(days=13)),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': leave_date,
|
||||
'request_date_to': leave_date,
|
||||
})
|
||||
hol12_user_group = hol12_employee_group.with_user(self.user_hruser_id)
|
||||
hol12_manager_group = hol12_employee_group.with_user(self.user_hrmanager_id)
|
||||
|
|
@ -75,7 +74,6 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
hol12_manager_group.action_approve()
|
||||
self.assertEqual(hol1_user_group.state, 'validate', 'hr_holidays: validates leave request should be in validate state')
|
||||
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
def test_01_leave_request_flow_limited(self):
|
||||
""" Testing leave request flow: limited type of leave request """
|
||||
|
|
@ -84,13 +82,15 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
Allocations = self.env['hr.leave.allocation']
|
||||
HolidaysStatus = self.env['hr.leave.type']
|
||||
|
||||
self.env.ref('hr.employee_admin').tz = "Europe/Brussels"
|
||||
|
||||
holiday_status_paid_time_off = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'no',
|
||||
'allocation_validation_type': 'officer',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': False,
|
||||
'allocation_validation_type': 'hr',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_id': self.env.ref('base.user_admin').id,
|
||||
'responsible_ids': [Command.link(self.env.ref('base.user_admin').id)],
|
||||
})
|
||||
|
||||
self.env['hr.leave.allocation'].create([
|
||||
|
|
@ -102,38 +102,40 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-%m-01'),
|
||||
}, {
|
||||
'name': 'Paid Time off for David',
|
||||
'name': 'Paid Time off for Admin',
|
||||
'holiday_status_id': holiday_status_paid_time_off.id,
|
||||
'number_of_days': 20,
|
||||
'employee_id': self.ref('hr.employee_admin'),
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-%m-01'),
|
||||
}
|
||||
]).action_validate()
|
||||
]).action_approve()
|
||||
|
||||
def _check_holidays_status(holiday_status, ml, lt, rl, vrl):
|
||||
self.assertEqual(holiday_status.max_leaves, ml,
|
||||
def _check_holidays_status(holiday_status, employee, ml, lt, rl, vrl):
|
||||
result = holiday_status.get_allocation_data(employee)[employee][0][1]
|
||||
self.assertEqual(result['max_leaves'], ml,
|
||||
'hr_holidays: wrong type days computation')
|
||||
self.assertEqual(holiday_status.leaves_taken, lt,
|
||||
self.assertEqual(result['leaves_taken'], lt,
|
||||
'hr_holidays: wrong type days computation')
|
||||
self.assertEqual(holiday_status.remaining_leaves, rl,
|
||||
self.assertEqual(result['remaining_leaves'], rl,
|
||||
'hr_holidays: wrong type days computation')
|
||||
self.assertEqual(holiday_status.virtual_remaining_leaves, vrl,
|
||||
self.assertEqual(result['virtual_remaining_leaves'], vrl,
|
||||
'hr_holidays: wrong type days computation')
|
||||
|
||||
# HrManager creates some holiday statuses
|
||||
HolidayStatusManagerGroup = HolidaysStatus.with_user(self.user_hrmanager_id)
|
||||
HolidayStatusManagerGroup.create({
|
||||
'name': 'WithMeetingType',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
self.holidays_status_limited = HolidayStatusManagerGroup.create({
|
||||
'name': 'Limited',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'no',
|
||||
'allocation_validation_type': 'officer',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': False,
|
||||
'allocation_validation_type': 'hr',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_ids': [Command.link(self.env.ref('base.user_admin').id)]
|
||||
})
|
||||
HolidaysEmployeeGroup = Requests.with_user(self.user_employee_id)
|
||||
|
||||
|
|
@ -147,33 +149,35 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
'date_from': time.strftime('%Y-%m-01'),
|
||||
})
|
||||
# HrUser validates the first step
|
||||
self.env.flush_all()
|
||||
|
||||
# HrManager validates the second step
|
||||
aloc1_user_group.with_user(self.user_hrmanager_id).action_validate()
|
||||
aloc1_user_group.with_user(self.user_hrmanager_id).action_approve()
|
||||
# Checks Employee has effectively some days left
|
||||
hol_status_2_employee_group = self.holidays_status_limited.with_user(self.user_employee_id)
|
||||
_check_holidays_status(hol_status_2_employee_group, 2.0, 0.0, 2.0, 2.0)
|
||||
_check_holidays_status(hol_status_2_employee_group, self.employee_emp, 2.0, 0.0, 2.0, 2.0)
|
||||
|
||||
# Employee creates a leave request in the limited category, now that he has some days left
|
||||
hol2 = HolidaysEmployeeGroup.create({
|
||||
'name': 'Hol22',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_status_limited.id,
|
||||
'date_from': (datetime.today() + relativedelta(days=2)).strftime('%Y-%m-%d %H:%M'),
|
||||
'date_to': (datetime.today() + relativedelta(days=3)),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': (date.today() + relativedelta(days=2)),
|
||||
'request_date_to': (date.today() + relativedelta(days=2)),
|
||||
})
|
||||
self.env.flush_all()
|
||||
hol2_user_group = hol2.with_user(self.user_hruser_id)
|
||||
# Check left days: - 1 virtual remaining day
|
||||
hol_status_2_employee_group.invalidate_model()
|
||||
_check_holidays_status(hol_status_2_employee_group, 2.0, 0.0, 2.0, 1.0)
|
||||
_check_holidays_status(hol_status_2_employee_group, self.employee_emp, 2.0, 0.0, 2.0, 1.0)
|
||||
|
||||
# HrManager validates the second step
|
||||
hol2_user_group.with_user(self.user_hrmanager_id).action_validate()
|
||||
hol2_user_group.with_user(self.user_hrmanager_id).action_approve()
|
||||
self.assertEqual(hol2.state, 'validate',
|
||||
'hr_holidays: second validation should lead to validate state')
|
||||
# Check left days: - 1 day taken
|
||||
_check_holidays_status(hol_status_2_employee_group, 2.0, 1.0, 1.0, 1.0)
|
||||
hol_status_2_employee_group.invalidate_model(['max_leaves', 'leaves_taken'])
|
||||
_check_holidays_status(hol_status_2_employee_group, self.employee_emp, 2.0, 1.0, 1.0, 1.0)
|
||||
|
||||
# HrManager finds an error: he refuses the leave request
|
||||
hol2.with_user(self.user_hrmanager_id).action_refuse()
|
||||
|
|
@ -182,17 +186,11 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
# Check left days: 2 days left again
|
||||
|
||||
hol_status_2_employee_group.invalidate_model(['max_leaves'])
|
||||
_check_holidays_status(hol_status_2_employee_group, 2.0, 0.0, 2.0, 2.0)
|
||||
_check_holidays_status(hol_status_2_employee_group, self.employee_emp, 2.0, 0.0, 2.0, 2.0)
|
||||
|
||||
self.assertEqual(hol2.state, 'refuse',
|
||||
'hr_holidays: hr_user should not be able to reset a refused leave request')
|
||||
|
||||
# HrManager resets the request
|
||||
hol2_manager_group = hol2.with_user(self.user_hrmanager_id)
|
||||
hol2_manager_group.action_draft()
|
||||
self.assertEqual(hol2.state, 'draft',
|
||||
'hr_holidays: resetting should lead to draft state')
|
||||
|
||||
employee_id = self.ref('hr.employee_admin')
|
||||
# cl can be of maximum 20 days for employee_admin
|
||||
hol3_status = holiday_status_paid_time_off.with_context(employee_id=employee_id)
|
||||
|
|
@ -200,24 +198,19 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
hol3 = Requests.create({
|
||||
'name': 'Sick Time Off',
|
||||
'holiday_status_id': hol3_status.id,
|
||||
'date_from': datetime.today().strftime('%Y-%m-10 10:00:00'),
|
||||
'date_to': datetime.today().strftime('%Y-%m-11 19:00:00'),
|
||||
'request_date_from': date.today() + relativedelta(day=10),
|
||||
'request_date_to': date.today() + relativedelta(day=10),
|
||||
'employee_id': employee_id,
|
||||
'number_of_days': 1,
|
||||
})
|
||||
# I find a small mistake on my leave request to I click on "Refuse" button to correct a mistake.
|
||||
hol3.action_refuse()
|
||||
self.assertEqual(hol3.state, 'refuse', 'hr_holidays: refuse should lead to refuse state')
|
||||
# I again set to draft and then confirm.
|
||||
hol3.action_draft()
|
||||
self.assertEqual(hol3.state, 'draft', 'hr_holidays: resetting should lead to draft state')
|
||||
hol3.action_confirm()
|
||||
self.assertEqual(hol3.state, 'confirm', 'hr_holidays: confirming should lead to confirm state')
|
||||
# I validate the holiday request by clicking on "To Approve" button.
|
||||
hol3.action_validate()
|
||||
# Validate it again
|
||||
hol3.action_approve()
|
||||
self.assertEqual(hol3.state, 'validate', 'hr_holidays: validation should lead to validate state')
|
||||
# Check left days for casual leave: 19 days left
|
||||
_check_holidays_status(hol3_status, 20.0, 1.0, 19.0, 19.0)
|
||||
_check_holidays_status(hol3_status, self.env['hr.employee'].browse(employee_id), 20.0, 1.0, 19.0, 19.0)
|
||||
|
||||
def test_10_leave_summary_reports(self):
|
||||
# Print the HR Holidays(Summary Employee) Report through the wizard
|
||||
|
|
@ -240,11 +233,11 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
|
||||
holiday_status_paid_time_off = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'requires_allocation': 'yes',
|
||||
'employee_requests': 'no',
|
||||
'allocation_validation_type': 'officer',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': False,
|
||||
'allocation_validation_type': 'hr',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_id': self.env.ref('base.user_admin').id,
|
||||
'responsible_ids': [Command.link(self.env.ref('base.user_admin').id)],
|
||||
})
|
||||
|
||||
self.env['hr.leave.allocation'].create({
|
||||
|
|
@ -255,34 +248,29 @@ class TestHolidaysFlow(TestHrHolidaysCommon):
|
|||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-%m-01'),
|
||||
'date_to': time.strftime('%Y-12-31'),
|
||||
}).action_validate()
|
||||
}).action_approve()
|
||||
|
||||
leave_vals = {
|
||||
'name': 'Sick Time Off',
|
||||
'holiday_status_id': holiday_status_paid_time_off.id,
|
||||
'date_from': datetime.today().strftime('%Y-%m-11 19:00:00'),
|
||||
'date_to': datetime.today().strftime('%Y-%m-10 10:00:00'),
|
||||
'request_date_from': date.today() + relativedelta(day=11),
|
||||
'request_date_to': date.today() + relativedelta(day=10),
|
||||
'employee_id': self.ref('hr.employee_admin'),
|
||||
'number_of_days': 1,
|
||||
}
|
||||
with mute_logger('odoo.sql_db'):
|
||||
with self.assertRaises(IntegrityError):
|
||||
with self.cr.savepoint():
|
||||
self.env['hr.leave'].create(leave_vals)
|
||||
with mute_logger('odoo.sql_db'), self.assertRaises(IntegrityError):
|
||||
self.env['hr.leave'].create(leave_vals)
|
||||
|
||||
leave_vals = {
|
||||
'name': 'Sick Time Off',
|
||||
'holiday_status_id': holiday_status_paid_time_off.id,
|
||||
'date_from': datetime.today().strftime('%Y-%m-10 10:00:00'),
|
||||
'date_to': datetime.today().strftime('%Y-%m-11 19:00:00'),
|
||||
'request_date_from': date.today() + relativedelta(day=10),
|
||||
'request_date_to': date.today() + relativedelta(day=11),
|
||||
'employee_id': self.ref('hr.employee_admin'),
|
||||
'number_of_days': 1,
|
||||
}
|
||||
leave = self.env['hr.leave'].create(leave_vals)
|
||||
with mute_logger('odoo.sql_db'):
|
||||
with self.assertRaises(IntegrityError): # No ValidationError
|
||||
with self.cr.savepoint():
|
||||
leave.write({
|
||||
'date_from': datetime.today().strftime('%Y-%m-11 19:00:00'),
|
||||
'date_to': datetime.today().strftime('%Y-%m-10 10:00:00'),
|
||||
})
|
||||
|
||||
with mute_logger('odoo.sql_db'), self.assertRaises(IntegrityError):
|
||||
leave.write({
|
||||
'request_date_from': date.today() + relativedelta(day=11),
|
||||
'request_date_to': date.today() + relativedelta(day=10),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import time
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
from .common import TestHrHolidaysCommon
|
||||
from odoo.addons.mail.tests.common import MailCase
|
||||
|
||||
|
||||
class TestHolidaysMail(TestHrHolidaysCommon, MailCase):
|
||||
"""Test that mails are correctly sent when a timeoff is taken"""
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
def test_email_sent_when_approved(self):
|
||||
""" Testing leave request flow: limited type of leave request """
|
||||
with freeze_time('2022-01-15'):
|
||||
self.env.ref('hr.employee_admin').tz = "Europe/Brussels"
|
||||
|
||||
holiday_status_paid_time_off = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'requires_allocation': True,
|
||||
'employee_requests': False,
|
||||
'allocation_validation_type': 'hr',
|
||||
'leave_validation_type': 'both',
|
||||
'responsible_ids': [Command.link(self.env.ref('base.user_admin').id)],
|
||||
})
|
||||
|
||||
self.env['hr.leave.allocation'].create([
|
||||
{
|
||||
'name': 'Paid Time off for David',
|
||||
'holiday_status_id': holiday_status_paid_time_off.id,
|
||||
'number_of_days': 20,
|
||||
'employee_id': self.employee_emp_id,
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-%m-01'),
|
||||
}
|
||||
]).action_approve()
|
||||
|
||||
self.env['hr.leave.allocation'].create([
|
||||
{
|
||||
'name': 'Paid Time off for Mitchell',
|
||||
'holiday_status_id': holiday_status_paid_time_off.id,
|
||||
'number_of_days': 20,
|
||||
'employee_id': self.ref('hr.employee_admin'),
|
||||
'state': 'confirm',
|
||||
'date_from': time.strftime('%Y-%m-01'),
|
||||
},
|
||||
]).action_approve()
|
||||
|
||||
leave_vals = {
|
||||
'name': 'Sick Time Off',
|
||||
'holiday_status_id': holiday_status_paid_time_off.id,
|
||||
'request_date_from': date.today() + relativedelta(day=2),
|
||||
'request_date_to': date.today() + relativedelta(day=3),
|
||||
'employee_id': self.ref('hr.employee_admin'),
|
||||
}
|
||||
leave = self.env['hr.leave'].create(leave_vals)
|
||||
leave.action_approve()
|
||||
with self.mock_mail_gateway():
|
||||
leave.action_approve()
|
||||
admin_emails = self._new_mails.filtered(lambda x: x.partner_ids.employee_ids.id == self.ref('hr.employee_admin'))
|
||||
self.assertEqual(len(admin_emails), 1, "Mitchell Admin should receive an email")
|
||||
self.assertTrue("has been accepted" in admin_emails.preview)
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo import Command
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
class TestHolidaysFlow(TestHrHolidaysCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.employee = cls.env['hr.employee'].create({
|
||||
'name': 'Sky'
|
||||
})
|
||||
cls.departure_date = date.today()
|
||||
departure_reason = cls.env['hr.departure.reason'].create({'name': "Fired"})
|
||||
cls.departure_wizard = cls.env['hr.departure.wizard'].create({
|
||||
'departure_reason_id': departure_reason.id,
|
||||
'departure_date': cls.departure_date,
|
||||
'employee_ids': [Command.link(cls.employee.id)],
|
||||
})
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
def test_departure_without_leave_and_allocation_employee(self):
|
||||
self._check_action_departure()
|
||||
|
||||
def test_departure_leave_before_departure_date(self):
|
||||
leave = self.env['hr.leave'].with_context(leave_fast_create=True).create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': self.departure_date + timedelta(days=-6),
|
||||
'request_date_to': self.departure_date,
|
||||
})
|
||||
leave.state = 'validate'
|
||||
|
||||
self._check_action_departure()
|
||||
|
||||
def test_departure_leave_after_departure_date(self):
|
||||
leave = self.env['hr.leave'].with_context(leave_fast_create=True).create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': self.departure_date + timedelta(days=6),
|
||||
'request_date_to': self.departure_date + timedelta(days=8),
|
||||
})
|
||||
leave.state = 'validate'
|
||||
|
||||
self._check_action_departure()
|
||||
|
||||
def test_departure_leave_with_departure_date(self):
|
||||
leave = self.env['hr.leave'].with_context(leave_fast_create=True).create({
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': self.departure_date + timedelta(days=-6),
|
||||
'request_date_to': self.departure_date + timedelta(days=8),
|
||||
})
|
||||
leave.state = 'validate'
|
||||
|
||||
self._check_action_departure()
|
||||
message = "<p>End date has been updated because the employee will leave the company on %(departure_date)s.</p>" % {
|
||||
'departure_date': self.departure_date}
|
||||
self.assertTrue(message in leave.message_ids.mapped('body'))
|
||||
|
||||
cancel_message = "<p>The time off request has been cancelled for the following reason:</p><p>The employee will leave the company on %(departure_date)s.</p>" % {
|
||||
'departure_date': self.departure_date
|
||||
}
|
||||
self.assertTrue(cancel_message in self.env['hr.leave'].search([
|
||||
('request_date_from', '=', self.departure_date + timedelta(days=1)),
|
||||
('request_date_to', '=', self.departure_date + timedelta(days=8)),
|
||||
("employee_id", "=", self.employee.id)
|
||||
]).message_ids.mapped('body'))
|
||||
|
||||
def test_departure_allocation_before_departure_date(self):
|
||||
self.env['hr.leave.allocation'].create([{
|
||||
'name': 'allocation',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'number_of_days': 15,
|
||||
'employee_id': self.employee.id,
|
||||
'state': 'confirm',
|
||||
'date_from': self.departure_date + timedelta(days=-10),
|
||||
'date_to': self.departure_date,
|
||||
}]).action_approve()
|
||||
self._check_action_departure()
|
||||
|
||||
def test_departure_allocation_after_departure_date(self):
|
||||
self.env['hr.leave.allocation'].create([{
|
||||
'name': 'allocation',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'number_of_days': 15,
|
||||
'employee_id': self.employee.id,
|
||||
'state': 'confirm',
|
||||
'date_from': self.departure_date + timedelta(days=1),
|
||||
'date_to': self.departure_date + timedelta(days=10),
|
||||
}]).action_approve()
|
||||
self._check_action_departure()
|
||||
|
||||
def test_departure_allocation_with_departure_date(self):
|
||||
allocation = self.env['hr.leave.allocation'].create([{
|
||||
'name': 'allocation',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'number_of_days': 15,
|
||||
'employee_id': self.employee.id,
|
||||
'state': 'confirm',
|
||||
'date_from': self.departure_date + timedelta(days=-10),
|
||||
'date_to': self.departure_date + timedelta(days=10),
|
||||
}])
|
||||
allocation.action_approve()
|
||||
self._check_action_departure()
|
||||
|
||||
allocation_msg = '<p>Validity End date has been updated because the employee will leave the company on %(departure_date)s.</p>' % {
|
||||
'departure_date': self.departure_date
|
||||
}
|
||||
self.assertTrue(allocation_msg in allocation.message_ids.mapped('body'))
|
||||
|
||||
def _check_action_departure(self):
|
||||
self.departure_wizard.action_register_departure()
|
||||
self._check_employee_allocation()
|
||||
self._check_employee_leave()
|
||||
|
||||
def _check_employee_leave(self):
|
||||
leaves_after_departure_date = self.env['hr.leave'].search([
|
||||
('employee_id', '=', self.employee.id),
|
||||
('date_from', '>', self.departure_date),
|
||||
('state', '!=', 'cancel')
|
||||
])
|
||||
|
||||
self.assertFalse(leaves_after_departure_date)
|
||||
leaves_before_departure_date = self.env['hr.leave'].search([
|
||||
('employee_id', '=', self.employee.id),
|
||||
('date_from', '<=', self.departure_date),
|
||||
])
|
||||
self.assertFalse(any(leave.date_to.date() > self.departure_date for leave in leaves_before_departure_date))
|
||||
|
||||
def _check_employee_allocation(self):
|
||||
allocations = self.env['hr.leave.allocation'].search([
|
||||
('employee_id', '=', self.employee.id),
|
||||
'|',
|
||||
('date_from', '>', self.departure_date),
|
||||
('date_to', '>', self.departure_date),
|
||||
])
|
||||
self.assertFalse(allocations)
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
|
||||
from .common import TestHrHolidaysCommon
|
||||
|
||||
|
|
@ -15,22 +14,21 @@ class TestHrHolidaysCancelLeave(TestHrHolidaysCommon):
|
|||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
leave_start_datetime = datetime(2018, 2, 5, 7, 0, 0, 0) # this is monday
|
||||
leave_end_datetime = leave_start_datetime + relativedelta(days=3)
|
||||
leave_start_date = date(2018, 2, 5) # this is monday
|
||||
leave_end_date = leave_start_date + relativedelta(days=2)
|
||||
|
||||
cls.hr_leave_type = cls.env['hr.leave.type'].with_user(cls.user_hrmanager).create({
|
||||
'name': 'Leave Type',
|
||||
'requires_allocation': 'no',
|
||||
'name': 'Time Off Type',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
cls.holiday = cls.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True).with_user(cls.user_employee).create({
|
||||
'name': 'Leave 1',
|
||||
'name': 'Time Off 1',
|
||||
'employee_id': cls.employee_emp.id,
|
||||
'holiday_status_id': cls.hr_leave_type.id,
|
||||
'date_from': leave_start_datetime,
|
||||
'date_to': leave_end_datetime,
|
||||
'number_of_days': (leave_end_datetime - leave_start_datetime).days,
|
||||
'request_date_from': leave_start_date,
|
||||
'request_date_to': leave_end_date,
|
||||
})
|
||||
cls.holiday.with_user(cls.user_hrmanager).action_validate()
|
||||
cls.holiday.with_user(cls.user_hrmanager).action_approve()
|
||||
|
||||
@freeze_time('2018-02-05') # useful to be able to cancel the validated time off
|
||||
def test_action_cancel_leave(self):
|
||||
|
|
@ -38,7 +36,7 @@ class TestHrHolidaysCancelLeave(TestHrHolidaysCommon):
|
|||
self.env['hr.holidays.cancel.leave'].with_user(self.user_employee).with_context(default_leave_id=self.holiday.id) \
|
||||
.new({'reason': 'Test remove holiday'}) \
|
||||
.action_cancel_leave()
|
||||
self.assertFalse(self.holiday.active, 'The validated leave should be canceled, that is archived.')
|
||||
self.assertEqual(self.holiday.state, 'cancel', 'The validated leave should be canceled.')
|
||||
|
||||
def test_action_cancel_leave_in_past(self):
|
||||
""" Test if the user may cancel a validated leave in the past. """
|
||||
|
|
@ -61,5 +59,5 @@ class TestHrHolidaysCancelLeave(TestHrHolidaysCommon):
|
|||
self.env['hr.holidays.cancel.leave'].with_user(self.user_employee).with_context(default_leave_id=self.holiday.id) \
|
||||
.new({'reason': 'Test remove holiday'}) \
|
||||
.action_cancel_leave()
|
||||
with self.assertRaises(UserError, msg='The user should not be able to manually unarchive the leave.'):
|
||||
self.holiday.with_user(self.user_employee).write({'active': False})
|
||||
with self.assertRaises(UserError, msg='Only a manager can modify a canceled leave.'):
|
||||
self.holiday.with_user(self.user_employee).write({'state': 'cancel'})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.tests import HttpCase
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
|
|
@ -8,8 +10,12 @@ from datetime import date
|
|||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestHrHolidaysTour(HttpCase):
|
||||
@freeze_time('01/17/2022')
|
||||
def test_hr_holidays_tour(self):
|
||||
admin_user = self.env.ref('base.user_admin')
|
||||
admin_user.write({
|
||||
'email': 'mitchell.admin@example.com',
|
||||
})
|
||||
admin_employee = admin_user.employee_id
|
||||
HRLeave = self.env['hr.leave']
|
||||
date_from = date(2022, 1, 17)
|
||||
|
|
@ -25,11 +31,11 @@ class TestHrHolidaysTour(HttpCase):
|
|||
|
||||
holidays_type_1 = LeaveType.create({
|
||||
'name': 'NotLimitedHR',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'hr',
|
||||
})
|
||||
# add allocation
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
self.env['hr.leave.allocation'].create({
|
||||
'name': 'Expired Allocation',
|
||||
'employee_id': admin_employee.id,
|
||||
'holiday_status_id': holidays_type_1.id,
|
||||
|
|
@ -38,6 +44,5 @@ class TestHrHolidaysTour(HttpCase):
|
|||
'date_from': '2022-01-01',
|
||||
'date_to': '2022-12-31',
|
||||
})
|
||||
allocation.action_validate()
|
||||
|
||||
self.start_tour('/web', 'hr_holidays_tour', login="admin")
|
||||
self.start_tour('/odoo', 'hr_holidays_tour', login="admin")
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.exceptions import AccessError, ValidationError
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
|
@ -12,19 +10,58 @@ from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
|||
class TestHrLeaveType(TestHrHolidaysCommon):
|
||||
|
||||
def test_time_type(self):
|
||||
employee = self.env['hr.employee'].create({'name': 'Test Employee'})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Paid Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
leave_type.allow_request_on_top = True
|
||||
|
||||
worked_leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Worked Time',
|
||||
'time_type': 'other',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
worked_leave_type.elligible_for_accrual_rate = False
|
||||
|
||||
leave_0 = self.env['hr.leave'].create({
|
||||
'name': 'Remote Work',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': worked_leave_type.id,
|
||||
'request_date_from': '2025-09-01', # Monday
|
||||
'request_date_to': '2025-09-05',
|
||||
})
|
||||
leave_0.action_approve()
|
||||
self.assertEqual(
|
||||
self.env['resource.calendar.leaves'].search([('holiday_id', '=', leave_0.id)]).time_type,
|
||||
'other',
|
||||
)
|
||||
with freeze_time('2025-09-03 13:00:00'):
|
||||
employee._compute_leave_status()
|
||||
self.assertFalse(employee.is_absent)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
leave_1 = self.env['hr.leave'].create({
|
||||
'name': 'Doctor Appointment',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': '2025-09-03',
|
||||
'request_date_to': '2025-09-03',
|
||||
})
|
||||
|
||||
worked_leave_type.allow_request_on_top = True
|
||||
leave_1 = self.env['hr.leave'].create({
|
||||
'name': 'Doctor Appointment',
|
||||
'employee_id': self.employee_hruser_id,
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'date_from': (datetime.today() - relativedelta(days=1)),
|
||||
'date_to': datetime.today(),
|
||||
'number_of_days': 1,
|
||||
'request_date_from': '2025-09-03',
|
||||
'request_date_to': '2025-09-03',
|
||||
})
|
||||
leave_1.action_approve()
|
||||
|
||||
|
|
@ -32,13 +69,16 @@ class TestHrLeaveType(TestHrHolidaysCommon):
|
|||
self.env['resource.calendar.leaves'].search([('holiday_id', '=', leave_1.id)]).time_type,
|
||||
'leave'
|
||||
)
|
||||
with freeze_time('2025-09-03 13:00:00'):
|
||||
employee._compute_leave_status()
|
||||
self.assertTrue(employee.is_absent)
|
||||
|
||||
def test_type_creation_right(self):
|
||||
# HrUser creates some holiday statuses -> crash because only HrManagers should do this
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['hr.leave.type'].with_user(self.user_hruser_id).create({
|
||||
'name': 'UserCheats',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
def test_users_tz_shift_back(self):
|
||||
|
|
@ -61,7 +101,7 @@ class TestHrLeaveType(TestHrHolidaysCommon):
|
|||
'employee_id': employee.id,
|
||||
'date_from': '2024-08-19',
|
||||
'date_to': '2024-08-20',
|
||||
}).action_validate()
|
||||
}).action_approve()
|
||||
|
||||
leave_types = self.env['hr.leave.type'].with_context(
|
||||
default_date_from='2024-08-20 21:00:00',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests import HttpCase
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
from datetime import date
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestHrLeaveTypeTour(HttpCase):
|
||||
@freeze_time('01/17/2022')
|
||||
def test_hr_leave_type_tour(self):
|
||||
"""
|
||||
Test Time Off multi company rule defined in hr_holidays_security for hr_leave_type.
|
||||
The available leave types are the ones whose:
|
||||
- Company is one of the selected companies.
|
||||
- Company is false but whose country is one the countries of the selected companies.
|
||||
- Company is false and country is false
|
||||
|
||||
Define:
|
||||
- 2 Companies: company_1 and company_2
|
||||
- 3 Leave Types:
|
||||
* leave_type_1 whose country is set to the country of company_1.
|
||||
* leave_type_2 whose company is set to company_2.
|
||||
* leave_type_3 whose country and company are both False.
|
||||
|
||||
leave_type_1 will be available if company_1 is one of the selected companies.
|
||||
leave_type_2 will be available if company_2 is one of the selected companies.
|
||||
leave_type_3 will always be available.
|
||||
"""
|
||||
admin_user = self.env.ref('base.user_admin')
|
||||
admin_user.write({
|
||||
'email': 'mitchell.admin@example.com',
|
||||
})
|
||||
admin_employee = admin_user.employee_id
|
||||
HRLeave = self.env['hr.leave']
|
||||
date_from = date(2022, 1, 17)
|
||||
date_to = date(2022, 1, 18)
|
||||
leaves_on_freeze_date = HRLeave.search([
|
||||
('date_from', '>=', date_from),
|
||||
('date_to', "<=", date_to),
|
||||
('employee_id', '=', admin_employee.id)
|
||||
])
|
||||
leaves_on_freeze_date.sudo().unlink()
|
||||
company_1 = self.env.company
|
||||
company_1.name = 'company_1'
|
||||
company_2 = self.env['res.company'].create({'name': 'company_2'})
|
||||
self.env["res.users"].browse(2).write({
|
||||
"company_ids": [Command.clear(), Command.link(company_1.id), Command.link(company_2.id)]
|
||||
})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].with_user(admin_user)
|
||||
|
||||
leave_type.create({
|
||||
'name': 'leave_type_1',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'hr',
|
||||
'country_id': company_1.country_id.id
|
||||
})
|
||||
leave_type.create({
|
||||
'name': 'leave_type_2',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'hr',
|
||||
'company_id': company_2.id
|
||||
})
|
||||
leave_type.create({
|
||||
'name': 'leave_type_3',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'hr'
|
||||
})
|
||||
self.start_tour('/web', 'hr_leave_type_tour', login="admin")
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,25 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.release import version_info
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
@tagged('-at_install', 'post_install', 'post_install_l10n')
|
||||
class TestLeaveTypeData(TransactionCase):
|
||||
|
||||
def test_ensure_hr_leave_type_definition(self):
|
||||
# Make sure leave types are defined in hr_holidays in master (and not in other modules)
|
||||
# In the case this tests breaks during a forward port, move the time off type definition
|
||||
# to hr_work_entry and make a upgrade script accordingly.
|
||||
if version_info[3] != 'alpha':
|
||||
return
|
||||
leave_types_xmlids = self.env['hr.leave.type'].search([])._get_external_ids()
|
||||
invalid_xmlids = []
|
||||
for xmlids in leave_types_xmlids.values():
|
||||
for xmlid in xmlids:
|
||||
module = xmlid.split('.')[0]
|
||||
if module not in ['hr_holidays', '__export__', '__custom__'] and not module.startswith('test_'):
|
||||
invalid_xmlids.append(xmlid)
|
||||
if invalid_xmlids:
|
||||
raise ValidationError("Some time off types are defined outside of module hr_holidays.\n%s" % '\n'.join(invalid_xmlids))
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import tests
|
||||
from odoo.tests import Form, new_test_user, TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
@tests.tagged('access_rights', 'post_install', '-at_install')
|
||||
class TestHrLeaveMandatoryDays(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.default_calendar = cls.env['resource.calendar'].create({
|
||||
'name': 'moon calendar',
|
||||
})
|
||||
|
||||
cls.company = cls.env['res.company'].create({
|
||||
'name': 'super company',
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
|
||||
cls.employee_user = new_test_user(cls.env, login='user', groups='base.group_user', company_ids=[(6, 0, cls.company.ids)], company_id=cls.company.id)
|
||||
cls.manager_user = new_test_user(cls.env, login='manager', groups='base.group_user,hr_holidays.group_hr_holidays_manager', company_ids=[(6, 0, cls.company.ids)], company_id=cls.company.id)
|
||||
|
||||
cls.employee_emp = cls.env['hr.employee'].create({
|
||||
'name': 'Toto Employee',
|
||||
'company_id': cls.company.id,
|
||||
'user_id': cls.employee_user.id,
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
cls.manager_emp = cls.env['hr.employee'].create({
|
||||
'name': 'Toto Mananger',
|
||||
'company_id': cls.company.id,
|
||||
'user_id': cls.manager_user.id,
|
||||
})
|
||||
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Unlimited',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': False,
|
||||
'company_id': cls.company.id,
|
||||
})
|
||||
|
||||
cls.mandatory_day = cls.env['hr.leave.mandatory.day'].create({
|
||||
'name': 'Super Event',
|
||||
'company_id': cls.company.id,
|
||||
'start_date': datetime(2021, 11, 2),
|
||||
'end_date': datetime(2021, 11, 2),
|
||||
'color': 1,
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
cls.mandatory_week = cls.env['hr.leave.mandatory.day'].create({
|
||||
'name': 'Super Event End Of Week',
|
||||
'company_id': cls.company.id,
|
||||
'start_date': datetime(2021, 11, 8),
|
||||
'end_date': datetime(2021, 11, 12),
|
||||
'color': 2,
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_request_mandatory_days(self):
|
||||
# An employee can request time off outside mandatory days
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 3),
|
||||
'request_date_to': datetime(2021, 11, 3),
|
||||
})
|
||||
|
||||
# Taking a time off during a Mandatory Day is not allowed for a simple employee...
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 3),
|
||||
'request_date_to': datetime(2021, 11, 17),
|
||||
})
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 9),
|
||||
'request_date_to': datetime(2021, 11, 9),
|
||||
})
|
||||
|
||||
# ... but is allowed for a Time Off Officer
|
||||
self.env['hr.leave'].with_user(self.manager_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 2),
|
||||
'request_date_to': datetime(2021, 11, 2),
|
||||
})
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_get_mandatory_days(self):
|
||||
mandatory_days = self.employee_emp.get_mandatory_days('2021-11-01', '2021-11-30')
|
||||
|
||||
# Mandatory Days spanning multiple days should be split in single days
|
||||
expected_data = {'2021-11-02': 1, '2021-11-08': 2, '2021-11-09': 2, '2021-11-10': 2, '2021-11-11': 2, '2021-11-12': 2}
|
||||
|
||||
self.assertEqual(len(mandatory_days), len(expected_data))
|
||||
for day, color in expected_data.items():
|
||||
self.assertTrue(day in mandatory_days)
|
||||
self.assertEqual(color, mandatory_days[day])
|
||||
|
||||
with Form(self.env['hr.leave'].with_user(self.employee_user.id).with_context(default_employee_id=self.employee_emp.id)) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = datetime(2021, 11, 1)
|
||||
leave_form.request_date_to = datetime(2021, 11, 1)
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertFalse(leave_form.record.has_mandatory_day)
|
||||
|
||||
leave_form.request_date_to = datetime(2021, 11, 5)
|
||||
|
||||
leave_form.save() # need to be saved to have access to record
|
||||
self.assertTrue(leave_form.record.has_mandatory_day)
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_department_mandatory_days(self):
|
||||
production_department = self.env['hr.department'].create({
|
||||
'name': 'Production Department',
|
||||
'company_id': self.company.id,
|
||||
})
|
||||
post_production_department = self.env['hr.department'].create({
|
||||
'name': 'Post-Production Department',
|
||||
'company_id': self.company.id,
|
||||
'parent_id': production_department.id,
|
||||
})
|
||||
deployment_department = self.env['hr.department'].create({
|
||||
'name': 'Deployment Department',
|
||||
'company_id': self.company.id,
|
||||
'parent_id': production_department.id,
|
||||
})
|
||||
|
||||
self.employee_emp.write({
|
||||
'department_id': post_production_department.id
|
||||
})
|
||||
|
||||
# Create one mandatory day for each department
|
||||
self.env['hr.leave.mandatory.day'].create({
|
||||
'name': 'Last Rush Before Launch (production)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 3),
|
||||
'end_date': datetime(2021, 11, 3),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [production_department.id],
|
||||
})
|
||||
self.env['hr.leave.mandatory.day'].create({
|
||||
'name': 'Last Rush Before Launch (post-production)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 4),
|
||||
'end_date': datetime(2021, 11, 4),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [post_production_department.id],
|
||||
})
|
||||
self.env['hr.leave.mandatory.day'].create({
|
||||
'name': 'Last Rush Before Launch (deployment)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 5),
|
||||
'end_date': datetime(2021, 11, 5),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [deployment_department.id],
|
||||
})
|
||||
|
||||
# The employee should only be able to create a time off on mandatory days
|
||||
# that do not include his department
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 3),
|
||||
'request_date_to': datetime(2021, 11, 3),
|
||||
})
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 4),
|
||||
'request_date_to': datetime(2021, 11, 4),
|
||||
})
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 5),
|
||||
'request_date_to': datetime(2021, 11, 5),
|
||||
})
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_job_position_mandatory_days(self):
|
||||
"""
|
||||
Test mandatory leave restrictions based on job positions and departments.
|
||||
|
||||
This test ensures that employees cannot request time off on mandatory leave days
|
||||
that are assigned to their specific job position or department. The logic includes:
|
||||
|
||||
- Creating a production department and job positions.
|
||||
- Assigning an employee to a department and job position.
|
||||
- Defining mandatory leave days for specific jobs and departments.
|
||||
- Validating that the employee cannot take leave on restricted days.
|
||||
- Allowing leave requests on days without conflicts.
|
||||
|
||||
Expected behavior:
|
||||
- Raises a ValidationError if the employee's department or job position is linked to a mandatory leave day.
|
||||
- Allows leave requests on days that do not conflict with their assigned job or department.
|
||||
"""
|
||||
production_department = self.env['hr.department'].create({
|
||||
'name': 'Production Department',
|
||||
'company_id': self.company.id,
|
||||
})
|
||||
|
||||
# Create job positions
|
||||
production_manager, post_production_manager = self.env['hr.job'].create([{
|
||||
'name': 'Production Manager',
|
||||
'company_id': self.company.id,
|
||||
},{
|
||||
'name': 'Post-Production Manager',
|
||||
'company_id': self.company.id,
|
||||
}])
|
||||
|
||||
self.employee_emp.write({
|
||||
'department_id': production_department.id,
|
||||
'job_id': production_manager.id,
|
||||
})
|
||||
|
||||
# Create mandatory leave days for job positions and departments
|
||||
self.env['hr.leave.mandatory.day'].create([{
|
||||
'name': 'Production Deadline',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 3),
|
||||
'end_date': datetime(2021, 11, 3),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [production_department.id],
|
||||
'job_ids': [production_manager.id],
|
||||
},{
|
||||
'name': 'Post-Production Deadline',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 4),
|
||||
'end_date': datetime(2021, 11, 4),
|
||||
'color': 2,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [production_department.id],
|
||||
}, {
|
||||
'name': 'Team General Meeting',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 6),
|
||||
'end_date': datetime(2021, 11, 6),
|
||||
'color': 3,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'job_ids': [production_manager.id]
|
||||
}, {
|
||||
'name': 'Department General Meeting',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 5),
|
||||
'end_date': datetime(2021, 11, 5),
|
||||
'color': 3,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [production_department.id],
|
||||
'job_ids': [post_production_manager.id]
|
||||
}])
|
||||
|
||||
# The employee should only be able to create a time off on mandatory days
|
||||
# that do not conflict with their job or department restrictions
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'Vacation during Production Deadline',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 3),
|
||||
'request_date_to': datetime(2021, 11, 3),
|
||||
})
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'Vacation during Post-Production Deadline',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 4),
|
||||
'request_date_to': datetime(2021, 11, 4),
|
||||
})
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'Holiday During Team General Meating',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 6),
|
||||
'request_date_to': datetime(2021, 11, 6),
|
||||
})
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'Vacation during General Meeting',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 5),
|
||||
'request_date_to': datetime(2021, 11, 5),
|
||||
})
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_multiple_employees_mandatory_days(self):
|
||||
production_department = self.env['hr.department'].create({
|
||||
'name': 'Production Department',
|
||||
'company_id': self.company.id,
|
||||
})
|
||||
post_production_department, deployment_department = self.env['hr.department'].create([{
|
||||
'name': 'Post-Production Department',
|
||||
'company_id': self.company.id,
|
||||
'parent_id': production_department.id,
|
||||
}, {
|
||||
'name': 'Deployment Department',
|
||||
'company_id': self.company.id,
|
||||
'parent_id': production_department.id,
|
||||
}])
|
||||
|
||||
employee_emp_2 = self.env['hr.employee'].create({
|
||||
'name': 'Tototo Employee',
|
||||
'company_id': self.company.id,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_id': deployment_department.id,
|
||||
})
|
||||
|
||||
self.employee_emp.write({
|
||||
'department_id': post_production_department.id
|
||||
})
|
||||
|
||||
self.env['hr.leave.mandatory.day'].create([{
|
||||
'name': 'Last Rush Before Launch (post-production)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 4),
|
||||
'end_date': datetime(2021, 11, 4),
|
||||
'color': 3,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [post_production_department.id],
|
||||
}, {
|
||||
'name': 'Last Rush Before Launch (deployment)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 5),
|
||||
'end_date': datetime(2021, 11, 5),
|
||||
'color': 4,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [deployment_department.id],
|
||||
}])
|
||||
|
||||
mandatory_days = (self.employee_emp + employee_emp_2).get_mandatory_days('2021-11-01', '2021-11-30')
|
||||
|
||||
# Mandatory Days spanning multiple days should be split in single days
|
||||
expected_data = {'2021-11-02': 1, '2021-11-04': 3, '2021-11-05': 4, '2021-11-08': 2, '2021-11-09': 2, '2021-11-10': 2, '2021-11-11': 2, '2021-11-12': 2}
|
||||
|
||||
# All mandatory days for both employees should be returned
|
||||
self.assertEqual(len(mandatory_days), len(expected_data))
|
||||
for day, color in expected_data.items():
|
||||
self.assertTrue(day in mandatory_days)
|
||||
self.assertEqual(color, mandatory_days[day])
|
||||
|
||||
# Check that has_mandatory_day is computed correctly for multiple leaves
|
||||
leave_1, leave_2 = self.env['hr.leave'].with_user(self.manager_user.id).create([{
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2021, 11, 3),
|
||||
'request_date_to': datetime(2021, 11, 4),
|
||||
},
|
||||
{
|
||||
'name': 'have been given the gray spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': employee_emp_2.id,
|
||||
'request_date_from': datetime(2021, 11, 6),
|
||||
'request_date_to': datetime(2021, 11, 6),
|
||||
}])
|
||||
|
||||
self.assertTrue(leave_1.has_mandatory_day)
|
||||
self.assertFalse(leave_2.has_mandatory_day)
|
||||
|
|
@ -0,0 +1,429 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime, date
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests import Form, freeze_time, tagged
|
||||
from odoo.addons.hr_holidays.tests.common import TestHolidayContract
|
||||
|
||||
|
||||
@tagged('multi_contract')
|
||||
class TestHolidaysMultiContract(TestHolidayContract):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
def test_move_contract_in_leave(self):
|
||||
# test move contract dates such that a leave is across two contracts
|
||||
start = datetime.strptime('2015-11-05 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-12-15 18:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.contract_cdi.write({'contract_date_start': datetime.strptime('2015-12-30', '%Y-%m-%d').date()})
|
||||
# begins during contract, ends after contract
|
||||
leave = self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
# move contract in the middle of the leave
|
||||
with self.assertRaises(ValidationError):
|
||||
self.contract_cdi.contract_date_start = datetime.strptime('2015-11-17', '%Y-%m-%d').date()
|
||||
|
||||
def test_create_contract_in_leave(self):
|
||||
# test create contract such that a leave is across two contracts
|
||||
start = datetime.strptime('2015-11-05 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-12-15 18:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.contract_cdi.contract_date_start = datetime.strptime('2015-12-30', '%Y-%m-%d').date() # remove this contract to be able to create the leave
|
||||
# begins during contract, ends after contract
|
||||
leave = self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
# move contract in the middle of the leave
|
||||
with self.assertRaises(ValidationError):
|
||||
self.jules_emp.create_version({
|
||||
'date_version': datetime.strptime('2015-11-30', '%Y-%m-%d').date(),
|
||||
'contract_date_start': datetime.strptime('2015-11-30', '%Y-%m-%d').date(),
|
||||
'contract_date_end': False,
|
||||
'name': 'Contract for Richard',
|
||||
'resource_calendar_id': self.calendar_40h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
|
||||
def test_leave_outside_contract(self):
|
||||
# Leave outside contract => should not raise
|
||||
start = datetime.strptime('2014-10-18 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2014-10-20 09:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
|
||||
# begins before contract, ends during contract => should not raise
|
||||
start = datetime.strptime('2014-10-25 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-01-15 18:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
|
||||
# begins during contract, ends after contract => should not raise
|
||||
self.contract_cdi.date_end = datetime.strptime('2015-11-30', '%Y-%m-%d').date()
|
||||
start = datetime.strptime('2015-11-25 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-12-05 18:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
|
||||
def test_no_leave_overlapping_contracts(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
# Overlap two contracts
|
||||
start = datetime.strptime('2015-11-12 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-11-17 18:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
|
||||
# Leave inside fixed term contract => should not raise
|
||||
start = datetime.strptime('2015-11-04 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-11-07 09:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
|
||||
# Leave inside contract (no end) => should not raise
|
||||
start = datetime.strptime('2015-11-18 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-11-20 09:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
|
||||
def test_leave_request_next_contracts(self):
|
||||
start = datetime.strptime('2015-11-23 07:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
end = datetime.strptime('2015-11-24 18:00:00', '%Y-%m-%d %H:%M:%S')
|
||||
leave = self.create_leave(start, end, name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
self.assertEqual(leave.number_of_hours, 14, "It should count hours according to the future contract.")
|
||||
|
||||
def test_leave_multi_contracts_same_schedule(self):
|
||||
# TODO DBE / ARPI : Is this test still valid ?
|
||||
# Allow leaves overlapping multiple contracts if same
|
||||
# resource calendar
|
||||
leave = self.create_leave(datetime(2022, 6, 1, 7, 0, 0), datetime(2022, 6, 30, 18, 0, 0), name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
self.contract_cdi.contract_date_end = date(2022, 6, 15)
|
||||
|
||||
self.jules_emp.create_version({
|
||||
'date_version': date(2022, 6, 16),
|
||||
'contract_date_start': date(2022, 6, 16),
|
||||
'contract_date_end': False,
|
||||
'name': 'New Contract for Jules',
|
||||
'resource_calendar_id': self.calendar_35h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
|
||||
def test_leave_same_contract_multiple_versions_with_different_schedules(self):
|
||||
self.contract_cdi.contract_date_end = date(2022, 7, 30)
|
||||
self.jules_emp.create_version({
|
||||
'date_version': date(2022, 6, 10),
|
||||
'contract_date_start': self.contract_cdi.contract_date_start,
|
||||
'contract_date_end': self.contract_cdi.contract_date_end,
|
||||
'name': 'New Version with a different schedule for Jules',
|
||||
'resource_calendar_id': self.calendar_40h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
with self.assertRaises(ValidationError):
|
||||
leave = self.create_leave(datetime(2022, 6, 1, 7, 0, 0), datetime(2022, 6, 30, 18, 0, 0), name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
|
||||
leave = self.create_leave(datetime(2022, 5, 1, 7, 0, 0), datetime(2022, 5, 10, 18, 0, 0), name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
|
||||
leave = self.create_leave(datetime(2022, 6, 15, 7, 0, 0), datetime(2022, 7, 20, 18, 0, 0), name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
|
||||
def test_leave_multi_contracts_split(self):
|
||||
# Check that setting a contract as running correctly
|
||||
# splits the existing time off for this employee that
|
||||
# are ovelapping with another contract with another
|
||||
# working schedule
|
||||
leave = self.create_leave(date(2022, 6, 1), date(2022, 6, 30), name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
self.assertEqual(leave.number_of_days, 22)
|
||||
self.assertEqual(leave.state, 'validate')
|
||||
|
||||
self.contract_cdi.contract_date_end = date(2022, 6, 15)
|
||||
self.jules_emp.create_version({
|
||||
'date_version': date(2022, 6, 16),
|
||||
'contract_date_start': date(2022, 6, 16),
|
||||
'contract_date_end': False,
|
||||
'name': 'New Contract for Jules',
|
||||
'resource_calendar_id': self.calendar_40h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
|
||||
leaves = self.env['hr.leave'].search([('employee_id', '=', self.jules_emp.id)])
|
||||
self.assertEqual(len(leaves), 3)
|
||||
self.assertEqual(leave.state, 'refuse')
|
||||
|
||||
first_leave = leaves.filtered(lambda l: l.date_from.day == 1 and l.date_to.day == 15)
|
||||
self.assertEqual(first_leave.state, 'validate')
|
||||
self.assertEqual(first_leave.number_of_days, 11)
|
||||
|
||||
second_leave = leaves.filtered(lambda l: l.date_from.day == 16 and l.date_to.day == 30)
|
||||
self.assertEqual(second_leave.state, 'confirm')
|
||||
self.assertEqual(second_leave.number_of_days, 11)
|
||||
|
||||
def test_multi_contracts_draft(self):
|
||||
# Check that setting a contract as running correctly
|
||||
# make the existing time off to draft for this employee
|
||||
# which is after another contract with another
|
||||
# working schedule
|
||||
leave = self.create_leave(date(2022, 6, 27), date(2022, 6, 30), name="Doctor Appointment", employee_id=self.jules_emp.id)
|
||||
leave.action_approve()
|
||||
self.assertEqual(leave.number_of_days, 4)
|
||||
self.assertEqual(leave.state, 'validate')
|
||||
|
||||
self.contract_cdi.contract_date_end = date(2022, 6, 15)
|
||||
self.jules_emp.create_version({
|
||||
'date_version': date(2022, 6, 16),
|
||||
'contract_date_start': date(2022, 6, 16),
|
||||
'contract_date_end': False,
|
||||
'name': 'New Contract for Jules',
|
||||
'resource_calendar_id': self.calendar_40h.id,
|
||||
'wage': 5000.0,
|
||||
})
|
||||
|
||||
leaves = self.env['hr.leave'].search([('employee_id', '=', self.jules_emp.id)])
|
||||
self.assertEqual(len(leaves), 1)
|
||||
self.assertEqual(leave.state, 'confirm')
|
||||
self.assertEqual(leave.number_of_days, 4)
|
||||
|
||||
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,
|
||||
})
|
||||
employee.create_version({
|
||||
'name': 'Full time (5/5)',
|
||||
'date_version': datetime.strptime('2023-01-01', '%Y-%m-%d').date(),
|
||||
'contract_date_start': datetime.strptime('2023-01-01', '%Y-%m-%d').date(),
|
||||
'contract_date_end': datetime.strptime('2023-06-30', '%Y-%m-%d').date(),
|
||||
'resource_calendar_id': calendar_full.id,
|
||||
'wage': 1000.0,
|
||||
})
|
||||
employee.create_version({
|
||||
'name': 'Partial time (4/5)',
|
||||
'date_version': datetime.strptime('2023-07-01', '%Y-%m-%d').date(),
|
||||
'contract_date_start': datetime.strptime('2023-07-01', '%Y-%m-%d').date(),
|
||||
'contract_date_end': datetime.strptime('2023-12-31', '%Y-%m-%d').date(),
|
||||
'resource_calendar_id': calendar_partial.id,
|
||||
'wage': 1000.0,
|
||||
})
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Leave Type',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'leave_validation_type': 'hr',
|
||||
'request_unit': 'day',
|
||||
})
|
||||
allocation = 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(),
|
||||
})
|
||||
allocation.action_approve()
|
||||
leave_during_full_time, leave_during_partial_time = self.env['hr.leave'].create([
|
||||
{
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': '2023-01-03', # Tuesday
|
||||
'request_date_to': '2023-01-05', # Thursday
|
||||
},
|
||||
{
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': '2023-12-05', # Tuesday
|
||||
'request_date_to': '2023-12-07', # Thursday
|
||||
},
|
||||
])
|
||||
self.assertEqual(leave_during_full_time.number_of_days, 3)
|
||||
self.assertEqual(leave_during_partial_time.number_of_days, 2)
|
||||
self.assertEqual(leave_during_full_time.number_of_hours, 24)
|
||||
self.assertEqual(leave_during_partial_time.number_of_hours, 16)
|
||||
# Simulate the unit change days/hours of the time off type
|
||||
(leave_during_full_time + leave_during_partial_time)._compute_duration()
|
||||
self.assertEqual(leave_during_full_time.number_of_days, 3)
|
||||
self.assertEqual(leave_during_partial_time.number_of_days, 2)
|
||||
self.assertEqual(leave_during_full_time.number_of_hours, 24)
|
||||
self.assertEqual(leave_during_partial_time.number_of_hours, 16)
|
||||
# Check after leave approval
|
||||
(leave_during_full_time + leave_during_partial_time).action_approve()
|
||||
self.assertEqual(leave_during_full_time.number_of_hours, 24)
|
||||
self.assertEqual(leave_during_partial_time.number_of_hours, 16)
|
||||
|
||||
@freeze_time('2024-01-05')
|
||||
def test_multi_contract_out_of_office(self):
|
||||
"""
|
||||
Test that the out of office feature works correctly with multiple contracts
|
||||
The Case is when the employee is out of the office for a period that overlaps multiple contracts
|
||||
"""
|
||||
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'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Evening', '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 Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
|
||||
]
|
||||
},
|
||||
])
|
||||
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': 'Employee',
|
||||
'resource_calendar_id': calendar_full.id,
|
||||
})
|
||||
|
||||
employee.create_version({
|
||||
'name': 'Full time (5/5)',
|
||||
'date_version': datetime.strptime('2024-01-01', '%Y-%m-%d').date(),
|
||||
'contract_date_start': datetime.strptime('2024-01-01', '%Y-%m-%d').date(),
|
||||
'contract_date_end': datetime.strptime('2024-01-31', '%Y-%m-%d').date(),
|
||||
'resource_calendar_id': calendar_full.id,
|
||||
'wage': 1000.0,
|
||||
})
|
||||
employee.create_version({
|
||||
'name': 'Partial time (4/5)',
|
||||
'date_version': datetime.strptime('2024-02-01', '%Y-%m-%d').date(),
|
||||
'contract_date_start': datetime.strptime('2024-02-01', '%Y-%m-%d').date(),
|
||||
'contract_date_end': False,
|
||||
'resource_calendar_id': calendar_partial.id,
|
||||
'wage': 1000.0,
|
||||
# 'state': 'draft',
|
||||
})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Leave Type',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'hr',
|
||||
'request_unit': 'day',
|
||||
})
|
||||
|
||||
leave1 = self.env['hr.leave'].create({
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': '2024-01-01',
|
||||
'request_date_to': '2024-01-31',
|
||||
})
|
||||
|
||||
leave2 = self.env['hr.leave'].create({
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': '2024-02-01',
|
||||
'request_date_to': '2024-02-29',
|
||||
})
|
||||
|
||||
leave1.action_approve()
|
||||
leave2.action_approve()
|
||||
|
||||
employee._compute_leave_status()
|
||||
self.assertEqual(employee.leave_date_to, date(2024, 3, 4))
|
||||
|
||||
def test_multi_contracts_with_different_work_schedules(self):
|
||||
"""
|
||||
Test that the employee can have multiple non-overlapping versions with different work schedules,
|
||||
and that the leave requests are correctly calculated based on corresponding the contract's working schedule.
|
||||
"""
|
||||
calendar_full, calendar_partial = self.env['resource.calendar'].create([
|
||||
{
|
||||
'name': 'Full time (5/5, 8h/day)',
|
||||
'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'}),
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': 'Partial time (5/5, 6h/day)',
|
||||
'attendance_ids': [
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Afternoon', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 9, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Friday Afternoon', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
]
|
||||
},
|
||||
])
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': "Employee",
|
||||
"resource_calendar_id": calendar_partial.id,
|
||||
})
|
||||
employee.create_version({
|
||||
"name": "Full time (5/5)",
|
||||
"date_version": datetime.strptime('2023-01-01', '%Y-%m-%d').date(),
|
||||
"contract_date_start": datetime.strptime('2023-01-01', '%Y-%m-%d').date(),
|
||||
"contract_date_end": datetime.strptime('2023-06-30', '%Y-%m-%d').date(),
|
||||
"resource_calendar_id": calendar_full.id,
|
||||
"wage": 1000.0,
|
||||
})
|
||||
employee.create_version({
|
||||
"name": "Partial time (5/5)",
|
||||
"date_version": datetime.strptime('2023-07-01', '%Y-%m-%d').date(),
|
||||
"contract_date_start": datetime.strptime('2023-07-01', '%Y-%m-%d').date(),
|
||||
"contract_date_end": datetime.strptime('2023-12-31', '%Y-%m-%d').date(),
|
||||
"resource_calendar_id": calendar_partial.id,
|
||||
"wage": 1000.0,
|
||||
})
|
||||
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Leave Type',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': False,
|
||||
'request_unit': 'day',
|
||||
})
|
||||
|
||||
with Form(self.env['hr.leave'].with_context(default_employee_id=employee.id)) as leave_form:
|
||||
leave_form.holiday_status_id = leave_type
|
||||
leave_form.request_date_from = date(2023, 2, 14) # full-time calendar
|
||||
leave_form.request_date_to = date(2023, 2, 14)
|
||||
|
||||
leave = leave_form.save()
|
||||
# Assert based on full-time calendar (8h)
|
||||
self.assertEqual(leave.number_of_days, 1)
|
||||
self.assertEqual(leave.number_of_hours, 8)
|
||||
# Change to date under partial-time contract
|
||||
leave.write({
|
||||
'request_date_from': date(2023, 7, 14),
|
||||
'request_date_to': date(2023, 7, 14),
|
||||
})
|
||||
# Assert based on partial-time calendar
|
||||
self.assertEqual(leave.number_of_days, 1)
|
||||
self.assertEqual(leave.number_of_hours, 6)
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
from odoo import tests
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
@tests.tagged('post_install', '-at_install')
|
||||
class TestHrHolidaysAccessRightsCommon(TestHrHolidaysCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestHrHolidaysAccessRightsCommon, cls).setUpClass()
|
||||
cls.company_2 = cls.env['res.company'].create({'name': 'Test company 2'})
|
||||
|
||||
def test_unrelated_public_leave(self):
|
||||
public_leave = self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Time Off for Company 2',
|
||||
'resource_id': False,
|
||||
'date_from': datetime(2024, 1, 3, 6, 0, 0),
|
||||
'date_to': datetime(2024, 1, 3, 19, 0, 0),
|
||||
})
|
||||
public_leave.company_id = self.company_2
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Test Leave Type',
|
||||
'requires_allocation': False,
|
||||
'request_unit': 'day',
|
||||
'company_id': False,
|
||||
})
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': '3 days leave',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': date(2024, 1, 2),
|
||||
'request_date_to': datetime(2024, 1, 4),
|
||||
})
|
||||
self.assertNotEqual(public_leave.company_id, self.employee_emp.company_id)
|
||||
self.assertEqual(
|
||||
leave.number_of_days, 3,
|
||||
"The leave should not depend on other companies public leaves.")
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.tests.common import tagged
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
@tagged('negative_time_off')
|
||||
class TestNegative(TestHrHolidaysCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Limited with negative',
|
||||
'leave_validation_type': 'no_validation',
|
||||
'requires_allocation': True,
|
||||
'company_id': cls.company.id,
|
||||
'allows_negative': True,
|
||||
'max_allowed_negative': 5,
|
||||
})
|
||||
|
||||
cls.allocation_2022 = cls.env['hr.leave.allocation'].create({
|
||||
'employee_id': cls.employee_emp_id,
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
'date_from': '2022-01-01',
|
||||
'date_to': '2022-12-31',
|
||||
'number_of_days': 1,
|
||||
})
|
||||
cls.allocation_2022.action_approve()
|
||||
|
||||
cls.allocation_2023 = cls.env['hr.leave.allocation'].create({
|
||||
'employee_id': cls.employee_emp_id,
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
'date_from': '2023-01-01',
|
||||
'number_of_days': 5,
|
||||
})
|
||||
cls.allocation_2023.action_approve()
|
||||
|
||||
def test_negative_time_off(self):
|
||||
with freeze_time('2022-10-02'):
|
||||
# At the start of 2022, the user receives 1 days, his balance is at 1
|
||||
# The first 2022 leave brings the user balance at -4
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'first 2022 leave of 5 days',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2022, 10, 24),
|
||||
'request_date_to': datetime(2022, 10, 28),
|
||||
})
|
||||
|
||||
with freeze_time('2023-10-02'):
|
||||
# At the start of 2023, the user receives 5 days, his balance is at 1
|
||||
# The first leave of 2023 brings the balance at -4
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'first 2023 leave of 5 days',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'request_date_from': datetime(2023, 10, 9),
|
||||
'request_date_to': datetime(2023, 10, 13),
|
||||
})
|
||||
|
||||
# The leave should not be possible to take since it would bring the balance at -9
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'not takable leaves of 5 days',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp_id,
|
||||
'request_date_from': datetime(2023, 10, 16),
|
||||
'request_date_to': datetime(2023, 10, 20),
|
||||
})
|
||||
|
||||
# The second leave of 2023 brings the balance at -5
|
||||
one_day_leave = self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'Second 2023 leave of 1 day',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp_id,
|
||||
'request_date_from': datetime(2023, 10, 23),
|
||||
'request_date_to': datetime(2023, 10, 23),
|
||||
})
|
||||
|
||||
# The leave should not be possible to edit since it would bring the balance at -6
|
||||
with self.assertRaises(ValidationError):
|
||||
one_day_leave.with_user(self.user_hrmanager_id).write({
|
||||
'date_to': datetime(2023, 10, 24),
|
||||
})
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import date, datetime, timezone
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import fields
|
||||
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
|
||||
from odoo.tests.common import tagged, users, warmup
|
||||
from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
from odoo.addons.mail.tools.discuss import Store
|
||||
|
||||
|
||||
@tagged('out_of_office')
|
||||
@tagged("post_install", "-at_install", "out_of_office")
|
||||
class TestOutOfOffice(TestHrHolidaysCommon):
|
||||
|
||||
@classmethod
|
||||
|
|
@ -19,29 +21,41 @@ class TestOutOfOffice(TestHrHolidaysCommon):
|
|||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Legal Leaves',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
|
||||
@freeze_time('2024-06-06')
|
||||
def test_leave_ooo(self):
|
||||
self.assertNotEqual(self.employee_hruser.user_id.im_status, 'leave_offline', 'user should not be on leave')
|
||||
self.assertNotEqual(self.employee_hruser.user_id.partner_id.im_status, 'leave_offline', 'user should not be on leave')
|
||||
leave_date_end = (datetime.today() + relativedelta(days=3))
|
||||
leave = self.env['hr.leave'].create({
|
||||
# validate a leave from 2024-06-05 (Wednesday) to 2024-06-07 (Friday)
|
||||
first_leave = self.env['hr.leave'].create({
|
||||
'name': 'Christmas',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'date_from': (datetime.today() - relativedelta(days=1)),
|
||||
'date_to': leave_date_end,
|
||||
'number_of_days': 4,
|
||||
'request_date_from': "2024-06-05",
|
||||
'request_date_to': "2024-06-07",
|
||||
})
|
||||
leave.action_approve()
|
||||
first_leave.action_approve()
|
||||
# validate a leave from 2024-06-10 (Monday) to 2024-06-11 (Tuesday)
|
||||
second_leave = self.env['hr.leave'].create({
|
||||
'name': 'Christmas',
|
||||
'employee_id': self.employee_hruser.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': "2024-06-10",
|
||||
'request_date_to': "2024-06-11",
|
||||
})
|
||||
second_leave.action_approve()
|
||||
# missing dependencies on compute functions
|
||||
self.employee_hruser.user_id.invalidate_recordset(["im_status"])
|
||||
self.employee_hruser.user_id.partner_id.invalidate_recordset(["im_status"])
|
||||
self.assertEqual(self.employee_hruser.user_id.im_status, 'leave_offline', 'user should be out (leave_offline)')
|
||||
self.assertEqual(self.employee_hruser.user_id.partner_id.im_status, 'leave_offline', 'user should be out (leave_offline)')
|
||||
|
||||
partner = self.employee_hruser.user_id.partner_id
|
||||
partner2 = self.user_employee.partner_id
|
||||
|
||||
channel = self.env['mail.channel'].with_user(self.user_employee).with_context({
|
||||
channel = self.env['discuss.channel'].with_user(self.user_employee).with_context({
|
||||
'mail_create_nolog': True,
|
||||
'mail_create_nosubscribe': True,
|
||||
}).create({
|
||||
|
|
@ -49,14 +63,23 @@ class TestOutOfOffice(TestHrHolidaysCommon):
|
|||
'channel_type': 'chat',
|
||||
'name': 'test'
|
||||
})
|
||||
channel_info = channel.channel_info()[0]
|
||||
# shape of channelMembers is [('insert', data...)], [0][1] accesses the data
|
||||
members_data = channel_info['channel']['channelMembers'][0][1]
|
||||
self.assertEqual(len(members_data), 2, "Channel info should get info for the 2 members")
|
||||
partner_info = next(member for member in members_data if member['persona']['partner']['email'] == partner.email)
|
||||
partner2_info = next(member for member in members_data if member['persona']['partner']['email'] == partner2.email)
|
||||
self.assertFalse(partner2_info['persona']['partner']['out_of_office_date_end'], "current user should not be out of office")
|
||||
self.assertEqual(partner_info['persona']['partner']['out_of_office_date_end'], leave_date_end.strftime(DEFAULT_SERVER_DATE_FORMAT), "correspondent should be out of office")
|
||||
data = Store().add(channel).get_result()
|
||||
partner_info = next(p for p in data["res.partner"] if p["id"] == partner.id)
|
||||
partner2_info = next(p for p in data["res.partner"] if p["id"] == partner2.id)
|
||||
user_info = next(u for u in data["res.users"] if u["id"] == partner_info["main_user_id"])
|
||||
user2_info = next(u for u in data["res.users"] if u["id"] == partner2_info["main_user_id"])
|
||||
employee_info = next(e for e in data["hr.employee"] if e["id"] == user_info["employee_ids"][0])
|
||||
employee2_info = next(e for e in data["hr.employee"] if e["id"] == user2_info["employee_ids"][0])
|
||||
self.assertFalse(employee2_info["leave_date_to"], "current user should not be out of office")
|
||||
# The employee will be back in the office the day after his second leave ends
|
||||
self.assertEqual(
|
||||
employee_info["leave_date_to"], "2024-06-12", "correspondent should be out of office"
|
||||
)
|
||||
self.assertEqual(
|
||||
self.employee_hruser.user_id.with_context(formatted_display_name=True).display_name,
|
||||
'armande (base.group_user,hr_holidays.group_hr_holidays_user) \t ✈ --Back on Jun 12, 2024--',
|
||||
'formatted display name should show the "Back on" formatted date'
|
||||
)
|
||||
|
||||
|
||||
@tagged('out_of_office')
|
||||
|
|
@ -68,16 +91,15 @@ class TestOutOfOfficePerformance(TestHrHolidaysCommon, TransactionCaseWithUserDe
|
|||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Legal Leaves',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
cls.leave_date_end = (datetime.today() + relativedelta(days=3))
|
||||
cls.leave_date_end = (datetime.today() + relativedelta(days=2))
|
||||
cls.leave = cls.env['hr.leave'].create({
|
||||
'name': 'Christmas',
|
||||
'employee_id': cls.employee_hruser_id,
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
'date_from': (datetime.today() - relativedelta(days=1)),
|
||||
'date_to': (datetime.today() + relativedelta(days=3)),
|
||||
'number_of_days': 4,
|
||||
'request_date_from': (date.today() - relativedelta(days=1)),
|
||||
'request_date_to': cls.leave_date_end,
|
||||
})
|
||||
|
||||
cls.hr_user = cls.employee_hruser.user_id
|
||||
|
|
@ -87,7 +109,7 @@ class TestOutOfOfficePerformance(TestHrHolidaysCommon, TransactionCaseWithUserDe
|
|||
@users('__system__', 'demo')
|
||||
@warmup
|
||||
def test_leave_im_status_performance_partner_offline(self):
|
||||
with self.assertQueryCount(__system__=2, demo=2):
|
||||
with self.assertQueryCount(__system__=4, demo=4):
|
||||
self.assertEqual(self.employer_partner.im_status, 'offline')
|
||||
|
||||
@users('__system__', 'demo')
|
||||
|
|
@ -101,13 +123,13 @@ class TestOutOfOfficePerformance(TestHrHolidaysCommon, TransactionCaseWithUserDe
|
|||
@warmup
|
||||
def test_leave_im_status_performance_partner_leave_offline(self):
|
||||
self.leave.write({'state': 'validate'})
|
||||
with self.assertQueryCount(__system__=2, demo=2):
|
||||
with self.assertQueryCount(__system__=4, demo=4):
|
||||
self.assertEqual(self.hr_partner.im_status, 'leave_offline')
|
||||
|
||||
def test_search_absent_employee(self):
|
||||
present_employees = self.env['hr.employee'].search([('is_absent', '!=', True)])
|
||||
absent_employees = self.env['hr.employee'].search([('is_absent', '=', True)])
|
||||
today_date = datetime.utcnow().date()
|
||||
today_date = datetime.now(timezone.utc).date()
|
||||
holidays = self.env['hr.leave'].sudo().search([
|
||||
('employee_id', '!=', False),
|
||||
('state', '=', 'validate'),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import date
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.tests import tagged
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install', 'accruals')
|
||||
class TestAccrualAllocations(TestHrHolidaysCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestAccrualAllocations, cls).setUpClass()
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Accrual Time Off',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'no',
|
||||
})
|
||||
cls.accrual_plan = cls.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
|
||||
'name': 'Test Seniority Plan',
|
||||
'level_ids': [
|
||||
(0, 0, {
|
||||
'milestone_date': 'after',
|
||||
'start_count': 1,
|
||||
'start_type': 'day',
|
||||
'added_value': 1,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 10000,
|
||||
}),
|
||||
(0, 0, {
|
||||
'milestone_date': 'after',
|
||||
'start_count': 4,
|
||||
'start_type': 'year',
|
||||
'added_value': 1,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 10000,
|
||||
}),
|
||||
(0, 0, {
|
||||
'milestone_date': 'after',
|
||||
'start_count': 8,
|
||||
'start_type': 'year',
|
||||
'added_value': 1,
|
||||
'added_value_type': 'day',
|
||||
'frequency': 'yearly',
|
||||
'cap_accrued_time': True,
|
||||
'maximum_leave': 10000,
|
||||
}),
|
||||
]
|
||||
})
|
||||
|
||||
def _test_past_accrual(self):
|
||||
with freeze_time("2023-12-01"):
|
||||
allocation = self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.employee_emp_id,
|
||||
'allocation_type': 'accrual',
|
||||
'accrual_plan_id': self.accrual_plan.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'date_from': date(2000, 1, 1),
|
||||
'number_of_days': 0,
|
||||
})
|
||||
|
||||
allocation._process_accrual_plans()
|
||||
|
||||
self.assertEqual(allocation.number_of_days, 0)
|
||||
|
|
@ -1,25 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo import Command, fields
|
||||
from odoo.tests.common import new_test_user, tagged, TransactionCase, users
|
||||
from odoo.addons.mail.tools.discuss import Store
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests.common import tagged, TransactionCase
|
||||
from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestPartner(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
@freeze_time('2024-06-04')
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
# use a single value for today throughout the tests to avoid weird scenarios around midnight
|
||||
cls.today = date.today()
|
||||
cls.today = fields.Date.today()
|
||||
baseUser = cls.env['res.users'].create({
|
||||
'email': 'e.e@example.com',
|
||||
'groups_id': [Command.link(cls.env.ref('base.group_user').id)],
|
||||
'group_ids': [Command.link(cls.env.ref('base.group_user').id)],
|
||||
'login': 'emp',
|
||||
'name': 'Ernest Employee',
|
||||
'notification_type': 'inbox',
|
||||
|
|
@ -36,32 +35,49 @@ class TestPartner(TransactionCase):
|
|||
'user_id': user.id,
|
||||
} for user in cls.users])
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'requires_allocation': 'no',
|
||||
'requires_allocation': False,
|
||||
'name': 'Legal Leaves',
|
||||
'time_type': 'leave',
|
||||
'responsible_ids': cls.users.ids
|
||||
})
|
||||
cls.leaves = cls.env['hr.leave'].create([{
|
||||
'date_from': cls.today + relativedelta(days=-2),
|
||||
'date_to': cls.today + relativedelta(days=2),
|
||||
'request_date_from': "2024-06-03",
|
||||
'request_date_to': "2024-06-06",
|
||||
'employee_id': cls.employees[0].id,
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
}, {
|
||||
'date_from': cls.today + relativedelta(days=-2),
|
||||
'date_to': cls.today + relativedelta(days=3),
|
||||
'request_date_from': "2024-06-02",
|
||||
'request_date_to': "2024-06-05",
|
||||
'employee_id': cls.employees[1].id,
|
||||
'holiday_status_id': cls.leave_type.id,
|
||||
}])
|
||||
cls.user_no_hr_access = new_test_user(
|
||||
cls.env, login="user_no_hr_access",
|
||||
)
|
||||
|
||||
def test_res_partner_mail_partner_format(self):
|
||||
@freeze_time('2024-06-04')
|
||||
def test_res_partner_to_store(self):
|
||||
self.leaves.write({'state': 'validate'})
|
||||
self.assertEqual(
|
||||
self.partner.mail_partner_format()[self.partner]['out_of_office_date_end'],
|
||||
(self.today + relativedelta(days=2)).strftime(DEFAULT_SERVER_DATE_FORMAT),
|
||||
'Return date is the first return date of all users associated with a partner',
|
||||
Store().add(self.partner).get_result()["hr.employee"][0]["leave_date_to"],
|
||||
"2024-06-07",
|
||||
"Return date is the return date of the main user of the partner",
|
||||
)
|
||||
self.leaves[1].action_refuse()
|
||||
self.leaves[0].action_refuse()
|
||||
self.assertEqual(
|
||||
self.partner.mail_partner_format()[self.partner]['out_of_office_date_end'],
|
||||
Store().add(self.partner).get_result()["hr.employee"][0]["leave_date_to"],
|
||||
False,
|
||||
'Partner is not considered out of office if one of their users is not on holiday',
|
||||
"Partner is not considered out of office if their main user is not on holiday",
|
||||
)
|
||||
|
||||
@freeze_time("2024-06-04")
|
||||
@users("user_no_hr_access")
|
||||
def test_res_partner_to_store_no_hr_access(self):
|
||||
self.leaves.write({"state": "validate"})
|
||||
data = Store().add(self.partner.with_user(self.user_no_hr_access)).get_result()
|
||||
self.assertEqual(
|
||||
data["hr.employee"][0]["leave_date_to"],
|
||||
"2024-06-07",
|
||||
"Return date is the return date of the main user of the partner, "
|
||||
"even if the user has no access to the company",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests.common import tagged
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHolidayContract
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestResourceCalendar(TestHolidayContract):
|
||||
|
||||
def test_time_off_half_day_duration(self):
|
||||
"""if working schedule is of full day period, Duration should be correct"""
|
||||
|
||||
new_calendar_40h = self.env['resource.calendar'].create({
|
||||
'name': '40h calendar new',
|
||||
'attendance_ids': [
|
||||
Command.create({
|
||||
'name': 'Monday full day',
|
||||
'dayofweek': '0',
|
||||
'hour_from': 10,
|
||||
'hour_to': 18,
|
||||
'day_period': 'full_day',
|
||||
}),
|
||||
Command.create({
|
||||
'name': 'Tuesday full day',
|
||||
'dayofweek': '1',
|
||||
'hour_from': 10,
|
||||
'hour_to': 18,
|
||||
'day_period': 'full_day',
|
||||
}),
|
||||
],
|
||||
})
|
||||
self.jules_emp.version_id.resource_calendar_id = new_calendar_40h.id
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Test half day type',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'no_validation',
|
||||
'request_unit': 'half_day',
|
||||
})
|
||||
|
||||
leave_morning, leave_afternoon, leave_one_and_half = self.env['hr.leave'].create([
|
||||
{
|
||||
'name': 'Half Day Leave(morning)',
|
||||
'employee_id': self.jules_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': date(2025, 4, 21),
|
||||
'request_date_to': date(2025, 4, 21),
|
||||
'request_date_from_period': 'am',
|
||||
'request_date_to_period': 'am',
|
||||
},
|
||||
{
|
||||
'name': 'Half Day Leave(afternoon)',
|
||||
'employee_id': self.jules_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': date(2025, 4, 22),
|
||||
'request_date_to': date(2025, 4, 22),
|
||||
'request_date_from_period': 'pm',
|
||||
'request_date_to_period': 'pm',
|
||||
},
|
||||
{
|
||||
'name': 'One and half Day Leave',
|
||||
'employee_id': self.jules_emp.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'request_date_from': date(2025, 4, 14),
|
||||
'request_date_to': date(2025, 4, 15),
|
||||
'request_date_from_period': 'am',
|
||||
'request_date_to_period': 'am',
|
||||
},
|
||||
])
|
||||
self.assertEqual(leave_morning.number_of_days, 0.5)
|
||||
self.assertEqual(leave_afternoon.number_of_days, 0.5)
|
||||
self.assertEqual(leave_one_and_half.number_of_days, 1.5)
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import tests
|
||||
from odoo.tests import new_test_user
|
||||
from odoo.tests.common import Form, TransactionCase
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
@tests.tagged('access_rights', 'post_install', '-at_install')
|
||||
class TestHrLeaveStressDays(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.default_calendar = cls.env['resource.calendar'].create({
|
||||
'name': 'moon calendar',
|
||||
})
|
||||
|
||||
cls.company = cls.env['res.company'].create({
|
||||
'name': 'super company',
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
|
||||
cls.employee_user = new_test_user(cls.env, login='user', groups='base.group_user', company_ids=[(6, 0, cls.company.ids)], company_id=cls.company.id)
|
||||
cls.manager_user = new_test_user(cls.env, login='manager', groups='base.group_user,hr_holidays.group_hr_holidays_manager', company_ids=[(6, 0, cls.company.ids)], company_id=cls.company.id)
|
||||
|
||||
cls.employee_emp = cls.env['hr.employee'].create({
|
||||
'name': 'Toto Employee',
|
||||
'company_id': cls.company.id,
|
||||
'user_id': cls.employee_user.id,
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
cls.manager_emp = cls.env['hr.employee'].create({
|
||||
'name': 'Toto Mananger',
|
||||
'company_id': cls.company.id,
|
||||
'user_id': cls.manager_user.id,
|
||||
})
|
||||
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Unlimited',
|
||||
'leave_validation_type': 'hr',
|
||||
'requires_allocation': 'no',
|
||||
'company_id': cls.company.id,
|
||||
})
|
||||
|
||||
cls.stress_day = cls.env['hr.leave.stress.day'].create({
|
||||
'name': 'Super Event',
|
||||
'company_id': cls.company.id,
|
||||
'start_date': datetime(2021, 11, 2),
|
||||
'end_date': datetime(2021, 11, 2),
|
||||
'color': 1,
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
cls.stress_week = cls.env['hr.leave.stress.day'].create({
|
||||
'name': 'Super Event End Of Week',
|
||||
'company_id': cls.company.id,
|
||||
'start_date': datetime(2021, 11, 8),
|
||||
'end_date': datetime(2021, 11, 12),
|
||||
'color': 2,
|
||||
'resource_calendar_id': cls.default_calendar.id,
|
||||
})
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_request_stress_days(self):
|
||||
# An employee can request time off outside stress days
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 3),
|
||||
'date_to': datetime(2021, 11, 3),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
|
||||
# Taking a time off during a Stress Day is not allowed for a simple employee...
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 3),
|
||||
'date_to': datetime(2021, 11, 17),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 9),
|
||||
'date_to': datetime(2021, 11, 9),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
|
||||
# ... but is allowed for a Time Off Officer
|
||||
self.env['hr.leave'].with_user(self.manager_user.id).create({
|
||||
'name': 'coucou',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 2),
|
||||
'date_to': datetime(2021, 11, 2),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_get_stress_days(self):
|
||||
stress_days = self.employee_emp.get_stress_days('2021-11-01', '2021-11-30')
|
||||
|
||||
# Stress Days spanning multiple days should be split in single days
|
||||
expected_data = {'2021-11-02': 1, '2021-11-08': 2, '2021-11-09': 2, '2021-11-10': 2, '2021-11-11': 2, '2021-11-12': 2}
|
||||
|
||||
self.assertEqual(len(stress_days), len(expected_data))
|
||||
for day, color in expected_data.items():
|
||||
self.assertTrue(day in stress_days)
|
||||
self.assertEqual(color, stress_days[day])
|
||||
|
||||
with self.assertRaises(ValidationError), Form(self.env['hr.leave'].with_user(self.employee_user.id).with_context(default_employee_id=self.employee_emp.id)) as leave_form:
|
||||
leave_form.holiday_status_id = self.leave_type
|
||||
leave_form.request_date_from = datetime(2021, 11, 1)
|
||||
leave_form.request_date_to = datetime(2021, 11, 1)
|
||||
self.assertFalse(leave_form.has_stress_day)
|
||||
|
||||
leave_form.request_date_to = datetime(2021, 11, 5)
|
||||
self.assertTrue(leave_form.has_stress_day)
|
||||
|
||||
@freeze_time('2021-10-15')
|
||||
def test_department_stress_days(self):
|
||||
production_department = self.env['hr.department'].create({
|
||||
'name': 'Production Department',
|
||||
'company_id': self.company.id,
|
||||
})
|
||||
post_production_department = self.env['hr.department'].create({
|
||||
'name': 'Post-Production Department',
|
||||
'company_id': self.company.id,
|
||||
'parent_id': production_department.id,
|
||||
})
|
||||
deployment_department = self.env['hr.department'].create({
|
||||
'name': 'Deployment Department',
|
||||
'company_id': self.company.id,
|
||||
'parent_id': production_department.id,
|
||||
})
|
||||
|
||||
self.employee_emp.write({
|
||||
'department_id': post_production_department.id
|
||||
})
|
||||
|
||||
# Create one stress day for each department
|
||||
self.env['hr.leave.stress.day'].create({
|
||||
'name': 'Last Rush Before Launch (production)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 3),
|
||||
'end_date': datetime(2021, 11, 3),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [production_department.id],
|
||||
})
|
||||
self.env['hr.leave.stress.day'].create({
|
||||
'name': 'Last Rush Before Launch (post-production)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 4),
|
||||
'end_date': datetime(2021, 11, 4),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [post_production_department.id],
|
||||
})
|
||||
self.env['hr.leave.stress.day'].create({
|
||||
'name': 'Last Rush Before Launch (deployment)',
|
||||
'company_id': self.company.id,
|
||||
'start_date': datetime(2021, 11, 5),
|
||||
'end_date': datetime(2021, 11, 5),
|
||||
'color': 1,
|
||||
'resource_calendar_id': self.default_calendar.id,
|
||||
'department_ids': [deployment_department.id],
|
||||
})
|
||||
|
||||
# The employee should only be able to create a time off on stress days
|
||||
# that do not include his department
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 3),
|
||||
'date_to': datetime(2021, 11, 3),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 4),
|
||||
'date_to': datetime(2021, 11, 4),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
self.env['hr.leave'].with_user(self.employee_user.id).create({
|
||||
'name': 'have been given the black spot',
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'employee_id': self.employee_emp.id,
|
||||
'date_from': datetime(2021, 11, 5),
|
||||
'date_to': datetime(2021, 11, 5),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from odoo.tests import HttpCase, tagged
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestTimeOffAllocationTour(HttpCase):
|
||||
|
||||
def test_time_off_allocation_warning_tour(self):
|
||||
self.start_tour("/", "time_off_allocation_warning_tour", login="admin")
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from odoo.tests import HttpCase, tagged, users
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestTimeOffCardTour(HttpCase):
|
||||
|
||||
@users('admin')
|
||||
def test_time_off_card_tour(self):
|
||||
leave_type = self.env['hr.leave.type'].create({
|
||||
'name': 'Time Off with no validation for approval',
|
||||
'time_type': 'leave',
|
||||
'requires_allocation': True,
|
||||
'allocation_validation_type': 'no_validation',
|
||||
})
|
||||
self.env['hr.leave.allocation'].create({
|
||||
'employee_id': self.env.user.employee_id.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'allocation_type': 'regular',
|
||||
'type_request_unit': 'half_day',
|
||||
})
|
||||
self.start_tour('/', 'time_off_card_tour', login='admin')
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from odoo.tests import HttpCase, tagged, users
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestTimeOffGraphViewTour(HttpCase):
|
||||
|
||||
@users('admin')
|
||||
def test_time_off_graph_view_tour(self):
|
||||
self.start_tour('/', 'time_off_graph_view_tour', login='admin')
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
|
||||
|
||||
|
||||
class TestTimeoffEvent(TestHrHolidaysCommon):
|
||||
|
||||
def test_no_videocall_url_in_timeoff_event(self):
|
||||
""" Test that the timeoff event does not need a video call """
|
||||
|
||||
self.hr_leave_type = self.env['hr.leave.type'].with_user(self.user_hrmanager).create({
|
||||
'name': 'Time Off Type',
|
||||
'requires_allocation': False,
|
||||
})
|
||||
self.holiday = self.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True).with_user(self.user_employee).create({
|
||||
'name': 'Time Off 1 sura',
|
||||
'employee_id': self.employee_emp.id,
|
||||
'holiday_status_id': self.hr_leave_type.id,
|
||||
'request_date_from': datetime(2020, 1, 15),
|
||||
'request_date_to': datetime(2020, 1, 15) + relativedelta(days=1),
|
||||
})
|
||||
self.holiday.with_user(self.user_hrmanager).action_approve()
|
||||
|
||||
# Finding the event corresponding to the leave
|
||||
search_criteria = [
|
||||
('name', 'like', self.holiday.employee_id.name),
|
||||
('start_date', '>=', self.holiday.request_date_from),
|
||||
('stop_date', '<=', self.holiday.request_date_to),
|
||||
]
|
||||
timeoff_event = self.env['calendar.event'].search(search_criteria)
|
||||
self.assertTrue(timeoff_event, "The timeoff event should exist")
|
||||
self.assertFalse(timeoff_event._need_video_call(), "The timeoff event does not need a video call")
|
||||
|
|
@ -15,7 +15,7 @@ class TestHrLeaveUninstall(TransactionCase):
|
|||
holiday = self.env['hr.leave'].create({
|
||||
'name': 'Time Off',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': self.env.ref('hr_holidays.holiday_status_sl').id,
|
||||
'holiday_status_id': self.env.ref('hr_holidays.leave_type_sick_time_off').id,
|
||||
'request_date_from': date(2020, 1, 7),
|
||||
'date_from': date(2020, 1, 7),
|
||||
'request_date_to': date(2020, 1, 9),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, datetime
|
||||
from odoo.addons.hr_calendar.tests.common import TestHrCalendarCommon
|
||||
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('work_hours')
|
||||
class TestWorkingHours(TestHrCalendarCommon):
|
||||
""" Test global leaves for a whole company, conflict resolutions """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
# YTI TODO: Those tests seem to be never launched from now.
|
||||
if 'hr.version' in cls.env:
|
||||
cls.skipTest(cls,
|
||||
"hr_contract module is installed. To test these features you need to install hr_holidays_contract"
|
||||
)
|
||||
|
||||
cls.leave_type = cls.env['hr.leave.type'].create({
|
||||
'name': 'Unpaid Time Off',
|
||||
'requires_allocation': False,
|
||||
'leave_validation_type': 'no_validation',
|
||||
})
|
||||
|
||||
def test_multi_companies_2_employees_2_selected_companies_holidays(self):
|
||||
"""
|
||||
INPUT:
|
||||
======
|
||||
Employees Companies
|
||||
employee A company A ---> 35h [X] A <- main company
|
||||
employee A company B ---> 28h [X] B
|
||||
employee A company A take a day off for monday and tuesday.
|
||||
OUTPUT:
|
||||
=======
|
||||
The schedule will be : off on monday, following 28h schedule on tuesday and the union for the rest of the week.
|
||||
"""
|
||||
self.env.user.company_id = self.company_A
|
||||
self.env.user.company_ids = [self.company_A.id, self.company_B.id]
|
||||
|
||||
self.env['hr.leave'].create({
|
||||
'name': 'holiday from monday to tuesday',
|
||||
'employee_id': self.employeeA.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'request_date_from': datetime(2023, 12, 25),
|
||||
'request_date_to': datetime(2023, 12, 26, 23, 59, 59),
|
||||
})
|
||||
|
||||
work_hours = self.env['res.partner'].get_working_hours_for_all_attendees(
|
||||
[self.partnerA.id],
|
||||
datetime(2023, 12, 25).isoformat(),
|
||||
datetime(2023, 12, 31).isoformat()
|
||||
)
|
||||
self.assertEqual(work_hours, [
|
||||
{'daysOfWeek': [2], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [2], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [3], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [3], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [4], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [4], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [5], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [5], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
])
|
||||
|
||||
def test_multi_companies_2_employees_2_selected_companies_company_holidays(self):
|
||||
"""
|
||||
INPUT:
|
||||
======
|
||||
Employees Companies
|
||||
employee A company A ---> 35h [X] A <- main company
|
||||
employee A company B ---> 28h [X] B
|
||||
Company A give a day off for everyone on monday and tuesday.
|
||||
OUTPUT:
|
||||
=======
|
||||
The schedule will be : off on monday, following 28h schedule on tuesday and the union for the rest of the week.
|
||||
"""
|
||||
self.env.user.company_id = self.company_A
|
||||
self.env.user.company_ids = [self.company_A.id, self.company_B.id]
|
||||
|
||||
company_leave = self.env['hr.leave.generate.multi.wizard'].create({
|
||||
'name': 'holiday from monday to tuesday',
|
||||
'allocation_mode': 'company',
|
||||
'company_id': self.company_A.id,
|
||||
'holiday_status_id': self.leave_type.id,
|
||||
'date_from': date(2023, 12, 25),
|
||||
'date_to': date(2023, 12, 26),
|
||||
})
|
||||
company_leave.action_generate_time_off()
|
||||
|
||||
work_hours = self.env['res.partner'].get_working_hours_for_all_attendees(
|
||||
[self.partnerA.id],
|
||||
datetime(2023, 12, 25).isoformat(),
|
||||
datetime(2023, 12, 31).isoformat()
|
||||
)
|
||||
self.assertEqual(work_hours, [
|
||||
{'daysOfWeek': [2], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [2], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [3], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [3], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [4], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [4], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [5], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [5], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
])
|
||||
|
||||
def test_multi_companies_2_employees_2_selected_companies_global_holidays(self):
|
||||
"""
|
||||
INPUT:
|
||||
======
|
||||
Employees Companies
|
||||
employee A company A ---> 35h [X] A <- main company
|
||||
employee A company B ---> 28h [X] B
|
||||
Global leave for calendar 35h on monday and tuesday.
|
||||
OUTPUT:
|
||||
=======
|
||||
The schedule will be : off on monday, following 28h schedule on tuesday and the union for the rest of the week.
|
||||
"""
|
||||
self.env.user.company_id = self.company_A
|
||||
self.env.user.company_ids = [self.company_A.id, self.company_B.id]
|
||||
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': 'Global Time Off',
|
||||
'date_from': datetime(2023, 12, 25),
|
||||
'date_to': datetime(2023, 12, 26, 23, 59, 59),
|
||||
'calendar_id': self.calendar_35h.id,
|
||||
})
|
||||
|
||||
work_hours = self.env['res.partner'].get_working_hours_for_all_attendees(
|
||||
[self.partnerA.id],
|
||||
datetime(2023, 12, 25).isoformat(),
|
||||
datetime(2023, 12, 31).isoformat()
|
||||
)
|
||||
self.assertEqual(work_hours, [
|
||||
{'daysOfWeek': [2], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [2], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [3], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [3], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [4], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [4], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
{'daysOfWeek': [5], 'startTime': '08:00', 'endTime': '12:00'},
|
||||
{'daysOfWeek': [5], 'startTime': '13:00', 'endTime': '16:00'},
|
||||
])
|
||||
Loading…
Add table
Add a link
Reference in a new issue