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.