[FIX] hr_holidays: ignore unconfirmed leaves in current leave status
As of this commit, only confirmed leaves will be considered when computing the current leave or absence of an employee. Additionally, a bug in the is_absent search implementation was fixed: before this commit, the same results were returned for absent and non-absent searches. opw-2877328 closes odoo/odoo#124968 X-original-commit: 28ccbbab07a0c25570ca9b940428b8e541d262d7 Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools.float_utils import float_round
|
||||
from odoo.addons.resource.models.utils import HOURS_PER_DAY
|
||||
|
||||
@@ -129,7 +130,7 @@ class HrEmployeeBase(models.AbstractModel):
|
||||
('employee_id', 'in', self.ids),
|
||||
('date_from', '<=', fields.Datetime.now()),
|
||||
('date_to', '>=', fields.Datetime.now()),
|
||||
('state', 'not in', ('cancel', 'refuse'))
|
||||
('state', '=', 'validate'),
|
||||
])
|
||||
leave_data = {}
|
||||
for holiday in holidays:
|
||||
@@ -163,6 +164,8 @@ class HrEmployeeBase(models.AbstractModel):
|
||||
employee.show_leaves = False
|
||||
|
||||
def _search_absent_employee(self, operator, value):
|
||||
if operator not in ('=', '!=') or not isinstance(value, bool):
|
||||
raise UserError(_('Operation not supported'))
|
||||
# This search is only used for the 'Absent Today' filter however
|
||||
# this only returns employees that are absent right now.
|
||||
today_date = datetime.datetime.utcnow().date()
|
||||
@@ -170,14 +173,12 @@ class HrEmployeeBase(models.AbstractModel):
|
||||
today_end = fields.Datetime.to_string(today_date + relativedelta(hours=23, minutes=59, seconds=59))
|
||||
holidays = self.env['hr.leave'].sudo().search([
|
||||
('employee_id', '!=', False),
|
||||
('state', 'not in', ['cancel', 'refuse']),
|
||||
('state', '=', 'validate'),
|
||||
('date_from', '<=', today_end),
|
||||
('date_to', '>=', today_start),
|
||||
])
|
||||
op = 'not in'
|
||||
if (operator == '=' and value) or (operator == '!=' and not value):
|
||||
op = 'in'
|
||||
return [('id', op, holidays.mapped('employee_id').ids)]
|
||||
operator = ['in', 'not in'][(operator == '=') != value]
|
||||
return [('id', operator, holidays.mapped('employee_id').ids)]
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
@@ -242,7 +243,7 @@ class HrEmployee(models.Model):
|
||||
('employee_id', 'in', self.ids),
|
||||
('date_from', '<=', fields.Datetime.now()),
|
||||
('date_to', '>=', fields.Datetime.now()),
|
||||
('state', 'not in', ('cancel', 'refuse'))
|
||||
('state', '=', 'validate'),
|
||||
])
|
||||
for holiday in holidays:
|
||||
employee = self.filtered(lambda e: e.id == holiday.employee_id.id)
|
||||
|
||||
@@ -51,7 +51,7 @@ class User(models.Model):
|
||||
self.env['hr.leave'].flush_model(['user_id', 'state', 'date_from', 'date_to'])
|
||||
self.env.cr.execute('''SELECT res_users.%s FROM res_users
|
||||
JOIN hr_leave ON hr_leave.user_id = res_users.id
|
||||
AND state in ('validate')
|
||||
AND state = 'validate'
|
||||
AND hr_leave.active = 't'
|
||||
AND res_users.active = 't'
|
||||
AND date_from <= %%s AND date_to >= %%s''' % field, (now, now))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime, date
|
||||
from datetime import datetime, date, timedelta
|
||||
import time
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from freezegun import freeze_time
|
||||
@@ -1057,3 +1057,48 @@ class TestLeaveRequests(TestHrHolidaysCommon):
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create(trigger_error_leave)
|
||||
|
||||
def test_current_leave_status(self):
|
||||
types = ('no_validation', 'manager', 'hr', 'both')
|
||||
employee = self.employee_emp
|
||||
|
||||
def run_validation_flow(leave_validation_type):
|
||||
LeaveType = self.env['hr.leave.type'].with_user(self.user_hrmanager_id)
|
||||
leave_type = LeaveType.with_context(tracking_disable=True).create({
|
||||
'name': leave_validation_type.capitalize(),
|
||||
'leave_validation_type': leave_validation_type,
|
||||
'requires_allocation': 'no',
|
||||
})
|
||||
current_leave = self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'Holiday Request',
|
||||
'holiday_type': 'employee',
|
||||
'employee_id': employee.id,
|
||||
'holiday_status_id': leave_type.id,
|
||||
'date_from': datetime.today() - timedelta(days=1),
|
||||
'date_to': datetime.today() + timedelta(days=1),
|
||||
})
|
||||
|
||||
if leave_validation_type in ('manager', 'both'):
|
||||
self.assertFalse(employee.is_absent)
|
||||
self.assertFalse(employee.current_leave_id)
|
||||
self.assertEqual(employee.filtered_domain([('is_absent', '=', False)]), employee)
|
||||
self.assertFalse(employee.filtered_domain([('is_absent', '=', True)]))
|
||||
current_leave.with_user(self.user_hruser_id).action_approve()
|
||||
|
||||
if leave_validation_type in ('hr', 'both'):
|
||||
self.assertFalse(employee.is_absent)
|
||||
self.assertFalse(employee.current_leave_id)
|
||||
self.assertEqual(employee.filtered_domain([('is_absent', '=', False)]), employee)
|
||||
self.assertFalse(employee.filtered_domain([('is_absent', '=', True)]))
|
||||
current_leave.with_user(self.user_hrmanager_id).action_validate()
|
||||
|
||||
self.assertTrue(employee.is_absent)
|
||||
self.assertEqual(employee.current_leave_id, current_leave.holiday_status_id)
|
||||
self.assertFalse(employee.filtered_domain([('is_absent', '=', False)]))
|
||||
self.assertEqual(employee.filtered_domain([('is_absent', '=', True)]), employee)
|
||||
|
||||
raise RuntimeError()
|
||||
|
||||
for leave_validation_type in types:
|
||||
with self.assertRaises(RuntimeError), self.env.cr.savepoint():
|
||||
run_validation_flow(leave_validation_type)
|
||||
|
||||
@@ -105,12 +105,12 @@ class TestOutOfOfficePerformance(TestHrHolidaysCommon, TransactionCaseWithUserDe
|
||||
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')])
|
||||
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()
|
||||
holidays = self.env['hr.leave'].sudo().search([
|
||||
('employee_id', '!=', False),
|
||||
('state', 'not in', ['cancel', 'refuse']),
|
||||
('state', '=', 'validate'),
|
||||
('date_from', '<=', today_date),
|
||||
('date_to', '>=', today_date),
|
||||
])
|
||||
|
||||
@@ -119,7 +119,7 @@ class ResourceCalendarLeaves(models.Model):
|
||||
('employee_id', 'in', employee_ids_all),
|
||||
('date_from', '<=', max_date),
|
||||
('date_to', '>=', min_date),
|
||||
('state', 'not in', ('cancel', 'refuse')),
|
||||
('state', '=', 'validate'),
|
||||
], ['employee_id'], ['date_from:array_agg', 'date_to:array_agg'])
|
||||
holidays_by_employee = {
|
||||
employee.id: [
|
||||
|
||||
Reference in New Issue
Block a user