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.00for attendance,006.00for public holidays,000.00for out of contract.country_idThe 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_asworking_timeorabsence. 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_rateThe paid rate:
1.0is paid at 100%,0is unpaid,1.5is paid at 150%. A worked days line is paid if its rate is not zero, and its amount is multiplied by the rate.sequenceThe 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(): the002.00type of the company’s country, or the generic attendance type;hr_payrollfirst 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 the006.00type of the country.
Work entry values¶
The generation returns a list of dicts. A final value holds:
dateThe day (
datetime.date), in the timezone of the version.durationThe number of hours (
float, never zero).work_entry_type_id,employee_id,version_id,company_idRecords, 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) andtrimmed_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:
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._generate_work_entries()clips the period to the validity of each version (hr.version.date_startanddate_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._get_work_entries_values()and_get_version_work_entries_values()compute the raw values (see below)._generate_work_entries_postprocess()splits the spans at each local midnight, setsdateandduration, 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()returnsTrue(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 withhas_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 theresource.calendar.leavesof the period: global leaves of the company and the schedule, and leaves of the employee (_get_leave_domain()). They are split on thecount_asstored 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()returnsFalse: 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_idandemployee_domain(a domain onhr.employee).- Threshold
threshold_operator(exceedfor overtime,less_thanfor undertime) andworking_hours_mode:schedule_day/schedule_weekcompare with the schedule of the employee or a reference schedule (calendar_source,resource_calendar_id),day/weekcompare withexpected_hours.quantity_period(dayorweek) andweek_startfollow.employer_tolerance/employee_toleranceare the hours of excess / deficit ignored.- Timing window
apply_mondaytoapply_sunday,apply_on_public_holidays, andtiming_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. Withhr_holidays,leave_compensation_rateandallocation_type_idcredit 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
createandwriteof a source (employee, span, type, state, …), unless the context hasskip_time_rules. Only the rules that can already be decided run: the dailyexceedrules always, the dailyless_thanrules if the record ended (in the employee’s timezone) before today, the weeklyexceedrules 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:
have
employee_id,work_entry_type_idandtime_rule_idfields;set
_time_rule_source_field(many2one from an output to its source),_time_rule_output_field(the inverse one2many),_time_rule_span_start_fieldand_time_rule_span_end_field;implement
_apply_record_output(),_get_time_rule_deficit_occupied(),_get_time_rule_output_vals()and_get_time_rule_remainder_vals(); usually also_get_source_extra_fields_domain()(e.g. only validated records) and_get_write_source_extra_source_fields().
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.dateis 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_asused to classify a leave is the one stored onresource.calendar.leaves, copied from the time off type when the time off is validated.Overriding
has_static_work_entries()to returnFalsealso 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 own002.00type.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.typewith acountry_idand a uniquecode, plus their payroll fields and the salary rules readingworked_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()returnIntervals.- Adding or replacing values
Override
_get_version_work_entries_values(), callsuper()and add or change raw values (e.g.hr_holidays_attendanceadds 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_frozensetand_get_source_annotation_valsattach 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.
- count_as: str¶
- Required
- Name
Count As
- Selection
working_time: Working Timeabsence: Absence- Default
working_time
Determines if the entry counts as working time or absence.
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
selfbetween 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 (
datetimeis refused)date_stop (date) – last day, included (
datetimeis 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 timezoneduration: hours (float, never zero)work_entry_type_id,employee_id,version_id,company_id: recordsoptional keys added by extensions:
leave_ids(hr.leave),attendance_ids(hr.attendance),category_options_ids,trimmed_duration(hr_payroll)…
- 返回类型
- _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()- 返回类型
- _get_work_entries_values(date_start, date_stop)[源代码]¶
Return the raw work entry values of the versions of
selfbetween 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 ofdate_startto the end ofdate_stopin each version’s timezone.- 参数
date_start – period start,
datetime(naive is considered UTC) ordatedate_stop – period end,
datetime(naive is considered UTC) ordate(included)
- 返回
raw values, see
_get_version_work_entries_values()- 返回类型
- _get_version_work_entries_values(date_start, date_stop)[源代码]¶
Compute the raw work entry values of the versions of
selfover a period.Core of the generation, batched over versions. For each version:
expected attendances:
_get_attendance_intervals();leaves:
_get_resource_calendar_leaves(), clipped to the period and filtered by_get_valid_leave_intervals(), split on the calendar leave’scount_as:absenceleaves replace the expected attendances they overlap,working_timeleaves (e.g. home working) produce worked leave entries;real attendances = attendances minus both kinds of leaves (
_get_real_attendances()); the leave intervals actually generated depend on the calendar type (fully flexible, flexible, static, or attendance based).
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_attendanceadds thehr.attendancebased 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_startanddate_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 carrydateanddurationdirectly.- 返回类型
- _generate_work_entries_postprocess(vals_list)[源代码]¶
Turn raw work entry values into final
date/durationvalues.Steps:
values without
date_start/date_stopmust already havedateandduration(UserErrorotherwise) and are kept as is;the others are split at each local midnight (timezone of
version_id);dateis the local day of the start;durationis kept if given, otherwise computed from the calendar or from the span, see_generate_work_entries_postprocess_adapt_to_calendar();date_startanddate_stopare removed, then_enrich_work_entry_val()is called;values with a zero duration (3 digits) are dropped, the others are merged by
_get_work_entry_merge_key(), summingdurationandtrimmed_durationand unioning the_get_work_entry_source_fields()keys.
A raw value providing
durationshould also providedate, 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()- 返回类型
- 引发
UserError – when a version has no timezone or a value has neither dates nor
dateandduration
- _generate_work_entries_postprocess_adapt_to_calendar(vals)[源代码]¶
Tell whether the duration of a value is computed from the working schedule.
True:durationis the number of hours the version’s calendar plans in the value’s span (_get_work_days_data_batchwithcompute_leaves=False), so a time off counts the planned hours.False:durationis the real length of the span. Default:Truefor absence types.hr_holidaysalso returnsTruefor time off types not requested in hours (butFalsefor time rule outputs);hr_holidays_attendancereturnsFalsefor attendance values and fully flexible versions. Overrides must not rely onvalskeys other than the raw ones.
- _enrich_work_entry_val(val)[源代码]¶
Hook called on each value once
dateanddurationare set, before merging.Modify
valin place. At this pointleave_ids(if any) is still a single record, so field access is safe.hr_payrollsetstrimmed_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_payrolladdscategory_options_ids).
- _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_idsinhr_holidays,attendance_idsinhr_holidays_attendance).
- 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_attendancereturnsnot attendance_based, worked time then coming from validatedhr.attendancerecords. See_get_attendance_intervals()for what an override must also do.- 返回类型
- _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 (seehr_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- 返回类型
- _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_startanddate_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()- 返回类型
- _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_attendancealso removes the days where the employee has realhr.attendancerecords.- 参数
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
leavesthat 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_idand for attendances without type. Default: the002.00type of the version’s country, else the generichr_work_entry.generic_work_entry_type_attendance.hr_payrollgives precedence to the structure type’sdefault_work_entry_type_id.- 返回
an
hr.work.entry.typeid, orFalseif none is found- 返回类型
- _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);payloadis usually aresource.calendar.attendanceorresource.calendar.leavesrecordset- 返回类型
hr.work.entry.type
- _get_leave_work_entry_type(leave)[源代码]¶
Return the work entry type of a
resource.calendar.leavesrecord.hr_holidaysreturns 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
leavefor the slice[date_from, date_to].Override to make the type depend on the period, e.g.
l10n_be_hr_payrollswitches a sick leave to another type after 30 days. Default:_get_leave_work_entry_type().- 参数
leave – the
resource.calendar.leavesrecord covering the slicedate_from (datetime) – slice start, naive UTC
date_to (datetime) – slice end, naive UTC
employee – the
hr.employeeof 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
leavesthat contains the wholeinterval(through_get_leave_work_entry_type_dates()).hr_holidaysoverrides it to arbitrate overlapping leaves: bypassing time off, then global leaves (public holidays), then other time off.- 参数
interval (tuple) –
(start, stop, payload), tz-aware in the employee timezoneleaves (list) –
(start, stop, resource.calendar.leaves)tuplesbypassing_codes (list) – result of
_get_bypassing_work_entry_type_codes()
- 返回
the leave’s type, or
hr_work_entry.generic_work_entry_type_leaveif 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().
- _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 todict(), so later pairs override earlier ones. Extensions callsuper()and append, e.g.hr_holidays_attendanceadds('attendance_ids', attendance)andhr_payrolladds('category_options_ids', ...). A key that holds records to be unioned on merge must also be listed in_get_work_entry_source_fields().
- _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_holidaysappends('leave_ids', holiday)for each leave ofleavesthat containsintervaland is linked to a time off.
- _get_leave_domain(start_dt, end_dt)[源代码]¶
Return the
resource.calendar.leavesdomain 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_holidaysalso 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.leavesto consider betweenstart_dtandend_dt.Override to exclude leaves from generation (
hr_payrolldrops 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_attendanceremoves the parts where the employee actually worked (absence leaves) or where an attendance won (working time leaves).
- _get_no_wet_or_wet_match(leave, leave_entry_type)[源代码]¶
Tell whether
leaveis a source of a leave work entry of typeleave_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 noleave_idson the generated values.
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_idswithin 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 orexpected_hours, seeworking_hours_mode), the excess or the missing time gets the typework_entry_type_id.employer_tolerance/employee_toleranceare the hours ignored before an excess / a deficit counts.Rules are evaluated by
_evaluate_rules()onhr.attendanceandhr.leaverecords (the sources, seehr.time.rule.source.mixin) and their result is written back on the sources by_apply_output().- 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: Exceedless_than: Less than- Default
exceed
- working_hours_mode: str¶
- Required
- Name
Working Hours Mode
- Selection
schedule_day: the daily scheduleschedule_week: the weekly scheduleday: per dayweek: 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 Schedulereference: Reference Schedule
Which schedule to use as the expected-hours baseline.
- quantity_period: str¶
- Name
Quantity Period
- Readonly
this field is not supposed to/cannot be set manually
- Selection
day: Dayweek: Week
- week_start: str¶
- Name
Week Starts On
- Selection
0: Monday1: Tuesday2: Wednesday3: Thursday4: Friday5: Saturday6: Sunday- Default
0
- condition_work_entry_type_ids: Sequence[odoo.orm.commands.Command]¶
- Required
- Name
Time Type
- Comodel
If set, only the selected types will be considered by this rule.
- work_entry_type_id: int¶
- Name
Set Excess to
- Comodel
Define a time type that will be created to count the amount of time in excess or missing.
- _evaluate_rules(records, start_dt, end_dt)[源代码]¶
Run the rules of
selfin sequence on source records; nothing is written.The sources are turned into a pipeline of
_Ivintervals (naive local datetimes, see_get_pipeline_intervals_local). Each applicable rule, insequence, idorder, takes the intervals whose current type is in itscondition_work_entry_type_idsand 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> 9hrule then a> 8hrule 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.attendanceorhr.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):excessis{employee: {source: [_Iv]}}with the intervals classified by a rule (ppnot filled yet),deficitis{employee: {source: [(start, stop, rule)]}},active_ivis{employee: {source: Intervals}}of the pipeline segments of each source- 返回类型
- _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_sourceis set, elseexpected_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 aboveemployer_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 aboveemployee_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 periodschedule (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- 返回类型
- _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.
- 参数
- 返回
(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 byhr_holidaysfor compensatory allocations). Returns(None, frozenset(), [], [])when there is no source.- 返回类型
- _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
wetto the rule’swork_entry_type_id(else the current type) andppto the premium pay ids of the classifying rule and of the rules it displaced (acc).hr_holidaysalso merges consecutive slices with the same output.
- _get_applicable_employees(employees)[源代码]¶
Return the employees of
employeesthe rule applies to.Filters on the rule’s company (or else country), then on
employee_domain(evaluated as superuser).hr_attendancealso drops employees without calendar for rules based on the employee schedule.- 参数
employees –
hr.employeerecordset- 返回类型
hr.employee
- 引发
ValidationError – if
employee_domaincannot 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 whencalendar_sourceis'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.employeerecordsetstart_dt (datetime) – period start (only the date is used)
end_dt (datetime) – period end (only the date is used)
schedule_calendar – reference
resource.calendarreplacing 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)- 返回类型
- _build_work_intervals_by_calendar(employees, start_dt, end_dt)[源代码]¶
Compute
_get_work_intervals()once per baseline calendar used by the rules ofself.- 返回
{calendar_id: work_intervals}, keyFalsefor the employees’ own schedules- 返回类型
- _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 theapply_<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- 返回类型
- _build_hour_window_intervals(employees, day_intervals_by_employee)[源代码]¶
Restrict whole-day intervals to the rule’s
timing_start-timing_stophours.- 参数
day_intervals_by_employee –
{employee: Intervals}of whole days- 返回
{employee: Intervals}of naive local datetimes- 返回类型
- _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.- 返回类型
- _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_*_valsmethods set on new output records (e.g.l10n_be_premium_pay_rule_ids).
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.attendanceandhr.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 hasskip_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_idandtime_rule_idfields, set the class attributes below and implement the hooks raisingNotImplementedError:_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
selfthat 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
exceedrules only;periods ending before the current week (started on the earliest
week_startof the active weekly rules): weeklyexceedrules.
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_operatorand 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 timezonerule_period (str) –
'day'or'week'to only process that kind of rule; both ifNonerule_operator (str) –
'exceed'or'less_than'to only process that operatorrule_week_start (str) – only process the weekly rules with this
week_start
- _collect_time_rule_outputs(rules, ranges_by_employee)[源代码]¶
Evaluate
ruleson the sources of each employee’s date range; nothing is written.Employees sharing the same range are evaluated in one
_evaluate_rulescall, after_on_sources_collected().
- _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_startis 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.employeecheck_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_holidaysreverses 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.rulemethod built onhr.time.rule._apply_output(_apply_attendance_output,_apply_leave_output), adding model specific effects such as allocations.
- _get_pipeline_intervals_local(schedule)[源代码]¶
Return the segments of the record that enter the pipeline.
Default: the whole span.
hr.leaveclips 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- 返回类型
- _get_source_extra_fields_domain()[源代码]¶
Return the domain a record must match to be a source, e.g. being validated.
- 返回类型
- _get_write_source_extra_source_fields()[源代码]¶
Return the fields, besides the employee and the span, whose write triggers the rules.
- _get_time_rule_break_hours()[源代码]¶
Return the break hours of the record, not counted as worked time by the rules.
- 返回类型
- _get_time_rule_end_write_vals(end_utc, stop_local)[源代码]¶
Return the values to write to move the end of the record.
hr.leavealso writesrequest_date_toand the request hours.- 参数
end_utc (datetime) – new end, naive UTC
stop_local (datetime) – the same instant, naive in the employee’s timezone
- 返回类型
- _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.rulestart_utc (datetime) – naive UTC
end_utc (datetime) – naive UTC
- 返回类型
- _get_time_rule_output_vals(rule, df, dt, pp)[源代码]¶
Return the creation values of an output record of
rule; must be implemented.
- _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_idmust be left out: the caller sets the source’s original type.- 参数
df (datetime) – start, naive UTC
dt (datetime) – end, naive UTC
- 返回类型