19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:00 +01:00
parent a1137a1456
commit e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions

View file

@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import hr_attendance
from . import hr_attendance_overtime_rule
from . import hr_attendance_overtime
from . import hr_leave_allocation
from . import hr_leave_accrual_plan_level
from . import hr_leave_type
from . import hr_leave
from . import res_company
from . import res_users
from . import ir_ui_menu
from . import hr_employee
from . import resource_calendar_leaves

View file

@ -1,14 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.osv.expression import AND
class HrAttendance(models.Model):
_inherit = "hr.attendance"
def _get_overtime_leave_domain(self):
domain = super()._get_overtime_leave_domain()
# resource_id = False => Public holidays
return AND([domain, ['|', ('holiday_id.holiday_status_id.time_type', '=', 'leave'), ('resource_id', '=', False)]])
def init(self):
super().init()
self.env.cr.execute("""
CREATE INDEX IF NOT EXISTS hr_attendance_check_in_check_out_employee_id ON hr_attendance (check_in, check_out, employee_id);
""")

View file

@ -0,0 +1,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields
class HrAttendanceOvertimeLine(models.Model):
_name = 'hr.attendance.overtime.line'
_inherit = 'hr.attendance.overtime.line'
compensable_as_leave = fields.Boolean("Compensable as Time Off")

View file

@ -0,0 +1,31 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields
class HrAttendanceOvertimeRule(models.Model):
_name = 'hr.attendance.overtime.rule'
_inherit = 'hr.attendance.overtime.rule'
compensable_as_leave = fields.Boolean("Give back as time off", default=False)
def _extra_overtime_vals(self):
if not self:
return {
**super()._extra_overtime_vals(),
'compensable_as_leave': False,
}
res = super()._extra_overtime_vals()
res['compensable_as_leave'] = any(self.mapped('compensable_as_leave'))
if self.ruleset_id.rate_combination_mode == 'sum' and any(self.mapped('paid')):
combined_rate = 1.0
combined_rate += sum(r.amount_rate - 1.0 for r in self.filtered(
lambda r: r.paid and not r.compensable_as_leave
))
combined_rate += sum(r.amount_rate for r in self.filtered(
lambda r: r.paid and r.compensable_as_leave
))
res['amount_rate'] = combined_rate
return res

View file

@ -0,0 +1,82 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import models
class HrEmployee(models.Model):
_inherit = "hr.employee"
def _get_deductible_employee_overtime(self):
# return dict {employee: number of hours}
diff_by_employee = defaultdict(lambda: 0)
for employee, hours in self.env['hr.attendance.overtime.line'].sudo()._read_group(
domain=[
('compensable_as_leave', '=', True),
('employee_id', 'in', self.ids),
('status', '=', 'approved'),
],
groupby=['employee_id'],
aggregates=['manual_duration:sum'],
):
diff_by_employee[employee] += hours
for employee, hours in self.env['hr.leave']._read_group(
domain=[
('holiday_status_id.overtime_deductible', '=', True),
('holiday_status_id.requires_allocation', '=', False),
('employee_id', 'in', self.ids),
('state', 'not in', ['refuse', 'cancel']),
],
groupby=['employee_id'],
aggregates=['number_of_hours:sum'],
):
diff_by_employee[employee] -= hours
for employee, hours in self.env['hr.leave.allocation']._read_group(
domain=[
('holiday_status_id.overtime_deductible', '=', True),
('employee_id', 'in', self.ids),
('state', 'in', ['confirm', 'validate', 'validate1']),
],
groupby=['employee_id'],
aggregates=['number_of_hours_display:sum'],
):
diff_by_employee[employee] -= hours
return diff_by_employee
def get_overtime_data_by_employee(self):
"""
Provide a summary of an employee's overtime.
A compensable overtime is an overtime that can be cumulated to be used
as time off.
Extra hours and overtime is used interchangably.
"""
# Make so that at least all employees are present in return value
overtime_data = {}
for employee_id in self.ids:
overtime_data[employee_id] = {
"compensable_overtime": 0,
"not_compensable_overtime": 0,
"unspent_compensable_overtime": 0,
}
unspent_overtime = self._get_deductible_employee_overtime()
for employee in unspent_overtime:
overtime_data[employee.id]['unspent_compensable_overtime'] += max(
0, unspent_overtime[employee]
)
all_overtimes = self.env['hr.attendance.overtime.line']._read_group(
domain=[
('employee_id', 'in', self.ids),
],
groupby=["employee_id", "compensable_as_leave"],
aggregates=["duration:sum"],
)
for employee, is_compensable, amount in all_overtimes:
overtime_type = (
'compensable_overtime'
if is_compensable
else 'not_compensable_overtime'
)
overtime_data[employee.id][overtime_type] += amount
return overtime_data

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
@ -9,17 +8,16 @@ from odoo.exceptions import ValidationError
from odoo.tools import float_round
class HRLeave(models.Model):
class HrLeave(models.Model):
_inherit = 'hr.leave'
overtime_id = fields.Many2one('hr.attendance.overtime', string='Extra Hours', groups='hr_holidays.group_hr_holidays_user')
employee_overtime = fields.Float(related='employee_id.total_overtime')
employee_overtime = fields.Float(compute='_compute_employee_overtime', groups='base.group_user')
overtime_deductible = fields.Boolean(compute='_compute_overtime_deductible')
@api.depends('holiday_status_id')
def _compute_overtime_deductible(self):
for leave in self:
leave.overtime_deductible = leave.holiday_status_id.overtime_deductible and leave.holiday_status_id.requires_allocation == 'no'
leave.overtime_deductible = leave.holiday_status_id.overtime_deductible and not leave.holiday_status_id.requires_allocation
@api.model_create_multi
def create(self, vals_list):
@ -29,84 +27,50 @@ class HRLeave(models.Model):
def write(self, vals):
res = super().write(vals)
fields_to_check = {'number_of_days', 'date_from', 'date_to', 'state', 'employee_id', 'holiday_status_id'}
fields_to_check = {'number_of_days', 'request_date_from', 'request_date_to', 'state', 'employee_id', 'holiday_status_id'}
if not any(field for field in fields_to_check if field in vals):
return res
if vals.get('holiday_status_id'):
self._check_overtime_deductible(self)
#User may not have access to overtime_id field
for leave in self.sudo().filtered('overtime_id'):
# It must always be possible to refuse leave based on overtime
if vals.get('state') in ['refuse']:
continue
employee = leave.employee_id
duration = leave.number_of_hours_display
overtime_duration = leave.overtime_id.sudo().duration
if overtime_duration != -1 * duration:
if duration > employee.total_overtime - overtime_duration:
raise ValidationError(_('The employee does not have enough extra hours to extend this leave.'))
leave.overtime_id.sudo().duration = -1 * duration
return res
def _check_overtime_deductible(self, leaves):
# If the type of leave is overtime deductible, we have to check that the employee has enough extra hours
for leave in leaves:
if not leave.overtime_deductible:
leave.sudo().overtime_id.unlink()
continue
employee = leave.employee_id.sudo()
duration = leave.number_of_hours_display
if duration > employee.total_overtime:
if employee.user_id == self.env.user:
raise ValidationError(_('You do not have enough extra hours to request this leave'))
raise ValidationError(_('The employee does not have enough extra hours to request this leave.'))
if not leave.sudo().overtime_id:
leave.sudo().overtime_id = self.env['hr.attendance.overtime'].sudo().create({
'employee_id': employee.id,
'date': leave.date_from,
'adjustment': True,
'duration': -1 * duration,
})
def action_draft(self):
overtime_leaves = self.filtered('overtime_deductible')
res = super().action_draft()
overtime_leaves.overtime_id.sudo().unlink()
return res
def action_confirm(self):
res = super().action_confirm()
self._check_overtime_deductible(self)
return res
def action_refuse(self):
res = super().action_refuse()
self.sudo().overtime_id.unlink()
return res
@api.model
def _get_deductible_employee_overtime(self, employees):
# Uses the calculation logic now located in the hr.employee model.
return employees._get_deductible_employee_overtime()
def _validate_leave_request(self):
super()._validate_leave_request()
self._update_leaves_overtime()
def _remove_resource_leave(self):
res = super()._remove_resource_leave()
self._update_leaves_overtime()
return res
def _update_leaves_overtime(self):
employee_dates = defaultdict(set)
@api.depends('number_of_hours', 'employee_id', 'holiday_status_id')
def _compute_employee_overtime(self):
diff_by_employee = self.employee_id._get_deductible_employee_overtime()
for leave in self:
if leave.employee_id and leave.employee_company_id.hr_attendance_overtime:
for d in range((leave.date_to - leave.date_from).days + 1):
employee_dates[leave.employee_id].add(self.env['hr.attendance']._get_day_start_and_day(leave.employee_id, leave.date_from + timedelta(days=d)))
if employee_dates:
self.env['hr.attendance']._update_overtime(employee_dates)
leave.employee_overtime = diff_by_employee[leave.employee_id]
def unlink(self):
# TODO master change to ondelete
self.sudo().overtime_id.unlink()
return super().unlink()
def _check_overtime_deductible(self, leaves):
# If the type of leave is overtime deductible, we have to check that the employee has enough extra hours
hours = leaves.employee_id._get_deductible_employee_overtime()
for leave in leaves.filtered('overtime_deductible'):
if hours[leave.employee_id] < 0:
if leave.employee_id.user_id == self.env.user:
raise ValidationError(_('You do not have enough extra hours to request this leave'))
raise ValidationError(_('The employee does not have enough extra hours to request this leave.'))
def action_reset_confirm(self):
self._check_overtime_deductible(self)
res = super().action_reset_confirm()
return res
def action_approve(self, check_state=True):
res = super().action_approve(check_state)
self._check_overtime_deductible(self)
return res
def _update_leaves_overtime(self): # TODO: Remove in master, since its no longer used.
date_from, date_to = self.mapped('date_from'), self.mapped('date_to')
if date_from and date_to:
self.env['hr.attendance'].sudo().search([
('check_in', '<=', max(date_to)),
('check_out', '>=', min(date_from)),
('employee_id', 'in', self.employee_id.ids),
])._update_overtime()
def _force_cancel(self, *args, **kwargs):
super()._force_cancel(*args, **kwargs)
self.sudo().overtime_id.unlink()

View file

@ -0,0 +1,30 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.exceptions import ValidationError
class HrLeaveAccrualLevel(models.Model):
_inherit = "hr.leave.accrual.level"
frequency = fields.Selection(
selection_add=[('worked_hours', 'Per Hour Worked')],
ondelete={'worked_hours': 'cascade'},
compute='_compute_frequency',
store=True,
readonly=False,
)
@api.constrains('frequency')
def _check_worked_hours(self):
for level in self:
if level.frequency == 'worked_hours' and level.accrued_gain_time == 'start':
raise ValidationError(self.env._("You can't base accrued time on hours worked, because time is accrued at the start of the period."))
@api.depends('accrued_gain_time')
def _compute_frequency(self):
for level in self:
if level.accrued_gain_time == 'start' and level.frequency == 'worked_hours':
level.frequency = 'hourly'
def _get_hourly_frequencies(self):
return super()._get_hourly_frequencies() + ["worked_hours"]

View file

@ -1,51 +1,49 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_round
from odoo.osv import expression
from odoo.fields import Domain
class HolidaysAllocation(models.Model):
class HrLeaveAllocation(models.Model):
_inherit = 'hr.leave.allocation'
@api.model
def default_get(self, fields):
res = super().default_get(fields)
if 'holiday_status_id' in fields and self.env.context.get('deduct_extra_hours'):
domain = [('overtime_deductible', '=', True), ('requires_allocation', '=', 'yes')]
domain = Domain('overtime_deductible', '=', True) & Domain('requires_allocation', '=', True)
if self.env.context.get('deduct_extra_hours_employee_request', False):
# Prevent loading manager allocated time off type in self request contexts
domain = expression.AND([domain, [('employee_requests', '=', 'yes')]])
domain &= Domain('employee_requests', '=', True)
leave_type = self.env['hr.leave.type'].search(domain, limit=1)
res['holiday_status_id'] = leave_type.id
return res
overtime_deductible = fields.Boolean(compute='_compute_overtime_deductible')
overtime_id = fields.Many2one('hr.attendance.overtime', string='Extra Hours', groups='hr_holidays.group_hr_holidays_user')
employee_overtime = fields.Float(related='employee_id.total_overtime')
hr_attendance_overtime = fields.Boolean(related='employee_company_id.hr_attendance_overtime')
employee_overtime = fields.Float(compute='_compute_employee_overtime', groups='base.group_user')
@api.depends('holiday_status_id')
def _compute_overtime_deductible(self):
for allocation in self:
allocation.overtime_deductible = allocation.hr_attendance_overtime and allocation.holiday_status_id.overtime_deductible
allocation.overtime_deductible = allocation.holiday_status_id.overtime_deductible
@api.depends('employee_id')
def _compute_employee_overtime(self):
diff_by_employee = self.employee_id._get_deductible_employee_overtime()
for allocation in self:
allocation.employee_overtime = diff_by_employee.get(allocation.employee_id, 0)
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
deductible = res.employee_id._get_deductible_employee_overtime()
for allocation in res:
if allocation.overtime_deductible and allocation.holiday_type == 'employee':
duration = allocation.number_of_hours_display
if duration > allocation.employee_id.total_overtime:
if allocation.overtime_deductible:
if deductible[allocation.employee_id] < 0:
raise ValidationError(_('The employee does not have enough overtime hours to request this leave.'))
if not allocation.overtime_id:
allocation.sudo().overtime_id = self.env['hr.attendance.overtime'].sudo().create({
'employee_id': allocation.employee_id.id,
'date': allocation.date_from,
'adjustment': True,
'duration': -1 * duration,
})
return res
def write(self, vals):
@ -54,34 +52,33 @@ class HolidaysAllocation(models.Model):
return res
if not self.env.user.has_group("hr_holidays.group_hr_holidays_user") and any(allocation.state not in ('draft', 'confirm') for allocation in self):
raise ValidationError(_('Only an Officer or Administrator is allowed to edit the allocation duration in this status.'))
for allocation in self.sudo().filtered('overtime_id'):
employee = allocation.employee_id
duration = allocation.number_of_hours_display
overtime_duration = allocation.overtime_id.sudo().duration
if overtime_duration != -1 * duration:
if duration > employee.total_overtime - overtime_duration:
raise ValidationError(_('The employee does not have enough extra hours to extend this allocation.'))
allocation.overtime_id.sudo().duration = -1 * duration
return res
def action_draft(self):
overtime_allocations = self.filtered('overtime_deductible')
if any([a.employee_overtime < float_round(a.number_of_hours_display, 2) for a in overtime_allocations]):
raise ValidationError(_('The employee does not have enough extra hours to request this allocation.'))
res = super().action_draft()
overtime_allocations.overtime_id.sudo().unlink()
for allocation in overtime_allocations:
overtime = self.env['hr.attendance.overtime'].sudo().create({
'employee_id': allocation.employee_id.id,
'date': allocation.date_from,
'adjustment': True,
'duration': -1 * allocation.number_of_hours_display
})
allocation.sudo().overtime_id = overtime.id
deductible = self.employee_id._get_deductible_employee_overtime()
for allocation in self.sudo().filtered('overtime_deductible'):
if deductible[allocation.employee_id] < 0:
raise ValidationError(_('The employee does not have enough overtime hours to request this leave.'))
return res
def action_refuse(self):
res = super().action_refuse()
self.overtime_id.sudo().unlink()
return res
def _get_accrual_plan_level_work_entry_prorata(self, level, start_period, start_date, end_period, end_date):
self.ensure_one()
if level.frequency != 'worked_hours':
return super()._get_accrual_plan_level_work_entry_prorata(level, start_period, start_date, end_period, end_date)
datetime_min_time = datetime.min.time()
start_dt = datetime.combine(start_date, datetime_min_time)
end_dt = datetime.combine(end_date, datetime_min_time)
# Search for any attendance overlapping the window
attendances = self.env['hr.attendance'].sudo().search([
('employee_id', '=', self.employee_id.id),
('check_in', '<', end_dt),
('check_out', '>', start_dt),
])
total_worked_hours = 0.0
for attendance in attendances:
total_worked_hours += attendance._get_worked_hours_in_range(start_dt, end_dt)
return total_worked_hours

View file

@ -1,64 +1,66 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tools.misc import format_duration
from odoo import _, api, fields, models
class HRLeaveType(models.Model):
class HrLeaveType(models.Model):
_inherit = 'hr.leave.type'
hr_attendance_overtime = fields.Boolean(compute='_compute_hr_attendance_overtime')
overtime_deductible = fields.Boolean(
"Deduct Extra Hours", default=False,
help="Once a time off of this type is approved, extra hours in attendances will be deducted.")
def name_get(self):
@api.depends('overtime_deductible', 'requires_allocation')
@api.depends_context('request_type', 'leave', 'holiday_status_display_name', 'employee_id')
def _compute_display_name(self):
# Exclude hours available in allocation contexts, it might be confusing otherwise
if not self.requested_name_get() or self._context.get('request_type', 'leave') == 'allocation':
return super().name_get()
employee_id = False
overtime_leaves = self.env['hr.leave.type']
res = []
for leave_type in self:
if leave_type.overtime_deductible and leave_type.requires_allocation == 'no':
if not employee_id:
employee_id = self.env['hr.employee'].browse(self._context.get('employee_id')).sudo()
if employee_id.total_overtime > 0:
overtime_leaves |= leave_type
name = "%(name)s (%(count)s)" % {
'name': leave_type.name,
'count': _('%s hours available',
format_duration(employee_id.total_overtime)),
}
res.append((leave_type.id, name))
res += super(HRLeaveType, self - overtime_leaves).name_get()
return res
if not self.requested_display_name() or self.env.context.get('request_type', 'leave') == 'allocation':
return super()._compute_display_name()
def get_employees_days(self, employee_ids, date=None):
res = super().get_employees_days(employee_ids, date)
deductible_time_off_type_ids = self.env['hr.leave.type'].search([
employee = self.env['hr.employee'].browse(self.env.context.get('employee_id')).sudo()
unspent_overtime = employee._get_deductible_employee_overtime()[employee]
if not unspent_overtime:
return super()._compute_display_name()
overtime_leaves = self.filtered(lambda l_type: l_type.overtime_deductible and not l_type.requires_allocation)
for leave_type in overtime_leaves:
leave_type.display_name = "%(name)s (%(count)s)" % {
'name': leave_type.name,
'count': _('%s hours available',
format_duration(unspent_overtime)),
}
super(HrLeaveType, self - overtime_leaves)._compute_display_name()
def get_allocation_data(self, employees, target_date=None):
res = super().get_allocation_data(employees, target_date)
deductible_time_off_types = self.env['hr.leave.type'].search([
('overtime_deductible', '=', True),
('requires_allocation', '=', 'no')]).ids
for employee_id, allocations in res.items():
for allocation_id in allocations:
if allocation_id in deductible_time_off_type_ids:
res[employee_id][allocation_id]['virtual_remaining_leaves'] = self.env['hr.employee'].sudo().browse(employee_id).total_overtime
res[employee_id][allocation_id]['overtime_deductible'] = True
else:
res[employee_id][allocation_id]['overtime_deductible'] = False
('requires_allocation', '=', False)])
unspent_overtime = employees._get_deductible_employee_overtime()
for employee in employees:
for leave_type in deductible_time_off_types:
if leave_type in self and employee.sudo().total_overtime > 0:
lt_info = (
leave_type.name,
{
'remaining_leaves': unspent_overtime[employee],
'virtual_remaining_leaves': unspent_overtime[employee],
'max_leaves': 0,
'leaves_taken': 0,
'virtual_leaves_taken': 0,
'closest_allocation_remaining': 0,
'closest_allocation_expire': False,
'total_virtual_excess': 0,
'virtual_excess_data': {},
'request_unit': leave_type.request_unit,
'icon': leave_type.sudo().icon_id.url,
'allows_negative': leave_type.allows_negative,
'max_allowed_negative': leave_type.max_allowed_negative,
'overtime_deductible': True,
'employee_company': employee.company_id.id,
},
leave_type.requires_allocation,
leave_type.id)
res[employee].append(lt_info)
return res
def _get_days_request(self):
res = super()._get_days_request()
res[1]['overtime_deductible'] = self.overtime_deductible
return res
@api.depends('company_id.hr_attendance_overtime')
def _compute_hr_attendance_overtime(self):
# If no company is linked to the time off type, use the current company's setting
for leave_type in self:
if leave_type.company_id:
leave_type.hr_attendance_overtime = leave_type.company_id.hr_attendance_overtime
else:
leave_type.hr_attendance_overtime = self.env.company.hr_attendance_overtime

View file

@ -0,0 +1,16 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class IrUiMenu(models.Model):
_inherit = 'ir.ui.menu'
def _load_menus_blacklist(self):
res = super()._load_menus_blacklist()
if not (
self.env.user.has_group('hr_attendance.group_hr_attendance_manager') and
self.env.user.has_group('hr_holidays.group_hr_holidays_user')
):
res.append(self.env.ref('hr_holidays_attendance.hr_leave_attendance_report').id)
return res

View file

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
class ResCompany(models.Model):
_inherit = 'res.company'
@api.model
def _check_extra_hours_time_off(self):
extra_hours_time_off_type = self.env.ref('hr_holidays_attendance.holiday_status_extra_hours', raise_if_not_found=False)
if not extra_hours_time_off_type:
return
all_companies = self.env['res.company'].sudo().search([])
# Unarchive time of type if the feature is enabled
if any(company.hr_attendance_overtime and not extra_hours_time_off_type.active for company in all_companies):
extra_hours_time_off_type.toggle_active()
# Archive time of type if the feature is disabled for all the company
if all(not company.hr_attendance_overtime and extra_hours_time_off_type.active for company in all_companies):
extra_hours_time_off_type.toggle_active()
def write(self, vals):
res = super().write(vals)
if 'hr_attendance_overtime' in vals:
self._check_extra_hours_time_off()
return res

View file

@ -1,32 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class ResUsers(models.Model):
_inherit = 'res.users'
request_overtime = fields.Boolean(compute='_compute_request_overtime')
@property
def SELF_READABLE_FIELDS(self):
return super().SELF_READABLE_FIELDS + ['request_overtime']
@api.depends_context('uid')
@api.depends('total_overtime')
def _compute_request_overtime(self):
is_holiday_user = self.env.user.has_group('hr_holidays.group_hr_holidays_user')
time_off_types = self.env['hr.leave.type'].search_count([
('requires_allocation', '=', 'yes'),
('employee_requests', '=', 'yes'),
('overtime_deductible', '=', True)
])
for user in self:
if user.total_overtime >= 1:
if is_holiday_user:
user.request_overtime = True
else:
user.request_overtime = time_off_types
else:
user.request_overtime = False

View file

@ -0,0 +1,56 @@
from odoo import api, models
from odoo.fields import Domain
class ResourceCalendarLeaves(models.Model):
_inherit = "resource.calendar.leaves"
def _get_attendance_domain(self):
domain = []
leaves_time_type_leave = self.filtered(lambda leave: leave.time_type == 'leave')
resource_leaves = leaves_time_type_leave.filtered(lambda leave: leave.resource_id.employee_id)
domain.extend([[
('check_in', '<=', max(leaves.mapped('date_to'))),
('check_out', '>=', min(leaves.mapped('date_from'))),
('employee_id', 'in', resource.employee_id.ids),
] for resource, leaves in resource_leaves.grouped('resource_id').items()])
for leave in (leaves_time_type_leave - resource_leaves):
leave_domain = [
('check_in', '<=', leave.date_to),
('check_out', '>=', leave.date_from),
]
if leave.company_id:
leave_domain.append(('employee_id.company_id', '=', leave.company_id.id))
if leave.calendar_id:
leave_domain.append(('employee_id.resource_calendar_id', '=', leave.calendar_id.id))
domain.append(leave_domain)
return Domain.OR(domain)
def _update_attendances_overtime(self, domain=None):
self.env['hr.attendance'].sudo().search(domain or self._get_attendance_domain())._update_overtime()
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
res._update_attendances_overtime()
return res
def write(self, vals):
affects_attendances = bool({'date_from', 'date_to', 'resource_id', 'calendar_id', 'company_id', 'time_type'} & set(vals.keys()))
if affects_attendances:
before_domain = self._get_attendance_domain()
res = super().write(vals)
if affects_attendances:
after_domain = self._get_attendance_domain()
self._update_attendances_overtime(Domain.OR([before_domain, after_domain]))
return res
def unlink(self):
before_domain = self._get_attendance_domain()
res = super().unlink()
self._update_attendances_overtime(before_domain)
return res