Skip to content

Commit c251172

Browse files
Jaime Arroyochandni299
Jaime Arroyo
authored andcommitted
[13.0][MIG] resource_hook
1 parent cb8b7fc commit c251172

File tree

6 files changed

+272
-104
lines changed

6 files changed

+272
-104
lines changed

resource_hook/README.rst

+30-7
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ Resource Hook
1414
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
1515
:alt: License: AGPL-3
1616
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr-lightgray.png?logo=github
17-
:target: https://github.com/OCA/hr/tree/12.0/resource_hook
17+
:target: https://github.com/OCA/hr/tree/13.0/resource_hook
1818
:alt: OCA/hr
1919
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
20-
:target: https://translation.odoo-community.org/projects/hr-12-0/hr-12-0-resource_hook
20+
:target: https://translation.odoo-community.org/projects/hr-13-0/hr-13-0-resource_hook
2121
:alt: Translate me on Weblate
2222
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
23-
:target: https://runbot.odoo-community.org/runbot/116/12.0
23+
:target: https://runbot.odoo-community.org/runbot/116/13.0
2424
:alt: Try me on Runbot
2525

2626
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -40,9 +40,32 @@ Changelog
4040
11.0.1.0.0 (2019-05-28)
4141
~~~~~~~~~~~~~~~~~~~~~~~
4242

43-
* Introduce hook in method '_get_work_days_data' of ResourceMixing class in
43+
* Introduce hook in method `_get_work_days_data` of ResourceMixing class in
4444
order to allow changes in the logic to compute working hours within an
45-
interval. A new method '_get_work_hours' is now provided for this purpose.
45+
interval. A new method `_get_work_hours` is now provided for this purpose.
46+
The method can be inherit to alter the standard behaviour.
47+
48+
12.0.1.0.2 (2020-06-09)
49+
~~~~~~~~~~~~~~~~~~~~~~~
50+
51+
* Introduce hook in method `get_work_hours_count` of ResourceCalendar class in
52+
order to allow changes in the logic to compute working hours within an
53+
interval. A new method `_get_work_hours` is now provided for this purpose.
54+
The method can be inherit to alter the standard behaviour.
55+
56+
57+
13.0.1.0.0 (2020-09-29)
58+
~~~~~~~~~~~~~~~~~~~~~~~
59+
60+
* Introduce hook in methods `list_work_time_per_day` and `list_leaves` of
61+
ResourceMixing class in order to allow changes in the logic to compute
62+
working hours within an interval.
63+
64+
* Introduce hook in methods `_get_days_data`, `_get_resources_day_total`,
65+
`get_work_hours_count`, `plan_hours` and `_compute_hours_per_day` of
66+
ResourceCalendar class in order to allow changes in the logic to compute
67+
working hours within an interval or an attendance. A new method
68+
`_get_work_hours_attendance` is now provided for this purpose.
4669
The method can be inherit to alter the standard behaviour.
4770

4871
Bug Tracker
@@ -51,7 +74,7 @@ Bug Tracker
5174
Bugs are tracked on `GitHub Issues <https://github.com/OCA/hr/issues>`_.
5275
In case of trouble, please check there if your issue has already been reported.
5376
If you spotted it first, help us smashing it by providing a detailed and welcomed
54-
`feedback <https://github.com/OCA/hr/issues/new?body=module:%20resource_hook%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
77+
`feedback <https://github.com/OCA/hr/issues/new?body=module:%20resource_hook%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
5578

5679
Do not contact contributors directly about support or help with technical issues.
5780

@@ -82,6 +105,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
82105
mission is to support the collaborative development of Odoo features and
83106
promote its widespread use.
84107

85-
This module is part of the `OCA/hr <https://github.com/OCA/hr/tree/12.0/resource_hook>`_ project on GitHub.
108+
This module is part of the `OCA/hr <https://github.com/OCA/hr/tree/13.0/resource_hook>`_ project on GitHub.
86109

87110
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

resource_hook/__manifest__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"name": "Resource Hook",
66
"summary": """
77
Extends the resource with hooks to standard methods.""",
8-
"version": "12.0.1.0.2",
8+
"version": "13.0.1.0.0",
99
"license": "AGPL-3",
1010
"author": "Creu Blanca, Odoo Community Association (OCA)",
1111
"website": "https://github.com/OCA/hr",

resource_hook/hooks.py

+167-73
Original file line numberDiff line numberDiff line change
@@ -4,67 +4,39 @@
44
from pytz import utc
55

66
from odoo.tools import float_utils
7-
8-
from odoo.addons.resource.models.resource import ResourceCalendar
9-
from odoo.addons.resource.models.resource_mixin import ROUNDING_FACTOR, ResourceMixin
10-
11-
12-
def post_load_hook():
13-
if not hasattr(ResourceMixin, "get_work_days_data_original"):
14-
ResourceMixin.get_work_days_data_original = ResourceMixin.get_work_days_data
15-
if not hasattr(ResourceCalendar, "get_work_hours_count_original"):
16-
ResourceCalendar.get_work_hours_count_original = (
17-
ResourceCalendar.get_work_hours_count
18-
)
19-
20-
def __new_get_work_days_data(
21-
self,
22-
from_datetime,
23-
to_datetime,
24-
compute_leaves=True,
25-
calendar=None,
26-
domain=None,
27-
):
28-
"""
29-
By default the resource calendar is used, but it can be
30-
changed using the `calendar` argument.
31-
32-
`domain` is used in order to recognise the leaves to take,
33-
None means default value ('time_type', '=', 'leave')
34-
35-
Returns a dict {'days': n, 'hours': h} containing the
36-
quantity of working time expressed as days and as hours.
37-
"""
38-
resource = self.resource_id
39-
calendar = calendar or self.resource_calendar_id
40-
41-
# naive datetimes are made explicit in UTC
42-
if not from_datetime.tzinfo:
43-
from_datetime = from_datetime.replace(tzinfo=utc)
44-
if not to_datetime.tzinfo:
45-
to_datetime = to_datetime.replace(tzinfo=utc)
46-
47-
# total hours per day: retrieve attendances with one extra day margin,
48-
# in order to compute the total hours on the first and last days
49-
from_full = from_datetime - timedelta(days=1)
50-
to_full = to_datetime + timedelta(days=1)
51-
intervals = calendar._attendance_intervals(from_full, to_full, resource)
52-
day_total = defaultdict(float)
53-
for start, stop, meta in intervals:
54-
day_total[start.date()] += self._get_work_hours(start, stop, meta)
55-
56-
# actual hours per day
57-
if compute_leaves:
58-
intervals = calendar._work_intervals(
59-
from_datetime, to_datetime, resource, domain
60-
)
61-
else:
62-
intervals = calendar._attendance_intervals(
63-
from_datetime, to_datetime, resource
64-
)
7+
from odoo.tools.float_utils import float_round
8+
9+
from odoo.addons.resource.models.resource import (
10+
ROUNDING_FACTOR,
11+
ResourceCalendar,
12+
make_aware,
13+
partial,
14+
)
15+
from odoo.addons.resource.models.resource_mixin import ResourceMixin
16+
17+
18+
def post_load_hook(): # noqa: C901
19+
20+
# Functions to override
21+
resource_functions = [
22+
(ResourceCalendar, "_get_days_data"),
23+
(ResourceCalendar, "_get_resources_day_total"),
24+
(ResourceCalendar, "get_work_hours_count"),
25+
(ResourceCalendar, "plan_hours"),
26+
(ResourceCalendar, "_compute_hours_per_day"),
27+
(ResourceMixin, "list_work_time_per_day"),
28+
(ResourceMixin, "list_leaves"),
29+
]
30+
31+
for func in resource_functions:
32+
if not hasattr(func[0], func[1] + "_original"):
33+
old_function = getattr(func[0], func[1])
34+
setattr(func[0], func[1] + "_original", old_function)
35+
36+
def __new__get_days_data(self, intervals, day_total):
6537
day_hours = defaultdict(float)
6638
for start, stop, meta in intervals:
67-
day_hours[start.date()] += self._get_work_hours(start, stop, meta)
39+
day_hours[start.date()] += self._get_work_hours_interval(start, stop, meta)
6840

6941
# compute number of days as quarters
7042
days = sum(
@@ -77,32 +49,154 @@ def __new_get_work_days_data(
7749
"hours": sum(day_hours.values()),
7850
}
7951

80-
def __new_get_work_hours_count(
81-
self, start_dt, end_dt, compute_leaves=True, domain=None
52+
def __new__get_resources_day_total(
53+
self, from_datetime, to_datetime, resources=None
8254
):
83-
"""
84-
`compute_leaves` controls whether or not this method is taking into
85-
account the global leaves.
55+
self.ensure_one()
56+
resources = self.env["resource.resource"] if not resources else resources
57+
resources_list = list(resources) + [self.env["resource.resource"]]
58+
# total hours per day: retrieve attendances with one extra day margin,
59+
# in order to compute the total hours on the first and last days
60+
from_full = from_datetime - timedelta(days=1)
61+
to_full = to_datetime + timedelta(days=1)
62+
intervals = self._attendance_intervals_batch(
63+
from_full, to_full, resources=resources
64+
)
8665

87-
`domain` controls the way leaves are recognized.
88-
None means default value ('time_type', '=', 'leave')
66+
result = defaultdict(lambda: defaultdict(float))
67+
for resource in resources_list:
68+
day_total = result[resource.id]
69+
for start, stop, meta in intervals[resource.id]:
70+
day_total[start.date()] += self._get_work_hours_interval(
71+
start, stop, meta
72+
)
73+
return result
8974

90-
Counts the number of work hours between two datetimes.
91-
"""
75+
def __new_get_work_hours_count(
76+
self, start_dt, end_dt, compute_leaves=True, domain=None
77+
):
9278
# Set timezone in UTC if no timezone is explicitly given
9379
if not start_dt.tzinfo:
9480
start_dt = start_dt.replace(tzinfo=utc)
9581
if not end_dt.tzinfo:
9682
end_dt = end_dt.replace(tzinfo=utc)
9783

9884
if compute_leaves:
99-
intervals = self._work_intervals(start_dt, end_dt, domain=domain)
85+
intervals = self._work_intervals_batch(start_dt, end_dt, domain=domain)[
86+
False
87+
]
10088
else:
101-
intervals = self._attendance_intervals(start_dt, end_dt)
89+
intervals = self._attendance_intervals_batch(start_dt, end_dt)[False]
10290

10391
return sum(
104-
self._get_work_hours(start, stop, meta) for start, stop, meta in intervals
92+
self._get_work_hours_interval(start, stop, meta)
93+
for start, stop, meta in intervals
10594
)
10695

107-
ResourceMixin.get_work_days_data = __new_get_work_days_data
108-
ResourceCalendar.get_work_hours_count = __new_get_work_hours_count
96+
def __new_plan_hours(
97+
self, hours, day_dt, compute_leaves=False, domain=None, resource=None
98+
):
99+
day_dt, revert = make_aware(day_dt)
100+
101+
# which method to use for retrieving intervals
102+
if compute_leaves:
103+
get_intervals = partial(
104+
self._work_intervals, domain=domain, resource=resource
105+
)
106+
else:
107+
get_intervals = self._attendance_intervals
108+
109+
if hours >= 0:
110+
delta = timedelta(days=14)
111+
for n in range(100):
112+
dt = day_dt + delta * n
113+
for start, stop, meta in get_intervals(dt, dt + delta):
114+
interval_hours = self._get_work_hours_interval(start, stop, meta)
115+
if hours <= interval_hours:
116+
return revert(start + timedelta(hours=hours))
117+
hours -= interval_hours
118+
return False
119+
else:
120+
hours = abs(hours)
121+
delta = timedelta(days=14)
122+
for n in range(100):
123+
dt = day_dt - delta * n
124+
for start, stop, meta in reversed(get_intervals(dt - delta, dt)):
125+
interval_hours = self._get_work_hours_interval(start, stop, meta)
126+
if hours <= interval_hours:
127+
return revert(stop - timedelta(hours=hours))
128+
hours -= interval_hours
129+
return False
130+
131+
def __new__compute_hours_per_day(self, attendances):
132+
if not attendances:
133+
return 0
134+
135+
hour_count = 0.0
136+
for attendance in attendances:
137+
hour_count += self._get_work_hours_attendance(attendance)
138+
139+
if self.two_weeks_calendar:
140+
number_of_days = len(
141+
set(
142+
attendances.filtered(lambda cal: cal.week_type == "1").mapped(
143+
"dayofweek"
144+
)
145+
)
146+
)
147+
number_of_days += len(
148+
set(
149+
attendances.filtered(lambda cal: cal.week_type == "0").mapped(
150+
"dayofweek"
151+
)
152+
)
153+
)
154+
else:
155+
number_of_days = len(set(attendances.mapped("dayofweek")))
156+
157+
return float_round(hour_count / float(number_of_days), precision_digits=2)
158+
159+
def __new_list_work_time_per_day(
160+
self, from_datetime, to_datetime, calendar=None, domain=None
161+
):
162+
resource = self.resource_id
163+
calendar = calendar or self.resource_calendar_id
164+
165+
# naive datetimes are made explicit in UTC
166+
if not from_datetime.tzinfo:
167+
from_datetime = from_datetime.replace(tzinfo=utc)
168+
if not to_datetime.tzinfo:
169+
to_datetime = to_datetime.replace(tzinfo=utc)
170+
171+
intervals = calendar._work_intervals_batch(
172+
from_datetime, to_datetime, resource, domain
173+
)[resource.id]
174+
result = defaultdict(float)
175+
for start, stop, meta in intervals:
176+
result[start.date()] += self._get_work_hours_interval(start, stop, meta)
177+
return sorted(result.items())
178+
179+
def __new_list_leaves(self, from_datetime, to_datetime, calendar=None, domain=None):
180+
resource = self.resource_id
181+
calendar = calendar or self.resource_calendar_id
182+
183+
# naive datetimes are made explicit in UTC
184+
if not from_datetime.tzinfo:
185+
from_datetime = from_datetime.replace(tzinfo=utc)
186+
if not to_datetime.tzinfo:
187+
to_datetime = to_datetime.replace(tzinfo=utc)
188+
189+
attendances = calendar._attendance_intervals_batch(
190+
from_datetime, to_datetime, resource
191+
)[resource.id]
192+
leaves = calendar._leave_intervals_batch(
193+
from_datetime, to_datetime, resource, domain
194+
)[resource.id]
195+
result = []
196+
for start, stop, leave in leaves & attendances:
197+
hours = self._get_work_hours_interval(start, stop, leave)
198+
result.append((start.date(), hours, leave))
199+
return result
200+
201+
for func in resource_functions:
202+
setattr(func[0], func[1], locals()["__new_" + func[1]])

resource_hook/models/resource_calendar.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
class ResourceCalendar(models.Model):
88
_inherit = "resource.calendar"
99

10-
def _get_work_hours(self, start, stop, meta):
10+
def _get_work_hours_interval(self, start, stop, meta):
1111
"""
1212
This method now returns the hours between the two ends of the
1313
interval. Extend this method if you want to alter the logic.
1414
:param interval:
1515
:return: float representing the time worked.
1616
"""
1717
return (stop - start).total_seconds() / 3600
18+
19+
def _get_work_hours_attendance(self, attendance):
20+
return attendance.hour_to - attendance.hour_from

resource_hook/readme/HISTORY.rst

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
11.0.1.0.0 (2019-05-28)
22
~~~~~~~~~~~~~~~~~~~~~~~
33

4-
* Introduce hook in method '_get_work_days_data' of ResourceMixing class in
4+
* Introduce hook in method `_get_work_days_data` of ResourceMixing class in
55
order to allow changes in the logic to compute working hours within an
6-
interval. A new method '_get_work_hours' is now provided for this purpose.
6+
interval. A new method `_get_work_hours` is now provided for this purpose.
7+
The method can be inherit to alter the standard behaviour.
8+
9+
12.0.1.0.2 (2020-06-09)
10+
~~~~~~~~~~~~~~~~~~~~~~~
11+
12+
* Introduce hook in method `get_work_hours_count` of ResourceCalendar class in
13+
order to allow changes in the logic to compute working hours within an
14+
interval. A new method `_get_work_hours` is now provided for this purpose.
15+
The method can be inherit to alter the standard behaviour.
16+
17+
18+
13.0.1.0.0 (2020-09-29)
19+
~~~~~~~~~~~~~~~~~~~~~~~
20+
21+
* Introduce hook in methods `list_work_time_per_day` and `list_leaves` of
22+
ResourceMixing class in order to allow changes in the logic to compute
23+
working hours within an interval.
24+
25+
* Introduce hook in methods `_get_days_data`, `_get_resources_day_total`,
26+
`get_work_hours_count`, `plan_hours` and `_compute_hours_per_day` of
27+
ResourceCalendar class in order to allow changes in the logic to compute
28+
working hours within an interval or an attendance. A new method
29+
`_get_work_hours_attendance` is now provided for this purpose.
730
The method can be inherit to alter the standard behaviour.

0 commit comments

Comments
 (0)