Resources

A resource (resource.resource) is something that works according to a working schedule (calendar_id, required), in a timezone (tz, required). It is either a human (resource_type = 'user', optionally linked to a user) or a material resource (a machine, a meeting room).

Business models rarely use resource.resource directly: they inherit from the abstract resource.mixin, which creates a resource for each record and exposes its calendar, timezone and the work time computations. hr.employee and mrp.workcenter are the main examples.

The resource mixin

A model inheriting from resource.mixin gets:

  • resource_id: the resource.resource of the record, created automatically on create (_prepare_resource_values builds its values);

  • resource_calendar_id, company_id and tz, related to the resource.

class MaintenanceTeam(models.Model):
    _name = 'my.maintenance.team'
    _inherit = ['resource.mixin']
    _description = 'Maintenance Team'

    name = fields.Char(related='resource_id.name', store=True, readonly=False)

The mixin provides the usual work time computations for a batch of records. Contrary to the calendar-level methods of resource.calendar, they take the leaves of each resource into account:

_get_work_days_data_batch()

The work duration of each record between two datetimes, as {record_id: {'days': float, 'hours': float}}.

_get_leave_days_data_batch()

The leave duration of each record, in the same format.

_list_work_time_per_day()

The work hours of each record, per day: {record_id: [(date, hours), ...]}.

_adjust_to_calendar()

Snaps a period to the working hours of each record: the first work interval start of the start day and the last work interval end of the end day.

_get_calendars()

The calendar of each record at a date. hr.employee overrides it to return the calendar of the employee’s version at that date.

Example

from datetime import datetime
from zoneinfo import ZoneInfo

employees = env['hr.employee'].search([], limit=3)
brussels = ZoneInfo('Europe/Brussels')
start = datetime(2026, 10, 19, tzinfo=brussels)
end = datetime(2026, 10, 24, tzinfo=brussels)

employees._get_work_days_data_batch(start, end)
# {employee_id: {'days': 5.0, 'hours': 40.0}, ...}
employees._list_work_time_per_day(start, end)
# {employee_id: [(date(2026, 10, 19), 8.0), (date(2026, 10, 20), 8.0), ...], ...}

Resources with several calendars

The calendar of a resource can change over time. In resource, a resource has a single calendar, but hr makes it follow the versions of the employee. To support this, the methods of resource.resource work with calendar validity periods:

_get_calendars_validity_within_period()

Returns {resource_id: {calendar: Intervals}}: which calendar applies to each resource, when, within a period. In resource, one calendar over the whole period.

_get_valid_work_intervals()

Returns the work intervals of each resource, computed with the right calendar on each period, as a tuple ({resource_id: Intervals}, {calendar_id: Intervals}). This is the method to use to get the work intervals of resources whose calendar may change, e.g. in planning applications.

_get_calendar_data_at()

The calendar, hours per week and hours per day valid at a date.

_get_unavailable_intervals()

The unavailable intervals of each resource, in UTC, for Gantt views.

Example

resources = employees.resource_id
work_intervals, calendar_intervals = resources._get_valid_work_intervals(start, end)
work_intervals[resources[0].id]  # Intervals of the first resource

Flexible resources

Planning applications need a work budget rather than work intervals for resources with a flexible calendar: _get_flexible_resource_valid_work_intervals() returns whole-day intervals with a daily and a weekly hours budget, and _get_flexible_resource_work_hours() computes how many hours can be consumed in some intervals within these budgets.

Pitfalls

  • _get_work_interval is deprecated: use _adjust_to_calendar.

  • _adjust_to_calendar and the mixin methods accept naive datetimes and consider them as UTC.

  • _get_leave_days_data_batch and _list_work_time_per_day use the current calendar of the records (resource_calendar_id), while _get_work_days_data_batch uses _get_calendars.

  • Pass compute_leaves=False in the context of _list_work_time_per_day to ignore leaves.

Reference

Original definition from resource

model odoo.addons.resource.models.resource_resource.ResourceResource[fuente]
_name = resource.resource
name: str
Required

Name

Name

resource_type: str
Required

Name

Type

Selection

user: Human material: Material

Default

user

calendar_id: int
Required

Name

Working Time

Comodel

resource.calendar

Define the working schedule of the resource. Assign a flexible calendar for fully flexible working hours.

tz: str
Required

Name

Timezone

user_id: int
Name

User

Comodel

res.users

Related user name for the resource to manage its access.

company_id: int
Name

Company

Comodel

res.company

time_efficiency: float
Required

Name

Efficiency Factor

Default

100

This field is used to calculate the expected duration of a work order at this work center. For example, if a work order takes one hour and the efficiency factor is 100%, then the expected duration will be one hour. If the efficiency factor is 200%, however the expected duration will be 30 minutes.

_get_resources_per_tz()[fuente]

Group the resources by timezone, in the format expected by the resources_per_tz argument of the resource.calendar interval methods.

Devuelve

{ZoneInfo: resource.resource recordset} (empty dict for no resource)

Tipo del valor devuelto

dict

_get_calendars_validity_within_period(start, end, default_company=None)[fuente]

Return, for each resource, the calendars it uses within the period and when each of them applies. Here a resource has a single calendar valid on the whole period; hr overrides it so that an employee uses the calendar of each of its contract versions on their own validity period.

Parámetros
  • start (datetime) – timezone-aware start of the period

  • end (datetime) – timezone-aware end of the period

  • default_company – res.company whose default calendar is used when self is empty (current company by default)

Devuelve

{resource_id: {resource.calendar: Intervals}} (nested defaultdict). When self is empty, the key False maps the default calendar to the whole period.

Tipo del valor devuelto

dict

_get_valid_work_intervals(start, end, calendars=None, compute_leaves=True)[fuente]

Return the work intervals of each resource between start and end, taking into account that a resource may use several calendars over the period (see _get_calendars_validity_within_period()): the work intervals of each calendar are restricted to its validity period, then united.

This is the entry point to know when resources work, e.g. in planning, project or MRP scheduling. Flexible calendars give the intervals described in resource.calendar._attendance_intervals_batch; a falsy calendar (possible with overrides) gives the whole period.

Parámetros
  • start (datetime) – timezone-aware start of the period

  • end (datetime) – timezone-aware end of the period

  • calendars – optional extra resource.calendar records for which the calendar-level work intervals are wanted in the second returned dict

  • compute_leaves (bool) – deduct leaves

Devuelve

a pair (resource_work_intervals, calendar_work_intervals): {resource_id: Intervals} (in each resource’s timezone) and {calendar_id: Intervals} (calendar-level result of every calendar used)

Tipo del valor devuelto

tuple

_get_calendar_data_at(date_target, tz=False)[fuente]

Return the calendar data applying to each resource at date_target. Here it is the resource’s own calendar; hr overrides it to use the employee’s contract version valid at that date (date_target being converted to tz).

Devuelve

{resource: {'resource_calendar_id': resource.calendar, 'hours_per_week': float, 'hours_per_day': float}}

Tipo del valor devuelto

dict

_get_unavailable_intervals(start, end)[fuente]

Return the periods during which each resource does not work between start and end, according to its calendar (resource.calendar._unavailable_intervals_batch), e.g. to grey out Gantt views (planning, project, MRP).

Parámetros
  • start (datetime) – start of the period, naive means UTC

  • end (datetime) – end of the period, naive means UTC

Devuelve

{resource_id: Intervals} expressed in UTC (only the leaves for flexible resources, which are missing when they have no leave)

Tipo del valor devuelto

dict

_adjust_to_calendar(start, end, compute_leaves=True)[fuente]

Snap start and end to the closest work interval boundaries of each resource’s calendar (see resource.calendar._get_closest_work_time), e.g. to propose default dates in planning or time off forms.

start is matched against interval starts between the beginning of its day and the end of the day of end; end is matched against interval ends between start and the end of its day. Computation happens in the resource timezone.

Example: with attendances 8:00-13:00 and 14:00-17:00, start=9:00 and end=18:00 (same day) give (8:00, 17:00).

Parámetros
  • start (datetime) – start to adjust, naive means UTC

  • end (datetime) – end to adjust, naive means UTC

  • compute_leaves (bool) – deduct leaves from the work intervals

Devuelve

{resource: (start, end)}; each value is in the timezone of the given argument (naive UTC if it was naive), or None if no boundary was found

Tipo del valor devuelto

dict

_is_flexible()[fuente]

Return whether the resource’s calendar is flexible (type undefined), see resource.calendar._is_flexible.

_is_fully_flexible()[fuente]

Return whether the resource’s calendar is fully flexible (no attendance and no hours target), see resource.calendar._is_fully_flexible.

_get_flexible_resource_valid_work_intervals(start, end)[fuente]

Flexible counterpart of _get_valid_work_intervals(), for resources that all have a flexible calendar. Since they have no attendance, it returns availability as whole days plus hour budgets, to be consumed by _get_flexible_resource_work_hours().

The budgets are computed on the full weeks (per the user language’s first day of the week) containing the period. Each leave day (leaves counted as absence only, as for the work intervals) is removed from the intervals and its hours_per_day deducted from the budgets. Fully flexible resources get intervals but no budget.

Parámetros
  • start (datetime) – timezone-aware start of the period

  • end (datetime) – timezone-aware end of the period

Devuelve

a triple (work_intervals, hours_per_day, hours_per_week):

  • {resource_id: Intervals}: whole-day intervals in the resource timezone, leave days removed, restricted to [start, end];

  • {resource_id: {date: float}}: daily budget (at most hours_per_day), for the days of the period;

  • {resource_id: {(year, week_number): float}}: weekly budget (at most hours_per_week, or the calendar’s full time hours).

Three empty dicts when self is empty.

Tipo del valor devuelto

tuple

_get_flexible_resource_work_hours(intervals, flexible_resources_hours_per_day, flexible_resources_hours_per_week, work_hours_per_day=None)[fuente]

Return how many hours this flexible resource can work within intervals, capped day by day by the daily budget and the remaining weekly budget, as computed by _get_flexible_resource_valid_work_intervals(). For a fully flexible resource (or without hours_per_day), simply return the total duration of intervals.

Parámetros
  • intervals – intervals of the resource to evaluate, e.g. a planning slot intersected with its work intervals

  • flexible_resources_hours_per_day (dict) – {date: float} budget of this resource

  • flexible_resources_hours_per_week (dict) – {(year, week_number): float} budget of this resource (not modified)

  • work_hours_per_day – optional defaultdict(float) filled in place with the hours retained per date

Tipo del valor devuelto

float

Original definition from resource

model odoo.addons.resource.models.resource_mixin.ResourceMixin[fuente]
_name = resource.mixin
resource_id: int
Required

Name

Resource

Comodel

resource.resource

resource_calendar_id: int
Name

None

Comodel

:ref:` <model->`

company_id: int
Name

None

Comodel

:ref:` <model->`

tz: str
Name

None

_get_calendars(date_from=None)[fuente]

Return the calendar of each record. Here it is always resource_calendar_id; hr overrides it to return the calendar of the contract version valid at date_from.

Parámetros

date_from – date or datetime at which the calendar is wanted

Devuelve

{record_id: resource.calendar} (keys are ids of self, not resources)

Tipo del valor devuelto

dict

_get_work_days_data_batch(from_datetime, to_datetime, compute_leaves=True, calendar=None, domain=None)[fuente]

Return the working time of each record between two datetimes, in days and hours: work intervals of its resource (its own leaves and global leaves deducted) converted by resource.calendar._get_attendance_intervals_days_data.

Parámetros
  • from_datetime (datetime) – start of the period, naive means UTC

  • to_datetime (datetime) – end of the period, naive means UTC

  • compute_leaves (bool) – deduct leaves

  • calendar – resource.calendar to use for all records instead of the one given by _get_calendars() at from_datetime

  • domain (list) – leave domain, None means [('count_as', '=', 'absence')]

Devuelve

{record_id: {'days': float, 'hours': float}}

Tipo del valor devuelto

dict

_get_leave_days_data_batch(from_datetime, to_datetime, calendar=None, domain=None)[fuente]

Return the leave time of each record between two datetimes, in days and hours: the part of its attendance intervals covered by leaves (its own and global ones), converted by resource.calendar._get_attendance_intervals_days_data.

For a fully flexible calendar, the whole period is returned (days being the number of full 24-hour periods, as a float).

Parámetros
  • from_datetime (datetime) – start of the period, naive means UTC

  • to_datetime (datetime) – end of the period, naive means UTC

  • calendar – resource.calendar to use for all records instead of their resource_calendar_id

  • domain (list) – leave domain, None means [('count_as', '=', 'absence')]

Devuelve

{record_id: {'days': float, 'hours': float}}

Tipo del valor devuelto

dict

_list_work_time_per_day(from_datetime, to_datetime, calendar=None, domain=None)[fuente]

Return the work hours of each record per day, from the work intervals of its resource (leaves deducted unless the context key compute_leaves is False). Days are those of the interval starts, in the resource timezone.

Parámetros
  • from_datetime (datetime) – start of the period, naive means UTC

  • to_datetime (datetime) – end of the period, naive means UTC

  • calendar – resource.calendar to use for all records instead of their resource_calendar_id

  • domain (list) – leave domain, None means [('count_as', '=', 'absence')]

Devuelve

{record_id: [(date, hours), ...]} sorted by date, only days with work

Tipo del valor devuelto

dict

_adjust_to_calendar(start, end)[fuente]

Same as resource.resource._adjust_to_calendar (leaves deducted), keyed by the records of self.

Devuelve

{record: (start, end)}

Tipo del valor devuelto

dict

_prepare_resource_values(vals, tz)[fuente]