-
-
Notifications
You must be signed in to change notification settings - Fork 151
Expand file tree
/
Copy pathhr_leave.py
More file actions
166 lines (146 loc) · 6.24 KB
/
hr_leave.py
File metadata and controls
166 lines (146 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Copyright 2016-2019 Onestein (<https://www.onestein.eu>)
# Copyright 2024- Le Filament (https://le-filament.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from dateutil.relativedelta import relativedelta
from pytz import timezone, utc
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
class HrLeave(models.Model):
_inherit = "hr.leave"
repeat_every = fields.Selection(
[
("workday", "Every workday"),
("week", "Every week"),
("biweek", "Every two weeks"),
("month", "Every four weeks"),
]
)
repeat_mode = fields.Selection(
[("times", "Number of Times"), ("date", "End Date")], default="times"
)
holiday_type_repeat = fields.Boolean(related="holiday_status_id.repeat")
repeat_limit = fields.Integer(default=1, string="Repeat # times")
repeat_end_date = fields.Date(default=lambda self: fields.Date.today())
@api.model
def _update_repeated_workday_dates(self, resource_calendar, from_dt, to_dt, days):
client_tz = timezone(self._context.get("tz") or self.env.user.tz or "UTC")
from_dt = from_dt.astimezone(client_tz)
to_dt = to_dt.astimezone(client_tz)
work_hours = resource_calendar.get_work_hours_count(
from_dt, to_dt, compute_leaves=False
)
while work_hours:
previous_from_dt = from_dt
previous_to_dt = to_dt
from_dt = from_dt + relativedelta(days=days)
to_dt = to_dt + relativedelta(days=days)
# Handle case where Daylight Saving Time changes between 2 dates
# We want to keep the same hours in localized time
from_dt = from_dt.astimezone(client_tz)
to_dt = to_dt.astimezone(client_tz)
from_dt_dst_diff = previous_from_dt.dst() - from_dt.dst()
to_dt_dst_diff = previous_to_dt.dst() - to_dt.dst()
from_dt = from_dt + from_dt_dst_diff
from_dt = from_dt.astimezone(client_tz)
to_dt = to_dt + to_dt_dst_diff
to_dt = to_dt.astimezone(client_tz)
new_work_hours = resource_calendar.get_work_hours_count(
from_dt, to_dt, compute_leaves=True
)
if new_work_hours and work_hours <= new_work_hours:
break
return from_dt, to_dt
@api.model
def _get_repeated_vals_dict(self):
return {
"workday": {
"days": 1,
"user_error_msg": self.env._(
"The repetition is based on workdays: the duration of "
"the leave request must not exceed 1 day."
),
},
"week": {
"days": 7,
"user_error_msg": self.env._(
"The repetition is every week: the duration of the "
"leave request must not exceed 1 week."
),
},
"biweek": {
"days": 14,
"user_error_msg": self.env._(
"The repetition is every two weeks: the duration of the "
"leave request must not exceed 2 weeks."
),
},
"month": {
"days": 28,
"user_error_msg": self.env._(
"The repetition is every four weeks: the duration of the "
"leave request must not exceed 28 days."
),
},
}
@api.model
def _update_repeated_leave_vals(self, leave, resource_calendar):
vals_dict = self._get_repeated_vals_dict()
param_dict = vals_dict[leave.repeat_every]
from_dt = fields.Datetime.from_string(leave.date_from)
to_dt = fields.Datetime.from_string(leave.date_to)
if (to_dt - from_dt).days > param_dict["days"]:
raise UserError(param_dict["user_error_msg"])
from_dt, to_dt = self._update_repeated_workday_dates(
resource_calendar, from_dt, to_dt, param_dict["days"]
)
return {
"employee_id": leave.employee_id.id,
"date_from": from_dt.astimezone(utc).replace(tzinfo=None),
"date_to": to_dt.astimezone(utc).replace(tzinfo=None),
"request_date_from": from_dt,
"request_date_to": to_dt,
}
@api.model
def create_repeated_handler(self, leave, resource_calendar):
def _check_repeating(count, leave, date_to):
repeat_mode = leave.repeat_mode
if repeat_mode == "times" and count < leave.repeat_limit:
return True
if repeat_mode == "date" and date_to.date() <= leave.repeat_end_date:
return True
return False
count = 1
leaves = self.env["hr.leave"]
vals = self._update_repeated_leave_vals(leave, resource_calendar)
while _check_repeating(count, leave, vals.get("date_to")):
leave = leave.with_context(skip_create_handler=True).copy(vals)
leaves += leave
count += 1
vals = self._update_repeated_leave_vals(leave, resource_calendar)
return leaves
@api.model_create_multi
def create(self, vals_list):
res = super().create(vals_list)
skip_create_handler = self.env.context.get("skip_create_handler")
if skip_create_handler:
return res
for leave in res.filtered(
lambda leave: leave.repeat_every and leave.repeat_mode
):
resource_calendar = leave.resource_calendar_id
res += self.create_repeated_handler(leave, resource_calendar)
return res
@api.constrains("repeat_mode", "repeat_limit", "repeat_end_date")
def _check_repeat_limit(self):
for record in self:
if record.repeat_mode == "times" and record.repeat_limit < 0:
raise ValidationError(
self.env._("Please set a positive amount of repetitions.")
)
if (
record.repeat_mode == "date"
and record.repeat_end_date < record.date_from.date()
):
raise ValidationError(
self.env._("The Repeat End Date cannot be before the leave.")
)