Skip to content

Commit

Permalink
[IMP] hr_holidays: refactor leave duration
Browse files Browse the repository at this point in the history
The idea of current date_{from,to} computations is as follows:

The user selects the request_date_{from,end} (and optionally
request_hour_{from,to} and these inputs are then processed into a
date_{to,from}, taking into account the type of leave, the work schedule
(resource_calendar) and time zone (since date_{to,from} are saved in UTC
while the request_dates are stored in the user's timezone.

However, in practice this computation is very messy, resulting in
date_{to,from} needing to be specified in all demo data and test cases,
even though it should be derived from the request dates. Various
superfluous or poorly named methods also exist in this flow
(eg _get_start_or_end_from_attendance which really performs a timezone
conversion, the logic of which resource calendar to use is scattered
across the whole model etc).

date_{to,from} are used many times as inputs throughout the code, with
code being present te inverse compute request_date_{from,to} from these
values. However in reality this is not possible to do consistently.

Therefore with this commit, we restore request_date_{from,to} as the
sole possible inputs, with date_{from,to} being derived from them. In
addition, the timezone and resource calendar are consolidated into their
own fields, with a single computation method computing them.

task-3081565

Part-of: #119317
  • Loading branch information
robcoekaerts committed Sep 26, 2023
1 parent 88b55a6 commit 6074d86
Show file tree
Hide file tree
Showing 35 changed files with 762 additions and 942 deletions.
72 changes: 24 additions & 48 deletions addons/hr_holidays/data/hr_holidays_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,17 @@
<record id="hr_holidays_cl" model="hr.leave">
<field name="name">Trip with Family</field>
<field name="holiday_status_id" ref="holiday_status_comp"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="date_from"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="date_to"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="request_date_from"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(day=1, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(day=1, weekday=0) + relativedelta(weekday=2))"/>
<field name="employee_id" ref="hr.employee_admin"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_admin'))]"/>
</record>

<record id="hr_holidays_sl" model="hr.leave">
<field name="name">Doctor Appointment</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now() + relativedelta(day=20, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="date_from"/>
<field eval="(datetime.now() + relativedelta(day=20, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="date_to"/>
<field eval="(datetime.now() + relativedelta(day=20, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="request_date_from"/>
<field eval="(datetime.now() + relativedelta(day=20, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(day=20, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(day=20, weekday=0) + relativedelta(weekday=2))"/>
<field name="employee_id" ref="hr.employee_admin"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_admin'))]"/>
<field name="state">confirm</field>
Expand Down Expand Up @@ -189,10 +185,8 @@
<record id="hr_holidays_cl_al" model="hr.leave">
<field name="name">Trip with Friends</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field eval="time.strftime('%Y-%m-04')" name="date_from"/>
<field eval="time.strftime('%Y-%m-10')" name="date_to"/>
<field eval="time.strftime('%Y-%m-04')" name="request_date_from"/>
<field eval="time.strftime('%Y-%m-10')" name="request_date_to"/>
<field name="request_date_from" eval="time.strftime('%Y-%m-14')"/>
<field name="request_date_to" eval="time.strftime('%Y-%m-20')"/>
<field name="employee_id" ref="hr.employee_al"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_al'))]"/>
</record>
Expand All @@ -203,10 +197,8 @@
<record id="hr_holidays_sl_al" model="hr.leave">
<field name="name">Dentist appointment</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now()+relativedelta(months=1, day=17, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="date_from"/>
<field eval="(datetime.now()+relativedelta(months=1, day=17, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="date_to"/>
<field eval="(datetime.now()+relativedelta(months=1, day=17, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="request_date_from"/>
<field eval="(datetime.now()+relativedelta(months=1, day=17, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(months=1, day=17, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(months=1, day=17, weekday=0) + relativedelta(weekday=2))"/>
<field name="employee_id" ref="hr.employee_al"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_al'))]"/>
<field name="state">confirm</field>
Expand Down Expand Up @@ -246,10 +238,8 @@
<record id="hr_holidays_cl_mit" model="hr.leave">
<field name="name">Trip to Paris</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field eval="time.strftime('%Y-%m-18')" name="date_from"/>
<field eval="time.strftime('%Y-%m-24')" name="date_to"/>
<field eval="time.strftime('%Y-%m-18')" name="request_date_from"/>
<field eval="time.strftime('%Y-%m-24')" name="request_date_to"/>
<field name="request_date_from" eval="time.strftime('%Y-%m-22')"/>
<field name="request_date_to" eval="time.strftime('%Y-%m-28')"/>
<field name="employee_id" ref="hr.employee_mit"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_mit'))]"/>
</record>
Expand All @@ -260,10 +250,8 @@
<record id="hr_holidays_cl_mit_2" model="hr.leave">
<field name="name">Trip</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field eval="(datetime.now() + relativedelta(day=5, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="date_from"/>
<field eval="(datetime.now() + relativedelta(day=5, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="date_to"/>
<field eval="(datetime.now() + relativedelta(day=5, weekday=0)).strftime('%Y-%m-%d 04:00:00')" name="request_date_from"/>
<field eval="(datetime.now() + relativedelta(day=5, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 20:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(day=5, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(day=5, weekday=0) + relativedelta(weekday=2))"/>
<field name="employee_id" ref="hr.employee_mit"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_mit'))]"/>
<field name="state">confirm</field>
Expand Down Expand Up @@ -303,10 +291,8 @@
<record id="hr_holidays_cl_qdp" model="hr.leave">
<field name="name">Sick day</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now()+relativedelta(months=1, day=3, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="date_from"/>
<field eval="(datetime.now()+relativedelta(months=1, day=3, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 23:00:00')" name="date_to"/>
<field eval="(datetime.now()+relativedelta(months=1, day=3, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="request_date_from"/>
<field eval="(datetime.now()+relativedelta(months=1, day=3, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 23:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date()+relativedelta(months=1, day=3, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date()+relativedelta(months=1, day=3, weekday=0) + relativedelta(weekday=2))"/>
<field name="employee_id" ref="hr.employee_qdp"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_qdp'))]"/>
<field name="state">confirm</field>
Expand All @@ -318,10 +304,8 @@
<record id="hr_holidays_sl_qdp" model="hr.leave">
<field name="name">Sick day</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="date_from"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0) + relativedelta(days=2)).strftime('%Y-%m-%d 23:00:00')" name="date_to"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="request_date_from"/>
<field eval="(datetime.now() + relativedelta(day=1, weekday=0) + relativedelta(days=2)).strftime('%Y-%m-%d 23:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(day=1, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(day=1, weekday=0) + relativedelta(days=2))"/>
<field name="employee_id" ref="hr.employee_qdp"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_qdp'))]"/>
<field name="state">confirm</field>
Expand Down Expand Up @@ -387,10 +371,8 @@
<record id="hr_holidays_cl_vad" model="hr.leave">
<field name="name">Trip to London</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field eval="time.strftime('%Y-%m-09')" name="date_from"/>
<field eval="time.strftime('%Y-%m-16')" name="date_to"/>
<field eval="time.strftime('%Y-%m-09')" name="request_date_from"/>
<field eval="time.strftime('%Y-%m-16')" name="request_date_to"/>
<field name="request_date_from" eval="time.strftime('%Y-%m-09')"/>
<field name="request_date_to" eval="time.strftime('%Y-%m-16')"/>
<field name="employee_id" ref="hr.employee_niv"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_niv'))]"/>
<field name="state">confirm</field>
Expand All @@ -402,10 +384,8 @@
<record id="hr_holidays_sl_vad" model="hr.leave">
<field name="name">Doctor Appointment</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now() + relativedelta(day=25, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="date_from"/>
<field eval="(datetime.now() + relativedelta(day=25, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 23:00:00')" name="date_to"/>
<field eval="(datetime.now() + relativedelta(day=25, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="request_date_from"/>
<field eval="(datetime.now() + relativedelta(day=25, weekday=0) + relativedelta(weekday=2)).strftime('%Y-%m-%d 23:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(day=25, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(day=25, weekday=0) + relativedelta(weekday=2))"/>
<field name="employee_id" ref="hr.employee_niv"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_niv'))]"/>
<field name="state">confirm</field>
Expand Down Expand Up @@ -442,10 +422,8 @@
<record id="hr_holidays_sl_kim" model="hr.leave">
<field name="name">Dentist appointment</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now()+relativedelta(months=1, day=1, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="date_from"/>
<field eval="(datetime.now()+relativedelta(months=1, day=1, weekday=0)).strftime('%Y-%m-%d 23:00:00')" name="date_to"/>
<field eval="(datetime.now()+relativedelta(months=1, day=1, weekday=0)).strftime('%Y-%m-%d 01:00:00')" name="request_date_from"/>
<field eval="(datetime.now()+relativedelta(months=1, day=1, weekday=0)).strftime('%Y-%m-%d 23:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date() + relativedelta(months=1, day=1, weekday=0))"/>
<field name="request_date_to" eval="(datetime.today().date() + relativedelta(months=1, day=1, weekday=0))"/>
<field name="employee_id" ref="hr.employee_jve"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_jve'))]"/>
<field name="state">confirm</field>
Expand All @@ -457,10 +435,8 @@
<record id="hr_holidays_sl_kim_2" model="hr.leave">
<field name="name">Second dentist appointment</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="(datetime.now()+relativedelta(months=4, day=1, weekday=2)).strftime('%Y-%m-%d 01:00:00')" name="date_from"/>
<field eval="(datetime.now()+relativedelta(months=4, day=1, weekday=2)).strftime('%Y-%m-%d 23:00:00')" name="date_to"/>
<field eval="(datetime.now()+relativedelta(months=4, day=1, weekday=2)).strftime('%Y-%m-%d 01:00:00')" name="request_date_from"/>
<field eval="(datetime.now()+relativedelta(months=4, day=1, weekday=2)).strftime('%Y-%m-%d 23:00:00')" name="request_date_to"/>
<field name="request_date_from" eval="(datetime.today().date()+relativedelta(months=4, day=1, weekday=2))"/>
<field name="request_date_to" eval="(datetime.today().date()+relativedelta(months=4, day=1, weekday=2))"/>
<field name="employee_id" ref="hr.employee_jve"/>
<field name="employee_ids" eval="[(4, ref('hr.employee_jve'))]"/>
<field name="state">confirm</field>
Expand Down
16 changes: 15 additions & 1 deletion addons/hr_holidays/models/hr_employee.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytz

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_round
from odoo.addons.resource.models.utils import HOURS_PER_DAY

Expand Down Expand Up @@ -220,6 +220,20 @@ def write(self, values):
# remove users from the Responsible group if they are no longer leave managers
old_managers.sudo()._clean_leave_responsible_users()

# Change the resource calendar of the employee's leaves in the future
# Other modules can disable this behavior by setting the context key
# 'no_leave_resource_calendar_update'
if 'resource_calendar_id' in values and not self.env.context.get('no_leave_resource_calendar_update'):
try:
self.env['hr.leave'].search([
('employee_id', 'in', self.ids),
('resource_calendar_id', '!=', int(values['resource_calendar_id'])),
('date_from', '>', fields.Datetime.now())]).write({'resource_calendar_id': values['resource_calendar_id']})
except ValidationError:
raise ValidationError(_("Changing this working schedule results in the affected employee(s) not having enough "
"leaves allocated to accomodate for their leaves already taken in the future. Please "
"review this employee's leaves and adjust their allocation accordingly."))

if 'parent_id' in values or 'department_id' in values:
today_date = fields.Datetime.now()
hr_vals = {}
Expand Down

0 comments on commit 6074d86

Please sign in to comment.