[MOV]hr_holidays_timeoff_analysis:move hr_holidays_timeoff_analysis from elabore-addons repos to hr-tools repos
This commit is contained in:
1
hr_holidays_timeoff_analysis/models/__init__.py
Normal file
1
hr_holidays_timeoff_analysis/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import hr_leave_timeoff_day
|
||||
196
hr_holidays_timeoff_analysis/models/hr_leave_timeoff_day.py
Normal file
196
hr_holidays_timeoff_analysis/models/hr_leave_timeoff_day.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import timedelta
|
||||
|
||||
import pytz
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class TimeOffDay(models.Model):
|
||||
_name = "hr.leave.timeoff.day"
|
||||
_description = "Timeoff Day"
|
||||
_order = "date desc"
|
||||
|
||||
date = fields.Date()
|
||||
employee_id = fields.Many2one("hr.employee")
|
||||
hr_leave_id = fields.Many2one("hr.leave")
|
||||
hr_leave_type = fields.Many2one(related="hr_leave_id.holiday_status_id")
|
||||
leave_duration_by_day = fields.Float()
|
||||
|
||||
def employee_is_scheduled_to_work_this_day(self, date, employee, leave):
|
||||
"""
|
||||
Check if the employee is scheduled to work on this day according to his
|
||||
calendar.
|
||||
"""
|
||||
calendar = self.get_calendar(employee, date)
|
||||
if not calendar or not calendar.attendance_ids:
|
||||
return False
|
||||
day_of_week = str(date.weekday())
|
||||
attendances = calendar.attendance_ids.filtered(
|
||||
lambda att: att.dayofweek == day_of_week
|
||||
and not att.display_type
|
||||
)
|
||||
if calendar.two_weeks_calendar:
|
||||
# Only keep attendances of the week matching the target date
|
||||
week_type = str(
|
||||
self.env["resource.calendar.attendance"].get_week_type(date)
|
||||
)
|
||||
attendances = attendances.filtered(
|
||||
lambda att: att.week_type == week_type
|
||||
)
|
||||
return bool(attendances)
|
||||
|
||||
def get_calendar(self, employee, date, leave=None):
|
||||
"""
|
||||
Get the working time calendar of the employee.
|
||||
"""
|
||||
calendar = False
|
||||
# check if hr_employee_calendar model exists
|
||||
# (module hr_employee_calendar_planning is installed)
|
||||
if "hr.employee.calendar" in self.env:
|
||||
hr_employee_calendar = self.env["hr.employee.calendar"].search(
|
||||
[
|
||||
("employee_id", "=", employee.id),
|
||||
("date_start", "<=", date),
|
||||
"|",
|
||||
("date_end", ">=", date),
|
||||
("date_end", "=", False),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if hr_employee_calendar and hr_employee_calendar.calendar_id:
|
||||
calendar = hr_employee_calendar.calendar_id
|
||||
# If no hr_employee_calendar found or calendar found
|
||||
# in hr_employee_calendar, take the one set on the employee
|
||||
if not calendar and employee.resource_calendar_id:
|
||||
calendar = employee.resource_calendar_id
|
||||
# If no specific calendar is set on the employee, take the one of the company
|
||||
if not calendar and employee.company_id.resource_calendar_id:
|
||||
calendar = employee.company_id.resource_calendar_id
|
||||
return calendar
|
||||
|
||||
def _convert_to_employee_tz(self, date, employee):
|
||||
"""
|
||||
Convert a UTC datetime to the employee's timezone datetime.
|
||||
"""
|
||||
employee_tz = pytz.timezone(employee.tz or "UTC")
|
||||
if date.tzinfo is None:
|
||||
dt = pytz.utc.localize(date)
|
||||
return dt.astimezone(employee_tz)
|
||||
|
||||
def _is_public_holiday_according_to_employe_tz(self, date, employee):
|
||||
if not date or not employee:
|
||||
return False
|
||||
# get public holidays for the employee
|
||||
public_holidays = employee._get_public_holidays(date, date)
|
||||
if not public_holidays:
|
||||
return False
|
||||
if len(public_holidays) > 1:
|
||||
raise UserError(
|
||||
_(
|
||||
"Several holidays have been found on the"
|
||||
" date '%s'. Please correct the anomaly"
|
||||
" before continuing."
|
||||
)
|
||||
% date
|
||||
)
|
||||
ph = public_holidays[0]
|
||||
# Convert public holiday to the employee timezone
|
||||
ph_datetime_from_tz = self._convert_to_employee_tz(ph.date_from, employee)
|
||||
ph_datetime_to_tz = self._convert_to_employee_tz(ph.date_to, employee)
|
||||
# Convert datetime to date
|
||||
ph_date_from = ph_datetime_from_tz.date()
|
||||
ph_date_to = ph_datetime_to_tz.date()
|
||||
# Check if the stat date falls within the public
|
||||
# holiday range after conversion in employee tz
|
||||
if ph_date_from <= date <= ph_date_to:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def compute_leave_duration_by_day(self, leave):
|
||||
"""
|
||||
Compute the leave duration by day based on the leave type.
|
||||
"""
|
||||
leave_duration_by_day = 0.0
|
||||
# Full day case
|
||||
if leave.request_unit_half:
|
||||
leave_duration_by_day = 0.5
|
||||
elif leave.request_unit_hours:
|
||||
leave_duration_by_day = leave.number_of_days
|
||||
else:
|
||||
leave_duration_by_day = 1.0
|
||||
return leave_duration_by_day
|
||||
|
||||
@api.model
|
||||
def cron_manage_timeoff_days(self):
|
||||
self.cron_create_timeoff_days()
|
||||
self.cron_delete_timeoff_days()
|
||||
|
||||
def cron_create_timeoff_days(self):
|
||||
# Browse all validated leaves
|
||||
leaves = self.env["hr.leave"].search(
|
||||
[
|
||||
("state", "=", "validate"),
|
||||
("request_date_from", "!=", False),
|
||||
("request_date_to", "!=", False),
|
||||
("employee_id", "!=", False),
|
||||
]
|
||||
)
|
||||
for leave in leaves:
|
||||
current_date = leave.request_date_from
|
||||
employee = leave.employee_id
|
||||
while current_date <= leave.request_date_to:
|
||||
if self.employee_is_scheduled_to_work_this_day(
|
||||
current_date, employee, leave
|
||||
) and not self._is_public_holiday_according_to_employe_tz(
|
||||
current_date, employee
|
||||
):
|
||||
# The employee is scheduled to work this day
|
||||
# according his calendar and it's not a
|
||||
# public holiday,
|
||||
# so create a timeoff day record if it does not already exist
|
||||
if not self.search(
|
||||
[
|
||||
("date", "=", current_date),
|
||||
("employee_id", "=", employee.id),
|
||||
("hr_leave_id", "=", leave.id),
|
||||
],
|
||||
limit=1,
|
||||
):
|
||||
self.create(
|
||||
{
|
||||
"date": current_date,
|
||||
"employee_id": employee.id,
|
||||
"hr_leave_id": leave.id,
|
||||
"leave_duration_by_day": (
|
||||
self.compute_leave_duration_by_day(leave)
|
||||
),
|
||||
}
|
||||
)
|
||||
current_date += timedelta(days=1)
|
||||
|
||||
def cron_delete_timeoff_days(self):
|
||||
# Browse all unvalidated or canceled leaves
|
||||
leaves = self.env["hr.leave"].search(
|
||||
[
|
||||
("state", "!=", "validate"),
|
||||
("request_date_from", "!=", False),
|
||||
("request_date_to", "!=", False),
|
||||
("employee_id", "!=", False),
|
||||
]
|
||||
)
|
||||
# Delete timeoff days for leaves that are no longer validated
|
||||
for leave in leaves:
|
||||
self.search(
|
||||
[
|
||||
("hr_leave_id", "=", leave.id),
|
||||
]
|
||||
).unlink()
|
||||
# Delete timeoff days that are not linked to any leave
|
||||
self.search(
|
||||
[
|
||||
("hr_leave_id", "=", False),
|
||||
]
|
||||
).unlink()
|
||||
Reference in New Issue
Block a user