|
1 |
| -from odoo.addons.resource.models.resource_mixin import ResourceMixin |
| 1 | +from odoo.addons.resource.models.resource_mixin import ResourceMixin, ROUNDING_FACTOR |
2 | 2 | from datetime import timedelta
|
3 | 3 | from odoo.tools import float_utils
|
| 4 | +from pytz import utc |
| 5 | +from collections import defaultdict |
4 | 6 |
|
5 | 7 |
|
6 | 8 | def post_load_hook():
|
7 | 9 | if not hasattr(ResourceMixin, 'get_work_days_data_original'):
|
8 | 10 | ResourceMixin.get_work_days_data_original = \
|
9 | 11 | ResourceMixin.get_work_days_data
|
10 | 12 |
|
11 |
| - def __new_get_work_days_data(self, from_datetime, to_datetime, |
12 |
| - calendar=None): |
13 |
| - if not hasattr(self, '_get_work_hours'): |
14 |
| - return self.get_day_work_hours_count_original( |
15 |
| - from_datetime, to_datetime, calendar=calendar) |
16 |
| - days_count = 0.0 |
17 |
| - total_work_time = timedelta() |
| 13 | + def __new_get_work_days_data( |
| 14 | + self, from_datetime, to_datetime, compute_leaves=True, |
| 15 | + calendar=None, domain=None |
| 16 | + ): |
| 17 | + """ |
| 18 | + By default the resource calendar is used, but it can be |
| 19 | + changed using the `calendar` argument. |
| 20 | +
|
| 21 | + `domain` is used in order to recognise the leaves to take, |
| 22 | + None means default value ('time_type', '=', 'leave') |
| 23 | +
|
| 24 | + Returns a dict {'days': n, 'hours': h} containing the |
| 25 | + quantity of working time expressed as days and as hours. |
| 26 | + """ |
| 27 | + resource = self.resource_id |
18 | 28 | calendar = calendar or self.resource_calendar_id
|
19 |
| - calendar = calendar.with_context( |
20 |
| - no_tz_convert=self.env.context.get('no_tz_convert', False)) |
21 |
| - for day_intervals in calendar._iter_work_intervals( |
22 |
| - from_datetime, to_datetime, self.resource_id.id, |
23 |
| - compute_leaves=True): |
24 |
| - theoric_hours = self.get_day_work_hours_count( |
25 |
| - day_intervals[0][0].date(), calendar=calendar) |
26 |
| - # Here we introduce the hook method '_get_work_hours'. |
27 |
| - work_time = sum((self._get_work_hours(interval) |
28 |
| - for interval in day_intervals), timedelta()) |
29 |
| - total_work_time += work_time |
30 |
| - if theoric_hours: |
31 |
| - days_count += float_utils.round( |
32 |
| - (work_time.total_seconds() / 3600 / theoric_hours) * 4) / 4 |
| 29 | + |
| 30 | + # naive datetimes are made explicit in UTC |
| 31 | + if not from_datetime.tzinfo: |
| 32 | + from_datetime = from_datetime.replace(tzinfo=utc) |
| 33 | + if not to_datetime.tzinfo: |
| 34 | + to_datetime = to_datetime.replace(tzinfo=utc) |
| 35 | + |
| 36 | + # total hours per day: retrieve attendances with one extra day margin, |
| 37 | + # in order to compute the total hours on the first and last days |
| 38 | + from_full = from_datetime - timedelta(days=1) |
| 39 | + to_full = to_datetime + timedelta(days=1) |
| 40 | + intervals = calendar._attendance_intervals( |
| 41 | + from_full, to_full, resource |
| 42 | + ) |
| 43 | + day_total = defaultdict(float) |
| 44 | + for start, stop, meta in intervals: |
| 45 | + day_total[start.date()] += (stop - start).total_seconds() / 3600 |
| 46 | + |
| 47 | + # actual hours per day |
| 48 | + if compute_leaves: |
| 49 | + intervals = calendar._work_intervals( |
| 50 | + from_datetime, to_datetime, resource, domain |
| 51 | + ) |
| 52 | + else: |
| 53 | + intervals = calendar._attendance_intervals( |
| 54 | + from_datetime, to_datetime, resource |
| 55 | + ) |
| 56 | + day_hours = defaultdict(float) |
| 57 | + for start, stop, meta in intervals: |
| 58 | + day_hours[start.date()] += self._get_work_hours(start, stop, meta) |
| 59 | + |
| 60 | + # compute number of days as quarters |
| 61 | + days = sum( |
| 62 | + float_utils.round( |
| 63 | + ROUNDING_FACTOR * day_hours[day] / day_total[day] |
| 64 | + ) / ROUNDING_FACTOR |
| 65 | + for day in day_hours |
| 66 | + ) |
33 | 67 | return {
|
34 |
| - 'days': days_count, |
35 |
| - 'hours': total_work_time.total_seconds() / 3600, |
| 68 | + 'days': days, |
| 69 | + 'hours': sum(day_hours.values()), |
36 | 70 | }
|
37 | 71 |
|
38 | 72 | ResourceMixin.get_work_days_data = __new_get_work_days_data
|
0 commit comments