Work entries

A work entry tells how long an employee worked, or was absent, on a given day, and with which work entry type (Time Type in the user interface): attendance, overtime, sick leave, public holiday, etc. Payroll uses them to build the worked days of the payslips, which are the base of the salary.

In 20.0, work entries are not stored: there is no hr.work.entry model anymore. They are Python dicts computed on the fly from the employee’s version (hr.version), its working schedule, the leaves of the schedule and, when the corresponding modules are installed, the time off (hr.leave) and attendances (hr.attendance) of the employee. Each payslip computation generates them again, so there is nothing to regenerate, no conflicts to solve and no validated state: changing a time off or an attendance is visible at the next refresh of the payslip.

The module hr_work_entry holds the work entry types, the generation and the time rules, which reclassify attendances and time off (overtime, undertime). The other modules plug into it:

  • hr_holidays: time off types are work entry types, validated time off produce leaves;

  • hr_attendance: attendances are work entry sources for the time rules;

  • hr_holidays_attendance: work entries generated from validated attendances;

  • hr_payroll (Enterprise): worked days, and payroll fields on the types.

其他资料

Working schedules for the schedules, leaves and intervals the generation is built on, and Worked days for the conversion of work entries into worked days.

Work entry types

A hr.work.entry.type record classifies time. Its main fields drive the generation and the pay:

code (Payroll Code)

The key used by everything downstream: worked days are grouped by code and salary rules read them with worked_days['<code>']. A code is unique per country, and a type without country conflicts with any type of the same code (_check_code_unicity()). Localizations reuse the same codes for the same concepts, e.g. 002.00 for attendance, 006.00 for public holidays, 000.00 for out of contract.

country_id

The localization of the type. Companies, schedule attendances and schedule leaves are offered the types of their country, or the types without country if the country has none. The generic types (hr_work_entry/data/hr_work_entry_type_data.xml, e.g. hr_work_entry.generic_work_entry_type_attendance) have no country.

count_as

working_time or absence. During the generation, absence leaves replace the expected attendances while working time leaves (e.g. working from home) are worked time; the duration of an absence is computed from the schedule. A schedule attendance with an absence type is not working time.

amount_rate

The paid rate: 1.0 is paid at 100%, 0 is unpaid, 1.5 is paid at 150%. A worked days line is paid if its rate is not zero, and its amount is multiplied by the rate.

sequence

The display order, also the priority when an attendance and a working time leave overlap (the lowest sequence wins).

Since hr.leave.type was merged into hr.work.entry.type, hr_holidays adds its time off fields to the same model: request_unit (day, half_day or hour), requires_allocation, time_off_selectable, include_public_holidays_in_duration, etc. hr.leave points to its type with work_entry_type_id. hr_payroll adds the payroll fields: round_days_type (rounding of the days of the worked days), category_ids (salary rule categories credited with the amount of the worked days, e.g. BASIC), is_extra_hours (type in the EXTRA_HOURS category) and the reset mechanism shared with the salary rules (modified_by_user, action_reset_rule()). With hr_payroll, only the superuser can delete types; users archive them.

During the generation, the type of a work entry comes from:

  • the schedule attendance (resource.calendar.attendance.work_entry_type_id), or else the default type of the version (_get_default_work_entry_type_id(): the 002.00 type of the company’s country, or the generic attendance type; hr_payroll first takes the default type of the structure type);

  • the attendance (hr.attendance.work_entry_type_id, by default the attendance type of the company);

  • the leave (resource.calendar.leaves.work_entry_type_id, which is the time off type for the leaves created by time off). Global leaves default to the 006.00 type of the country.

Work entry values

The generation returns a list of dicts. A final value holds:

date

The day (datetime.date), in the timezone of the version.

duration

The number of hours (float, never zero).

work_entry_type_id, employee_id, version_id, company_id

Records, not ids: vals['work_entry_type_id'].code.

optional keys

Added by extensions: leave_ids (hr.leave, hr_holidays), attendance_ids (hr.attendance, hr_holidays_attendance), category_options_ids (hr.salary.rule.category, premium pays, hr_payroll) and trimmed_duration (hr_payroll: the part of the duration coming from a time off split by a time rule, which payroll does not round to whole days).

There is at most one value per merge key (_get_work_entry_merge_key()): date, type, employee, version and company (and category options with hr_payroll). Merged values sum their durations and unite the recordsets of _get_work_entry_source_fields().

Internally, the generation first produces raw values, which hold a span instead of a day: date_start and date_stop are naive UTC datetimes. A raw value may also directly give date and duration, then it must not cross midnight.

Example

An employee working 8:00-16:00 (Brussels) from Monday to Friday, with an unpaid time off on Monday and Tuesday:

from datetime import date, datetime

from odoo.fields import Command

employee = env['hr.employee'].search([('name', '=', 'Ronnie Hart')])
version = employee.version_id
unpaid = env['hr.work.entry.type'].search([
    ('code', '=', '158.00'), ('country_id.code', '=', 'US'),
])
leave = env['hr.leave'].create({
    'employee_id': employee.id,
    'work_entry_type_id': unpaid.id,
    'request_date_from': date(2026, 11, 16),
    'request_date_to': date(2026, 11, 17),
})
leave.state  # 'validate': only validated time off produce work entries

vals_list = version.generate_work_entries(date(2026, 11, 16), date(2026, 11, 20))
vals_list[0]
# {'work_entry_type_id': hr.work.entry.type(542,), 'employee_id': hr.employee(5,),
#  'version_id': hr.version(5,), 'company_id': res.company(1,),
#  'date': datetime.date(2026, 11, 18), 'duration': 8.0, 'trimmed_duration': 0.0}
[(vals['date'].isoformat(), vals['work_entry_type_id'].code, vals['duration'])
 for vals in vals_list]
# [('2026-11-18', '002.00', 8.0), ('2026-11-19', '002.00', 8.0),
#  ('2026-11-20', '002.00', 8.0), ('2026-11-16', '158.00', 8.0),
#  ('2026-11-17', '158.00', 8.0)]

# raw values: spans in naive UTC
version._get_work_entries_values(datetime(2026, 11, 16), datetime(2026, 11, 16, 23, 59))
# [{'date_start': datetime.datetime(2026, 11, 16, 7, 0),
#   'date_stop': datetime.datetime(2026, 11, 16, 15, 0),
#   'work_entry_type_id': hr.work.entry.type(544,), 'employee_id': hr.employee(5,),
#   'company_id': res.company(1,), 'version_id': hr.version(5,),
#   'leave_ids': hr.leave(...)}]

Generating work entries

generate_work_entries() is the entry point. It takes two dates (first and last day, included; datetimes raise an AssertionError) and returns the final values of the versions of self. hr.employee.generate_work_entries() is a shortcut selecting the versions of the employees with contract days in the period; on an empty recordset, it processes all the employees.

The generation goes through these steps:

  1. generate_work_entries() turns the days into naive UTC datetimes (midnight to midnight in the timezone of each version), and processes the versions by company and timezone, as superuser and in the company of the versions.

  2. _generate_work_entries() clips the period to the validity of each version (hr.version.date_start and date_end: a version is valid until the next version of the employee, and within the contract dates). Several versions in a period each produce their own values.

  3. _get_work_entries_values() and _get_version_work_entries_values() compute the raw values (see below).

  4. _generate_work_entries_postprocess() splits the spans at each local midnight, sets date and duration, calls _enrich_work_entry_val(), drops the zero durations and merges the values. The duration is the real duration of the span, or the hours planned by the schedule in the span when _generate_work_entries_postprocess_adapt_to_calendar() returns True (absences, and time off types not requested by hour), so that a full day of time off counts as the hours of a working day.

Example

A span from 21:00 to 3:00 (Brussels) gives one value per day:

work = env['hr.work.entry.type'].search([
    ('code', '=', '002.00'), ('country_id.code', '=', 'US'),
])
vals_list = version._generate_work_entries_postprocess([{
    'date_start': datetime(2026, 11, 2, 20, 0),  # naive UTC: 21:00 in Brussels
    'date_stop': datetime(2026, 11, 3, 2, 0),    # 03:00 in Brussels
    'work_entry_type_id': work,
    'employee_id': employee,
    'version_id': version,
    'company_id': employee.company_id,
}])
[(vals['date'].isoformat(), vals['duration']) for vals in vals_list]
# [('2026-11-02', 3.0), ('2026-11-03', 3.0)]

Computing the raw values

_get_version_work_entries_values() combines three sets of intervals (see Intervals) for each version:

Expected attendances

_get_attendance_intervals(): the attendance intervals of the schedule of the versions with has_static_work_entries(), leaves not deducted. A fully flexible schedule gives one interval over the whole period, hence 24 hours of work per day.

Leaves

_get_resource_calendar_leaves() returns the resource.calendar.leaves of the period: global leaves of the company and the schedule, and leaves of the employee (_get_leave_domain()). They are split on the count_as stored on the leave into absences and worked leaves. The absences are intersected with the schedule: a time off on a day off produces nothing.

Real attendances

The expected attendances minus all leaves (_get_real_attendances()).

Attendance intervals become values typed by _get_interval_work_entry_type(); leave intervals are typed by _get_interval_leave_work_entry_type(). When a time off and a public holiday overlap, the public holiday wins, unless the time off type code is in _get_bypassing_work_entry_type_codes() (e.g. l10n_be lets some leaves win over public holidays).

Static and attendance-based versions

Without hr_holidays_attendance, work entries always come from the schedule. With it:

  • validated attendances (state = 'validated', with a check out) produce values, with their own type and their real duration (break deducted). An attendance and a working time time off that overlap are resolved by the type sequence;

  • for a static version, the schedule still produces values, except on the days with a real attendance, where the attendances replace the schedule;

  • for an attendance-based version (hr.version.attendance_based, defaulted from the company), has_static_work_entries() returns False: the schedule produces nothing and only attendances and time off count.

Absences are cut where the employee actually worked. When attendances must be validated depends on the company setting attendance_validation.

How payroll uses the values

hr.payslip generates the values of its version (and of the other versions of the same contract starting in the period) from the day before to the day after its period, then filters them on date. hr_payroll adds get_work_hours(), which sums the durations of the values of a period by type and category options. The payslip turns these hours into worked days, see Worked days.

work_hours = version.get_work_hours(
    date(2026, 11, 1), date(2026, 11, 30),
    version.generate_work_entries(date(2026, 11, 1), date(2026, 11, 30)),
)
{
    (wet.code, tuple(options.mapped('code'))): hours
    for (wet, options), hours in work_hours.items()
}
# {('002.00', ()): 128.0, ('158.00', ()): 16.0, ('013.00', ()): 24.0}

Time rules

Time rules (hr.time.rule) classify the time beyond, or missing from, a threshold: overtime, night work, undertime, etc. Unlike work entries, they work on stored records, the sources: hr.attendance and hr.leave. When a source is created or changed, the applicable rules run and reclassify it in place (its work_entry_type_id, time_rule_id and span) and/or create output records of the same model, linked to the source. The generation of work entries just reads these records. Both mechanisms are independent: rules do not run during the generation.

A rule reads as: when the time of the types condition_work_entry_type_ids, within the timing window, exceeds (or is less than) the expected hours of the period, the excess (or the missing time) gets the type work_entry_type_id.

Applicability

company_id, country_id and employee_domain (a domain on hr.employee).

Threshold

threshold_operator (exceed for overtime, less_than for undertime) and working_hours_mode: schedule_day / schedule_week compare with the schedule of the employee or a reference schedule (calendar_source, resource_calendar_id), day / week compare with expected_hours. quantity_period (day or week) and week_start follow. employer_tolerance / employee_tolerance are the hours of excess / deficit ignored.

Timing window

apply_monday to apply_sunday, apply_on_public_holidays, and timing_start / timing_stop (hours of the day, e.g. 22 to 24 for night work).

Output

work_entry_type_id (Set Excess to), amount_rate (computed from the output type). Without output type, the rule only annotates the source. With hr_holidays, leave_compensation_rate and allocation_type_id credit the excess to (or deduct the deficit from) a time off allocation.

Rules run in sequence order and compose: once a rule has reclassified a part of the time, the next rules see the new type. For instance, with a first rule giving the type Overtime 200% to the attendance time beyond 9 hours per day, and a second rule giving Overtime 150% to the attendance time beyond 8 hours, a 10-hour day gives 8 hours of attendance, 1 hour at 150% and 1 hour at 200%.

Example

A daily overtime rule on a schedule of 8:00-16:00, and an attendance from 9:00 to 19:00:

overtime = env['hr.work.entry.type'].search([('code', '=', 'USOVERTIME150')])
rule = env['hr.time.rule'].create({
    'name': 'Daily overtime',
    'company_id': employee.company_id.id,
    'threshold_operator': 'exceed',
    'working_hours_mode': 'schedule_day',
    'condition_work_entry_type_ids': [Command.set(work.ids)],
    'work_entry_type_id': overtime.id,
    'sequence': 1,
})
rule.quantity_period, rule.calendar_source, rule.amount_rate  # ('day', 'employee', 1.5)

attendance = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 13, 7, 0),   # 9:00 in Brussels (summer time)
    'check_out': datetime(2026, 10, 13, 17, 0),  # 19:00 in Brussels
})
attendance.check_out, attendance.work_entry_type_id.code
# (datetime.datetime(2026, 10, 13, 15, 0), '002.00'): the source is shortened to 8 hours
output = attendance.overtime_attendance_ids
output.check_in, output.check_out, output.work_entry_type_id.code, output.time_rule_id.name
# (datetime.datetime(2026, 10, 13, 15, 0), datetime.datetime(2026, 10, 13, 17, 0),
#  'USOVERTIME150', 'Daily overtime')

[(vals['work_entry_type_id'].code, vals['duration'])
 for vals in version.generate_work_entries(date(2026, 10, 13), date(2026, 10, 13))]
# [('002.00', 8.0), ('USOVERTIME150', 2.0)]

When rules run

  • On create and write of a source (employee, span, type, state, …), unless the context has skip_time_rules. Only the rules that can already be decided run: the daily exceed rules always, the daily less_than rules if the record ended (in the employee’s timezone) before today, the weekly exceed rules if it ended before the current week.

  • Daily undertime rules run by cron on the previous day (_cron_process_day_undertime_rules()), weekly rules on the past week (_cron_process_week_time_rules()). Weekly undertime is only computed by the cron.

  • With hr_holidays_attendance, creating or changing a public holiday re-runs the rules on the attendances of the affected employees.

Rules are evaluated by _evaluate_rules(), which works in naive local time (timezone of the employee) and writes nothing, then applied by the source model (_apply_record_output(), based on _apply_output()). Re-evaluating a source is idempotent: previous outputs are taken into account and previous allocation credits are reversed.

Time rule sources

hr.time.rule.source.mixin makes a model a source of the rules. hr.attendance (span check_in / check_out, outputs overtime_attendance_ids, valid when validated) and hr.leave (span date_from / date_to, outputs output_leave_ids, valid when approved; absences are clipped to the schedule) inherit it. A new source model must:

Pitfalls

  • Values hold records, not ids, and are not meant to be created anywhere: there is no model to create them in.

  • hr.version.generate_work_entries() only accepts dates; the private methods take naive UTC datetimes and run with the current user, not as superuser.

  • date is the local day of the version timezone and spans are split at local midnight: a night shift gives two values.

  • With a fully flexible schedule, every day counts as 24 hours of work; with a flexible schedule, every day (weekends included) counts as the average hours per day of the schedule.

  • The count_as used to classify a leave is the one stored on resource.calendar.leaves, copied from the time off type when the time off is validated.

  • Overriding has_static_work_entries() to return False also requires overriding _get_attendance_intervals(), which must still return a key for every resource.

  • Time rules match the records of condition_work_entry_type_ids: a rule on the generic attendance type does not apply to a company whose country has its own 002.00 type.

  • A daily undertime is only detected on days with at least one source record: a day without any attendance gives no deficit.

Extending

New localized types

Data records of hr.work.entry.type with a country_id and a unique code, plus their payroll fields and the salary rules reading worked_days['<code>']. See Writing a payroll localization.

Default worked type

_get_default_work_entry_type_id() returns its id.

Extra keys in the values

_get_more_vals_attendance_interval() and _get_more_vals_leave_interval() return a list of (key, value) pairs. Add recordset keys to _get_work_entry_source_fields() so that merging unites them, or to _get_work_entry_merge_key() to keep values apart.

Leave types

_get_leave_work_entry_type_dates() (type depending on the period), _get_bypassing_work_entry_type_codes() and _get_interval_leave_work_entry_type() (priority against public holidays).

Leave selection

_get_resource_calendar_leaves(), _get_sub_leave_domain() and _get_valid_leave_intervals().

Interval arithmetic

_get_real_attendances(), _get_real_leaves_static(), _get_real_leaves_static_attendance() and _get_real_worked_leaves() return Intervals.

Adding or replacing values

Override _get_version_work_entries_values(), call super() and add or change raw values (e.g. hr_holidays_attendance adds the attendances).

Durations

_generate_work_entries_postprocess_adapt_to_calendar() (schedule or real duration) and _enrich_work_entry_val() (modify a final value in place before merging).

Time rules

hr.time.rule._get_pp_frozenset and _get_source_annotation_vals attach premium pay categories to the reclassified time (hr_payroll), and the source mixin hooks described above add source models.

On the payroll side, see Extending.

Reference

Original definition from hr_work_entry

model odoo.addons.hr_work_entry.models.hr_work_entry_type.HrWorkEntryType[源代码]
_name = hr.work.entry.type
code: str
Required

Name

Payroll Code

The code is used as a reference in salary rules. Careful, changing an existing code can lead to unwanted behaviors.

country_id: int
Name

Country

Comodel

res.country

Possible_values

odoo/addons/base/data/res_country_data.xml

count_as: str
Required

Name

Count As

Selection

working_time: Working Time absence: Absence

Default

working_time

Determines if the entry counts as working time or absence.

amount_rate: float
Name

Rate

Default

1.0

The percentage of wages paid. For standard paid leave set to 100%, for unpaid leave to 0%, or for double-pay to 200%.

sequence: int
Name

Sequence

Default

25

external_code: str
Name

External Code

Use this code to export your data to a third party

resource_calendar_selectable: bool
Name

Selectable in Working Schedule

_check_code_unicity()[源代码]

There should be maximum one work entry type per code, per country.

Additional fields with hr_work_entry

model odoo.addons.hr_work_entry.models.hr_version.HrVersion[源代码]
generate_work_entries(date_start, date_stop)[源代码]

Return the final work entry values of the versions of self between two dates.

Main entry point, used by payroll (hr.payslip._compute_worked_days_line_ids). Nothing is stored: the values are computed on the fly. The days are localized in the version’s timezone, then the versions are processed per (company, timezone) as superuser with _generate_work_entries(), which clips the period to each version’s validity.

参数
  • date_start (date) – first day, included (datetime is refused)

  • date_stop (date) – last day, included (datetime is refused)

返回

list of dicts, at most one per (date, type, employee, version, company) and extra merge key parts (see _get_work_entry_merge_key()), with keys:

  • date: local day (datetime.date) in the version’s timezone

  • duration: hours (float, never zero)

  • work_entry_type_id, employee_id, version_id, company_id: records

  • optional keys added by extensions: leave_ids (hr.leave), attendance_ids (hr.attendance), category_options_ids, trimmed_duration (hr_payroll)…

返回类型

list[dict]

_generate_work_entries(date_start, date_stop)[源代码]

Compute and post-process the work entry values between two datetimes.

Clips the period to each version’s [date_start, date_end] (whole local days), groups versions sharing the same clipped period, calls _get_work_entries_values() and finally _generate_work_entries_postprocess().

参数
  • date_start (datetime) – period start, naive UTC, already localized by the caller

  • date_stop (datetime) – period end, naive UTC

返回

final values, see generate_work_entries()

返回类型

list[dict]

_get_work_entries_values(date_start, date_stop)[源代码]

Return the raw work entry values of the versions of self between two dates.

Thin wrapper on _get_version_work_entries_values(). With datetimes, the versions are processed in one batch. With plain dates, the period goes from the start of date_start to the end of date_stop in each version’s timezone.

参数
  • date_start – period start, datetime (naive is considered UTC) or date

  • date_stop – period end, datetime (naive is considered UTC) or date (included)

返回

raw values, see _get_version_work_entries_values()

返回类型

list[dict]

_get_version_work_entries_values(date_start, date_stop)[源代码]

Compute the raw work entry values of the versions of self over a period.

Core of the generation, batched over versions. For each version:

Override it to add or replace values for a localization (l10n_fr_hr_holidays, l10n_uz_hr_payroll) or for a new source of time (hr_holidays_attendance adds the hr.attendance based values). Values must be completed by _generate_work_entries_postprocess() before use.

参数
  • date_start (datetime) – period start; naive datetimes are considered UTC

  • date_stop (datetime) – period end; naive datetimes are considered UTC

返回

list of dicts with date_start and date_stop (naive UTC), work_entry_type_id, employee_id, version_id, company_id (recordsets) and the extra keys of the _get_more_vals_* hooks. Alternatively, a value may carry date and duration directly.

返回类型

list[dict]

_generate_work_entries_postprocess(vals_list)[源代码]

Turn raw work entry values into final date/duration values.

Steps:

  1. values without date_start/date_stop must already have date and duration (UserError otherwise) and are kept as is;

  2. the others are split at each local midnight (timezone of version_id);

  3. date is the local day of the start; duration is kept if given, otherwise computed from the calendar or from the span, see _generate_work_entries_postprocess_adapt_to_calendar(); date_start and date_stop are removed, then _enrich_work_entry_val() is called;

  4. values with a zero duration (3 digits) are dropped, the others are merged by _get_work_entry_merge_key(), summing duration and trimmed_duration and unioning the _get_work_entry_source_fields() keys.

A raw value providing duration should also provide date, and should not cross local midnight (each split part would keep the full duration).

参数

vals_list (list) – raw values, see _get_version_work_entries_values()

返回

final values, see generate_work_entries()

返回类型

list[dict]

引发

UserError – when a version has no timezone or a value has neither dates nor date and duration

_generate_work_entries_postprocess_adapt_to_calendar(vals)[源代码]

Tell whether the duration of a value is computed from the working schedule.

True: duration is the number of hours the version’s calendar plans in the value’s span (_get_work_days_data_batch with compute_leaves=False), so a time off counts the planned hours. False: duration is the real length of the span. Default: True for absence types. hr_holidays also returns True for time off types not requested in hours (but False for time rule outputs); hr_holidays_attendance returns False for attendance values and fully flexible versions. Overrides must not rely on vals keys other than the raw ones.

参数

vals (dict) – raw value (date_start/date_stop still set)

返回类型

bool

_enrich_work_entry_val(val)[源代码]

Hook called on each value once date and duration are set, before merging.

Modify val in place. At this point leave_ids (if any) is still a single record, so field access is safe. hr_payroll sets trimmed_duration, which is summed when values are merged. Default: no-op.

参数

val (dict) – one work entry value

_get_work_entry_merge_key(vals)[源代码]

Return the key identifying the work entry values to merge into one.

Values with the same key are merged by summing duration. Default key: (date, work_entry_type_id, employee_id, version_id, company_id). Extend the tuple to keep values apart (hr_payroll adds category_options_ids).

参数

vals (dict) – one work entry value, with date set

返回类型

tuple

_get_work_entry_source_fields()[源代码]

Return the keys whose values are unioned when work entry values are merged.

Used by _generate_work_entries_postprocess(): when two values share the same merge key, these recordset keys are combined with |. Extensions add their source keys (leave_ids in hr_holidays, attendance_ids in hr_holidays_attendance).

返回

list of keys to aggregate

返回类型

list[str]

has_static_work_entries()[源代码]

Tell whether the work entries of this version come from its working schedule.

True (default): worked time is generated from the calendar attendances. False: worked time comes from another source; hr_holidays_attendance returns not attendance_based, worked time then coming from validated hr.attendance records. See _get_attendance_intervals() for what an override must also do.

返回类型

bool

_get_attendance_intervals(start_dt, end_dt)[源代码]

Return the expected working intervals of the calendar based versions of self.

Only versions with has_static_work_entries() are computed, from their working schedule (_attendance_intervals_batch). A fully flexible version gets a single interval covering the whole period with an empty attendance payload (hence 24h per day once split per day).

A module making versions non-static must override this method to still put an (empty) entry for their resources, as _get_version_work_entries_values() indexes the result directly (see hr_holidays_attendance).

参数
  • start_dt (datetime) – period start, tz-aware (asserted)

  • end_dt (datetime) – period end, tz-aware (asserted)

返回

{resource_id: Intervals}, intervals being (start, stop, resource.calendar.attendance) in the resource timezone

返回类型

dict

_get_real_attendance_work_entry_vals(intervals)[源代码]

Convert attendance intervals into raw work entry values.

Meant for behavior override.

参数

intervals – iterable of (start, stop, payload), tz-aware

返回

one dict per interval with date_start and date_stop (naive UTC), work_entry_type_id (_get_interval_work_entry_type()), employee_id, version_id, company_id (all recordsets), plus the pairs of _get_more_vals_attendance_interval()

返回类型

list[dict]

_get_real_attendances(attendances, leaves, worked_leaves)[源代码]

Return the attendance intervals that produce worked time entries.

Default: expected attendances minus absence and worked leaves. hr_holidays_attendance also removes the days where the employee has real hr.attendance records.

参数
  • attendances (Intervals) – expected attendances

  • leaves (Intervals) – absence leaves

  • worked_leaves (Intervals) – working time leaves

返回类型

Intervals

_get_real_leaves_static(leaves, expected_attendances)[源代码]

Return the absence intervals to generate for a static (calendar based) version.

Default: the parts of leaves that overlap the expected attendances, i.e. a leave only produces entries where the employee was supposed to work.

返回类型

Intervals

_get_real_leaves_static_attendance(leaves, static_attendances)[源代码]

Return the absence intervals to generate for a non-static (attendance based) version.

The expected attendances are empty for such a version, so leaves are intersected with the intervals of its working schedule instead.

返回类型

Intervals

_get_real_worked_leaves(worked_leaves, real_leaves)[源代码]

Return the working time leave intervals to generate.

Default: worked leaves minus the absence intervals (an absence wins over a working time leave).

返回类型

Intervals

_get_default_work_entry_type_id()[源代码]

Return the id of the work entry type used for worked time without explicit type.

Used for calendar attendances without work_entry_type_id and for attendances without type. Default: the 002.00 type of the version’s country, else the generic hr_work_entry.generic_work_entry_type_attendance. hr_payroll gives precedence to the structure type’s default_work_entry_type_id.

返回

an hr.work.entry.type id, or False if none is found

返回类型

int

_get_interval_work_entry_type(interval)[源代码]

Return the work entry type of an interval from its payload.

The payload’s work_entry_type_id (first record) if any, else the version’s default type (_get_default_work_entry_type_id()).

参数

interval (tuple) – (start, stop, payload); payload is usually a resource.calendar.attendance or resource.calendar.leaves recordset

返回类型

hr.work.entry.type

_get_leave_work_entry_type(leave)[源代码]

Return the work entry type of a resource.calendar.leaves record.

hr_holidays returns the type of the linked time off (holiday_id) when there is one.

返回类型

hr.work.entry.type

_get_leave_work_entry_type_dates(leave, date_from, date_to, employee)[源代码]

Return the work entry type of leave for the slice [date_from, date_to].

Override to make the type depend on the period, e.g. l10n_be_hr_payroll switches a sick leave to another type after 30 days. Default: _get_leave_work_entry_type().

参数
  • leave – the resource.calendar.leaves record covering the slice

  • date_from (datetime) – slice start, naive UTC

  • date_to (datetime) – slice end, naive UTC

  • employee – the hr.employee of the version

返回类型

hr.work.entry.type

_get_interval_leave_work_entry_type(interval, leaves, bypassing_codes)[源代码]

Return the work entry type of a leave interval.

Base version: the type of the first leave of leaves that contains the whole interval (through _get_leave_work_entry_type_dates()). hr_holidays overrides it to arbitrate overlapping leaves: bypassing time off, then global leaves (public holidays), then other time off.

参数
返回

the leave’s type, or hr_work_entry.generic_work_entry_type_leave if no leave contains the interval

返回类型

hr.work.entry.type

_get_bypassing_work_entry_type_codes()[源代码]

Return the codes of the leave types that win over public holidays.

When a time off and a global leave (public holiday) overlap, the global leave normally wins. A time off whose type code is listed here wins instead (e.g. long term sick leave in l10n_be_hr_payroll). Used by _get_interval_leave_work_entry_type().

返回

list of hr.work.entry.type codes

返回类型

list[str]

_get_more_vals_attendance_interval(interval)[源代码]

Hook to add extra keys to the work entry values generated from an attendance interval.

The result is concatenated to the base (key, value) pairs and passed to dict(), so later pairs override earlier ones. Extensions call super() and append, e.g. hr_holidays_attendance adds ('attendance_ids', attendance) and hr_payroll adds ('category_options_ids', ...). A key that holds records to be unioned on merge must also be listed in _get_work_entry_source_fields().

参数

interval (tuple) – (start, stop, payload), tz-aware datetimes in the employee timezone; payload is a resource.calendar.attendance recordset (calendar based) or an hr.attendance record (attendance based)

返回

list of (key, value) 2-tuples, not a dict

返回类型

list

_get_more_vals_leave_interval(interval, leaves)[源代码]

Hook to add extra keys to the work entry values generated from a leave interval.

Same contract as _get_more_vals_attendance_interval(). hr_holidays appends ('leave_ids', holiday) for each leave of leaves that contains interval and is linked to a time off.

参数
  • interval (tuple) – (start, stop, payload), tz-aware in the employee timezone

  • leaves (list) – (start, stop, resource.calendar.leaves) tuples that may cover interval

返回

list of (key, value) 2-tuples

返回类型

list

_get_leave_domain(start_dt, end_dt)[源代码]

Return the resource.calendar.leaves domain used for work entry generation.

Matches the leaves overlapping [start_dt, end_dt] that are global (no resource) or belong to the versions’ employees, in the versions’ companies or without company.

参数
  • start_dt (datetime) – period start, UTC (tzinfo is dropped)

  • end_dt (datetime) – period end, UTC (tzinfo is dropped)

返回类型

odoo.fields.Domain

_get_sub_leave_domain()[源代码]

Return the calendar part of the domain of _get_leave_domain().

Default: leaves of the versions’ calendars or without calendar. hr_holidays also accepts the leaves linked to time off of the versions’ employees, whatever the calendar.

返回类型

odoo.fields.Domain

_get_resource_calendar_leaves(start_dt, end_dt)[源代码]

Return the resource.calendar.leaves to consider between start_dt and end_dt.

Override to exclude leaves from generation (hr_payroll drops time off whose payslip is blocked/deferred).

参数
  • start_dt (datetime) – period start, UTC

  • end_dt (datetime) – period end, UTC

返回类型

resource.calendar.leaves

_get_valid_leave_intervals(attendances, interval)[源代码]

Filter or cut a leave interval before it is used for generation.

Called for each leave clipped to the period. hr_holidays_attendance removes the parts where the employee actually worked (absence leaves) or where an attendance won (working time leaves).

参数
  • attendances (Intervals) – expected attendances of the version’s resource

  • interval (tuple) – (start, stop, resource.calendar.leaves), tz-aware in the employee timezone

返回

list of (start, stop, payload) tuples to keep, possibly empty

返回类型

list

_get_no_wet_or_wet_match(leave, leave_entry_type)[源代码]

Tell whether leave is a source of a leave work entry of type leave_entry_type.

True if the calendar leave has no type or the same type. Only the matching leaves are given to _get_more_vals_leave_interval(), so a mismatch means no leave_ids on the generated values.

参数
  • leave (tuple) – (start, stop, resource.calendar.leaves)

  • leave_entry_type – the hr.work.entry.type chosen for the interval

返回类型

bool

Original definition from hr_work_entry

model odoo.addons.hr_work_entry.models.hr_time_rule.HrTimeRule[源代码]
_name = hr.time.rule

Threshold rule reclassifying worked or absent time (overtime, undertime).

A rule reads: when the time of the types condition_work_entry_type_ids within the timing window (weekdays, public holidays, timing_start-timing_stop) exceeds (threshold_operator = 'exceed') or is less than ('less_than') the expected hours of the period (quantity_period: day or week, from the schedule or expected_hours, see working_hours_mode), the excess or the missing time gets the type work_entry_type_id. employer_tolerance / employee_tolerance are the hours ignored before an excess / a deficit counts.

Rules are evaluated by _evaluate_rules() on hr.attendance and hr.leave records (the sources, see hr.time.rule.source.mixin) and their result is written back on the sources by _apply_output().

sequence: int
Name

Sequence

Default

10

employee_domain: str
Name

Employees

Default

[]

The rule will automatically apply to all time entries for matching employees.

threshold_operator: str
Required

Name

Threshold Operator

Selection

exceed: Exceed less_than: Less than

Default

exceed

working_hours_mode: str
Required

Name

Working Hours Mode

Selection

schedule_day: the daily schedule schedule_week: the weekly schedule day: per day week: per week

Default

schedule_day

Define a condition based on the quantity of work made by the employee on a specific period of time.

calendar_source: str
Name

Calendar Source

Selection

employee: Employee Schedule reference: Reference Schedule

Which schedule to use as the expected-hours baseline.

resource_calendar_id: int
Name

Schedule

Comodel

resource.calendar

expected_hours: float
Name

Usual work hours

quantity_period: str
Name

Quantity Period

Readonly

this field is not supposed to/cannot be set manually

Selection

day: Day week: Week

week_start: str
Name

Week Starts On

Selection

0: Monday 1: Tuesday 2: Wednesday 3: Thursday 4: Friday 5: Saturday 6: Sunday

Default

0

timing_start: float
Name

From

Default

0

timing_stop: float
Name

To

Default

24

employer_tolerance: float
Name

Employer Tolerance

employee_tolerance: float
Name

Employee Tolerance

condition_work_entry_type_ids: Sequence[odoo.orm.commands.Command]
Required

Name

Time Type

Comodel

hr.work.entry.type

If set, only the selected types will be considered by this rule.

work_entry_type_id: int
Name

Set Excess to

Comodel

hr.work.entry.type

Define a time type that will be created to count the amount of time in excess or missing.

amount_rate: float
Name

Salary Rate

_evaluate_rules(records, start_dt, end_dt)[源代码]

Run the rules of self in sequence on source records; nothing is written.

The sources are turned into a pipeline of _Iv intervals (naive local datetimes, see _get_pipeline_intervals_local). Each applicable rule, in sequence, id order, takes the intervals whose current type is in its condition_work_entry_type_ids and inside its timing window, evaluates each period (_evaluate_period()) and reclassifies the excess in the pipeline, so later rules see the output of earlier ones (e.g. a > 9h rule then a > 8h rule on the remaining attendance type). Rules without output type and without threshold just tag the matching intervals. Pass the result to _apply_output().

参数
  • records – source records (hr.attendance or hr.leave)

  • start_dt (datetime) – evaluated period start (only the date is used, as a day of the employees’ timezone)

  • end_dt (datetime) – evaluated period end (only the date is used)

返回

(excess, deficit, active_iv): excess is {employee: {source: [_Iv]}} with the intervals classified by a rule (pp not filled yet), deficit is {employee: {source: [(start, stop, rule)]}}, active_iv is {employee: {source: Intervals}} of the pipeline segments of each source

返回类型

tuple

_evaluate_period(start, stop, record_intervals, schedule)[源代码]

Compare the time worked in one period (day or week) with the rule’s threshold.

Expected hours: the schedule in the period if calendar_source is set, else expected_hours. Worked hours: union of the source intervals minus their breaks (prorated to the part of the source in the period).

  • exceed: when the excess is above employer_tolerance, the expected hours are consumed in chronological order and what remains is the excess, taken from the end of the source intervals.

  • less_than: when the deficit is above employee_tolerance, the gaps of the schedule (or of the period) not covered by worked time form the deficit, all attached to the source ending last.

参数
  • start (datetime) – period start, naive local

  • stop (datetime) – period end, naive local

  • record_intervals (list) – (start, stop, source) tuples of the period

  • schedule (Intervals) – schedule restricted to the rule window and the period

返回

(excess_by_source, deficit_by_source), each {source: [(start, stop, rule)]}; at most one of them is non-empty

返回类型

tuple

_apply_output(excess, deficit, active_iv=None)[源代码]

Write the result of _evaluate_rules() on the source records.

Shared by attendances and leaves (called by _apply_attendance_output / _apply_leave_output). Writes use the source’s _time_rule_write_ctx, so they do not trigger the rules again.

  • Excess: if the first output interval starts with the source, the source itself is reclassified (type, time_rule_id, end); otherwise the source is cut at the first output start. Each other output interval becomes a new record (_get_time_rule_output_vals) and the uncovered parts of the source a remainder record keeping the source type (_get_time_rule_remainder_vals). Rules without output type only annotate the source (_get_source_annotation_vals()).

  • Deficit: per period, only the lowest sequence rule is kept; the missing hours are placed as new output records in the free slots after the deficit start (not covered by other records of the model), minus what earlier runs already created.

参数
  • excess (dict) – {employee: {source: [_Iv]}}

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

  • active_iv (dict) – {employee: {source: Intervals}} pipeline segments of the sources, used to compute the remainders

返回

(new_records, all_source_ids, excess_alloc, deficit_alloc): created records, ids of the processed sources, and allocation rows [employee, rule, hours, source, log_source] (log_source = record the credit is logged on, used by hr_holidays for compensatory allocations). Returns (None, frozenset(), [], []) when there is no source.

返回类型

tuple

_resolve_output_intervals(intervals)[源代码]

Return the excess intervals of a source in their final output form.

Sorts by start, drops empty intervals and intervals without type, sets wet to the rule’s work_entry_type_id (else the current type) and pp to the premium pay ids of the classifying rule and of the rules it displaced (acc). hr_holidays also merges consecutive slices with the same output.

参数

intervals (list) – _Iv excess intervals of one source

返回类型

list

_get_applicable_employees(employees)[源代码]

Return the employees of employees the rule applies to.

Filters on the rule’s company (or else country), then on employee_domain (evaluated as superuser). hr_attendance also drops employees without calendar for rules based on the employee schedule.

参数

employees – hr.employee recordset

返回类型

hr.employee

引发

ValidationError – if employee_domain cannot be evaluated

_get_schedule_calendar()[源代码]

Return the reference calendar of the rule.

返回

resource_calendar_id, else the company’s or the current company’s calendar when calendar_source is 'reference'; an empty recordset otherwise

返回类型

resource.calendar

_get_work_intervals(employees, start_dt, end_dt, schedule_calendar=None)[源代码]

Return the schedule data the rules compare worked time with, per employee.

Computed per version period of each employee, with naive datetimes in the employee’s timezone (as all the pipeline).

参数
  • employees – hr.employee recordset

  • start_dt (datetime) – period start (only the date is used)

  • end_dt (datetime) – period end (only the date is used)

  • schedule_calendar – reference resource.calendar replacing the versions’ calendars for the 'schedule' and 'public_leave' keys

返回

{key: {employee: Intervals}} with keys 'schedule' (planned attendances), 'public_leave' (global leaves of the calendar), 'leave' (absence leaves of the employee) and 'fully_flexible' (version periods with a fully flexible calendar)

返回类型

dict

_build_work_intervals_by_calendar(employees, start_dt, end_dt)[源代码]

Compute _get_work_intervals() once per baseline calendar used by the rules of self.

返回

{calendar_id: work_intervals}, key False for the employees’ own schedules

返回类型

dict

_build_rule_day_intervals(min_date, max_date, employees, work_intervals_by_type)[源代码]

Return the timing window of the rule, per employee.

The days of [min_date, max_date] enabled by the apply_<weekday> flags, plus or minus the employee’s public holidays (apply_on_public_holidays), restricted to the rule’s hours (_build_hour_window_intervals()).

参数

work_intervals_by_type – result of _get_work_intervals()

返回

{employee: Intervals} of naive local datetimes

返回类型

dict

_build_hour_window_intervals(employees, day_intervals_by_employee)[源代码]

Restrict whole-day intervals to the rule’s timing_start-timing_stop hours.

参数

day_intervals_by_employee – {employee: Intervals} of whole days

返回

{employee: Intervals} of naive local datetimes

返回类型

dict

_dates_to_day_intervals(intervals)[源代码]

Return one whole-day interval (00:00 to 23:59:59.999999) per day touched by intervals.

返回类型

Intervals

_get_pp_frozenset()[源代码]

Return the premium pay ids attached to the intervals this rule classifies.

Hook for localizations (l10n_be); default: empty.

返回类型

frozenset

_get_source_annotation_vals(accumulated_pp=frozenset({}))[源代码]

Return extra values written on a source record reused as the first output.

When the output starts with the source, the source itself is reclassified (written) instead of creating a new record. Override to write the fields the _get_output_*_vals methods set on new output records (e.g. l10n_be_premium_pay_rule_ids).

参数

accumulated_pp (frozenset) – premium pay ids of the output interval(s)

返回类型

dict

Original definition from hr_work_entry

model odoo.addons.hr_work_entry.models.hr_time_rule_source_mixin.HrTimeRuleSourceMixin[源代码]
_name = hr.time.rule.source.mixin

Make a model a source of the time rule pipeline (hr.time.rule).

Inherited by hr.attendance and hr.leave. Creating a source, or writing its employee, span or the fields of _get_write_source_extra_source_fields(), triggers the rules (_trigger_time_rules()) unless the context has skip_time_rules. The rules then reclassify the source in place and/or create output records of the same model, linked to the source.

The inheriting model must have employee_id, work_entry_type_id and time_rule_id fields, set the class attributes below and implement the hooks raising NotImplementedError:

  • _time_rule_source_field: many2one from an output record to its source (source_attendance_id, source_leave_id);

  • _time_rule_output_field: the inverse one2many (overtime_attendance_ids, output_leave_ids);

  • _time_rule_span_start_field / _time_rule_span_end_field: datetime fields (UTC) holding the span (check_in/check_out, date_from/date_to);

  • _time_rule_write_ctx: context used for the pipeline’s own writes.

_trigger_time_rules()[源代码]

Run the rules for the records of self that are valid sources.

Called on create and on write of a trigger field. Records without span or not matching _get_source_extra_fields_domain() are ignored. See _trigger_time_rules_for_affected() for which rules run.

_trigger_time_rules_for_affected(affected)[源代码]

Run the rules that can already be decided for the given periods.

  • periods ending before today: all daily rules;

  • periods ending today or later: daily exceed rules only;

  • periods ending before the current week (started on the earliest week_start of the active weekly rules): weekly exceed rules.

Weekly undertime only runs from _cron_process_week_time_rules(). The end of a period is its date in the employee’s timezone; today is the server’s (UTC) date, as for the crons.

参数

affected (list) – (employee, date_from, date_to) tuples

_process_time_rules_for(affected, rule_period=None, rule_operator=None, rule_week_start=None)[源代码]

Evaluate and apply the active rules for the given employees and periods.

Rules are those of the current companies (or without company), filtered by rule_operator and period. Daily rules are evaluated on each employee’s covering date range, weekly rules on that range widened to whole weeks; both results are applied together through _apply_record_output().

参数
  • affected (list) – (employee, date_from, date_to) tuples: dates in the employee’s timezone, or UTC datetimes (naive or aware), evaluated on their dates in the employee’s timezone

  • rule_period (str) – 'day' or 'week' to only process that kind of rule; both if None

  • rule_operator (str) – 'exceed' or 'less_than' to only process that operator

  • rule_week_start (str) – only process the weekly rules with this week_start

_collect_time_rule_outputs(rules, ranges_by_employee)[源代码]

Evaluate rules on the sources of each employee’s date range; nothing is written.

Employees sharing the same range are evaluated in one _evaluate_rules call, after _on_sources_collected().

参数
  • rules – hr.time.rule records

  • ranges_by_employee (dict) – {employee: (date_from, date_to)}, dates in the employee’s timezone

返回

merged (excess, deficit, active_iv), see hr.time.rule._evaluate_rules

返回类型

tuple

_cron_process_day_undertime_rules()[源代码]

Cron: evaluate the daily less_than (undertime) rules on yesterday’s records.

Undertime of the current day cannot be known while the day runs, so it is not evaluated on write. A day without any source record produces no deficit.

_cron_process_week_time_rules()[源代码]

Cron: evaluate the weekly rules (both operators) on the week that ended yesterday.

On day X, yesterday was the last day of every week starting on X: only the rules whose week_start is X are processed, on the records ending in [X-7, X-1]. This assumes a daily run.

_get_source_records_for_time_rules(start_dt, end_dt, employees=None, check_end=False)[源代码]

Return the source records (as superuser) overlapping a period.

参数
  • start_dt (datetime) – period start, UTC (tzinfo is dropped)

  • end_dt (datetime) – period end, UTC (tzinfo is dropped), excluded for the overlapping records

  • employees – restrict to these hr.employee

  • check_end (bool) – select the records ending in the period instead of overlapping it

返回类型

recordset of this model

_on_sources_collected(sources)[源代码]

Hook called with the sources of a batch before the rules are evaluated on them.

hr_holidays reverses the allocation credits previously logged on these sources, so the evaluation starts from a clean state.

参数

sources – records of this model

_apply_record_output(rules, excess, deficit, active_iv=None)[源代码]

Write the pipeline result on the records; must be implemented.

Implementations delegate to a hr.time.rule method built on hr.time.rule._apply_output (_apply_attendance_output, _apply_leave_output), adding model specific effects such as allocations.

参数
  • rules – the hr.time.rule records that were evaluated

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

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

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

_get_pipeline_intervals_local(schedule)[源代码]

Return the segments of the record that enter the pipeline.

Default: the whole span. hr.leave clips absence leaves to the schedule, so nights, breaks and days off of a leave do not count.

参数

schedule (Intervals) – schedule of the employee (naive local)

返回

list of (start, stop) naive datetimes in the employee’s timezone

返回类型

list

_get_source_extra_fields_domain()[源代码]

Return the domain a record must match to be a source, e.g. being validated.

返回类型

list

_get_write_source_extra_source_fields()[源代码]

Return the fields, besides the employee and the span, whose write triggers the rules.

返回类型

set[str]

_get_time_rule_break_hours()[源代码]

Return the break hours of the record, not counted as worked time by the rules.

返回类型

float

_get_time_rule_end_write_vals(end_utc, stop_local)[源代码]

Return the values to write to move the end of the record.

hr.leave also writes request_date_to and the request hours.

参数
  • end_utc (datetime) – new end, naive UTC

  • stop_local (datetime) – the same instant, naive in the employee’s timezone

返回类型

dict

_get_time_rule_deficit_occupied(employee_id, start_utc, period_end_utc)[源代码]

Return the time already occupied by records of the employee; must be implemented.

Deficit outputs are only placed in the free slots of [start_utc, period_end_utc].

参数
  • employee_id (int) – employee id

  • start_utc (datetime) – naive UTC

  • period_end_utc (datetime) – naive UTC

返回

intervals of naive UTC datetimes

返回类型

Intervals

_get_time_rule_deficit_already_covered(rule, start_utc, end_utc)[源代码]

Return the hours already covered by outputs of the rule’s type in [start_utc, end_utc].

Makes deficit outputs idempotent: the hours placed by a previous run are deducted, so no duplicate records are created.

参数
  • rule – the hr.time.rule

  • start_utc (datetime) – naive UTC

  • end_utc (datetime) – naive UTC

返回类型

float

_get_time_rule_output_vals(rule, df, dt, pp)[源代码]

Return the creation values of an output record of rule; must be implemented.

参数
  • rule – the hr.time.rule producing the output

  • df (datetime) – start, naive UTC

  • dt (datetime) – end, naive UTC

  • pp (frozenset) – premium pay ids

返回

values with at least the employee, the span, the rule’s type, time_rule_id and the link to the source

返回类型

dict

_get_time_rule_remainder_vals(df, dt)[源代码]

Return the creation values of a remainder record; must be implemented.

A remainder is a part of the source not covered by any output, when the source is cut. work_entry_type_id must be left out: the caller sets the source’s original type.

参数
  • df (datetime) – start, naive UTC

  • dt (datetime) – end, naive UTC

返回类型

dict