# -*- 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.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.ir.ir_model', 'odoo.models') def test_00_leave_request_flow(self): """ Testing leave request flow """ Holidays = self.env['hr.holidays'] HolidaysStatus = self.env['hr.holidays.status'] 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': 'NotLimited', 'limit': True, }) self.holidays_status_2 = HolidayStatusManagerGroup.create({ 'name': 'Limited', 'limit': False, 'double_validation': True, }) # -------------------------------------------------- # Case1: unlimited type of leave request # -------------------------------------------------- # Employee creates a leave request for another employee -> should crash HolidaysEmployeeGroup = Holidays.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, }) Holidays.search([('name', '=', 'Hol10')]).unlink() # Employee creates a leave request in a no-limit category 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) 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_user_group.state, 'confirm', 'hr_holidays: employee should not be able to validate its own leave request') # HrUser validates the employee leave request hol1_user_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=0)).strftime('%Y-%m-%d %H:%M'), 'date_to': (datetime.today() + relativedelta(days=1)), 'number_of_days_temp': 1, }) # Clean transaction Holidays.search([('name', 'in', ['Hol21', 'Hol22'])]).unlink() # HrUser allocates some leaves to the employee aloc1_user_group = Holidays.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, 'type': 'add', 'number_of_days_temp': 2, }) # HrUser validates the first step aloc1_user_group.action_approve() # 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 _check_holidays_status(hol_status_2_employee_group, 2.0, 0.0, 2.0, 1.0) # HrUser validates the first step hol2_user_group.action_approve() self.assertEqual(hol2.state, 'validate1', 'hr_holidays: first validation should lead to validate1 state') # 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 = Holidays.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, 'type': 'remove', '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) 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')