Resource¶
The resource module is the time engine shared by many applications: Employees, Time Off,
Planning, Project, Manufacturing, Field Service, Appointments, etc. It answers questions such as
“when is this person supposed to work next week?”, “how many working hours are there between
these two dates?” or “when will a 10-hour task started on Monday at 9:00 end?”.
It does not depend on any business application: the models are generic and other modules plug
into them (e.g. hr makes the working schedule of an employee change over time through its
versions).
Models overview¶
![digraph resource {
rankdir=LR;
node [shape=box, style=rounded, fontname="sans-serif", fontsize=10];
edge [fontname="sans-serif", fontsize=9];
calendar [label="resource.calendar\n(working schedule)"];
attendance [label="resource.calendar.attendance\n(expected work slot)"];
leave [label="resource.calendar.leaves\n(time off, public holiday)"];
resource [label="resource.resource\n(human or material)"];
mixin [label="resource.mixin\n(hr.employee, mrp.workcenter, ...)"];
calendar -> attendance [label="attendance_ids"];
calendar -> leave [label="leave_ids"];
leave -> resource [label="resource_id\n(empty = global)"];
resource -> calendar [label="calendar_id"];
mixin -> resource [label="resource_id"];
}](../../../_images/graphviz-8c5e5e5e1a23aca892f8e3ebb7e01f47553f983a.png)
- resource.calendar
A working schedule (Working Times in the user interface). It defines when work is expected through its attendances (e.g. Monday 8:00-12:00) and holds the leaves that interrupt it (public holidays, time off).
- resource.calendar.attendance
One expected work slot of a calendar: a weekday and hours for regular schedules, a date (optionally recurring) for variable ones.
- resource.calendar.leaves
A period during which work is not expected. A leave without resource is global (public holiday for every resource of the company), otherwise it only applies to that resource.
- resource.resource
Something that works according to a calendar, in a timezone: a human (linked to a user or an employee) or a material resource (a machine, a room).
- resource.mixin
An abstract model giving a business model its own
resource.resourcerecord, hence a calendar and a timezone.hr.employeeandmrp.workcenterinherit from it.
Intervals¶
All the computations of the module are expressed with Intervals
(odoo/tools/intervals.py): an ordered list of disjoint (start, stop, records) triples.
start and stop are timezone-aware datetimes and records is the recordset that produced the
interval (an attendance, a leave, …), called the payload.
The list is normalized at construction: empty intervals are dropped, overlapping intervals are
merged and their payloads are united. Adjacent intervals are merged too, unless
keep_distinct=True is given (overlapping intervals are still merged in that case).
Intervals support set operations, which is how work time is derived from attendances and leaves:
a | b: union, payloads are united;a & b: intersection, only the payload ofais kept;a - b: difference, only the payload ofais kept.
Example
from odoo.tools.intervals import Intervals
work = Intervals([(0, 10, {'work'})])
leaves = Intervals([(2, 3, {'leave'}), (5, 6, {'leave'})])
list(work - leaves) # [(0, 2, {'work'}), (3, 5, {'work'}), (6, 10, {'work'})]
list(work & leaves) # [(2, 3, {'work'}), (5, 6, {'work'})]
list(Intervals([(0, 2, {'a'})]) | Intervals([(1, 3, {'b'})])) # [(0, 3, {'a', 'b'})]
# adjacent intervals are merged, unless keep_distinct is set
list(Intervals([(1, 3, {'a'}), (3, 5, {'b'})])) # [(1, 5, {'a', 'b'})]
list(Intervals([(1, 3, {'a'}), (3, 5, {'b'})], keep_distinct=True))
# [(1, 3, {'a'}), (3, 5, {'b'})]
To get a number of hours, sum the interval durations:
hours = sum((stop - start).total_seconds() / 3600 for start, stop, _records in intervals)
Conventions¶
The interval methods of the module share a few conventions that are worth knowing before calling any of them.
- Timezone-aware datetimes
The
_*_intervals_batchmethods assert thatstart_dtandend_dtare timezone-aware. Attendance hours are local hours: an attendance from 8:00 to 12:00 means 8:00 to 12:00 in the timezone of the resource. The returned intervals are expressed in that timezone too (except for_unavailable_intervals_batch, which returns UTC intervals).Higher-level methods (
plan_hours,_adjust_to_calendar, …) also accept naive datetimes, which are then considered as UTC (Odoo always runs with the UTC process timezone), and return naive UTC datetimes.- Batch computation per timezone
Batch methods take a
resources_per_tzdictionary{ZoneInfo: resource.resource}, built withresources._get_resources_per_tz(), and return a dictionary{resource_id: Intervals}.- The
Falsekey In the returned dictionary, the key
Falseholds the calendar-level result: the calendar’s attendances minus its global leaves, for no specific resource. Resource keys also take the leaves of that resource into account.- Leave domains
Leave methods take an optional
domain(a list) onresource.calendar.leaves.Nonemeans[('count_as', '=', 'absence')]: leaves counted as working time (e.g. a training) are not deducted. Any other value, even[], replaces that default.