mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-27 01:12:01 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
""")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue