Time off allocations and accruals

An allocation (hr.leave.allocation) grants an employee an amount of time off of a type that requires it (requires_allocation, see Time off types). The amount is either fixed, or earned over time by an accrual plan (hr.leave.accrual.plan) and its levels (hr.leave.accrual.level). The time off requests of the employee are deducted from the allocations to compute the balance, which is checked when requesting time off.

Allocations are also created by hr_holidays itself: from the accrual plan of a working schedule, and from the time rules crediting overtime as compensatory time off.

其他資料

Time off for the time off types and requests.

備註

As in Time off, the examples run in odoo-bin shell with the demo data, as the administrator: env = env(user=env.ref('base.user_admin')).

Allocations

An allocation belongs to an employee (employee_id) and a type (work_entry_type_id), and is valid from date_from to date_to (both date; no date_to means no end). Time off is only deducted from an allocation for its part within the validity of the allocation.

number_of_days

The amount granted, always in days, also for types allocated in hours. number_of_hours is computed from it with the hours per day of the employee’s schedule at date_from (24 for a fully flexible schedule) and can be written, which sets number_of_days (for an accrual allocation, the schedule at its next accrual is used). type_request_unit is the unit the allocation is displayed and accrued in: the added_value_type of the accrual plan, else the type’s unit_of_measure (_get_request_unit()).

state

confirm (To Approve, the only state allowed at creation), validate1 (Second Approval), validate (Approved) or refuse. Only approved allocations count.

accrual_plan_id

Set for accrual allocations, see Accrual plans; the plan’s type must be the allocation’s type.

The approval works as for time off requests, with the type’s allocation_validation_type: types without validation are approved on creation, the others with action_approve() (to validate, or to validate1 for the first approval of a type validated twice) and action_refuse(). Only time off administrators approve their own allocations. Employees can request allocations of the types with employee_requests.

An approved allocation cannot be reduced below the time already taken from it; only allocations to approve or refused, without time off taken, can be deleted. A negative allocation is only allowed for types allowing a negative balance, within their max_allowed_negative.

Balances

_get_consumed_leaves() is the core of every balance. For a set of employees and types, it deducts the requests to approve and approved (confirm, validate1, validate) from the approved allocations, request by request in chronological order, and allocation by allocation in this order:

  1. the allocations with an end date, the one ending first first;

  2. the accrual allocations without end date;

  3. the other allocations without end date.

A request is only deducted from the allocations valid on its dates; the part of a request that no allocation covers is an excess. The result distinguishes:

  • the approved amounts: leaves_taken and remaining_leaves, counting approved requests only;

  • the virtual amounts: virtual_leaves_taken and virtual_remaining_leaves, also counting the requests to approve. The virtual remaining amount is what can still be requested.

Amounts are in the unit_of_measure of the type: keys are prefixed with days_ or hours_ (e.g. days_virtual_remaining_leaves), and the other unit is also given as <unit>_taken and <unit>_remaining. Types without allocation use the key False instead of an allocation.

On top of it:

get_allocation_data()

The balance per employee and type on a date, summed over the allocations valid on that date: {employee: [(type name, data, requires_allocation, type id), ...]}. It feeds the time off dashboard (through get_allocation_data_request) and the checks of the requests.

The computed fields max_leaves, leaves_taken and virtual_remaining_leaves

On hr.leave.allocation (per allocation, today), on hr.work.entry.type (for the employee of the context) and on hr.leave (max_leaves, virtual_remaining_leaves, for the request’s employee and type). They are for display: code should call the methods above.

Creating or changing a request of a type requiring an allocation checks that the virtual balance on its first day covers it (_check_validity()), down to -max_allowed_negative when the type allows a negative balance.

Example

Marc Demo has an allocation of 3 days valid in 2026 and an open-ended one of 10 days, an approved request of 4 days and a request of half a day to approve:

from datetime import date

env = env(user=env.ref('base.user_admin'))
demo = env.ref('base.user_demo')
marc = demo.employee_id
pto = env['hr.work.entry.type'].create({
    'name': 'Paid Time Off',
    'code': 'DOCPTO',
    'count_as': 'absence',
    'requires_allocation': True,
    'request_unit': 'half_day',
    'unit_of_measure': 'day',
    'leave_validation_type': 'manager',
    'allocation_validation_type': 'hr',
})
yearly, open_ended = env['hr.leave.allocation'].create([{
    'employee_id': marc.id,
    'work_entry_type_id': pto.id,
    'number_of_days': 3,
    'date_from': date(2026, 1, 1),
    'date_to': date(2026, 12, 31),
}, {
    'employee_id': marc.id,
    'work_entry_type_id': pto.id,
    'number_of_days': 10,
    'date_from': date(2026, 1, 1),
}])
yearly.state, yearly.name  # ('confirm', 'Paid Time Off (3.0 day(s))')
(yearly | open_ended).action_approve()
yearly.state, yearly.approver_id.name  # ('validate', 'Mitchell Admin')

approved = env['hr.leave'].create({
    'employee_id': marc.id,
    'work_entry_type_id': pto.id,
    'request_date_from': date(2026, 12, 1),
    'request_date_to': date(2026, 12, 4),
})
pending = env['hr.leave'].with_user(demo).create({
    'employee_id': marc.id,
    'work_entry_type_id': pto.id,
    'request_date_from': date(2026, 12, 14),
    'request_date_to': date(2026, 12, 14),
    'request_date_to_period': 'am',
})
(approved.state, approved.number_of_days), (pending.state, pending.number_of_days)
# (('validate', 4.0), ('confirm', 0.5))

consumed, to_recheck = marc._get_consumed_leaves(pto)
dict(consumed[marc][pto][yearly])  # a defaultdict
# {'days_max_leaves': 3.0, 'days_accrual_bonus': 0, 'days_leaves_taken': 3.0,
#  'days_virtual_leaves_taken': 3.0, 'days_remaining_leaves': 0.0,
#  'days_virtual_remaining_leaves': 0.0, 'hours_taken': 24.0, 'hours_remaining': 0.0}
dict(consumed[marc][pto][open_ended])
# {'days_max_leaves': 10.0, 'days_accrual_bonus': 0, 'days_leaves_taken': 1.0,
#  'days_virtual_leaves_taken': 1.5, 'days_remaining_leaves': 9.0,
#  'days_virtual_remaining_leaves': 8.5, 'hours_taken': 8.0, 'hours_remaining': 68.0}

name, data, requires_allocation, type_id = pto.get_allocation_data(marc)[marc][0]
{key: data[key] for key in ('max_leaves', 'leaves_taken', 'remaining_leaves',
                            'virtual_leaves_taken', 'virtual_remaining_leaves',
                            'leaves_requested')}
# {'max_leaves': 13.0, 'leaves_taken': 4.0, 'remaining_leaves': 9.0,
#  'virtual_leaves_taken': 4.5, 'virtual_remaining_leaves': 8.5, 'leaves_requested': 0.5}

env['hr.leave'].create({
    'employee_id': marc.id,
    'work_entry_type_id': pto.id,
    'request_date_from': date(2026, 12, 15),
    'request_date_to': date(2026, 12, 31),
})
# ValidationError: Marc Demo does not have a valid allocation for the leave type
# Paid Time Off to cover that request.

Accrual plans

An accrual plan (hr.leave.accrual.plan) earns time for its type (work_entry_type_id) over time. Its levels (level_ids, Milestones), ordered by start, each define a rate:

start_count, start_type, milestone_date

When the level starts, after the start of the allocation (milestone_date = 'creation': immediately). The first level may start later than the allocation.

added_value, added_value_type

The amount earned per period, in days or hours. The first level’s unit is the unit of the plan (added_value_type of the plan) and of its allocations (type_request_unit).

frequency

hourly (per worked hour, see below), daily, weekly (week_day), bimonthly (first_day, second_day), monthly (first_day), biyearly (first_month, first_month_day, second_month, second_month_day) or yearly (yearly_month, yearly_day): the accrual dates, computed by _get_next_date() and _get_previous_date().

cap_accrued_time, maximum_leave

A cap on the balance (earned minus taken): accrual stops while the balance is at the cap.

cap_accrued_time_yearly, maximum_leave_yearly

A cap on the amount earned per year (between two carry-over dates).

action_with_unused_accruals, carryover_options, max_carriedover_duration

What happens to the balance on the carry-over date: lost (reset), or carried over, without limit or up to max_carriedover_duration.

accrual_validity, accrual_validity_count, accrual_validity_type

The time carried over expires after this period.

On the plan:

accrued_gain_time

end (default): the time of a period is earned at its end (on the next accrual date); start: at its start.

transition_mode

immediately: a level starts on its start date; end_of_accrual: on its first accrual date from its start date, so that a period is never split between two levels (_get_lvls_boundaries()).

is_based_on_worked_time

Prorate the earned time on the worked time of the period: absences whose type is not elligible_for_accrual_rate reduce it (_get_accrual_plan_level_work_entry_prorata()). With hourly levels, the amount is earned per hour of the period (worked hours with this option, all planned hours without).

can_be_carryover, carryover_date, carryover_day, carryover_month

Whether unused time is carried over at all (otherwise it is lost on the carry-over date), and when: the start of the year, the anniversary of the allocation, or a custom day.

The first period of an allocation starting between two accrual dates is prorated on its number of days, unless the plan is based on worked time.

Running the accruals

An accrual allocation stores its progress: number_of_days (earned so far), lastcall and nextcall (the last and next accrual events: accrual dates, level transitions, carry-over dates, expirations), last_accrual, yearly_accrued_days, previous_carryover_number_of_days and carried_over_days_expiration_date.

The daily cron (Accrual Time Off: Updates the number of time off) runs model._get_to_update_accrual_allocations()._update_accrual(): _get_to_update_accrual_allocations() selects the approved accrual allocations valid today whose nextcall is due, and _update_accrual() processes every event up to today and writes the result. A new allocation starting in the past catches up at its first run.

_process_accrual_plans() does the computation without writing: it starts from the stored values (or the precomputed_allocations given), processes the events up to a date and returns {allocation: allocation_data}. The Accrual inconsistent helper methods (see the class docstring of HrLeaveAllocation) only read and update allocation_data, so that the same code serves:

  • the cron, which writes the result;

  • the future balance: the balance on a future date includes what the accrual will have earned by then (accrual_bonus, _get_additionnal_future_leaves_on()), and future requests are checked against it (to_recheck_leaves, exceeding_duration in the second result of _get_consumed_leaves());

  • the allocation form, which simulates the plan from date_from until today.

A second daily cron (_cancel_invalid_leaves()) cancels the requests of the next 31 days that the accruals will not cover.

Example

A plan earning 1.5 days per month at the end of the month, carrying over at most 5 days at the start of the year, and 2 days per month after two years:

from odoo.fields import Command

plan = env['hr.leave.accrual.plan'].create({
    'name': 'Monthly 1.5 days',
    'work_entry_type_id': pto.id,
    'accrued_gain_time': 'end',
    'can_be_carryover': True,
    'carryover_date': 'year_start',
    'level_ids': [Command.create({
        'added_value': 1.5,
        'added_value_type': 'day',
        'frequency': 'monthly',
        'first_day': '1',
        'action_with_unused_accruals': 'all',
        'carryover_options': 'limited',
        'max_carriedover_duration': 5,
    }), Command.create({
        'milestone_date': 'after',
        'start_count': 2,
        'start_type': 'year',
        'added_value': 2,
        'added_value_type': 'day',
        'frequency': 'monthly',
        'first_day': '1',
        'action_with_unused_accruals': 'all',
    })],
})
employee = env['hr.employee'].search([('name', '=', 'Ronnie Hart')])
allocation = env['hr.leave.allocation'].create({
    'employee_id': employee.id,
    'work_entry_type_id': pto.id,
    'accrual_plan_id': plan.id,
    'date_from': date(2026, 1, 1),
    'number_of_days': 0,
})
allocation.action_approve()
allocation.number_of_days, allocation.nextcall  # (0.0, False): not run yet

allocation._update_accrual()  # what the cron does (today is 2026-10-23)
allocation.number_of_days, allocation.lastcall, allocation.nextcall
# (13.5, datetime.date(2026, 10, 1), datetime.date(2026, 11, 1)): 9 months
allocation._get_lvls_boundaries()
# [datetime.date(2026, 1, 1), datetime.date(2028, 1, 1)]

allocation._process_accrual_plans(date(2027, 1, 1))[allocation]
# {'last_accrual': datetime.date(2027, 1, 1), 'lastcall': datetime.date(2027, 1, 1),
#  'nextcall': datetime.date(2027, 2, 1), 'allocated_duration': 6.5,
#  'yearly_accrued_duration': 0, 'carried_over_days_expiration_date': False,
#  'previous_carryover_allocated_duration': 5, 'leaves_taken': 0}
allocation.number_of_days  # 13.5: nothing written

data = pto.get_allocation_data(employee, date(2026, 12, 31))[employee][0][1]
data['max_leaves'], data['accrual_bonus']  # (16.5, 3.0)

On January 1, 2027, the 16.5 days earned in 2026 are capped to 5 by the carry-over, then December’s 1.5 days are earned (at the end of the period): 6.5 days.

Allocations from working schedules

A working schedule can have an accrual plan (resource.calendar.leave_accrual_plan_id). When an employee’s version uses a schedule with a work time rate above 100% and such a plan, the version gets an accrual allocation of the plan over its period (hr.version.allocation_from_working_schedule_id), recreated when the version’s schedule changes (_action_trigger_accrual_plan_working_schedule()). It compensates schedules longer than the legal working time with time off.

Allocations from time rules

hr_holidays adds two fields to the time rules (hr.time.rule, see Time rules): allocation_type_id (Allocate to, a type requiring an allocation) and leave_compensation_rate (Allocate %). The excess hours of such a rule, times the rate, divided by the employee’s hours per day, are credited to the employee’s approved allocation without end date of that type, created and approved if needed (_apply_allocation_credits()); the missing hours of an undertime rule are deducted from it, unless the allocation cannot cover them all (_filter_deficit_exceeding_allocation()).

Each credit is logged in a hr.time.rule.allocation.log record (the source record res_model and res_id, the allocation and the days). When the source is evaluated again, changed, refused or deleted, its logged credits are reversed first (_reverse_allocation_credits()); a reversal that would bring the allocation below what was already taken raises a ValidationError.

The sources are the time off requests (validated working time requests exceeding the schedule, e.g. a long business trip) and, with hr_holidays_attendance, the attendances: see Overtime and time off: hr_holidays_attendance.

Example

A business trip of 12 hours on a day of 8 planned hours, with a rule crediting the excess as compensatory days:

trip, compensatory = env['hr.work.entry.type'].create([{
    'name': 'Business Trip',
    'code': 'DOCTRIP',
    'count_as': 'working_time',
    'requires_allocation': False,
    'request_unit': 'hour',
    'leave_validation_type': 'no_validation',
}, {
    'name': 'Compensatory Days',
    'code': 'DOCCOMP',
    'count_as': 'absence',
    'requires_allocation': True,
    'request_unit': 'half_day',
    'unit_of_measure': 'day',
}])
env['hr.time.rule'].create({
    'name': 'Trip overtime to compensatory days',
    'company_id': employee.company_id.id,
    'threshold_operator': 'exceed',
    'working_hours_mode': 'schedule_day',
    'condition_work_entry_type_ids': [Command.set(trip.ids)],
    'leave_compensation_rate': 1.0,
    'allocation_type_id': compensatory.id,
})
leave = env['hr.leave'].create({
    'employee_id': employee.id,
    'work_entry_type_id': trip.id,
    'request_date_from': date(2026, 12, 2),
    'request_date_to': date(2026, 12, 2),
    'request_hour_from': 7.0,
    'request_hour_to': 19.0,
})
leave.state, leave.number_of_hours  # ('validate', 12.0)
credit = env['hr.leave.allocation'].search([
    ('employee_id', '=', employee.id), ('work_entry_type_id', '=', compensatory.id),
])
credit.state, credit.number_of_days, credit.date_to  # ('validate', 0.5, False)
env['hr.time.rule.allocation.log'].search([]).read(['res_model', 'res_id', 'days'])
# [{'id': ..., 'res_model': 'hr.leave', 'res_id': leave.id, 'days': 0.5}]

leave.action_refuse()
credit.number_of_days  # 0.0: the credit is reversed

Pitfalls

  • number_of_days is in days even for types allocated in hours; the hours are converted with the employee’s hours per day, so an allocation in hours keeps its hours when the schedule changes only because hr.version rewrites number_of_days.

  • The computed balance fields (max_leaves, virtual_remaining_leaves, …) are computed for today, or for the employee of the context on types: use get_allocation_data() with a date in code.

  • Approving an accrual allocation does not run its plan: number_of_days stays at its initial value until the cron or a call to _update_accrual().

  • A request is deducted from the allocations valid on its dates only: an allocation starting after the request, or ending before it, does not cover it, and an allocation ending during a request only covers its first part.

  • The accrual helpers flagged Accrual inconsistent must not read the allocation fields nor write: override them consistently with allocation_data.

  • Time rule credits go to the allocation without end date of the type: a fixed allocation of the same type without end date receives them too.

Extending

Accrual frequencies

_get_hourly_frequencies() (frequencies accruing per hour), with _get_next_date() and _get_previous_date() for new frequencies; hr_holidays_attendance adds a frequency on attendances this way.

Accrual amounts

_get_accrual_plan_level_work_entry_prorata() and _get_period_added_duration().

Balances

_set_leaves_from_consumption_data() to reuse an already computed _get_consumed_leaves().

Time rule credits

_get_output_leave_merge_key() (when consecutive outputs make one request) and the source hooks of Time rule sources.

Reference

Original definition from hr_holidays

model odoo.addons.hr_holidays.models.hr_leave_allocation.HrLeaveAllocation[原始碼]
_name = hr.leave.allocation

Allocation of time off: credits an employee with days or hours of a time type.

A regular allocation grants number_of_days (number_of_hours for types allocated in hours); an accrual allocation (accrual_plan_id set) earns it over time, see _update_accrual().

Some accrual methods are tagged Accrual inconsistent: they must not read the allocation fields loaded by _get_accrual_allocation_data() nor write any field, but read and update their allocation_data dict instead, so that the accrual can be simulated (e.g. a future balance) without writing anything. Methods tagged Possibly accrual inconsistent take allocation_data={} and read the fields when it is empty.

employee_id: int
Required

Name

Employee

Comodel

hr.employee

work_entry_type_id: int
Required

Name

Time Type

Comodel

hr.work.entry.type

state: str
Name

Status

Readonly

this field is not supposed to/cannot be set manually

Selection

confirm: To Approve refuse: Refused validate1: Second Approval validate: Approved

Default

confirm

The status is 『To Approve』, when an allocation request is created. The status is 『Refused』, when an allocation request is refused by manager. The status is 『Approved』, when an allocation request is approved by manager.

date_from: datetime.date
Required

Name

Start Date

date_to: datetime.date
Name

End Date

number_of_days: float
Name

Number of Days

Default

1

Duration in days. Reference field to use when necessary.

number_of_hours: float
Name

Duration (hours)

For an Accrual Allocation, this field contains the theorical amount of time given to the employee, due to a previous start date, on the first run of the plan. This can be manually edited.

type_request_unit: str
Name

Type Request Unit

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

Selection

hour: Hours half_day: Half-Day day: Day

accrual_plan_id: int
Name

Accrual Plan

Comodel

hr.leave.accrual.plan

max_leaves: float
Name

Max Leaves

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

leaves_taken: float
Name

Time off Taken

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

virtual_remaining_leaves: float
Name

Available Time Off

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

last_accrual: datetime.date
Name

Date of the last accrual allocation

lastcall: datetime.date
Name

Date of the last executed accrual event (accrual, carryover date, expiring carriedover days, …)

nextcall: datetime.date
Name

Date of the closest next accrual event (accrual, carryover date, expiring carriedover days, …)

yearly_accrued_days: float
Name

Number of accrued days since the last carryover

previous_carryover_number_of_days: float
Name

The number of days on previous carryover date

carried_over_days_expiration_date: datetime.date
Name

Carried over days expiration date

create(vals_list)[原始碼]

Create allocations in 'confirm' and start their approval.

Types without validation are approved at once.

引發

UserError – when another state is given

write(vals)[原始碼]

Write on allocations, checking the state transitions.

引發

ValidationError – when the change reduces the balance below the time already taken (beyond the negative cap of the type)

action_approve()[原始碼]

Approve the allocations: validate them, or give the first approval ('validate1').

引發

UserError – when the user can neither validate nor approve an allocation

_action_validate()[原始碼]

Set the allocations to 'validate' and record the approvers.

action_refuse()[原始碼]

Refuse the allocations.

引發

UserError – when an allocation is not 'confirm', 'validate1' or 'validate'

_check_approval_update(state, raise_if_not_possible=True)[原始碼]

Check that the current user can move the allocations to state.

Only time off administrators can approve their own allocations (unless the type needs no validation). The superuser can do everything.

參數
  • state (str) – target state

  • raise_if_not_possible (bool) – raise instead of returning False

回傳型別

bool

引發

UserError – when not possible and raise_if_not_possible

_get_next_states_by_state()[原始碼]

Return the states the current user can move the allocation to, from each state.

Same rules as for time off; types without validation can always be approved.

回傳

{state: set(next_states)}

回傳型別

dict

_get_responsible_for_approval()[原始碼]

Return the user who must approve the allocation in its current state.

Same rules as _get_responsible_for_approval().

回傳型別

res.users

_check_negative_allocation()[原始碼]

Allow negative allocations only for types allowing a negative balance, within their cap.

The negative allocations of the employee and type overlapping the validity period may not exceed max_allowed_negative in total.

_set_leaves_from_consumption_data(employee_days_per_allocation)[原始碼]

Set max_leaves, leaves_taken and virtual_remaining_leaves.

Lets extensions reuse an already fetched result (e.g. l10n_be_hr_payroll).

參數

employee_days_per_allocation (dict) – first element of the result of _get_consumed_leaves()

_get_request_unit()[原始碼]

Return the unit the allocation is computed in (type_request_unit).

The added_value_type of the accrual plan, else the unit_of_measure of the type.

回傳

'day' or 'hour'

回傳型別

str

_get_employee_hours_per_day(allocation_data={})[原始碼]

Return the hours per day of the employee, to convert days into hours.

Possibly accrual inconsistent. The schedule is the one at the accrual’s nextcall (accrual allocations) or at date_from; 24 for fully flexible employees.

參數

allocation_data (dict) – accrual data, see _process_accrual_plans()

回傳型別

float

_convert_duration(duration, src_unit, dst_unit, allocation_data={})[原始碼]

Convert a duration between days and hours with the employee’s hours per day.

Possibly accrual inconsistent, see _get_employee_hours_per_day().

參數
  • duration (float) – duration to convert

  • src_unit (str) – 'day', 'half_day' or 'hour'

  • dst_unit (str) – 'day', 'half_day' or 'hour'

  • allocation_data (dict) – accrual data, see _process_accrual_plans()

回傳型別

float

_convert_to_type_request_unit(duration, duration_unit, allocation_data={})[原始碼]

Convert a duration into the allocation’s unit (type_request_unit).

Possibly accrual inconsistent, see _convert_duration().

參數
  • duration (float) – duration to convert

  • duration_unit (str) – 'day', 'half_day' or 'hour'

回傳型別

float

_convert_from_type_request_unit(duration, duration_unit, allocation_data={})[原始碼]

Convert a duration expressed in the allocation’s unit into duration_unit.

Possibly accrual inconsistent, see _convert_duration().

參數
  • duration (float) – duration in type_request_unit

  • duration_unit (str) – 'day', 'half_day' or 'hour'

回傳型別

float

_get_to_update_accrual_allocations()[原始碼]

Return the allocations the accrual cron must update.

Validated accrual allocations valid today, whose nextcall is not set or is due.

回傳型別

hr.leave.allocation

_update_accrual(date_to=None)[原始碼]

Run the accrual plans of the allocations up to date_to and write the result.

Writes number_of_days (the credit of the allocation) and the accrual fields (last_accrual, lastcall, nextcall, yearly_accrued_days, previous_carryover_number_of_days, carried_over_days_expiration_date) computed by _process_accrual_plans(). Called daily by the cron on _get_to_update_accrual_allocations().

參數

date_to (datetime.date) – last day to process, today by default

_process_accrual_plans(date_to=None, precomputed_allocations={})[原始碼]

Compute the accrual of the allocations up to date_to, without writing.

Runs every accrual event (accrual dates, level transitions, carry-over dates, expiration of the carried over time) from nextcall up to date_to (today by default), from the values stored on the allocation or from precomputed_allocations. Allocations without accrual plan are left out.

參數
  • date_to (datetime.date) – last day to process

  • precomputed_allocations (dict) – {allocation: allocation_data} to start from, instead of the stored values

回傳

{allocation: allocation_data} where allocation_data holds last_accrual, lastcall, nextcall, carried_over_days_expiration_date (dates), allocated_duration, yearly_accrued_duration, previous_carryover_allocated_duration and leaves_taken (floats in type_request_unit)

回傳型別

dict

_get_accrual_allocation_data()[原始碼]

Return the accrual data of the allocations, read from their fields.

回傳

{allocation: allocation_data}, see _process_accrual_plans(); leaves_taken is None

回傳型別

dict

_init_accrual_calls(allocation_data)[原始碼]

Initialize last_accrual, lastcall and nextcall for a first accrual run.

Accrual inconsistent. lastcall is the start of the first level; nextcall is the same date when accruing at the start of the period, else the first accrual, carry-over or level transition date.

參數

allocation_data (dict) – accrual data, updated in place

_process_accrual_start(allocation_data, current_level, next_accrual)[原始碼]

Accrue the period starting on allocation_data['nextcall'], if it is an accrual date.

Accrual inconsistent. For plans accruing at the start of the period (accrued_gain_time = 'start').

參數
_process_accrual_end(allocation_data, current_level, current_level_idx, first_level_start_date)[原始碼]

Accrue the period ending on allocation_data['nextcall'], if it is an accrual date.

Accrual inconsistent. For plans accruing at the end of the period (accrued_gain_time = 'end').

參數
  • allocation_data (dict) – accrual data, see _process_accrual_plans()

  • current_level – hr.leave.accrual.level accruing

  • current_level_idx (int) – index of the level

  • first_level_start_date (datetime.date) – start of the first level

_add_days_to_allocation(allocation_data, current_level, period_start, period_end, start_date, end_date)[原始碼]

Add the gain of an accrual period to allocation_data.

Accrual inconsistent. The gain (_get_period_added_duration()) is capped by the yearly cap of the level and by its balance cap (maximum_leave, on the balance allocated_duration - leaves_taken). Updates allocated_duration, yearly_accrued_duration and last_accrual.

參數
_get_period_added_duration(allocation_data, level, period_start, start_date, period_end, end_date)[原始碼]

Return the duration earned over an accrual period, in type_request_unit.

Accrual inconsistent. added_value times the prorata of _get_accrual_plan_level_work_entry_prorata(); a partial period (e.g. the first one) is prorated on its number of days, unless the plan is based on worked time.

回傳型別

float

_get_accrual_plan_level_work_entry_prorata(level, start_period, start_date, end_period, end_date)[原始碼]

Return the factor applied to the added_value of the level for a period.

Possibly accrual inconsistent. 1 unless the plan is based on worked time or the frequency is hourly. For hourly frequencies, the number of hours of the period: worked hours plus absences eligible for accrual (plus the other absences when the plan is not based on worked time). Otherwise the ratio of those hours to all the planned hours.

參數
回傳型別

float

_process_carryover_date(allocation_data, current_level, carryover_date)[原始碼]

Apply the carry-over policy of the level on its carry-over date.

Accrual inconsistent. Unused time is lost (action_with_unused_accruals = 'lost'), kept, or kept up to max_carriedover_duration; with accrual_validity, the time carried over expires after the validity period (carried_over_days_expiration_date).

參數
_get_next_carryover_date(date_from, date_from_included=True)[原始碼]

Return the next carry-over date of the accrual plan from date_from.

參數
  • date_from (datetime.date) – reference date

  • date_from_included (bool) – return date_from if it is a carry-over date

回傳型別

datetime.date

_has_expiring_days(allocation_data)[原始碼]

Tell whether carried over time is still to expire after nextcall.

Accrual inconsistent.

回傳型別

bool

_get_days_after_expiration(allocation_data)[原始碼]

Return allocated_duration once the carried over time not taken has expired.

Accrual inconsistent.

參數

allocation_data (dict) – accrual data, see _process_accrual_plans()

回傳型別

float

_get_lvls_boundaries() → list[datetime.date] | None[原始碼]

Return the start date of each level of the accrual plan for this allocation.

A level starts start_count start_type after date_from; with the transition mode 'end_of_accrual', the levels after the first one start on their first accrual date on or after that date.

回傳

one date per level, in order

回傳型別

list[datetime.date] | None

_get_next_lvl_start(level_idx)[原始碼]

Return the start date of the level following level_idx, None for the last one.

回傳型別

datetime.date or None

_get_current_accrual_plan_levels(target_date) → dict[str, tuple] | None[原始碼]

Return the level(s) of the accrual plan in effect on target_date.

On a level transition, both levels are returned. None before the first level starts, after date_to, or without levels.

參數

target_date (datetime.date) – reference date

回傳

{'current_level': (level, index)}, plus 'previous_level': (level, index) on a transition

回傳型別

dict[str, tuple] | None

_get_current_accrual_plan_level_idx(date)[原始碼]

Return the level in effect on date; on a transition, the level that starts.

回傳

(level, index), (None, None) when no level applies

回傳型別

tuple

_get_current_accrual_plan_level_idx_for_accrual(target_date)[原始碼]

Return the level in effect and the level accruing on target_date.

They differ on a level transition when the plan accrues at the end of the period (accrued_gain_time = 'end'): the period that ends is accrued with the policy of the previous level.

回傳

{'current_level': (level, index), 'accrual_level': (level, index)}, or None when no level applies

回傳型別

dict or None

_get_additionnal_future_leaves_on(accrual_date, allocations_data={})[原始碼]

Return the time the accrual will add between now and accrual_date.

Possibly accrual inconsistent. 0 for past dates, non accrual or non validated allocations, and allocations expired on accrual_date.

參數
  • accrual_date (datetime.date) – future date

  • allocations_data (dict) – {allocation: allocation_data}; when the allocation is in it, the gain is computed from these data instead of the stored values

回傳

added duration, in the unit_of_measure of the type

回傳型別

float

_get_init_accrual_plan_values()[原始碼]

Return the values resetting the accrual of an allocation.

回傳型別

dict

_onchange_process_accrual_plans()[原始碼]

Simulate in the form the time the accrual plan grants from date_from until today.

Original definition from hr_holidays

model odoo.addons.hr_holidays.models.hr_leave_accrual_plan.HrLeaveAccrualPlan[原始碼]
_name = hr.leave.accrual.plan
work_entry_type_id: int
Name

Time Type

Comodel

hr.work.entry.type

Time type to be accrued

level_ids: Sequence[odoo.orm.commands.Command]
Name

Milestones

Comodel

hr.leave.accrual.level

accrued_gain_time: str
Required

Name

Accrued Gain Time

Selection

start: At the start of the accrual period end: At the end of the accrual period

Default

end

transition_mode: str
Required

Name

Transition Mode

Selection

immediately: Immediately end_of_accrual: After this accrual’s period

Default

immediately

is_based_on_worked_time: bool
Name

Is Based On Worked Time

Only excludes requests where the time type is set as unpaid kind of.

can_be_carryover: bool
Name

Can Be Carryover

carryover_date: str
Required

Name

Carry-Over Time

Selection

year_start: At the start of the year allocation: At the allocation date other: Custom date

Default

year_start

added_value_type: str
Name

Added Value Type

Selection

day: Days hour: Hours

Default

day

Original definition from hr_holidays

model odoo.addons.hr_holidays.models.hr_leave_accrual_plan_level.HrLeaveAccrualLevel[原始碼]
_name = hr.leave.accrual.level
start_count: int
Name

Start Count

The accrual starts after a defined period from the allocation start date. This field defines the number of days, months or years after which accrual is used.

start_type: str
Required

Name

Start Type

Selection

day: Days month: Months year: Years

Default

day

This field defines the unit of time after which the accrual starts.

added_value: float
Required

Name

Added Value

Default

1

added_value_type: str
Required

Name

Added Value Type

Selection

day: Day(s) hour: Hour(s)

frequency: str
Required

Name

Frequency

Selection

hourly: Hourly daily: Daily weekly: Weekly bimonthly: Twice a month monthly: Monthly biyearly: Twice a year yearly: Yearly

Default

daily

cap_accrued_time: bool
Name

Cap Accrued Time

When the field is checked the balance of an allocation using this accrual plan will never exceed the specified amount.

maximum_leave: float
Name

Maximum Leave

Default

0

Choose a cap for this accrual.

cap_accrued_time_yearly: bool
Name

Cap Accrued Time Yearly

When the field is checked the total amount accrued each year will be capped at the specified amount

maximum_leave_yearly: float
Name

Maximum Leave Yearly

action_with_unused_accruals: str
Required

Name

Action With Unused Accruals

Selection

lost: Lost (Reset) all: Carried over

Default

lost

When the Carry-Over Time is reached, according to Plan’s setting, select what you want to happen with the unused time off: Lost (time will be reset to zero), Carried over (accrued time carried over to the next period.)

carryover_options: str
Required

Name

Carryover Options

Selection

unlimited: Unlimited limited: Up to

Default

unlimited

You can limit the accrued time carried over for the next period.

max_carriedover_duration: int
Name

Max Carriedover Duration

Set a maximum of accruals an allocation keeps at the end of the year.

accrual_validity: bool
Name

Accrual Validity

_get_hourly_frequencies()[原始碼]

Return the frequencies accruing per hour of the period instead of per period.

回傳型別

list[str]

_get_next_date(last_call)[原始碼]

Return the first accrual date of the level after last_call (excluded).

參數

last_call (datetime.date) – reference date

回傳型別

datetime.date

_get_previous_date(last_call)[原始碼]

Return the last accrual date of the level on or before last_call (included).

For instance, the 1st of the month for a monthly level.

參數

last_call (datetime.date) – reference date

回傳型別

datetime.date

_get_start_date(date_from)[原始碼]

Return date_from plus the start delay of the level (start_count start_type).

回傳型別

datetime.date

_get_level_transition_date(allocation_start)[原始碼]

Return the date the level starts for an allocation starting on allocation_start.

回傳型別

datetime.date

Additional fields with hr_holidays

model odoo.addons.hr_holidays.models.hr_time_rule.HrTimeRule[原始碼]
allocation_type_id: int
Name

Allocate to

Comodel

hr.work.entry.type

leave_compensation_rate: float
Name

Allocate %

Default

0.0

Leave allocated or taken at this rate of the excess or deficit. 100% = 1:1.

_apply_leave_output(excess, deficit, active_iv=None)[原始碼]

Apply the time rule result on time off sources.

Creates the outputs (_apply_output, defined by hr_work_entry), credits the allocations (_apply_allocation_credits()) and recreates the resource leaves of the sources and outputs.

參數
  • excess (dict) – {employee: {source: [_Iv]}}

  • deficit (dict) – {employee: {source: [(start, stop, rule)]}}

  • active_iv (dict) – {employee: {source: Intervals}}

_apply_allocation_credits(excess_alloc, deficit_alloc, *, approve_ctx=None)[原始碼]

Credit (or deduct) the excess (or missing) hours of rules to allocations, and log it.

For rules with leave_compensation_rate and allocation_type_id, the hours times the rate, divided by the employee’s hours per day, are added to (or deducted from) the validated allocation without end date of that type, created if needed. Each credit is logged in hr.time.rule.allocation.log so it can be reversed.

參數
  • excess_alloc (list) – [employee, rule, hours, source, log_source] rows

  • deficit_alloc (list) – same rows for the deficits

  • approve_ctx (dict) – context to approve the created allocations with

_filter_deficit_exceeding_allocation(deficit)[原始碼]

Drop the deficits the allocation of the rule cannot cover.

When the total deduction of an employee and allocation type exceeds the balance, all the deficit intervals of that pair are dropped, so no output is created for them.

參數

deficit (dict) – {employee: {source: [(start, stop, rule)]}}

回傳

the filtered deficit

回傳型別

dict

_reverse_allocation_credits(source_model, source_ids)[原始碼]

Reverse the allocation credits logged for the given source records.

參數
  • source_model (str) – 'hr.leave' or 'hr.attendance'

  • source_ids – ids of the sources

引發

ValidationError – when the reversal would bring a balance below its minimum

_get_output_leave_vals(employee, rule, date_from, date_to, source_leave, accumulated_pp=frozenset({}))[原始碼]

Return the creation values of a validated time off output by rule.

The output has the rule’s type, time_rule_id, the given span and is linked to source_leave.

參數
  • employee – hr.employee

  • rule – hr.time.rule producing the output

  • date_from (datetime) – start, naive UTC

  • date_to (datetime) – end, naive UTC

  • source_leave – source hr.leave

  • accumulated_pp (frozenset) – premium pay ids

回傳型別

dict

_get_output_leave_merge_key(accumulated_pp=frozenset({}))[原始碼]

Return the hashable key deciding when consecutive excess slices make one time off.

Override to add discriminators (e.g. the premium pays in Belgium). Default: the rule.

_resolve_output_intervals(intervals)[原始碼]

Resolve the output intervals, then merge consecutive slices with the same merge key.

See _get_output_leave_merge_key().

回傳型別

list

Additional fields with hr_holidays

model odoo.addons.hr_holidays.models.hr_time_rule.HrTimeRuleSourceMixin[原始碼]
_on_sources_collected(sources)[原始碼]

Reverse the allocation credits of the sources before they are evaluated again.

參數

sources – records of the source model

Original definition from hr_holidays

model odoo.addons.hr_holidays.models.hr_time_rule_allocation_log.HrTimeRuleAllocationLog[原始碼]
_name = hr.time.rule.allocation.log
res_model: str
Required

Name

Model

res_id: dict
Required

Name

Record

allocation_id: int
Required

Name

Allocation

Comodel

hr.leave.allocation

days: float
Required

Name

Days

Net days credited to the allocation from this source record.Positive = excess credit added; negative = deficit deduction applied.

Additional fields with hr_holidays

model odoo.addons.hr_holidays.models.resource_calendar.ResourceCalendar[原始碼]
leave_accrual_plan_id: int
Name

Accrual Plan

Comodel

hr.leave.accrual.plan