Files
odoo_source/addons/hr_holidays/tests/test_holidays_flow.py
T
Lucas Lefèvre 8f3a5068ca [IMP] hr_holidays: Add specific approval for allocations
Purpose
=======

We want to allow different approvals for allocations and leaves of a same type.

e.g. In case of compensation days, a manager would approve the
allocation request, but would later not approve leave requests.

Specification
=============

Allocation modes
----------------
No change here except renames.
1. No limit (employee doesn't need an allocation to request a leave)
2. Allow employee requests
3. Fixed by Time Off Officer

Allocation approval
-------------------
A radio field to choose the approval mechanism
appears if the employee is allowed to request an allocation.
The following approval modes are possible:
1. Time Off Officer
2. Employee's Time Off Manager (leave_responsible_id)
3. Employee's Time Off Manager and Time Off Officer

These options are the same as leave approval options
(except the missing "No Validation").

If option 1 or 2 is selected, a o2m field should
allow to select which Time Off Officer is responsible
for approving those allocations.

Allocation approval policy is the same as the leave approval policy.

Task id: 1936742

Access rights based on task 1950998
2019-11-22 16:58:38 +01:00

289 lines
13 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
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.addons.hr_holidays.tests.common import TestHrHolidaysCommon
class TestHolidaysFlow(TestHrHolidaysCommon):
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
def test_00_leave_request_flow_unlimited(self):
""" Testing leave request flow: unlimited type of leave request """
Requests = self.env['hr.leave']
HolidaysStatus = self.env['hr.leave.type']
# HrManager creates some holiday statuses
HolidayStatusManagerGroup = HolidaysStatus.with_user(self.user_hrmanager_id)
HolidayStatusManagerGroup.create({
'name': 'WithMeetingType',
'allocation_type': 'no',
})
self.holidays_status_hr = HolidayStatusManagerGroup.create({
'name': 'NotLimitedHR',
'allocation_type': 'no',
'leave_validation_type': 'hr',
'validity_start': False,
})
self.holidays_status_manager = HolidayStatusManagerGroup.create({
'name': 'NotLimitedManager',
'allocation_type': 'no',
'leave_validation_type': 'manager',
'validity_start': False,
})
HolidaysEmployeeGroup = Requests.with_user(self.user_employee_id)
# 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_hr.id,
'date_from': (datetime.today() - relativedelta(days=1)),
'date_to': datetime.today(),
'number_of_days': 1,
})
hol1_user_group = hol1_employee_group.with_user(self.user_hruser_id)
hol1_manager_group = hol1_employee_group.with_user(self.user_hrmanager_id)
self.assertEqual(hol1_user_group.state, 'confirm', 'hr_holidays: newly created leave request should be in confirm state')
# 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_manager.id,
'date_from': (datetime.today() + relativedelta(days=12)),
'date_to': (datetime.today() + relativedelta(days=13)),
'number_of_days': 1,
})
hol12_user_group = hol12_employee_group.with_user(self.user_hruser_id)
hol12_manager_group = hol12_employee_group.with_user(self.user_hrmanager_id)
self.assertEqual(hol12_user_group.state, 'confirm', 'hr_holidays: newly created leave request should be in confirm state')
# 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')
@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 """
Requests = self.env['hr.leave']
Allocations = self.env['hr.leave.allocation']
HolidaysStatus = self.env['hr.leave.type']
holiday_status_paid_time_off = self.env['hr.leave.type'].create({
'name': 'Paid Time Off',
'allocation_type': 'fixed',
'leave_validation_type': 'both',
'validity_start': time.strftime('%Y-%m-01'),
'responsible_id': 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': 'validate',
}, {
'name': 'Paid Time off for David',
'holiday_status_id': holiday_status_paid_time_off.id,
'number_of_days': 20,
'employee_id': self.ref('hr.employee_admin'),
'state': 'validate',
}
])
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')
# HrManager creates some holiday statuses
HolidayStatusManagerGroup = HolidaysStatus.with_user(self.user_hrmanager_id)
HolidayStatusManagerGroup.create({
'name': 'WithMeetingType',
'allocation_type': 'no',
'validity_start': False,
})
self.holidays_status_limited = HolidayStatusManagerGroup.create({
'name': 'Limited',
'allocation_type': 'fixed',
'allocation_validation_type': 'both',
'leave_validation_type': 'both',
'validity_start': False,
})
HolidaysEmployeeGroup = Requests.with_user(self.user_employee_id)
# HrUser allocates some leaves to the employee
aloc1_user_group = Allocations.with_user(self.user_hruser_id).create({
'name': 'Days for limited category',
'employee_id': self.employee_emp_id,
'holiday_status_id': self.holidays_status_limited.id,
'number_of_days': 2,
})
# HrUser validates the first step
aloc1_user_group.action_approve()
# HrManager validates the second step
aloc1_user_group.with_user(self.user_hrmanager_id).action_validate()
# 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)
# 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,
})
hol2_user_group = hol2.with_user(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.with_user(self.user_hrmanager_id).action_approve()
self.assertEqual(hol2.state, 'validate1',
'hr_holidays: first validation should lead to validate1 state')
# HrManager validates the second step
hol2_user_group.with_user(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.with_user(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
hol_status_2_employee_group.invalidate_cache(['max_leaves'])
_check_holidays_status(hol_status_2_employee_group, 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)
# I assign the dates in the holiday request for 1 day
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'),
'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_approve()
hol3.action_validate()
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 Employee) Report through the wizard
ctx = {
'model': 'hr.employee',
'active_ids': [self.ref('hr.employee_admin')]
}
data_dict = {
'date_from': datetime.today().strftime('%Y-%m-01'),
'emp': [(6, 0, [self.ref('hr.employee_admin')])],
'holiday_type': 'Approved'
}
self.env.company.external_report_layout_id = self.env.ref('web.external_layout_standard').id
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')
def test_sql_constraint_dates(self):
# The goal is mainly to verify that a human friendly
# error message is triggered if the date_from is after
# date_to. Coming from a bug due to the new ORM 13.0
holiday_status_paid_time_off = self.env['hr.leave.type'].create({
'name': 'Paid Time Off',
'allocation_type': 'fixed',
'leave_validation_type': 'both',
'validity_start': time.strftime('%Y-%m-01'),
'responsible_id': 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.ref('hr.employee_admin'),
'state': 'validate',
})
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'),
'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)
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'),
'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'),
})