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

  1. # Copyright 2019 Eficent Business and IT Consulting Services S.L.
  2. # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
  3. import pytz
  4. import datetime
  5. from datetime import timedelta
  6. from odoo.addons.resource.models.resource import ResourceCalendar
  7. from odoo.tools.float_utils import float_compare
  8. def to_naive_user_tz(datetime, record):
  9. tz_name = record._context.get('tz') or record.env.user.tz
  10. tz = tz_name and pytz.timezone(tz_name) or pytz.UTC
  11. return pytz.UTC.localize(datetime.replace(
  12. tzinfo=None), is_dst=False).astimezone(tz).replace(tzinfo=None)
  13. def post_load_hook():
  14. def _new_schedule_days(self, days, day_dt, compute_leaves=False,
  15. resource_id=None):
  16. if not hasattr(self, '_get_schedule_days_iteration_limit'):
  17. return self._schedule_days_original(
  18. days, day_dt, compute_leaves=compute_leaves,
  19. resource_id=resource_id)
  20. # START OF HOOK: Introduce here the iterations limit
  21. iterations_limit = self._get_schedule_days_iteration_limit() or 100
  22. # END OF HOOK
  23. backwards = (days < 0)
  24. intervals = []
  25. planned_days, iterations = 0, 0
  26. day_dt_tz = to_naive_user_tz(day_dt, self.env.user)
  27. current_datetime = day_dt_tz.replace(
  28. hour=0, minute=0, second=0, microsecond=0)
  29. # HOOK. Use the iterations_limit here
  30. while planned_days < abs(days) and iterations < iterations_limit:
  31. working_intervals = self._get_day_work_intervals(
  32. current_datetime.date(),
  33. compute_leaves=compute_leaves, resource_id=resource_id)
  34. if not self or working_intervals:
  35. # no calendar -> no working hours, but day is
  36. # considered as worked
  37. planned_days += 1
  38. intervals += working_intervals
  39. # get next day
  40. if backwards:
  41. current_datetime = self._get_previous_work_day(
  42. current_datetime)
  43. else:
  44. current_datetime = self._get_next_work_day(current_datetime)
  45. # avoid infinite loops
  46. iterations += 1
  47. return intervals
  48. def _new_schedule_hours(self, hours, day_dt, compute_leaves=False,
  49. resource_id=None):
  50. if not hasattr(self, '_get_schedule_hours_iteration_limit'):
  51. return self._schedule_hours_original(
  52. hours, day_dt, compute_leaves=compute_leaves,
  53. resource_id=resource_id)
  54. self.ensure_one()
  55. # START OF HOOK: Introduce here the iterations limit
  56. iterations_limit = self._get_schedule_hours_iteration_limit() or 1000
  57. # END OF HOOK
  58. backwards = (hours < 0)
  59. intervals = []
  60. remaining_hours, iterations = abs(hours * 1.0), 0
  61. day_dt_tz = to_naive_user_tz(day_dt, self.env.user)
  62. current_datetime = day_dt_tz
  63. call_args = dict(compute_leaves=compute_leaves,
  64. resource_id=resource_id)
  65. # HOOK. Use the iterations_limit here
  66. while float_compare(remaining_hours, 0.0, precision_digits=2) in (
  67. 1, 0) and iterations < 1000:
  68. if backwards:
  69. call_args['end_time'] = current_datetime.time()
  70. else:
  71. call_args['start_time'] = current_datetime.time()
  72. working_intervals = self._get_day_work_intervals(
  73. current_datetime.date(), **call_args)
  74. if working_intervals:
  75. new_working_intervals = self._interval_schedule_hours(
  76. working_intervals, remaining_hours, backwards=backwards)
  77. res = timedelta()
  78. for interval in working_intervals:
  79. res += interval[1] - interval[0]
  80. remaining_hours -= res.total_seconds() / 3600.0
  81. intervals = intervals + new_working_intervals if \
  82. not backwards else new_working_intervals + intervals
  83. # get next day
  84. if backwards:
  85. current_datetime = datetime.datetime.combine(
  86. self._get_previous_work_day(current_datetime),
  87. datetime.time(23, 59, 59))
  88. else:
  89. current_datetime = datetime.datetime.combine(
  90. self._get_next_work_day(current_datetime),
  91. datetime.time())
  92. # avoid infinite loops
  93. iterations += 1
  94. return intervals
  95. if not hasattr(ResourceCalendar, '_schedule_days_original'):
  96. ResourceCalendar._schedule_days_original = \
  97. ResourceCalendar._schedule_days
  98. ResourceCalendar._schedule_days = _new_schedule_days
  99. if not hasattr(ResourceCalendar, '_schedule_hours_original'):
  100. ResourceCalendar._schedule_hours_original = \
  101. ResourceCalendar._schedule_hours
  102. ResourceCalendar._schedule_hours = _new_schedule_hours