You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

126 lines
4.9 KiB

# Copyright 2019 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import pytz
import datetime
from datetime import timedelta
from odoo.addons.resource.models.resource import ResourceCalendar
from odoo.tools.float_utils import float_compare
def to_naive_user_tz(datetime, record):
tz_name = record._context.get('tz') or record.env.user.tz
tz = tz_name and pytz.timezone(tz_name) or pytz.UTC
return pytz.UTC.localize(datetime.replace(
tzinfo=None), is_dst=False).astimezone(tz).replace(tzinfo=None)
def post_load_hook():
def _new_schedule_days(self, days, day_dt, compute_leaves=False,
resource_id=None):
if not hasattr(self, '_get_schedule_days_iteration_limit'):
return self._schedule_days_original(
days, day_dt, compute_leaves=compute_leaves,
resource_id=resource_id)
# START OF HOOK: Introduce here the iterations limit
iterations_limit = self._get_schedule_days_iteration_limit() or 100
# END OF HOOK
backwards = (days < 0)
intervals = []
planned_days, iterations = 0, 0
day_dt_tz = to_naive_user_tz(day_dt, self.env.user)
current_datetime = day_dt_tz.replace(
hour=0, minute=0, second=0, microsecond=0)
# HOOK. Use the iterations_limit here
while planned_days < abs(days) and iterations < iterations_limit:
working_intervals = self._get_day_work_intervals(
current_datetime.date(),
compute_leaves=compute_leaves, resource_id=resource_id)
if not self or working_intervals:
# no calendar -> no working hours, but day is
# considered as worked
planned_days += 1
intervals += working_intervals
# get next day
if backwards:
current_datetime = self._get_previous_work_day(
current_datetime)
else:
current_datetime = self._get_next_work_day(current_datetime)
# avoid infinite loops
iterations += 1
return intervals
def _new_schedule_hours(self, hours, day_dt, compute_leaves=False,
resource_id=None):
if not hasattr(self, '_get_schedule_hours_iteration_limit'):
return self._schedule_hours_original(
hours, day_dt, compute_leaves=compute_leaves,
resource_id=resource_id)
self.ensure_one()
# START OF HOOK: Introduce here the iterations limit
iterations_limit = self._get_schedule_hours_iteration_limit() or 1000
# END OF HOOK
backwards = (hours < 0)
intervals = []
remaining_hours, iterations = abs(hours * 1.0), 0
day_dt_tz = to_naive_user_tz(day_dt, self.env.user)
current_datetime = day_dt_tz
call_args = dict(compute_leaves=compute_leaves,
resource_id=resource_id)
# HOOK. Use the iterations_limit here
while float_compare(remaining_hours, 0.0, precision_digits=2) in (
1, 0) and iterations < 1000:
if backwards:
call_args['end_time'] = current_datetime.time()
else:
call_args['start_time'] = current_datetime.time()
working_intervals = self._get_day_work_intervals(
current_datetime.date(), **call_args)
if working_intervals:
new_working_intervals = self._interval_schedule_hours(
working_intervals, remaining_hours, backwards=backwards)
res = timedelta()
for interval in working_intervals:
res += interval[1] - interval[0]
remaining_hours -= res.total_seconds() / 3600.0
intervals = intervals + new_working_intervals if \
not backwards else new_working_intervals + intervals
# get next day
if backwards:
current_datetime = datetime.datetime.combine(
self._get_previous_work_day(current_datetime),
datetime.time(23, 59, 59))
else:
current_datetime = datetime.datetime.combine(
self._get_next_work_day(current_datetime),
datetime.time())
# avoid infinite loops
iterations += 1
return intervals
if not hasattr(ResourceCalendar, '_schedule_days_original'):
ResourceCalendar._schedule_days_original = \
ResourceCalendar._schedule_days
ResourceCalendar._schedule_days = _new_schedule_days
if not hasattr(ResourceCalendar, '_schedule_hours_original'):
ResourceCalendar._schedule_hours_original = \
ResourceCalendar._schedule_hours
ResourceCalendar._schedule_hours = _new_schedule_hours