[FIX] hr_holidays: remove allocation_id on hr.leave
As part of hr_holidays B2B, the holidays allowances were moved from hr_leave_type to hr_leave_allocation. This didn't allow to take a leave longer than the maximum duration of an allocation. Also, despite all the allocations could have enough days for a certain leave, it was not possible to use all available days without splitting the leaves across the different allocations. This commit moves the holidays allowance to the hr_leave_type to remediate the issues described above. task-2834887 closes odoo/odoo#98317 X-original-commit: 8c37d5c7cc397e517c43c1ec13a131b4b35e48b7 Signed-off-by: phwa-odoo <phwa@odoo.com>
This commit is contained in:
@@ -315,9 +315,9 @@ class HolidaysRequest(models.Model):
|
||||
|
||||
@api.constrains('holiday_status_id', 'number_of_days')
|
||||
def _check_allocation_duration(self):
|
||||
for holiday in self:
|
||||
if holiday.holiday_status_id.requires_allocation == 'yes' and holiday.holiday_allocation_id and holiday.number_of_days > holiday.holiday_allocation_id.number_of_days:
|
||||
raise ValidationError(_("You have several allocations for those type and period.\nPlease split your request to fit in their number of days."))
|
||||
# Deprecated as part of https://github.com/odoo/odoo/pull/96545
|
||||
# TODO: remove in master
|
||||
return
|
||||
|
||||
@api.depends_context('uid')
|
||||
def _compute_description(self):
|
||||
@@ -356,49 +356,9 @@ class HolidaysRequest(models.Model):
|
||||
|
||||
@api.depends('holiday_status_id.requires_allocation', 'validation_type', 'employee_id', 'date_from', 'date_to')
|
||||
def _compute_from_holiday_status_id(self):
|
||||
invalid_self = self.filtered(lambda leave: not leave.date_to or not leave.date_from)
|
||||
if invalid_self:
|
||||
invalid_self.update({'holiday_allocation_id': False})
|
||||
self = self - invalid_self
|
||||
if not self:
|
||||
return
|
||||
allocations = self.env['hr.leave.allocation'].search_read(
|
||||
[
|
||||
('holiday_status_id', 'in', self.holiday_status_id.ids),
|
||||
('employee_id', 'in', self.employee_id.ids),
|
||||
('state', '=', 'validate'),
|
||||
'|',
|
||||
('date_to', '>=', min(self.mapped('date_to'))),
|
||||
'&',
|
||||
('date_to', '=', False),
|
||||
('date_from', '<=', max(self.mapped('date_from'))),
|
||||
], ['id', 'date_from', 'date_to', 'holiday_status_id', 'employee_id', 'max_leaves', 'taken_leave_ids'], order="date_to, id"
|
||||
)
|
||||
allocations_dict = defaultdict(lambda: [])
|
||||
for allocation in allocations:
|
||||
allocation['taken_leaves'] = self.env['hr.leave'].browse(allocation.pop('taken_leave_ids'))\
|
||||
.filtered(lambda leave: leave.state in ['confirm', 'validate', 'validate1'])
|
||||
allocations_dict[(allocation['holiday_status_id'][0], allocation['employee_id'][0])].append(allocation)
|
||||
|
||||
for leave in self:
|
||||
if leave.holiday_status_id.requires_allocation == 'yes' and leave.date_from and leave.date_to:
|
||||
found_allocation = False
|
||||
date_to = leave.date_to.replace(tzinfo=UTC).astimezone(timezone(leave.tz)).date()
|
||||
date_from = leave.date_from.replace(tzinfo=UTC).astimezone(timezone(leave.tz)).date()
|
||||
leave_unit = 'number_of_%s_display' % ('hours' if leave.leave_type_request_unit == 'hour' else 'days')
|
||||
for allocation in allocations_dict[(leave.holiday_status_id.id, leave.employee_id.id)]:
|
||||
date_to_check = allocation['date_to'] >= date_to if allocation['date_to'] else True
|
||||
date_from_check = allocation['date_from'] <= date_from
|
||||
if (date_to_check and date_from_check):
|
||||
allocation_taken_leaves = allocation['taken_leaves'] - leave
|
||||
allocation_taken_number_of_units = sum(allocation_taken_leaves.mapped(leave_unit))
|
||||
leave_number_of_units = leave[leave_unit]
|
||||
if allocation['max_leaves'] >= allocation_taken_number_of_units + leave_number_of_units:
|
||||
found_allocation = allocation['id']
|
||||
break
|
||||
leave.holiday_allocation_id = self.env['hr.leave.allocation'].browse(found_allocation) if found_allocation else False
|
||||
else:
|
||||
leave.holiday_allocation_id = False
|
||||
# Deprecated as part of https://github.com/odoo/odoo/pull/96545
|
||||
# TODO: remove in master
|
||||
self.holiday_allocation_id = False
|
||||
|
||||
@api.depends('request_date_from_period', 'request_hour_from', 'request_hour_to', 'request_date_from', 'request_date_to',
|
||||
'request_unit_half', 'request_unit_hours', 'employee_id')
|
||||
@@ -735,13 +695,13 @@ class HolidaysRequest(models.Model):
|
||||
@api.constrains('state', 'number_of_days', 'holiday_status_id')
|
||||
def _check_holidays(self):
|
||||
for holiday in self:
|
||||
if holiday.holiday_type != 'employee' or not holiday.employee_id or not holiday.holiday_status_id or holiday.holiday_status_id.requires_allocation == 'no':
|
||||
mapped_days = self.holiday_status_id.get_employees_days(holiday.employee_id.ids, holiday.date_from.date())
|
||||
if holiday.holiday_type != 'employee' or not holiday.employee_id or holiday.holiday_status_id.requires_allocation == 'no':
|
||||
continue
|
||||
mapped_days = holiday.holiday_status_id.get_employees_days([holiday.employee_id.id], holiday.date_from)
|
||||
leave_days = mapped_days[holiday.employee_id.id][holiday.holiday_status_id.id]
|
||||
if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1:
|
||||
raise ValidationError(_('The number of remaining time off is not sufficient for this time off type.\n'
|
||||
'Please also check the time off waiting for validation.') + '\n- %s' % holiday.display_name)
|
||||
'Please also check the time off waiting for validation.'))
|
||||
|
||||
@api.constrains('date_from', 'date_to', 'employee_id')
|
||||
def _check_date_state(self):
|
||||
@@ -886,43 +846,15 @@ class HolidaysRequest(models.Model):
|
||||
|
||||
@api.constrains('holiday_allocation_id')
|
||||
def _check_allocation_id(self):
|
||||
for leave in self:
|
||||
if leave.holiday_type == 'employee' and not leave.multi_employee and\
|
||||
leave.holiday_status_id.requires_allocation == 'yes' and not leave.holiday_allocation_id:
|
||||
raise ValidationError(_(
|
||||
'Could not find an allocation of type %(leave_type)s for the requested time period.',
|
||||
leave_type=leave.holiday_status_id.display_name,
|
||||
) + '\n- %s' % (leave.employee_id.name))
|
||||
# Deprecated as part of https://github.com/odoo/odoo/pull/96545
|
||||
# TODO: remove in master
|
||||
return
|
||||
|
||||
@api.constrains('holiday_allocation_id', 'date_to', 'date_from')
|
||||
def _check_leave_type_validity(self):
|
||||
for leave in self:
|
||||
vstart = leave.holiday_allocation_id.date_from
|
||||
vstop = leave.holiday_allocation_id.date_to
|
||||
dfrom = leave.date_from
|
||||
dto = leave.date_to
|
||||
if vstart and vstop:
|
||||
if dfrom and dto and (dfrom.date() < vstart or dto.date() > vstop):
|
||||
raise ValidationError(_(
|
||||
'%(leave_type)s are only valid between %(start)s and %(end)s',
|
||||
leave_type=leave.holiday_status_id.display_name,
|
||||
start=vstart,
|
||||
end=vstop
|
||||
))
|
||||
elif vstart:
|
||||
if dfrom and (dfrom.date() < vstart):
|
||||
raise ValidationError(_(
|
||||
'%(leave_type)s are only valid starting from %(date)s',
|
||||
leave_type=leave.holiday_status_id.display_name,
|
||||
date=vstart
|
||||
))
|
||||
elif vstop:
|
||||
if dto and (dto.date() > vstop):
|
||||
raise ValidationError(_(
|
||||
'%(leave_type)s are only valid until %(date)s',
|
||||
leave_type=leave.holiday_status_id.display_name,
|
||||
date=vstop
|
||||
))
|
||||
# Deprecated as part of https://github.com/odoo/odoo/pull/96545
|
||||
# TODO: remove in master
|
||||
return
|
||||
|
||||
@api.constrains('date_from', 'date_to')
|
||||
def _check_stress_day(self):
|
||||
@@ -1003,8 +935,6 @@ class HolidaysRequest(models.Model):
|
||||
|
||||
holidays = super(HolidaysRequest, self.with_context(mail_create_nosubscribe=True)).create(vals_list)
|
||||
|
||||
holidays.filtered(lambda holiday: not holiday.holiday_allocation_id).with_user(SUPERUSER_ID)._compute_from_holiday_status_id()
|
||||
|
||||
for holiday in holidays:
|
||||
if not self._context.get('leave_fast_create'):
|
||||
# Everything that is done here must be done using sudo because we might
|
||||
|
||||
@@ -194,11 +194,10 @@ class HolidaysAllocation(models.Model):
|
||||
|
||||
@api.depends('employee_id', 'holiday_status_id', 'taken_leave_ids.number_of_days', 'taken_leave_ids.state')
|
||||
def _compute_leaves(self):
|
||||
employee_days_per_allocation = self.holiday_status_id._get_employees_days_per_allocation(self.employee_id.ids)
|
||||
for allocation in self:
|
||||
allocation.max_leaves = allocation.number_of_hours_display if allocation.type_request_unit == 'hour' else allocation.number_of_days
|
||||
allocation.leaves_taken = sum(taken_leave.number_of_hours_display if taken_leave.leave_type_request_unit == 'hour' else taken_leave.number_of_days\
|
||||
for taken_leave in allocation.taken_leave_ids\
|
||||
if taken_leave.state == 'validate')
|
||||
allocation.leaves_taken = employee_days_per_allocation[allocation.employee_id.id][allocation.holiday_status_id][allocation]['leaves_taken']
|
||||
|
||||
@api.depends('number_of_days')
|
||||
def _compute_number_of_days_display(self):
|
||||
|
||||
@@ -7,12 +7,14 @@ import datetime
|
||||
import logging
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import time, timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import format_date
|
||||
from odoo.tools.translate import _
|
||||
from odoo.tools.float_utils import float_round
|
||||
from odoo.addons.resource.models.resource import Intervals
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -220,7 +222,170 @@ class HolidaysType(models.Model):
|
||||
|
||||
return [('id', 'in', valid_leave_types.ids)]
|
||||
|
||||
def _get_employees_days_per_allocation(self, employee_ids, date=None):
|
||||
leaves = self.env['hr.leave'].search([
|
||||
('employee_id', 'in', employee_ids),
|
||||
('state', 'in', ['confirm', 'validate1', 'validate']),
|
||||
('holiday_status_id', 'in', self.ids)
|
||||
])
|
||||
|
||||
allocations = self.env['hr.leave.allocation'].search([
|
||||
('employee_id', 'in', employee_ids),
|
||||
('state', 'in', ['validate']),
|
||||
('holiday_status_id', 'in', self.ids),
|
||||
])
|
||||
|
||||
if not date:
|
||||
date = fields.Date.to_date(self.env.context.get('default_date_from')) or fields.Date.context_today(self)
|
||||
|
||||
# The allocation_employees dictionary groups the allocations based on the employee and the holiday type
|
||||
# The structure is the following:
|
||||
# - KEYS:
|
||||
# allocation_employees
|
||||
# |--employee_id
|
||||
# |--holiday_status_id
|
||||
# - VALUES:
|
||||
# Intervals with the start and end date of each allocation and associated allocations within this interval
|
||||
allocation_employees = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
### Creation of the allocation intervals ###
|
||||
for holiday_status_id in allocations.holiday_status_id:
|
||||
for employee_id in employee_ids:
|
||||
allocation_intervals = Intervals([(
|
||||
fields.datetime.combine(allocation.date_from, time.min),
|
||||
fields.datetime.combine(allocation.date_to or datetime.date.max, time.max),
|
||||
allocation)
|
||||
for allocation in allocations.filtered(lambda allocation: allocation.employee_id.id == employee_id and allocation.holiday_status_id == holiday_status_id)])
|
||||
|
||||
allocation_employees[employee_id][holiday_status_id] = allocation_intervals
|
||||
|
||||
# The leave_employees dictionary groups the leavess based on the employee and the holiday type
|
||||
# The structure is the following:
|
||||
# - KEYS:
|
||||
# leave_employees
|
||||
# |--employee_id
|
||||
# |--holiday_status_id
|
||||
# - VALUES:
|
||||
# Intervals with the start and end date of each leave and associated leave within this interval
|
||||
leaves_employees = defaultdict(lambda: defaultdict(list))
|
||||
leave_intervals = []
|
||||
|
||||
### Creation of the leave intervals ###
|
||||
if leaves:
|
||||
for holiday_status_id in leaves.holiday_status_id:
|
||||
for employee_id in employee_ids:
|
||||
leave_intervals = Intervals([(
|
||||
fields.datetime.combine(leave.date_from, time.min),
|
||||
fields.datetime.combine(leave.date_to, time.max),
|
||||
leave)
|
||||
for leave in leaves.filtered(lambda leave: leave.employee_id.id == employee_id and leave.holiday_status_id == holiday_status_id)])
|
||||
|
||||
leaves_employees[employee_id][holiday_status_id] = leave_intervals
|
||||
|
||||
# allocation_days_consumed is a dictionary to map the number of days/hours of leaves taken per allocation
|
||||
# The structure is the following:
|
||||
# - KEYS:
|
||||
# allocation_days_consumed
|
||||
# |--employee_id
|
||||
# |--holiday_status_id
|
||||
# |--allocation
|
||||
# |--virtual_leaves_taken
|
||||
# |--leaves_taken
|
||||
# |--virtual_remaining_leaves
|
||||
# |--remaining_leaves
|
||||
# |--max_leaves
|
||||
# |--closest_allocation_to_expire
|
||||
# - VALUES:
|
||||
# Integer representing the number of (virtual) remaining leaves, (virtual) leaves taken or max leaves for each allocation.
|
||||
# The unit is in hour or days depending on the leave type request unit
|
||||
allocations_days_consumed = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0))))
|
||||
|
||||
company_domain = [('company_id', 'in', list(set(self.env.company.ids + self.env.context.get('allowed_company_ids', []))))]
|
||||
|
||||
### Existing leaves assigned to allocations ###
|
||||
if leaves_employees:
|
||||
for employee_id, leaves_interval_by_status in leaves_employees.items():
|
||||
for holiday_status_id in leaves_interval_by_status:
|
||||
days_consumed = allocations_days_consumed[employee_id][holiday_status_id]
|
||||
if allocation_employees[employee_id][holiday_status_id]:
|
||||
allocations = allocation_employees[employee_id][holiday_status_id] & leaves_interval_by_status[holiday_status_id]
|
||||
available_allocations = self.env['hr.leave.allocation']
|
||||
for allocation_interval in allocations._items:
|
||||
available_allocations |= allocation_interval[2]
|
||||
# Consume the allocations that are close to expiration first
|
||||
sorted_available_allocations = available_allocations.filtered('date_to').sorted(key='date_to')
|
||||
sorted_available_allocations += available_allocations.filtered(lambda allocation: not allocation.date_to)
|
||||
allocations_days_consumed[employee_id][holiday_status_id][False]['closest_allocation_to_expire'] = sorted_available_allocations[0] if sorted_available_allocations else False
|
||||
leave_intervals = leaves_interval_by_status[holiday_status_id]._items
|
||||
for leave_interval in leave_intervals:
|
||||
leaves = leave_interval[2]
|
||||
for leave in leaves:
|
||||
if leave.leave_type_request_unit in ['day', 'half_day']:
|
||||
leave_duration = leave.number_of_days
|
||||
leave_unit = 'days'
|
||||
else:
|
||||
leave_duration = leave.number_of_hours_display
|
||||
leave_unit = 'hours'
|
||||
for available_allocation in sorted_available_allocations:
|
||||
virtual_remaining_leaves = (available_allocation.number_of_days if leave_unit == 'days' else available_allocation.number_of_hours_display) - allocations_days_consumed[employee_id][holiday_status_id][available_allocation]['virtual_leaves_taken']
|
||||
max_leaves = min(virtual_remaining_leaves, leave_duration)
|
||||
days_consumed[available_allocation]['virtual_leaves_taken'] += max_leaves
|
||||
if leave.state == 'validate':
|
||||
days_consumed[available_allocation]['leaves_taken'] += max_leaves
|
||||
leave_duration -= max_leaves
|
||||
if leave_duration > 0:
|
||||
# There are not enough allocation for the number of leaves
|
||||
days_consumed[False]['virtual_remaining_leaves'] -= leave_duration
|
||||
return allocations_days_consumed
|
||||
|
||||
# Future available leaves
|
||||
for employee_id, allocation_intervals_by_status in allocation_employees.items():
|
||||
for holiday_status_id, intervals in allocation_intervals_by_status.items():
|
||||
if not intervals:
|
||||
continue
|
||||
future_allocation_intervals = intervals & Intervals([(
|
||||
fields.datetime.combine(date, time.min),
|
||||
fields.datetime.combine(date, time.max) + timedelta(days=5*365),
|
||||
self.env['hr.leave'])])
|
||||
search_date = date
|
||||
allocations_of_that_type = intervals._items[0][2].filtered(lambda a: a.date_to and a.state == 'validate' and a.date_to >= date)
|
||||
allocations_sorted = sorted(allocations_of_that_type, key=lambda a: a.date_to)
|
||||
allocation_closest = allocations_sorted[0] if allocations_sorted else False
|
||||
allocations_days_consumed[employee_id][holiday_status_id][False]['closest_allocation_to_expire'] = allocation_closest
|
||||
for future_allocation_interval in future_allocation_intervals._items:
|
||||
if future_allocation_interval[0].date() > search_date:
|
||||
continue
|
||||
for allocation in future_allocation_interval[2]:
|
||||
days_consumed = allocations_days_consumed[employee_id][holiday_status_id][allocation]
|
||||
if future_allocation_interval[1] != fields.datetime.combine(date, time.max) + timedelta(days=5*365):
|
||||
# Compute the remaining number of days/hours in the allocation only if it has an end date
|
||||
quantity_available = allocation.employee_id._get_work_days_data_batch(
|
||||
future_allocation_interval[0],
|
||||
future_allocation_interval[1],
|
||||
compute_leaves=False,
|
||||
domain=company_domain)[employee_id]
|
||||
else:
|
||||
# If no end date to the allocation, consider the number of days remaining as infinite
|
||||
quantity_available = {'days': float('inf'), 'hours': float('inf')}
|
||||
if allocation.type_request_unit in ['day', 'half_day']:
|
||||
quantity_available = quantity_available['days']
|
||||
remaining_days_allocation = (allocation.number_of_days - days_consumed['virtual_leaves_taken'])
|
||||
else:
|
||||
quantity_available = quantity_available['hours']
|
||||
remaining_days_allocation = (allocation.number_of_hours_display - days_consumed['virtual_leaves_taken'])
|
||||
if quantity_available <= remaining_days_allocation:
|
||||
search_date = future_allocation_interval[1].date() + timedelta(days=1)
|
||||
days_consumed['virtual_remaining_leaves'] += min(quantity_available, remaining_days_allocation)
|
||||
days_consumed['max_leaves'] = allocation.number_of_days if allocation.type_request_unit == 'day' else allocation.number_of_hours_display
|
||||
days_consumed['remaining_leaves'] = days_consumed['max_leaves'] - days_consumed['leaves_taken']
|
||||
if remaining_days_allocation >= quantity_available:
|
||||
break
|
||||
|
||||
return allocations_days_consumed
|
||||
|
||||
|
||||
def get_employees_days(self, employee_ids, date=None):
|
||||
|
||||
result = {
|
||||
employee_id: {
|
||||
leave_type.id: {
|
||||
@@ -234,72 +399,21 @@ class HolidaysType(models.Model):
|
||||
} for employee_id in employee_ids
|
||||
}
|
||||
|
||||
requests = self.env['hr.leave'].search([
|
||||
('employee_id', 'in', employee_ids),
|
||||
('state', 'in', ['confirm', 'validate1', 'validate']),
|
||||
('holiday_status_id', 'in', self.ids)
|
||||
])
|
||||
allocations_days_consumed = self._get_employees_days_per_allocation(employee_ids, date)
|
||||
|
||||
if not date:
|
||||
date = self.env.context.get('default_date_from') or fields.Date.context_today(self)
|
||||
allocations = self.env['hr.leave.allocation'].search([
|
||||
('employee_id', 'in', employee_ids),
|
||||
('state', 'in', ['confirm', 'validate1', 'validate']),
|
||||
('holiday_status_id', 'in', self.ids),
|
||||
('date_from', '<=', date),
|
||||
'|', ('date_to', '=', False),
|
||||
('date_to', '>=', date),
|
||||
])
|
||||
leave_keys = ['max_leaves', 'leaves_taken', 'remaining_leaves', 'virtual_remaining_leaves', 'virtual_leaves_taken']
|
||||
|
||||
for request in requests:
|
||||
status_dict = result[request.employee_id.id][request.holiday_status_id.id]
|
||||
if not request.holiday_allocation_id or request.holiday_allocation_id in allocations:
|
||||
status_dict['virtual_remaining_leaves'] -= (request.number_of_hours_display
|
||||
if request.leave_type_request_unit == 'hour'
|
||||
else request.number_of_days)
|
||||
if request.holiday_status_id.requires_allocation == 'no':
|
||||
status_dict['virtual_leaves_taken'] += (request.number_of_hours_display
|
||||
if request.leave_type_request_unit == 'hour'
|
||||
else request.number_of_days)
|
||||
if request.state == 'validate':
|
||||
status_dict['leaves_taken'] += (request.number_of_hours_display
|
||||
if request.leave_type_request_unit == 'hour'
|
||||
else request.number_of_days)
|
||||
status_dict['remaining_leaves'] -= (request.number_of_hours_display
|
||||
if request.leave_type_request_unit == 'hour'
|
||||
else request.number_of_days)
|
||||
|
||||
allocation_closest_by_type = {}
|
||||
for holiday_status_id in self.ids:
|
||||
allocations_of_that_type = allocations.filtered(lambda a: a.holiday_status_id.id == holiday_status_id and a.date_to and a.state == 'validate')
|
||||
allocations_sorted = sorted(allocations_of_that_type, key=lambda a: a.date_to)
|
||||
allocation_closest = allocations_sorted[0] if allocations_sorted else False
|
||||
allocation_closest_by_type[holiday_status_id] = {
|
||||
'closest_allocation_to_expire': allocation_closest,
|
||||
}
|
||||
|
||||
for allocation in allocations.sudo():
|
||||
status_dict = result[allocation.employee_id.id][allocation.holiday_status_id.id]
|
||||
if allocation.state == 'validate':
|
||||
status_dict['virtual_remaining_leaves'] += (allocation.number_of_hours_display
|
||||
if allocation.type_request_unit == 'hour'
|
||||
else allocation.number_of_days)
|
||||
if allocation.holiday_status_id.requires_allocation == 'no':
|
||||
# note: add only validated allocation even for the virtual
|
||||
# count; otherwise pending then refused allocation allow
|
||||
# the employee to create more leaves than possible
|
||||
status_dict['max_leaves'] += (allocation.number_of_hours_display
|
||||
if allocation.type_request_unit == 'hour'
|
||||
else allocation.number_of_days)
|
||||
status_dict['remaining_leaves'] += (allocation.number_of_hours_display
|
||||
if allocation.type_request_unit == 'hour'
|
||||
else allocation.number_of_days)
|
||||
else:
|
||||
remaining_leaves = allocation.max_leaves - allocation.leaves_taken
|
||||
status_dict['max_leaves'] += allocation.max_leaves
|
||||
status_dict['remaining_leaves'] += remaining_leaves
|
||||
status_dict['leaves_taken'] += allocation.leaves_taken
|
||||
status_dict['closest_allocation_to_expire'] = allocation_closest_by_type[allocation.holiday_status_id.id]['closest_allocation_to_expire']
|
||||
for employee_id in allocations_days_consumed:
|
||||
for holiday_status_id in allocations_days_consumed[employee_id]:
|
||||
for allocation in allocations_days_consumed[employee_id][holiday_status_id]:
|
||||
if allocation:
|
||||
for leave_key in leave_keys:
|
||||
result[employee_id][holiday_status_id if isinstance(holiday_status_id, int) else holiday_status_id.id][leave_key] += allocations_days_consumed[employee_id][holiday_status_id][allocation][leave_key]
|
||||
else:
|
||||
result[employee_id][holiday_status_id if isinstance(holiday_status_id, int) else holiday_status_id.id]['closest_allocation_to_expire'] = allocations_days_consumed[employee_id][holiday_status_id][False]['closest_allocation_to_expire']
|
||||
for leave_key in leave_keys:
|
||||
if allocations_days_consumed[employee_id][holiday_status_id][False].get(leave_key):
|
||||
result[employee_id][holiday_status_id if isinstance(holiday_status_id, int) else holiday_status_id.id][leave_key] = allocations_days_consumed[employee_id][holiday_status_id][False][leave_key]
|
||||
return result
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -323,7 +323,7 @@ class TestCompanyLeave(TransactionCase):
|
||||
})
|
||||
company_leave._compute_date_from_to()
|
||||
|
||||
with self.assertQueryCount(__system__=350, admin=865):
|
||||
with self.assertQueryCount(__system__=774, admin=867): # 770 community
|
||||
# Original query count: 1987
|
||||
# Without tracking/activity context keys: 5154
|
||||
company_leave.action_validate()
|
||||
|
||||
@@ -92,17 +92,9 @@ class TestLeaveRequests(TestHrHolidaysCommon):
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_limited_type_no_days(self):
|
||||
""" Employee creates a leave request in a limited category but has not enough days left """
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'Hol22',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_type_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': 1,
|
||||
})
|
||||
# Deprecated as part of https://github.com/odoo/odoo/pull/96545
|
||||
# TODO: remove in master
|
||||
return
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_limited_type_days_left(self):
|
||||
@@ -164,16 +156,9 @@ class TestLeaveRequests(TestHrHolidaysCommon):
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_accrual_validity_time_not_valid(self):
|
||||
""" Employee ask leave when there's no valid allocation """
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.user_employee).create({
|
||||
'name': 'Sick Time Off',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_type_2.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'),
|
||||
'number_of_days': 1,
|
||||
})
|
||||
# Deprecated as part of https://github.com/odoo/odoo/pull/96545
|
||||
# TODO: remove in master
|
||||
return
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_department_leave(self):
|
||||
@@ -619,23 +604,8 @@ class TestLeaveRequests(TestHrHolidaysCommon):
|
||||
allocation1.action_validate()
|
||||
allocation2.action_validate()
|
||||
|
||||
# Neither allocation has enough days
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'Holiday Request',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_type_2.id,
|
||||
'date_from': '2022-01-01',
|
||||
'date_to': '2022-01-15',
|
||||
'number_of_days': 10,
|
||||
})
|
||||
|
||||
allocation_vals.update({'number_of_days': 10})
|
||||
allocation3 = self.env['hr.leave.allocation'].create(allocation_vals)
|
||||
allocation3.action_validate()
|
||||
|
||||
# The 3rd allocation has enough days, so it should be picked
|
||||
holiday = self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
# Able to create a leave of 10 days with two allocations of 5 days
|
||||
self.env['hr.leave'].with_user(self.user_employee_id).create({
|
||||
'name': 'Holiday Request',
|
||||
'employee_id': self.employee_emp_id,
|
||||
'holiday_status_id': self.holidays_type_2.id,
|
||||
@@ -643,11 +613,6 @@ class TestLeaveRequests(TestHrHolidaysCommon):
|
||||
'date_to': '2022-01-15',
|
||||
'number_of_days': 10,
|
||||
})
|
||||
holiday.sudo().action_validate()
|
||||
|
||||
self.assertEqual(allocation1.leaves_taken, 0.0, 'As this allocation does not have enough days, it should not be affected')
|
||||
self.assertEqual(allocation2.leaves_taken, 0.0, 'As this allocation does not have enough days, it should not be affected')
|
||||
self.assertEqual(allocation3.leaves_taken, 10.0, 'As this allocation has enough days, the leave days should be taken')
|
||||
|
||||
def test_several_allocations_split(self):
|
||||
Allocation = self.env['hr.leave.allocation']
|
||||
@@ -698,8 +663,10 @@ class TestLeaveRequests(TestHrHolidaysCommon):
|
||||
leaves = (leave_4days + leave_1day)
|
||||
leaves.action_approve()
|
||||
|
||||
self.assertEqual(allocation_4days.leaves_taken, leave_4days['number_of_%ss_display' % unit], 'As 4 days were available in this allocation, they should have been taken')
|
||||
self.assertEqual(allocation_1day.leaves_taken, leave_1day['number_of_%ss_display' % unit], 'As no days were available in previous allocation, they should have been taken in this one')
|
||||
allocation_days = self.holidays_type_2._get_employees_days_per_allocation([self.employee_emp_id])
|
||||
|
||||
self.assertEqual(allocation_days[self.employee_emp_id][self.holidays_type_2][allocation_4days]['leaves_taken'], leave_4days['number_of_%ss_display' % unit], 'As 4 days were available in this allocation, they should have been taken')
|
||||
self.assertEqual(allocation_days[self.employee_emp_id][self.holidays_type_2][allocation_1day]['leaves_taken'], leave_1day['number_of_%ss_display' % unit], 'As no days were available in previous allocation, they should have been taken in this one')
|
||||
leaves.action_refuse()
|
||||
allocations.action_refuse()
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
class="oe_stat_button"
|
||||
icon="fa-calendar"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', '|', ('id', '=', False), ('holiday_type', '!=', 'employee'), ('employee_id', '=', False)]}"
|
||||
invisible="1"
|
||||
name="%(hr_leave_action_holiday_allocation_id)d"
|
||||
help="Time off Taken/Total Allocated">
|
||||
<div class="o_stat_info">
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
|
||||
self.richard_emp.generate_work_entries(date(2018, 1, 1), date(2018, 1, 2))
|
||||
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
|
||||
|
||||
with self.assertQueryCount(__system__=90, admin=91):
|
||||
with self.assertQueryCount(__system__=93, admin=94):
|
||||
leave.action_validate()
|
||||
leave.action_refuse()
|
||||
|
||||
@@ -40,7 +40,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
|
||||
def test_performance_leave_write(self):
|
||||
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
|
||||
|
||||
with self.assertQueryCount(__system__=18, admin=18):
|
||||
with self.assertQueryCount(__system__=21, admin=30):
|
||||
leave.date_to = datetime(2018, 1, 1, 19, 0)
|
||||
leave.action_refuse()
|
||||
|
||||
@@ -56,7 +56,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
|
||||
def test_performance_leave_confirm(self):
|
||||
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
|
||||
leave.action_draft()
|
||||
with self.assertQueryCount(__system__=19, admin=20): # 18/19 com
|
||||
with self.assertQueryCount(__system__=23, admin=24):
|
||||
leave.action_confirm()
|
||||
leave.state = 'refuse'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user