hr_holidays_ru/models/holidays.py

198 lines
7.0 KiB
Python

# -*- coding: utf-8 -*-
import logging
from datetime import datetime, date, timedelta
from odoo import _, api, models, fields
from odoo.exceptions import ValidationError
logger = logging.getLogger(__name__)
class HolidaysCalendar(models.Model):
_name = "holidays.calendar"
name = fields.Char(string="Template name", required=True)
year = fields.Selection(
string="Year",
selection="_compute_years_selection",
default=lambda self: str(datetime.today().year),
)
global_leave_ids = fields.One2many(
"holidays.calendar.leaves",
"calendar_id",
"Global Leaves",
copy="true",
)
@api.returns("self", lambda value: value.id)
def copy(self, default=None):
default = dict(default or {})
if "name" not in default:
default["name"] = _("%s (Copy)", self.name)
return super().copy(default=default)
@api.model
def create(self, values):
if values:
self._validate_years(values)
return super(HolidaysCalendar, self).create(values)
@api.model
def get_all_holidays(self, range_start, range_end):
"""The function gets all the holiday dates that are specified in the production calendars for the current displayed year"""
range_start_date = datetime.strptime(range_start, "%Y-%m-%d").date()
range_end_date = datetime.strptime(range_end, "%Y-%m-%d").date()
production_calendars = self.env["holidays.calendar"].search(
[
("year", "in", [range_start_date.year, range_end_date.year]),
]
)
holidays = []
non_working_days = []
rescheduled_work_days = []
for calendar in production_calendars:
self._separate_holidays(
calendar,
holidays,
non_working_days,
rescheduled_work_days,
range_start_date,
range_end_date,
)
return holidays, non_working_days, rescheduled_work_days
def write(self, values=None):
if values:
self._validate_years(values)
return super(HolidaysCalendar, self).write(values)
@staticmethod
def _check_year(user_date, year):
user_year = (
datetime.strptime(user_date, "%Y-%m-%d").year
if isinstance(user_date, str)
else user_date.year
)
if int(year) != user_year:
raise ValidationError(
_("The date's year %s mismatch the template year") % user_year
)
@api.onchange("year")
def _change_years(self):
for event in self.global_leave_ids:
event.date_to = date(
year=int(self.year), month=event.date_to.month, day=event.date_to.day
)
event.date_from = date(
year=int(self.year),
month=event.date_from.month,
day=event.date_from.day,
)
@staticmethod
def _compute_years_selection():
current_year = datetime.today().year
result = [
("{}".format(year), "{}".format(year))
for year in range(current_year - 5, current_year + 10)
]
return result
def _fill_day_gaps(self, start_date, end_date):
"""The function fills in the date gaps between the beginning and the end of the period"""
current_date = start_date
dates_list = []
while current_date <= end_date:
dates_list.append(current_date.strftime("%Y-%m-%d"))
current_date += timedelta(days=1)
return dates_list
def _get_days_from_holidays(self, day, range_start_date, range_end_date):
"""The function gets the value of the start date and the end date of the period returns a list of dates in a string representation with filled intervals"""
start_date = day.date_from
end_date = day.date_to
if range_start_date <= start_date and end_date <= range_end_date:
days_list = self._fill_day_gaps(start_date, end_date)
elif range_start_date >= start_date and end_date <= range_end_date:
days_list = self._fill_day_gaps(range_start_date, end_date)
elif range_start_date <= start_date and end_date >= range_end_date:
days_list = self._fill_day_gaps(start_date, range_end_date)
elif range_start_date >= start_date and end_date >= range_end_date:
days_list = self._fill_day_gaps(range_start_date, range_end_date)
else:
days_list = []
return days_list
def _separate_holidays(
self,
calendar,
holidays,
non_working_days,
rescheduled_work_days,
range_start_date,
range_end_date,
):
"""The function sorts the dates in the production calendar according to the category of the day"""
days = calendar.global_leave_ids
for day in days:
days_list = self._get_days_from_holidays(
day, range_start_date, range_end_date
)
if day.type_transfer_day == "is_holiday":
holidays.extend(days_list)
elif day.type_transfer_day == "is_day_off":
non_working_days.extend(days_list)
elif day.type_transfer_day == "is_workday":
rescheduled_work_days.extend(days_list)
def _validate_years(self, values):
if type(values) != list:
values = [values]
for value in values:
year = value.get("year", self.year)
events = value.get("global_leave_ids", [])
for event in events:
changes = event[2]
if changes:
if changes.get("date_from"):
self._check_year(changes.get("date_from"), year)
if changes.get("date_to"):
self._check_year(changes.get("date_to"), year)
class HolidaysCalendarLeaves(models.Model):
_name = "holidays.calendar.leaves"
_order = "date_from asc"
name = fields.Char("Reason")
type_transfer_day = fields.Selection(
[
("is_holiday", "Holiday"),
("is_day_off", "Non-working day"),
("is_workday", "Rescheduled work day"),
],
string="Type of day",
required=True,
help="Type of day for right calculate count of working hours and vacation days",
)
calendar_id = fields.Many2one("holidays.calendar")
date_from = fields.Date("Start Date", required=True)
date_to = fields.Date("End Date", required=True)
time_type = fields.Selection(
[("leave", "Leave"), ("other", "Other")],
default="leave",
help="Whether this should be computed as a holiday or as work time (eg: formation)",
)
@api.constrains("date_from", "date_to")
def check_dates(self):
if self.filtered(lambda leave: leave.date_from > leave.date_to):
raise ValidationError(
_("The start date of the leave must be not later than end date.")
)