[FIX] hr_holidays: round the number of days of a time off

When the user is set in another timezone than the context timezone, the first or last work interval retrieved to calculate the number of days
between two dates is starting later or ending earlier depending on the user timezone.
The number of days displayed between two dates is therefore not an integer.

e.g.
A time off taken between 08/06/2021 and 10/06/2021 (timezone in the context is Europe/Brussels) will give:
- Number of days: 3 days if the user is in Europe/Brussels
- Number of days: 2.69 if the user is in Asia/Calcutta (first work interval starting later)
- Number of days: 2.5 if the user is in US/Pacific (last work interval ending earlier)

task-2646655

closes odoo/odoo#77036

Related: odoo/upgrade#3486
Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
Philippe Wauthy
2022-05-09 15:01:08 +02:00
parent be6f3638cf
commit 81c8a0564d
6 changed files with 165 additions and 109 deletions
+139 -87
View File
@@ -89,25 +89,34 @@ class HolidaysRequest(models.Model):
lt = self.env['hr.leave.type'].browse(defaults.get('holiday_status_id'))
defaults['state'] = 'confirm'
if 'date_from' and 'date_to' in fields_list:
now = fields.Datetime.now()
if 'date_from' not in defaults:
defaults['date_from'] = now.replace(hour=0, minute=0, second=0)
if 'date_to' not in defaults:
defaults['date_to'] = now.replace(hour=23, minute=59, second=59)
if (not self._context.get('leave_compute_date_from_to') and defaults['date_from'].time() == time(0, 0, 0) and
defaults['date_to'].time() == time(23, 59, 59)):
employee = self.env['hr.employee'].browse(defaults['employee_id']) if defaults.get('employee_id') else self.env.user.employee_id
date_from = defaults['date_from'].date()
date_to = defaults['date_to'].date()
attendance_from, attendance_to = self._get_attendances(employee, date_from, date_to)
defaults['date_from'] = self._get_start_or_end_from_attendance(attendance_from.hour_from, date_from, employee)
defaults['date_to'] = self._get_start_or_end_from_attendance(attendance_to.hour_to, date_to, employee)
defaults = self._default_get_request_parameters(defaults)
return defaults
def _default_get_request_parameters(self, values):
new_values = dict(values)
global_from, global_to = False, False
# TDE FIXME: consider a mapping on several days that is not the standard
# calendar widget 7-19 in user's TZ is some custom input
if values.get('date_from'):
user_tz = self.env.user.tz or 'UTC'
localized_dt = timezone('UTC').localize(values['date_from']).astimezone(timezone(user_tz))
global_from = localized_dt.time().hour == 7 and localized_dt.time().minute == 0
new_values['request_date_from'] = localized_dt.date()
new_values['request_date_from'] = values['date_from'].date()
if values.get('date_to'):
user_tz = self.env.user.tz or 'UTC'
localized_dt = timezone('UTC').localize(values['date_to']).astimezone(timezone(user_tz))
global_to = localized_dt.time().hour == 19 and localized_dt.time().minute == 0
new_values['request_date_to'] = localized_dt.date()
if global_from and global_to:
new_values['request_unit_custom'] = True
new_values['request_date_to'] = values['date_to'].date()
return new_values
active = fields.Boolean(default=True)
@@ -275,7 +284,6 @@ class HolidaysRequest(models.Model):
# request type
request_unit_half = fields.Boolean('Half Day', compute='_compute_request_unit_half', store=True, readonly=False)
request_unit_hours = fields.Boolean('Custom Hours', compute='_compute_request_unit_hours', store=True, readonly=False)
request_unit_custom = fields.Boolean('Days-long custom hours', compute='_compute_request_unit_custom', store=True, readonly=False)
# view
is_hatched = fields.Boolean('Hatched', compute='_compute_is_hatched')
is_striked = fields.Boolean('Striked', compute='_compute_is_hatched')
@@ -391,7 +399,7 @@ class HolidaysRequest(models.Model):
leave.holiday_allocation_id = False
@api.depends('request_date_from_period', 'request_hour_from', 'request_hour_to', 'request_date_from', 'request_date_to',
'request_unit_half', 'request_unit_hours', 'request_unit_custom', 'employee_id')
'request_unit_half', 'request_unit_hours', 'employee_id')
def _compute_date_from_to(self):
for holiday in self:
if holiday.request_date_from and holiday.request_date_to and holiday.request_date_from > holiday.request_date_to:
@@ -403,86 +411,41 @@ class HolidaysRequest(models.Model):
else:
if holiday.request_unit_half or holiday.request_unit_hours:
holiday.request_date_to = holiday.request_date_from
resource_calendar_id = holiday.employee_id.resource_calendar_id or self.env.company.resource_calendar_id
domain = [('calendar_id', '=', resource_calendar_id.id), ('display_type', '=', False)]
attendances = self.env['resource.calendar.attendance'].read_group(domain, ['ids:array_agg(id)', 'hour_from:min(hour_from)', 'hour_to:max(hour_to)', 'week_type', 'dayofweek', 'day_period'], ['week_type', 'dayofweek', 'day_period'], lazy=False)
# Must be sorted by dayofweek ASC and day_period DESC
attendances = sorted([DummyAttendance(group['hour_from'], group['hour_to'], group['dayofweek'], group['day_period'], group['week_type']) for group in attendances], key=lambda att: (att.dayofweek, att.day_period != 'morning'))
default_value = DummyAttendance(0, 0, 0, 'morning', False)
if resource_calendar_id.two_weeks_calendar:
# find week type of start_date
start_week_type = self.env['resource.calendar.attendance'].get_week_type(holiday.request_date_from)
attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == start_week_type]
attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != start_week_type]
# First, add days of actual week coming after date_from
attendance_filtred = [att for att in attendance_actual_week if int(att.dayofweek) >= holiday.request_date_from.weekday()]
# Second, add days of the other type of week
attendance_filtred += list(attendance_actual_next_week)
# Third, add days of actual week (to consider days that we have remove first because they coming before date_from)
attendance_filtred += list(attendance_actual_week)
end_week_type = self.env['resource.calendar.attendance'].get_week_type(holiday.request_date_to)
attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == end_week_type]
attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != end_week_type]
attendance_filtred_reversed = list(reversed([att for att in attendance_actual_week if int(att.dayofweek) <= holiday.request_date_to.weekday()]))
attendance_filtred_reversed += list(reversed(attendance_actual_next_week))
attendance_filtred_reversed += list(reversed(attendance_actual_week))
# find first attendance coming after first_day
attendance_from = attendance_filtred[0]
# find last attendance coming before last_day
attendance_to = attendance_filtred_reversed[0]
else:
# find first attendance coming after first_day
attendance_from = next((att for att in attendances if int(att.dayofweek) >= holiday.request_date_from.weekday()), attendances[0] if attendances else default_value)
# find last attendance coming before last_day
attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= holiday.request_date_to.weekday()), attendances[-1] if attendances else default_value)
attendance_from, attendance_to = holiday._get_attendances(holiday.employee_id, holiday.request_date_from, holiday.request_date_to)
compensated_request_date_from = holiday.request_date_from
compensated_request_date_to = holiday.request_date_to
if holiday.request_unit_half:
if holiday.request_date_from_period == 'am':
hour_from = float_to_time(attendance_from.hour_from)
hour_to = float_to_time(attendance_from.hour_to)
hour_from = attendance_from.hour_from
hour_to = attendance_from.hour_to
else:
hour_from = float_to_time(attendance_to.hour_from)
hour_to = float_to_time(attendance_to.hour_to)
hour_from = attendance_to.hour_from
hour_to = attendance_to.hour_to
elif holiday.request_unit_hours:
hour_from = float_to_time(float(holiday.request_hour_from))
hour_to = float_to_time(float(holiday.request_hour_to))
elif holiday.request_unit_custom:
hour_from = holiday.date_from.time()
hour_to = holiday.date_to.time()
compensated_request_date_from = holiday._adjust_date_based_on_tz(holiday.request_date_from, hour_from)
compensated_request_date_to = holiday._adjust_date_based_on_tz(holiday.request_date_to, hour_to)
hour_from = holiday.request_hour_from
hour_to = holiday.request_hour_to
else:
hour_from = float_to_time(attendance_from.hour_from)
hour_to = float_to_time(attendance_to.hour_to)
hour_from = attendance_from.hour_from
hour_to = attendance_to.hour_to
holiday.date_from = timezone(holiday.tz).localize(datetime.combine(compensated_request_date_from, hour_from)).astimezone(UTC).replace(tzinfo=None)
holiday.date_to = timezone(holiday.tz).localize(datetime.combine(compensated_request_date_to, hour_to)).astimezone(UTC).replace(tzinfo=None)
holiday.date_from = self._get_start_or_end_from_attendance(hour_from, compensated_request_date_from, holiday.employee_id or holiday)
holiday.date_to = self._get_start_or_end_from_attendance(hour_to, compensated_request_date_to, holiday.employee_id or holiday)
@api.depends('holiday_status_id', 'request_unit_hours', 'request_unit_custom')
@api.depends('holiday_status_id', 'request_unit_hours')
def _compute_request_unit_half(self):
for holiday in self:
if holiday.holiday_status_id or holiday.request_unit_hours or holiday.request_unit_custom:
if holiday.holiday_status_id or holiday.request_unit_hours:
holiday.request_unit_half = False
@api.depends('holiday_status_id', 'request_unit_half', 'request_unit_custom')
@api.depends('holiday_status_id', 'request_unit_half')
def _compute_request_unit_hours(self):
for holiday in self:
if holiday.holiday_status_id or holiday.request_unit_half or holiday.request_unit_custom:
if holiday.holiday_status_id or holiday.request_unit_half:
holiday.request_unit_hours = False
@api.depends('holiday_status_id', 'request_unit_half', 'request_unit_hours')
def _compute_request_unit_custom(self):
for holiday in self:
if holiday.holiday_status_id or holiday.request_unit_half or holiday.request_unit_hours:
holiday.request_unit_custom = False
@api.depends('employee_ids')
def _compute_from_employee_ids(self):
for holiday in self:
@@ -577,13 +540,11 @@ class HolidaysRequest(models.Model):
for leave in self:
leave.tz_mismatch = leave.tz != self.env.user.tz
@api.depends('request_unit_custom', 'employee_id', 'holiday_type', 'department_id.company_id.resource_calendar_id.tz', 'mode_company_id.resource_calendar_id.tz')
@api.depends('employee_id', 'holiday_type', 'department_id.company_id.resource_calendar_id.tz', 'mode_company_id.resource_calendar_id.tz')
def _compute_tz(self):
for leave in self:
tz = False
if leave.request_unit_custom:
tz = 'UTC' # custom -> already in UTC
elif leave.holiday_type == 'employee':
if leave.holiday_type == 'employee':
tz = leave.employee_id.tz
elif leave.holiday_type == 'department':
tz = leave.department_id.company_id.resource_calendar_id.tz
@@ -780,16 +741,22 @@ class HolidaysRequest(models.Model):
if holiday.state in ['cancel', 'refuse', 'validate1', 'validate']:
raise ValidationError(_("This modification is not allowed in the current state."))
def _get_number_of_days_batch(self, date_from, date_to, employee_ids):
""" Returns a float equals to the timedelta between two dates given as string."""
employee = self.env['hr.employee'].browse(employee_ids)
# We force the company in the domain as we are more than likely in a compute_sudo
domain = [('company_id', 'in', self.env.company.ids + self.env.context.get('allowed_company_ids', []))]
result = employee._get_work_days_data_batch(date_from, date_to, domain=domain)
for employee_id in result:
if self.request_unit_half and result[employee_id]['hours'] > 0:
result[employee_id]['days'] = 0.5
return result
def _get_number_of_days(self, date_from, date_to, employee_id):
""" Returns a float equals to the timedelta between two dates given as string."""
if employee_id:
employee = self.env['hr.employee'].browse(employee_id)
# We force the company in the domain as we are more than likely in a compute_sudo
domain = [('company_id', 'in', self.env.company.ids + self.env.context.get('allowed_company_ids', []))]
result = employee._get_work_days_data_batch(date_from, date_to, domain=domain)[employee.id]
if self.request_unit_half and result['hours'] > 0:
result['days'] = 0.5
return result
return self._get_number_of_days_batch(date_from, date_to, employee_id)[employee_id]
today_hours = self.env.company.resource_calendar_id.get_work_hours_count(
datetime.combine(date_from.date(), time.min),
@@ -966,6 +933,38 @@ class HolidaysRequest(models.Model):
@api.model_create_multi
def create(self, vals_list):
leave_date_employees = defaultdict(list)
employee_ids = []
for values in vals_list:
if values.get('employee_id'):
employee_ids.append(values['employee_id'])
if values.get('date_from') and values.get('date_to'):
date_from = fields.Datetime.to_datetime(values['date_from'])
date_to = fields.Datetime.to_datetime(values['date_to'])
if values['employee_id'] not in leave_date_employees[(date_from, date_to)]:
leave_date_employees[(date_from, date_to)].append(values['employee_id'])
employees = self.env['hr.employee'].browse(employee_ids)
if self._context.get('leave_compute_date_from_to') and employees:
employee_leave_date_duration = defaultdict(dict)
for (date_from, date_to), employee_ids in leave_date_employees.items():
employee_leave_date_duration[(date_from, date_to)] = self._get_number_of_days_batch(date_from, date_to, employee_ids)
for values in vals_list:
employee_id = values.get('employee_id')
if employee_id and values.get('date_from') and values.get('date_to'):
date_from = values.get('date_from')
date_to = values.get('date_to')
employee = employees.filtered(lambda emp: emp.id == employee_id)
attendance_from, attendance_to = self._get_attendances(employee, date_from.date(), date_to.date())
hour_from = max(values['date_from'].replace(tzinfo=UTC).astimezone(timezone(self.env.user.tz)).time(), float_to_time(attendance_from.hour_from))
hour_to = min(values['date_to'].replace(tzinfo=UTC).astimezone(timezone(self.env.user.tz)).time(), float_to_time(attendance_to.hour_to))
hour_from = hour_from.hour + hour_from.minute / 60
hour_to = hour_to.hour + hour_to.minute / 60
values['date_from'] = self._get_start_or_end_from_attendance(hour_from, date_from.date(), employee)
values['date_to'] = self._get_start_or_end_from_attendance(hour_to, date_to.date(), employee)
values['request_date_from'], values['request_date_to'] = values['date_from'].date(), values['date_to'].date()
values['number_of_days'] = employee_leave_date_duration[(date_from, date_to)][values['employee_id']]['days']
""" Override to avoid automatic logging of creation """
if not self._context.get('leave_fast_create'):
leave_types = self.env['hr.leave.type'].browse([values.get('holiday_status_id') for values in vals_list if values.get('holiday_status_id')])
@@ -976,7 +975,7 @@ class HolidaysRequest(models.Model):
leave_type_id = values.get('holiday_status_id')
# Handle automatic department_id
if not values.get('department_id'):
values.update({'department_id': self.env['hr.employee'].browse(employee_id).department_id.id})
values.update({'department_id': employees.filtered(lambda emp: emp.id == employee_id).department_id.id})
# Handle no_validation
if mapped_validation_type[leave_type_id] == 'no_validation':
@@ -1358,6 +1357,10 @@ class HolidaysRequest(models.Model):
leave_fast_create=True,
no_calendar_sync=True,
leave_skip_state_check=True,
# date_from and date_to are computed based on the employee tz
# If _compute_date_from_to is used instead, it will trigger _compute_number_of_days
# and create a conflict on the number of days calculation between the different leaves
leave_compute_date_from_to=True,
).create(values)
leaves._validate_leave_request()
@@ -1559,3 +1562,52 @@ class HolidaysRequest(models.Model):
@api.model
def get_unusual_days(self, date_from, date_to=None):
return self.env.user.employee_id.sudo(False)._get_unusual_days(date_from, date_to)
def _get_start_or_end_from_attendance(self, hour, date, employee):
hour = float_to_time(float(hour))
compensated_request_date = self._adjust_date_based_on_tz(date, hour)
holiday_tz = timezone(employee.tz or self.env.user.tz)
return holiday_tz.localize(datetime.combine(compensated_request_date, hour)).astimezone(UTC).replace(tzinfo=None)
def _get_attendances(self, employee, request_date_from, request_date_to):
resource_calendar_id = employee.resource_calendar_id or self.env.company.resource_calendar_id
domain = [('calendar_id', '=', resource_calendar_id.id), ('display_type', '=', False)]
attendances = self.env['resource.calendar.attendance'].read_group(domain,
['ids:array_agg(id)', 'hour_from:min(hour_from)', 'hour_to:max(hour_to)',
'week_type', 'dayofweek', 'day_period'],
['week_type', 'dayofweek', 'day_period'], lazy=False)
# Must be sorted by dayofweek ASC and day_period DESC
attendances = sorted([DummyAttendance(group['hour_from'], group['hour_to'], group['dayofweek'], group['day_period'], group['week_type']) for group in attendances], key=lambda att: (att.dayofweek, att.day_period != 'morning'))
default_value = DummyAttendance(0, 0, 0, 'morning', False)
if resource_calendar_id.two_weeks_calendar:
# find week type of start_date
start_week_type = self.env['resource.calendar.attendance'].get_week_type(request_date_from)
attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == start_week_type]
attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != start_week_type]
# First, add days of actual week coming after date_from
attendance_filtred = [att for att in attendance_actual_week if int(att.dayofweek) >= request_date_from.weekday()]
# Second, add days of the other type of week
attendance_filtred += list(attendance_actual_next_week)
# Third, add days of actual week (to consider days that we have remove first because they coming before date_from)
attendance_filtred += list(attendance_actual_week)
end_week_type = self.env['resource.calendar.attendance'].get_week_type(request_date_to)
attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == end_week_type]
attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != end_week_type]
attendance_filtred_reversed = list(reversed([att for att in attendance_actual_week if int(att.dayofweek) <= request_date_to.weekday()]))
attendance_filtred_reversed += list(reversed(attendance_actual_next_week))
attendance_filtred_reversed += list(reversed(attendance_actual_week))
# find first attendance coming after first_day
attendance_from = attendance_filtred[0]
# find last attendance coming before last_day
attendance_to = attendance_filtred_reversed[0]
else:
# find first attendance coming after first_day
attendance_from = next((att for att in attendances if int(att.dayofweek) >= request_date_from.weekday()), attendances[0] if attendances else default_value)
# find last attendance coming before last_day
attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= request_date_to.weekday()), attendances[-1] if attendances else default_value)
return (attendance_from, attendance_to)
@@ -2,10 +2,18 @@
import CalendarModel from "web.CalendarModel";
export const TimeOffCalendarModel = CalendarModel.extend({
_getFilterDomain: function() {
const company_domain = [['user_id.company_id', 'in', this.data.context.allowed_company_ids]];
return this._super().concat(company_domain);
},
calendarEventToRecord(event) {
const data = this._super(...arguments);
if (event.allDay) {
data.date_from = data.date_from.utc().startOf('day');
data.date_to = data.date_to.utc().endOf('day');
}
return data;
},
});
+1
View File
@@ -18,6 +18,7 @@ class TestHrHolidaysCommon(common.TransactionCase):
cls.user_hrmanager = mail_new_test_user(cls.env, login='bastien', groups='base.group_user,hr_holidays.group_hr_holidays_manager')
cls.user_hrmanager_id = cls.user_hrmanager.id
cls.user_hrmanager.tz = 'Europe/Brussels'
cls.user_employee = mail_new_test_user(cls.env, login='david', groups='base.group_user')
cls.user_employee_id = cls.user_employee.id
@@ -323,7 +323,7 @@ class TestCompanyLeave(TransactionCase):
})
company_leave._compute_date_from_to()
with self.assertQueryCount(__system__=250, admin=865): # 249 community
with self.assertQueryCount(__system__=353, admin=865):
# Original query count: 1987
# Without tracking/activity context keys: 5154
company_leave.action_validate()
+15 -15
View File
@@ -249,8 +249,8 @@ class TestLeaveRequests(TestHrHolidaysCommon):
'request_hour_from': '8', # 8:00 AM in the company's timezone
'request_hour_to': '17', # 5:00 PM in the company's timezone
})
self.assertEqual(leave.date_from, datetime(2019, 5, 5, 20, 0, 0), "It should have been localized before saving in UTC")
self.assertEqual(leave.date_to, datetime(2019, 5, 6, 5, 0, 0), "It should have been localized before saving in UTC")
self.assertEqual(leave.date_from, datetime(2019, 5, 6, 6, 0, 0), "It should have been localized in the Employee timezone")
self.assertEqual(leave.date_to, datetime(2019, 5, 6, 15, 0, 0), "It should have been localized in the Employee timezone")
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
def test_timezone_company_validated(self):
@@ -264,13 +264,15 @@ class TestLeaveRequests(TestHrHolidaysCommon):
leave_form.holiday_status_id = self.holidays_type_1
leave_form.request_date_from = date(2019, 5, 6)
leave_form.request_date_to = date(2019, 5, 6)
leave_form.date_from = datetime(2019, 5, 6, 0, 0, 0)
leave_form.date_to = datetime(2019, 5, 6, 23, 59, 59)
leave = leave_form.save()
leave.state = 'confirm'
leave.action_validate()
employee_leave = self.env['hr.leave'].search([('employee_id', '=', employee.id)])
self.assertEqual(
employee_leave.request_date_from, date(2019, 5, 6),
"Timezone should be kept between company and employee leave"
employee_leave.request_date_from, date(2019, 5, 5),
"Timezone should be be adapted on the employee leave"
)
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
@@ -290,8 +292,8 @@ class TestLeaveRequests(TestHrHolidaysCommon):
'request_hour_from': '8', # 8:00 AM in the department's timezone
'request_hour_to': '17', # 5:00 PM in the department's timezone
})
self.assertEqual(leave.date_from, datetime(2019, 5, 5, 20, 0, 0), "It should have been localized before saving in UTC")
self.assertEqual(leave.date_to, datetime(2019, 5, 6, 5, 0, 0), "It should have been localized before saving in UTC")
self.assertEqual(leave.date_from, datetime(2019, 5, 6, 6, 0, 0), "It should have been localized in the Employee timezone")
self.assertEqual(leave.date_to, datetime(2019, 5, 6, 15, 0, 0), "It should have been localized in the Employee timezone")
def test_number_of_hours_display(self):
# Test that the field number_of_hours_dispay doesn't change
@@ -416,12 +418,10 @@ class TestLeaveRequests(TestHrHolidaysCommon):
tz = timezone(tz)
# Mimic what is done by the calendar widget when clicking on a day. It
# will take the local datetime from 7:00 to 19:00 and then convert it
# to UTC before sending it. Values here are for PST (UTC -8) and
# represent a leave on 2019/1/1 from 7:00 to 19:00 local time.
# will take the local datetime from 0:00 to 23:59
values = {
'date_from': tz.localize(local_date_from).astimezone(UTC).replace(tzinfo=None),
'date_to': tz.localize(local_date_to).astimezone(UTC).replace(tzinfo=None), # note that this can be the next day in UTC
'date_from': local_date_from,
'date_to': local_date_to, # note that this can be the next day in UTC
}
values.update(self.env['hr.leave'].with_user(self.user_employee_id)._default_get_request_parameters(values))
@@ -449,14 +449,14 @@ class TestLeaveRequests(TestHrHolidaysCommon):
# 12 13 14 15 16 17 18
# 19 20 21 22 23 24 25
# 26 27 28 29 30 31
local_date_from = datetime(2020, 1, 1, 7, 0, 0)
local_date_to = datetime(2020, 1, 1, 19, 0, 0)
local_date_from = datetime(2020, 1, 1, 0, 0, 0)
local_date_to = datetime(2020, 1, 1, 23, 59, 59)
for tz in timezones_to_test:
self._test_leave_with_tz(tz, local_date_from, local_date_to, 1)
# We, Th, Fr, Mo, Tu, We => 6 days
local_date_from = datetime(2020, 1, 1, 7, 0, 0)
local_date_to = datetime(2020, 1, 8, 19, 0, 0)
local_date_from = datetime(2020, 1, 1, 0, 0, 0)
local_date_to = datetime(2020, 1, 8, 23, 59, 59)
for tz in timezones_to_test:
self._test_leave_with_tz(tz, local_date_from, local_date_to, 6)#TODO JUD check why this fails
@@ -316,11 +316,6 @@
<label for="request_unit_hours" attrs="{
'invisible': [('leave_type_request_unit', '!=', 'hour')]
}"/>
<field name="request_unit_custom" invisible="1" attrs="{
'readonly': [('state', 'not in', ('draft', 'confirm'))],
}" class="ml-5"/>
<label for="request_unit_custom" invisible="1"/>
</div>
<div class="o_row o_row_readonly">
<label for="request_hour_from" string="From"