Purpose of this commit is to simplify fields used in leave and allocation
used to store the duration of the requests. We want notably to get rid
of "number of days temp" that, contrary to its name, is not temporary
at all as it stores the real number of days. We also want to clarify
the hours or days encoding that is different on leave and allocation models.
Specifications
* number_of_days on leave and allocation is the master field holding the
leave or allocation duration in days;
* two fields are used for interface that are suffixed by _display in order
to indicate they are UX only: number_of_hours_display and number_of_days_
display. They are interface fields used for the user and correct number
of days is computed and stored in number_of_days;
* more specifically for leave model
* display fields are readonly as it is computed based on dates given by
the user, directly (from / to) or indirectly (half-day leaves for example);
* duration field is computed through an onchange. It could be directly
set by hand by managers if some confusion in the number of days appear;
* more specifically for allocation model
* display fields trigger an inverse as they are the real duration given
by HR officers or managers;
* duration field is computed through the inverse of display fields;
Various reports, data and use of leave / allocation models are updated to
use the correct field.
This commit is linked to task ID 1876795 and PR #26571.
204 lines
9.8 KiB
Python
204 lines
9.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
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
|
|
|
|
|
|
class Department(models.Model):
|
|
|
|
_inherit = 'hr.department'
|
|
|
|
absence_of_today = fields.Integer(
|
|
compute='_compute_leave_count', string='Absence by Today')
|
|
leave_to_approve_count = fields.Integer(
|
|
compute='_compute_leave_count', string='Leave to Approve')
|
|
allocation_to_approve_count = fields.Integer(
|
|
compute='_compute_leave_count', string='Allocation to Approve')
|
|
total_employee = fields.Integer(
|
|
compute='_compute_total_employee', string='Total Employee')
|
|
|
|
@api.multi
|
|
def _compute_leave_count(self):
|
|
Requests = self.env['hr.leave']
|
|
Allocations = self.env['hr.leave.allocation']
|
|
today_date = datetime.datetime.utcnow().date()
|
|
today_start = fields.Datetime.to_string(today_date) # get the midnight of the current utc day
|
|
today_end = fields.Datetime.to_string(today_date + relativedelta(hours=23, minutes=59, seconds=59))
|
|
|
|
leave_data = Requests.read_group(
|
|
[('department_id', 'in', self.ids),
|
|
('state', '=', 'confirm')],
|
|
['department_id'], ['department_id'])
|
|
allocation_data = Allocations.read_group(
|
|
[('department_id', 'in', self.ids),
|
|
('state', '=', 'confirm')],
|
|
['department_id'], ['department_id'])
|
|
absence_data = Requests.read_group(
|
|
[('department_id', 'in', self.ids), ('state', 'not in', ['cancel', 'refuse']),
|
|
('date_from', '<=', today_end), ('date_to', '>=', today_start)],
|
|
['department_id'], ['department_id'])
|
|
|
|
res_leave = dict((data['department_id'][0], data['department_id_count']) for data in leave_data)
|
|
res_allocation = dict((data['department_id'][0], data['department_id_count']) for data in allocation_data)
|
|
res_absence = dict((data['department_id'][0], data['department_id_count']) for data in absence_data)
|
|
|
|
for department in self:
|
|
department.leave_to_approve_count = res_leave.get(department.id, 0)
|
|
department.allocation_to_approve_count = res_allocation.get(department.id, 0)
|
|
department.absence_of_today = res_absence.get(department.id, 0)
|
|
|
|
@api.multi
|
|
def _compute_total_employee(self):
|
|
emp_data = self.env['hr.employee'].read_group([('department_id', 'in', self.ids)], ['department_id'], ['department_id'])
|
|
result = dict((data['department_id'][0], data['department_id_count']) for data in emp_data)
|
|
for department in self:
|
|
department.total_employee = result.get(department.id, 0)
|
|
|
|
|
|
class Employee(models.Model):
|
|
_inherit = "hr.employee"
|
|
|
|
remaining_leaves = fields.Float(
|
|
compute='_compute_remaining_leaves', string='Remaining Legal Leaves',
|
|
help='Total number of legal leaves allocated to this employee, change this value to create allocation/leave request. '
|
|
'Total based on all the leave types without overriding limit.')
|
|
current_leave_state = fields.Selection(compute='_compute_leave_status', string="Current Leave Status",
|
|
selection=[
|
|
('draft', 'New'),
|
|
('confirm', 'Waiting Approval'),
|
|
('refuse', 'Refused'),
|
|
('validate1', 'Waiting Second Approval'),
|
|
('validate', 'Approved'),
|
|
('cancel', 'Cancelled')
|
|
])
|
|
current_leave_id = fields.Many2one('hr.leave.type', compute='_compute_leave_status', string="Current Leave Type")
|
|
leave_date_from = fields.Date('From Date', compute='_compute_leave_status')
|
|
leave_date_to = fields.Date('To Date', compute='_compute_leave_status')
|
|
leaves_count = fields.Float('Number of Leaves', compute='_compute_leaves_count')
|
|
show_leaves = fields.Boolean('Able to see Remaining Leaves', compute='_compute_show_leaves')
|
|
is_absent_totay = fields.Boolean('Absent Today', compute='_compute_absent_employee', search='_search_absent_employee')
|
|
|
|
def _get_remaining_leaves(self):
|
|
""" Helper to compute the remaining leaves for the current employees
|
|
:returns dict where the key is the employee id, and the value is the remain leaves
|
|
"""
|
|
self._cr.execute("""
|
|
SELECT
|
|
sum(h.number_of_days) AS days,
|
|
h.employee_id
|
|
FROM
|
|
(
|
|
SELECT holiday_status_id, number_of_days,
|
|
state, employee_id
|
|
FROM hr_leave_allocation
|
|
UNION
|
|
SELECT holiday_status_id, (number_of_days * -1) as number_of_days,
|
|
state, employee_id
|
|
FROM hr_leave
|
|
) h
|
|
join hr_leave_type s ON (s.id=h.holiday_status_id)
|
|
WHERE
|
|
h.state='validate' AND
|
|
(s.allocation_type='fixed' OR s.allocation_type='fixed_allocation') AND
|
|
h.employee_id in %s
|
|
GROUP BY h.employee_id""", (tuple(self.ids),))
|
|
return dict((row['employee_id'], row['days']) for row in self._cr.dictfetchall())
|
|
|
|
@api.multi
|
|
def _compute_remaining_leaves(self):
|
|
remaining = self._get_remaining_leaves()
|
|
for employee in self:
|
|
employee.remaining_leaves = float_round(remaining.get(employee.id, 0.0), precision_digits=2)
|
|
|
|
@api.multi
|
|
def _compute_leave_status(self):
|
|
# Used SUPERUSER_ID to forcefully get status of other user's leave, to bypass record rule
|
|
holidays = self.env['hr.leave'].sudo().search([
|
|
('employee_id', 'in', self.ids),
|
|
('date_from', '<=', fields.Datetime.now()),
|
|
('date_to', '>=', fields.Datetime.now()),
|
|
('state', 'not in', ('cancel', 'refuse'))
|
|
])
|
|
leave_data = {}
|
|
for holiday in holidays:
|
|
leave_data[holiday.employee_id.id] = {}
|
|
leave_data[holiday.employee_id.id]['leave_date_from'] = holiday.date_from.date()
|
|
leave_data[holiday.employee_id.id]['leave_date_to'] = holiday.date_to.date()
|
|
leave_data[holiday.employee_id.id]['current_leave_state'] = holiday.state
|
|
leave_data[holiday.employee_id.id]['current_leave_id'] = holiday.holiday_status_id.id
|
|
|
|
for employee in self:
|
|
employee.leave_date_from = leave_data.get(employee.id, {}).get('leave_date_from')
|
|
employee.leave_date_to = leave_data.get(employee.id, {}).get('leave_date_to')
|
|
employee.current_leave_state = leave_data.get(employee.id, {}).get('current_leave_state')
|
|
employee.current_leave_id = leave_data.get(employee.id, {}).get('current_leave_id')
|
|
|
|
@api.multi
|
|
def _compute_leaves_count(self):
|
|
all_leaves = self.env['hr.leave.report'].read_group([
|
|
('employee_id', 'in', self.ids),
|
|
('state', '=', 'validate')
|
|
], fields=['number_of_days', 'employee_id'], groupby=['employee_id'])
|
|
mapping = dict([(leave['employee_id'][0], leave['number_of_days']) for leave in all_leaves])
|
|
for employee in self:
|
|
employee.leaves_count = float_round(mapping.get(employee.id, 0), precision_digits=2)
|
|
|
|
@api.multi
|
|
def _compute_show_leaves(self):
|
|
show_leaves = self.env['res.users'].has_group('hr_holidays.group_hr_holidays_user')
|
|
for employee in self:
|
|
if show_leaves or employee.user_id == self.env.user:
|
|
employee.show_leaves = True
|
|
else:
|
|
employee.show_leaves = False
|
|
|
|
@api.multi
|
|
def _compute_absent_employee(self):
|
|
today_date = datetime.datetime.utcnow().date()
|
|
today_start = fields.Datetime.to_string(today_date) # get the midnight of the current utc day
|
|
today_end = fields.Datetime.to_string(today_date + relativedelta(hours=23, minutes=59, seconds=59))
|
|
data = self.env['hr.leave'].read_group([
|
|
('employee_id', 'in', self.ids),
|
|
('state', 'not in', ['cancel', 'refuse']),
|
|
('date_from', '<=', today_end),
|
|
('date_to', '>=', today_start)
|
|
], ['employee_id'], ['employee_id'])
|
|
result = dict.fromkeys(self.ids, False)
|
|
for item in data:
|
|
if item['employee_id_count'] >= 1:
|
|
result[item['employee_id'][0]] = True
|
|
for employee in self:
|
|
employee.is_absent_totay = result[employee.id]
|
|
|
|
@api.multi
|
|
def _search_absent_employee(self, operator, value):
|
|
today_date = datetime.datetime.utcnow().date()
|
|
today_start = fields.Datetime.to_string(today_date) # get the midnight of the current utc day
|
|
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']),
|
|
('date_from', '<=', today_end),
|
|
('date_to', '>=', today_start)
|
|
])
|
|
return [('id', 'in', holidays.mapped('employee_id').ids)]
|
|
|
|
def write(self, values):
|
|
res = super(Employee, self).write(values)
|
|
if 'parent_id' in values or 'department_id' in values:
|
|
hr_vals = {}
|
|
if values.get('parent_id') is not None:
|
|
hr_vals['manager_id'] = values['parent_id']
|
|
if values.get('department_id') is not None:
|
|
hr_vals['department_id'] = values['department_id']
|
|
holidays = self.env['hr.leave'].search([('state', 'in', ['draft', 'confirm']), ('employee_id', 'in', self.ids)])
|
|
holidays.write(hr_vals)
|
|
allocations = self.env['hr.leave.allocation'].search([('state', 'in', ['draft', 'confirm']), ('employee_id', 'in', self.ids)])
|
|
allocations.write(hr_vals)
|
|
return res
|