|
4 | 4 | import itertools |
5 | 5 |
|
6 | 6 | from collections import defaultdict |
7 | | -from datetime import datetime, timedelta |
| 7 | +from datetime import datetime, timedelta, time |
8 | 8 | from functools import partial |
9 | 9 | from itertools import chain |
10 | 10 |
|
@@ -367,14 +367,25 @@ def _attendance_intervals_batch(self, start_dt, end_dt, resources=None, domain=N |
367 | 367 | res = result_per_tz[tz] |
368 | 368 | res_intervals = WorkIntervals(res) |
369 | 369 | for resource in resources: |
370 | | - if resource in per_resource_result: |
| 370 | + if resource and resource._is_flexible(): |
| 371 | + # If the resource is flexible, return the whole period from start_dt to end_dt with a dummy attendance |
| 372 | + dummy_attendance = self.env['resource.calendar.attendance'] |
| 373 | + result_per_resource_id[resource.id] = WorkIntervals([(start, end, dummy_attendance)]) |
| 374 | + elif resource in per_resource_result: |
371 | 375 | resource_specific_result = [(max(bounds_per_tz[tz][0], tz.localize(val[0])), min(bounds_per_tz[tz][1], tz.localize(val[1])), val[2]) |
372 | 376 | for val in per_resource_result[resource]] |
373 | 377 | result_per_resource_id[resource.id] = WorkIntervals(itertools.chain(res, resource_specific_result)) |
374 | 378 | else: |
375 | 379 | result_per_resource_id[resource.id] = res_intervals |
376 | 380 | return result_per_resource_id |
377 | 381 |
|
| 382 | + def _handle_flexible_leave_interval(self, dt0, dt1, leave): |
| 383 | + """Hook method to handle flexible leave intervals. Can be overridden in other modules.""" |
| 384 | + tz = dt0.tzinfo # Get the timezone information from dt0 |
| 385 | + dt0 = datetime.combine(dt0.date(), time.min).replace(tzinfo=tz) |
| 386 | + dt1 = datetime.combine(dt1.date(), time.max).replace(tzinfo=tz) |
| 387 | + return dt0, dt1 |
| 388 | + |
378 | 389 | def _leave_intervals(self, start_dt, end_dt, resource=None, domain=None, tz=None): |
379 | 390 | if resource is None: |
380 | 391 | resource = self.env['resource.resource'] |
@@ -431,6 +442,8 @@ def _leave_intervals_batch(self, start_dt, end_dt, resources=None, domain=None, |
431 | 442 | tz_dates[(tz, end_dt)] = end |
432 | 443 | dt0 = string_to_datetime(leave_date_from).astimezone(tz) |
433 | 444 | dt1 = string_to_datetime(leave_date_to).astimezone(tz) |
| 445 | + if leave_resource and leave_resource._is_flexible(): |
| 446 | + dt0, dt1 = self._handle_flexible_leave_interval(dt0, dt1, leave) |
434 | 447 | result[resource.id].append((max(start, dt0), min(end, dt1), leave)) |
435 | 448 |
|
436 | 449 | return {r.id: Intervals(result[r.id]) for r in resources_list} |
@@ -472,7 +485,7 @@ def _unavailable_intervals_batch(self, start_dt, end_dt, resources=None, domain= |
472 | 485 | resources_work_intervals = self._work_intervals_batch(start_dt, end_dt, resources, domain, tz) |
473 | 486 | result = {} |
474 | 487 | for resource in resources_list: |
475 | | - if resource and resource._is_flexible(): |
| 488 | + if resource and resource._is_fully_flexible(): |
476 | 489 | continue |
477 | 490 | work_intervals = [(start, stop) for start, stop, meta in resources_work_intervals[resource.id]] |
478 | 491 | # start + flatten(intervals) + end |
@@ -505,7 +518,10 @@ def _get_attendance_intervals_days_data(self, attendance_intervals): |
505 | 518 | # take durations in days proportionally to what is left of the interval. |
506 | 519 | interval_hours = (stop - start).total_seconds() / 3600 |
507 | 520 | day_hours[start.date()] += interval_hours |
508 | | - day_days[start.date()] += sum(meta.mapped('duration_days')) * interval_hours / sum(meta.mapped('duration_hours')) |
| 521 | + if len(self) == 1 and self.flexible_hours and self.hours_per_day: |
| 522 | + day_days[start.date()] += interval_hours / self.hours_per_day |
| 523 | + else: |
| 524 | + day_days[start.date()] += sum(meta.mapped('duration_days')) * interval_hours / sum(meta.mapped('duration_hours')) |
509 | 525 |
|
510 | 526 | return { |
511 | 527 | # Round the number of days to the closest 16th of a day. |
|
0 commit comments