Files
enterprise-addons/hr_attendance_gantt/models/hr_attendance.py
T

119 lines
5.8 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from dateutil.relativedelta import relativedelta
from pytz import UTC, utc, timezone
from odoo import api, fields, models
from odoo.fields import Domain
from odoo.tools import float_is_zero
class HrAttendance(models.Model):
_inherit = "hr.attendance"
color = fields.Integer("Color", compute='_compute_color')
overtime_progress = fields.Float(compute="_compute_overtime_progress")
def _compute_overtime_progress(self):
for attendance in self:
if not float_is_zero(attendance.worked_hours, precision_digits=2):
attendance.overtime_progress = 100 - ((attendance.overtime_hours / attendance.worked_hours) * 100)
else:
attendance.overtime_progress = 100
def _gantt_progress_bar(self, field, res_ids, start, stop):
if not self.env.user._is_internal():
return {}
if field == 'employee_id':
start, stop = utc.localize(start), utc.localize(stop)
return self._gantt_progress_bar_employee_ids(res_ids, start, stop)
raise NotImplementedError
def _gantt_compute_max_work_hours_within_interval(self, employee, start, stop):
"""
Compute the total work hours of the employee based on the intervals selected on the Gantt view.
The calculation takes into account the working calendar (flexible or not).
"""
user_tz = timezone(self.env.user.tz or 'UTC')
start = datetime.combine(start.astimezone(user_tz).date(), datetime.min.time(), tzinfo=UTC)
stop = datetime.combine(stop.astimezone(user_tz).date(), datetime.min.time(), tzinfo=UTC)
return self.env['resource.calendar']._get_attendance_intervals_days_data(employee._employee_attendance_intervals(start, stop))['hours']
def _get_gantt_progress_bar_domain(self, res_ids, start, stop):
return [
('employee_id', 'in', res_ids),
('check_in', '>=', start.replace(tzinfo=None)),
('check_out', '<=', stop.replace(tzinfo=None)),
]
def _gantt_progress_bar_employee_ids(self, res_ids, start, stop):
"""
Resulting display is worked hours / expected worked hours
"""
values = {}
worked_hours_group = self._read_group(
self._get_gantt_progress_bar_domain(res_ids, start, stop),
groupby=['employee_id'],
aggregates=['worked_hours:sum']
)
employee_data = {emp.id: worked_hours for emp, worked_hours in worked_hours_group}
employees = self.env['hr.employee'].browse(res_ids)
for employee in employees:
# Retrieve expected attendance for that employee
values[employee.id] = {
'value': employee_data.get(employee.id, 0),
'max_value': self._gantt_compute_max_work_hours_within_interval(employee, start, stop),
'is_fully_flexible_hours': employee.resource_id._is_fully_flexible(),
}
return values
@api.model
def get_gantt_data(self, domain, groupby, read_specification, limit=None, offset=0, unavailability_fields=None, progress_bar_fields=None, start_date=None, stop_date=None, scale=None):
"""
We override get_gantt_data to allow the display of open-ended records,
We also want to add in the gantt rows, the active emloyees that have a check in in the previous 60 days
"""
domain = Domain(domain) & Domain(self.env.context.get('active_domain') or Domain.TRUE)
open_ended_gantt_data = super().get_gantt_data(domain, groupby, read_specification, limit=limit, offset=offset, unavailability_fields=unavailability_fields, progress_bar_fields=progress_bar_fields, start_date=start_date, stop_date=stop_date, scale=scale)
if self.env.context.get('gantt_start_date') and groupby and groupby[0] == 'employee_id':
user_domain = Domain(self.env.context.get('user_domain'))
active_employees_domain = user_domain & Domain([
'&',
('check_out', '<', start_date),
('check_in', '>', fields.Datetime.from_string(start_date) - relativedelta(days=60)),
('employee_id', 'not in', [group['employee_id'][0] for group in open_ended_gantt_data['groups']]),
('employee_id.active', '=', True)
])
previously_active_employees = super().get_gantt_data(active_employees_domain, groupby, read_specification, limit=None, offset=0, unavailability_fields=unavailability_fields, progress_bar_fields=progress_bar_fields, start_date=start_date, stop_date=stop_date, scale=scale)
for group in previously_active_employees['groups']:
del group['__record_ids'] # Records are not needed here
open_ended_gantt_data['groups'].append(group)
open_ended_gantt_data['length'] += 1
if unavailability_fields:
for field in open_ended_gantt_data['unavailabilities']:
open_ended_gantt_data['unavailabilities'][field] |= previously_active_employees['unavailabilities'][field]
return open_ended_gantt_data
@api.model
def _gantt_unavailability(self, field, res_ids, start, stop, scale):
if field != "employee_id":
return super()._gantt_unavailability(field, res_ids, start, stop, scale)
employees = self.env['hr.employee'].browse(res_ids)
return employees._get_employee_unavailable_intervals(start, stop)
def action_open_details(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"res_model": "hr.attendance",
"views": [[self.env.ref('hr_attendance.hr_attendance_view_form').id, "form"]],
"res_id": self.id
}