As we have two parallel tasks changing the rights for the approval of leaves, we have to make sure that it stays coherent. Basically by default an hr_holiday manager can approve anything and the hr_holiday officer can only approve the leaves of his department and cannot approve his own leaves. But you can override this behaviour by setting the Validation By to "Manager" which will force the leave to be approved by an hr_holiday manager.
378 lines
18 KiB
Python
378 lines
18 KiB
Python
# -*- 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 odoo import fields
|
|
from odoo.exceptions import AccessError, ValidationError, UserError
|
|
from odoo.tools import mute_logger, test_reports
|
|
|
|
from odoo.addons.hr_holidays.tests.common import TestHrHolidaysBase
|
|
|
|
|
|
class TestHolidaysFlow(TestHrHolidaysBase):
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
|
def test_00_leave_request_flow(self):
|
|
""" Testing leave request flow """
|
|
Requests = self.env['hr.leave']
|
|
Allocations = self.env['hr.leave.allocation']
|
|
HolidaysStatus = self.env['hr.leave.type']
|
|
|
|
def _check_holidays_status(holiday_status, ml, lt, rl, vrl):
|
|
self.assertEqual(holiday_status.max_leaves, ml,
|
|
'hr_holidays: wrong type days computation')
|
|
self.assertEqual(holiday_status.leaves_taken, lt,
|
|
'hr_holidays: wrong type days computation')
|
|
self.assertEqual(holiday_status.remaining_leaves, rl,
|
|
'hr_holidays: wrong type days computation')
|
|
self.assertEqual(holiday_status.virtual_remaining_leaves, vrl,
|
|
'hr_holidays: wrong type days computation')
|
|
|
|
# HrUser creates some holiday statuses -> crash because only HrManagers should do this
|
|
with self.assertRaises(AccessError):
|
|
HolidaysStatus.sudo(self.user_hruser_id).create({
|
|
'name': 'UserCheats',
|
|
'limit': True,
|
|
})
|
|
|
|
# HrManager creates some holiday statuses
|
|
HolidayStatusManagerGroup = HolidaysStatus.sudo(self.user_hrmanager_id)
|
|
HolidayStatusManagerGroup.create({
|
|
'name': 'WithMeetingType',
|
|
'limit': True,
|
|
'categ_id': self.env['calendar.event.type'].sudo(self.user_hrmanager_id).create({'name': 'NotLimitedMeetingType'}).id
|
|
})
|
|
self.holidays_status_1 = HolidayStatusManagerGroup.create({
|
|
'name': 'NotLimitedHR',
|
|
'limit': True,
|
|
'validation_type': 'hr',
|
|
})
|
|
self.holidays_status_2 = HolidayStatusManagerGroup.create({
|
|
'name': 'Limited',
|
|
'limit': False,
|
|
'validation_type': 'both',
|
|
})
|
|
self.holidays_status_3 = HolidayStatusManagerGroup.create({
|
|
'name': 'NotLimitedManager',
|
|
'limit': True,
|
|
'validation_type': 'manager',
|
|
})
|
|
self.holiday_status_4 = HolidayStatusManagerGroup.create({
|
|
'name': 'TimeNotLimited',
|
|
'limit': True,
|
|
'validation_type': 'manager',
|
|
'validity_start': fields.Datetime.from_string('2017-01-01 00:00:00'),
|
|
'validity_stop': fields.Datetime.from_string('2017-06-01 00:00:00'),
|
|
})
|
|
|
|
# --------------------------------------------------
|
|
# Case1: unlimited type of leave request
|
|
# --------------------------------------------------
|
|
|
|
# Employee creates a leave request for another employee -> should crash
|
|
HolidaysEmployeeGroup = Requests.sudo(self.user_employee_id)
|
|
with self.assertRaises(ValidationError):
|
|
HolidaysEmployeeGroup.create({
|
|
'name': 'Hol10',
|
|
'employee_id': self.employee_hruser_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() - relativedelta(days=1)),
|
|
'date_to': datetime.today(),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
Requests.search([('name', '=', 'Hol10')]).unlink()
|
|
|
|
# Employee creates a leave request in a no-limit category hr manager only
|
|
hol1_employee_group = HolidaysEmployeeGroup.create({
|
|
'name': 'Hol11',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() - relativedelta(days=1)),
|
|
'date_to': datetime.today(),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
hol1_user_group = hol1_employee_group.sudo(self.user_hruser_id)
|
|
hol1_manager_group = hol1_employee_group.sudo(self.user_hrmanager_id)
|
|
self.assertEqual(hol1_user_group.state, 'confirm', 'hr_holidays: newly created leave request should be in confirm state')
|
|
|
|
# Employee validates its leave request -> should not work
|
|
with self.assertRaises(UserError):
|
|
hol1_employee_group.action_approve()
|
|
self.assertEqual(hol1_manager_group.state, 'confirm', 'hr_holidays: employee should not be able to validate its own leave request')
|
|
|
|
# HrUser validates the employee leave request -> should work
|
|
hol1_user_group.action_approve()
|
|
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
|
|
hol12_employee_group = HolidaysEmployeeGroup.create({
|
|
'name': 'Hol12',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_status_3.id,
|
|
'date_from': (datetime.today() + relativedelta(days=12)),
|
|
'date_to': (datetime.today() + relativedelta(days=13)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
hol12_user_group = hol12_employee_group.sudo(self.user_hruser_id)
|
|
hol12_manager_group = hol12_employee_group.sudo(self.user_hrmanager_id)
|
|
self.assertEqual(hol12_user_group.state, 'confirm', 'hr_holidays: newly created leave request should be in confirm state')
|
|
|
|
# Employee validates its leave request -> should not work
|
|
with self.assertRaises(UserError):
|
|
hol12_employee_group.action_approve()
|
|
self.assertEqual(hol12_user_group.state, 'confirm', 'hr_holidays: employee should not be able to validate its own leave request')
|
|
|
|
# HrUser validates the employee leave request -> should not work
|
|
with self.assertRaises(UserError):
|
|
hol12_user_group.action_approve()
|
|
|
|
# HrManager validate the employee leave request
|
|
hol12_manager_group.action_approve()
|
|
self.assertEqual(hol1_user_group.state, 'validate', 'hr_holidays: validates leave request should be in validate state')
|
|
|
|
# --------------------------------------------------
|
|
# Case2: limited type of leave request
|
|
# --------------------------------------------------
|
|
|
|
# Employee creates a new leave request at the same time -> crash, avoid interlapping
|
|
with self.assertRaises(ValidationError):
|
|
HolidaysEmployeeGroup.create({
|
|
'name': 'Hol21',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': datetime.today(),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
# Employee creates a leave request in a limited category -> crash, not enough days left
|
|
with self.assertRaises(ValidationError):
|
|
HolidaysEmployeeGroup.create({
|
|
'name': 'Hol22',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_status_2.id,
|
|
'date_from': (datetime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=2)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
# Clean transaction
|
|
Requests.search([('name', 'in', ['Hol21', 'Hol22'])]).unlink()
|
|
|
|
# HrUser allocates some leaves to the employee
|
|
aloc1_user_group = Allocations.sudo(self.user_hruser_id).create({
|
|
'name': 'Days for limited category',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_status_2.id,
|
|
'number_of_days_temp': 2,
|
|
})
|
|
# HrUser validates the first step
|
|
aloc1_user_group.action_approve()
|
|
# HrUser validates the second step -> should crash
|
|
with self.assertRaises(UserError):
|
|
aloc1_user_group.action_validate()
|
|
# HrManager validates the second step
|
|
aloc1_user_group.sudo(self.user_hrmanager_id).action_validate()
|
|
# Checks Employee has effectively some days left
|
|
hol_status_2_employee_group = self.holidays_status_2.sudo(self.user_employee_id)
|
|
_check_holidays_status(hol_status_2_employee_group, 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_2.id,
|
|
'date_from': (datetime.today() + relativedelta(days=2)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=3)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
hol2_user_group = hol2.sudo(self.user_hruser_id)
|
|
# Check left days: - 1 virtual remaining day
|
|
hol_status_2_employee_group.invalidate_cache()
|
|
_check_holidays_status(hol_status_2_employee_group, 2.0, 0.0, 2.0, 1.0)
|
|
|
|
# HrManager validates the first step
|
|
hol2_user_group.sudo(self.user_hrmanager_id).action_approve()
|
|
self.assertEqual(hol2.state, 'validate1',
|
|
'hr_holidays: first validation should lead to validate1 state')
|
|
|
|
# HrUser validates the second step -> should crash
|
|
with self.assertRaises(UserError):
|
|
hol2_user_group.action_validate()
|
|
# HrManager validates the second step
|
|
hol2_user_group.sudo(self.user_hrmanager_id).action_validate()
|
|
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)
|
|
|
|
# HrManager finds an error: he refuses the leave request
|
|
hol2.sudo(self.user_hrmanager_id).action_refuse()
|
|
self.assertEqual(hol2.state, 'refuse',
|
|
'hr_holidays: refuse should lead to refuse state')
|
|
# Check left days: 2 days left again
|
|
_check_holidays_status(hol_status_2_employee_group, 2.0, 0.0, 2.0, 2.0)
|
|
|
|
# Annoyed, HrUser tries to fix its error and tries to reset the leave request -> does not work, only HrManager
|
|
with self.assertRaises(UserError):
|
|
hol2_user_group.action_draft()
|
|
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.sudo(self.user_hrmanager_id)
|
|
hol2_manager_group.action_draft()
|
|
self.assertEqual(hol2.state, 'draft',
|
|
'hr_holidays: resetting should lead to draft state')
|
|
|
|
# HrManager changes the date and put too much days -> crash when confirming
|
|
hol2_manager_group.write({
|
|
'date_from': (datetime.today() + relativedelta(days=4)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=7)),
|
|
'number_of_days_temp': 4,
|
|
})
|
|
with self.assertRaises(ValidationError):
|
|
hol2_manager_group.action_confirm()
|
|
|
|
employee_id = self.ref('hr.employee_root')
|
|
# cl can be of maximum 20 days for employee_root
|
|
hol3_status = self.env.ref('hr_holidays.holiday_status_cl').with_context(employee_id=employee_id)
|
|
# I assign the dates in the holiday request for 1 day
|
|
hol3 = Requests.create({
|
|
'name': 'Sick Leave',
|
|
'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'),
|
|
'employee_id': employee_id,
|
|
'number_of_days_temp': 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_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)
|
|
|
|
Requests.create({
|
|
'name': 'Sick Leave',
|
|
'holiday_status_id': self.holiday_status_4.id,
|
|
'date_from': fields.Datetime.from_string('2017-03-03 06:00:00'),
|
|
'date_to': fields.Datetime.from_string('2017-03-11 19:00:00'),
|
|
'employee_id': employee_id,
|
|
'number_of_days_temp': 1,
|
|
}).unlink()
|
|
|
|
with self.assertRaises(ValidationError):
|
|
Requests.create({
|
|
'name': 'Sick Leave',
|
|
'holiday_status_id': self.holiday_status_4.id,
|
|
'date_from': fields.Datetime.from_string('2017-07-03 06:00:00'),
|
|
'date_to': fields.Datetime.from_string('2017-07-11 19:00:00'),
|
|
'employee_id': employee_id,
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
hol41 = HolidaysEmployeeGroup.create({
|
|
'name': 'Hol41',
|
|
'employee_id': self.employee_emp_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() + relativedelta(days=9)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=10)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
# A simple user should be able to reset it's own leave
|
|
hol41.action_draft()
|
|
hol41.unlink()
|
|
|
|
hol42 = Requests.sudo(self.user_hrmanager_id).create({
|
|
'name': 'Hol41',
|
|
'employee_id': self.employee_hrmanager_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() + relativedelta(days=9)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=10)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
# But not someone else's leave
|
|
with self.assertRaises(AccessError):
|
|
hol42.sudo(self.user_employee_id).action_draft()
|
|
|
|
# An officer should not be able to reset someone else's leave
|
|
with self.assertRaises(UserError):
|
|
hol42.sudo(self.user_hruser_id).action_draft()
|
|
|
|
# A manager should be able to reset someone else's leave
|
|
hol42.action_draft()
|
|
hol42.unlink()
|
|
|
|
# Officer should not be able to approve it's own leave
|
|
hol50 = HolidaysEmployeeGroup.sudo(self.user_hruser_id).create({
|
|
'name': 'Hol50',
|
|
'employee_id': self.employee_hruser_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() + relativedelta(days=15)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=16)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
with self.assertRaises(UserError):
|
|
hol50.action_approve()
|
|
|
|
# Manager should be able to approve it's own leave
|
|
hol51 = HolidaysEmployeeGroup.sudo(self.user_hrmanager_2_id).create({
|
|
'name': 'Hol51',
|
|
'employee_id': self.employee_hrmanager_2_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() + relativedelta(days=15)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=16)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
hol51.action_approve()
|
|
|
|
# Unless there is not manager above
|
|
hol52 = HolidaysEmployeeGroup.sudo(self.user_hrmanager_id).create({
|
|
'name': 'Hol52',
|
|
'employee_id': self.employee_hrmanager_id,
|
|
'holiday_status_id': self.holidays_status_1.id,
|
|
'date_from': (datetime.today() + relativedelta(days=15)).strftime('%Y-%m-%d %H:%M'),
|
|
'date_to': (datetime.today() + relativedelta(days=16)),
|
|
'number_of_days_temp': 1,
|
|
})
|
|
|
|
hol52.action_approve()
|
|
|
|
def test_10_leave_summary_reports(self):
|
|
# Print the HR Holidays(Summary Department) Report through the wizard
|
|
ctx = {
|
|
'model': 'hr.department',
|
|
'active_ids': [self.ref('hr.employee_root'), self.ref('hr.employee_qdp'), self.ref('hr.employee_al')]
|
|
}
|
|
data_dict = {
|
|
'date_from': datetime.today().strftime('%Y-%m-01'),
|
|
'depts': [(6, 0, [self.ref('hr.dep_sales')])],
|
|
'holiday_type': 'Approved'
|
|
}
|
|
test_reports.try_report_action(self.env.cr, self.env.uid, 'action_hr_holidays_summary_dept', wiz_data=data_dict, context=ctx, our_module='hr_holidays')
|
|
|
|
# Print the HR Holidays(Summary Employee) Report through the wizard
|
|
ctx = {
|
|
'model': 'hr.employee',
|
|
'active_ids': [self.ref('hr.employee_root'), self.ref('hr.employee_qdp'), self.ref('hr.employee_al')]
|
|
}
|
|
data_dict = {
|
|
'date_from': datetime.today().strftime('%Y-%m-01'),
|
|
'emp': [(6, 0, [self.ref('hr.employee_root'), self.ref('hr.employee_qdp'), self.ref('hr.employee_al')])],
|
|
'holiday_type': 'Approved'
|
|
}
|
|
test_reports.try_report_action(self.env.cr, self.env.uid, 'action_hr_holidays_summary_employee', wiz_data=data_dict, context=ctx, our_module='hr_holidays')
|