Attendances

An attendance (hr.attendance, module hr_attendance) is a period during which an employee was present: a check in, and a check out once the employee leaves. Attendances are recorded from a kiosk (badge or employee selection), from the systray of the backend, or manually. They feed three other mechanisms:

  • the time rules (hr.time.rule) of hr_work_entry, which reclassify the time beyond or missing from a threshold (overtime, undertime) on the attendances themselves;

  • the work entries, which payroll turns into worked days: with hr_holidays_attendance, validated attendances produce work entries, and attendance-based versions are paid on attendances only;

  • the time off allocations, credited with the overtime (hr_holidays_attendance).

In 20.0 there is no overtime model anymore (hr.attendance.overtime and the overtime rulesets were removed): overtime is time rule output stored as attendances.

Consulta

The attendance model

employee_id, check_in and check_out

The span, as naive UTC datetimes. An attendance without check_out is open: the employee is checked in.

date

The local day of check_in in the employee’s timezone (stored, indexed).

worked_hours

check_out - check_in in hours minus break_duration (an unpaid break in hours, only allowed on closed attendances). The schedule is not used: a lunch break planned in the schedule is not deducted.

work_entry_type_id (Time Type)

The type of the time, read by the time rules and the work entries. It defaults to the attendance type of the company (res.company.attendance_work_entry_type_id: the 002.00 type of the company’s country, else the generic attendance type).

state

draft, validated or refused, see Validation.

in_mode / out_mode

How the employee checked in and out: kiosk, systray, manual (default, also used for records created in code), technical (absence detection) and, for the check out only, auto_check_out. in_* / out_* fields store the location, IP address, browser and GPS coordinates when the company enables attendance_device_tracking, and in_image the check-in picture (attendance_capture_check_in).

time_rule_id, source_attendance_id, overtime_attendance_ids, source_stale

The time rule fields, see Time rules on attendances.

Three constraints protect the data: _check_validity_check_in_check_out() (check_out after check_in), _check_break_duration() and _check_validity(): an employee has at most one open attendance and no overlapping attendances. Attendances cannot be duplicated.

On hr.employee, hr_attendance adds attendance_ids, last_attendance_id (the last attendance that started before now), last_check_in, last_check_out, attendance_state (checked_in when the last attendance is open), attendance_manager_id (the Attendance Approver, made attendance officer automatically) and the hour counters described in Worked and overtime hours.

Example

from datetime import date, datetime

from odoo.exceptions import ValidationError

employee = env['hr.employee'].create({
    'name': 'Ann Example',
    'tz': 'Europe/Brussels',
    'date_version': date(2026, 1, 1),
    'contract_date_start': date(2026, 1, 1),
})
attendance = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 5, 6, 0),    # 8:00 in Brussels
    'check_out': datetime(2026, 10, 5, 15, 30),  # 17:30 in Brussels
    'break_duration': 0.5,
})
(attendance.date, attendance.worked_hours, attendance.in_mode, attendance.state,
 attendance.work_entry_type_id.code)
# (datetime.date(2026, 10, 5), 9.0, 'manual', 'validated', '002.00')

try:
    with env.cr.savepoint():
        env['hr.attendance'].create({
            'employee_id': employee.id,
            'check_in': datetime(2026, 10, 5, 14, 0),
            'check_out': datetime(2026, 10, 5, 16, 0),
        })
except ValidationError as e:
    print(e)
# Cannot create new attendance record for Ann Example, the employee was already checked in
# on 10/05/2026 04:00:00 PM

The constraints are checked when the records are flushed, after create returned: without the savepoint, the overlapping record would stay in the transaction.

The next examples are independent of each other; they only assume the records of this first example (employee, and company = employee.company_id), plus the overtime type once defined.

Checking in and out

_attendance_action_change() is the single entry point of the kiosk and the systray. Called on one employee, it checks the employee in (creates an attendance starting now) or out (sets check_out on the open attendance), then sends the new presence state on the bus. Its arguments are:

geo_information

A dict written on the attendance with the in_ prefix at check in and the out_ prefix at check out: mode, and with device tracking location, latitude, longitude, ip_address and browser.

check_in_image_data

{'image': bytes, 'type': mimetype}, stored in in_image and posted on the attendance.

It returns False, or a warning notification ({'type': 'warning', 'message': ...}) when the company requires a check-in picture that was not given. It raises a UserError if there is nothing to check out.

When the company enables single_check_in (only possible with the automatic check out), an employee with a non flexible schedule cannot check out: the call does nothing, and raises a UserError from the systray (context is_from_systray_check_in_out). The automatic check out then closes the attendance.

Example

employee.attendance_state  # 'checked_out'
employee._attendance_action_change({'mode': 'kiosk', 'location': 'Brussels office'})
# False
attendance = employee.last_attendance_id
(employee.attendance_state, attendance.in_mode, attendance.in_location,
 attendance.check_out)
# ('checked_in', 'kiosk', 'Brussels office', False)

employee._attendance_action_change({'mode': 'kiosk'})  # False
employee.attendance_state, attendance.out_mode, bool(attendance.check_out)
# ('checked_out', 'kiosk', True)

The HTTP layer is in hr_attendance/controllers/main.py (class HrAttendance):

Kiosk

/hr_attendance/<token> renders the public kiosk of the company whose attendance_kiosk_key is token (attendance_kiosk_url; regenerating the key with _regenerate_attendance_kiosk_key() invalidates the URL). Opening it logs out a user with a password. res.company.attendance_kiosk_mode selects how employees identify: barcode (badge or RFID: /hr_attendance/attendance_barcode_scanned, matching hr.employee.barcode), manual (selection in a list: /hr_attendance/manual_selection, checking hr.employee.pin when attendance_kiosk_use_pin is set) or both. These routes are auth='public': the token is the only credential, and most of them work as superuser on the employees of the token’s company.

Systray

/hr_attendance/systray_check_in_out checks in or out the current user’s employee (mode systray), if the company enables attendance_from_systray.

Breaks

With attendance_break_management, /hr_attendance/update_break_duration sets the break of the last attendance after the check out, as long as it is not validated.

The static helpers of the controller build the responses and are the place to add data: _get_user_attendance_data (state and hours of the employee, plus the company settings), _get_employee_info_response (what the kiosk displays), _get_geoip_response (the geo_information above) and _get_validated_check_in_image_and_type (decodes and checks the picture: JPEG or WebP data URI, at most 20 KB decoded).

Worked and overtime hours

worked_hours is the only stored duration. The employee counters are computed on the fly:

  • hours_today, hours_previously_today and last_attendance_worked_hours: the time worked today (employee’s timezone) until now, the breaks being prorated (_get_break_duration_within_period());

  • hours_last_month and hours_last_month_overtime: despite their names, the current month; the first sums the attendances that are not time rule outputs, the second those that are;

  • total_overtime: the hours of the validated attendances produced or reclassified by a time rule (time_rule_id set), whatever their type and date.

get_attendance_data_by_employee() returns, for a period, the hours of the attendances overlapping it (full worked_hours, not clipped to the period), per employee, with the overtime and one entry per type: {employee_id: {'worked_hours', 'overtime_hours', 'entries': [{'name', 'worked_hours', 'color'}]}} (see the example in Time rules on attendances).

The expected hours come from the schedule: _get_expected_attendances() (see Working time and calendars). The validation tolerance and the automatic check out use it; the time rules compute their own schedule intervals.

Validation

Only validated attendances count: they are the only time rule sources (_get_source_extra_fields_domain()), and the only attendances producing work entries or accruing time off. The initial state is set at creation by create(), according to res.company.attendance_validation:

no_validation (default)

Attendances are created validated.

manual_validation

Attendances are created draft; an officer validates (action_validate()) or refuses (action_refuse()) them.

tolerance_validation

Closed draft attendances are validated automatically when their duration (break included) differs from the expected hours of the day by at most attendance_validation_tolerance hours, or when no hours are expected that day (_update_tolerance_state(), called at creation and when check_out is written); the time rules then run on them. The others stay draft.

Time rule outputs (time_rule_id or source_attendance_id given) are always created validated. Passing state in the values bypasses the policy.

Example

company = employee.company_id
company.write({
    'attendance_validation': 'tolerance_validation',
    'attendance_validation_tolerance': 0.5,
})
attendances = env['hr.attendance'].create([{
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 6, 6, 0),    # 8h15 for 8 expected hours
    'check_out': datetime(2026, 10, 6, 14, 15),
}, {
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 7, 6, 0),    # 10 hours
    'check_out': datetime(2026, 10, 7, 16, 0),
}])
attendances.mapped('state')  # ['validated', 'draft']
attendances[1].action_validate()
attendances[1].state  # 'validated'

Time rules on attendances

hr.attendance inherits hr.time.rule.source.mixin: creating a validated attendance, or changing the employee, span, type, state or break of one, runs the time rules (see Time rules and When rules run). By default, a rule created in an attendance context matches the attendance type of the company (condition_work_entry_type_ids).

The rules write their result on the attendances themselves:

  • when the excess starts with the attendance, the attendance is reclassified in place: it gets the output type and time_rule_id, and ends where the excess ends;

  • otherwise the attendance is cut at the start of the excess, and the excess becomes a new output attendance: same employee, the rule’s type, time_rule_id set, source_attendance_id pointing to the source (inverse: overtime_attendance_ids), always validated;

  • an undertime (less_than) rule creates output attendances for the missing hours, in the free slots of the period (_get_time_rule_deficit_occupied()).

The span of an output is real time: the source and its outputs together cover the original check in to check out. Output attendances are regular attendances: they appear in the views, count in worked_hours and in the work entries with their own type.

Changing the span, employee, type or break of a source sets source_stale on the outputs that existed before (write(), and on deletion of the source); the rules run again on the source but do not delete the old outputs. A user reviews them, deletes them or clears the flag (action_mark_reviewed()).

Example

A daily overtime rule on the default 8-hour schedule (8:00-16: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,
    'work_entry_type_id': overtime.id,
})
rule.condition_work_entry_type_ids == employee.company_id.attendance_work_entry_type_id
# True

attendance = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 5, 6, 0),    # Monday 8:00 in Brussels
    'check_out': datetime(2026, 10, 5, 16, 0),  # 18:00 in Brussels
})
attendance.check_out, attendance.worked_hours, attendance.work_entry_type_id.code
# (datetime.datetime(2026, 10, 5, 14, 0), 8.0, '002.00'): cut at 16:00
output = attendance.overtime_attendance_ids
(output.check_in, output.check_out, output.work_entry_type_id.code,
 output.time_rule_id.name, output.state)
# (datetime.datetime(2026, 10, 5, 14, 0), datetime.datetime(2026, 10, 5, 16, 0),
#  'USOVERTIME150', 'Daily overtime', 'validated')
employee.total_overtime  # 2.0
employee.get_attendance_data_by_employee(datetime(2026, 10, 5), datetime(2026, 10, 6))
# {38: {'worked_hours': 10.0, 'overtime_hours': 2.0,
#       'entries': [{'name': 'Work', 'worked_hours': 8.0, 'color': 0},
#                   {'name': 'Overtime (1.5x)', 'worked_hours': 2.0, 'color': 4}]}}

# no schedule on Saturday: the whole attendance is overtime, reclassified in place
saturday = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 10, 7, 0),
    'check_out': datetime(2026, 10, 10, 10, 0),
})
saturday.work_entry_type_id.code, saturday.time_rule_id.name, saturday.overtime_attendance_ids
# ('USOVERTIME150', 'Daily overtime', hr.attendance())

# shortening the source flags its output
attendance.check_out = datetime(2026, 10, 5, 13, 0)
attendance.worked_hours, output.source_stale  # (7.0, True)
output.unlink()
attendance.check_out = datetime(2026, 10, 5, 16, 0)  # evaluated again: new output
attendance.check_out, attendance.overtime_attendance_ids.check_in
# (datetime.datetime(2026, 10, 5, 14, 0), datetime.datetime(2026, 10, 5, 14, 0))

The attendance implementation of the source hooks is small: outputs are built by _get_output_attendance_vals() (override it to add fields to outputs, as hr_payroll_attendance does), the result is applied by _apply_attendance_output(), and _get_applicable_employees() skips the employees without schedule for the rules based on the employee schedule.

Attendance-based work entries

With hr_holidays_attendance installed, the validated, closed attendances produce work entries with their own type and their real duration (break deducted); each value carries its attendances in attendance_ids. Two behaviors exist, chosen per version by hr.version.attendance_based (Attendance Based, defaulted from res.company.attendance_based and copied from contract templates):

static version (attendance_based = False)

The schedule still produces work entries, except on the days with an attendance, where the attendances replace it.

attendance-based version

has_static_work_entries() returns False: the schedule produces nothing, only attendances and time off count.

In both cases absences are cut where the employee actually worked, and an attendance overlapping a working time leave is resolved by the type sequence. The generation itself is described in Static and attendance-based versions.

Example

With the daily overtime rule above, 10 hours on Monday, 4 hours on Tuesday and nothing on Wednesday:

env['hr.attendance'].create([{
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 5, 6, 0),    # Monday 8:00-18:00
    'check_out': datetime(2026, 10, 5, 16, 0),
}, {
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 6, 7, 0),    # Tuesday 9:00-13:00
    'check_out': datetime(2026, 10, 6, 11, 0),
}])
version = employee.version_id

def summary(vals_list):
    return sorted(
        (v['date'].isoformat(), v['work_entry_type_id'].code, v['duration'])
        for v in vals_list
    )

version.attendance_based, version.has_static_work_entries()  # (False, True)
summary(version.generate_work_entries(date(2026, 10, 5), date(2026, 10, 7)))
# [('2026-10-05', '002.00', 8.0), ('2026-10-05', 'USOVERTIME150', 2.0),
#  ('2026-10-06', '002.00', 4.0), ('2026-10-07', '002.00', 8.0)]

version.attendance_based = True
summary(version.generate_work_entries(date(2026, 10, 5), date(2026, 10, 7)))
# [('2026-10-05', '002.00', 8.0), ('2026-10-05', 'USOVERTIME150', 2.0),
#  ('2026-10-06', '002.00', 4.0)]

On Wednesday, the static version is paid by its schedule; the attendance-based version is not. On Tuesday, both get the 4 hours worked, not the 8 hours of the schedule.

Scheduled actions

Attendance: Automatically check-out employees (every 4 hours)

_cron_auto_check_out(), for the companies with auto_check_out. In tolerance mode (_cron_auto_check_out_tolerance()), an open attendance is closed once the time worked on its day exceeds the expected hours plus auto_check_out_tolerance; the check out is set to the moment the limit was reached. Employees with a flexible schedule are skipped. In specific_time mode (_cron_auto_check_out_specific_time()), open attendances are closed at auto_check_out_specific_time (local hour). The check out mode is auto_check_out and a message is posted.

Attendance: Detect Absences for employees (every 4 hours)

_cron_absence_detection(), for the companies with absence_management: for each employee in contract with a schedule and without attendance on the previous day, it creates a validated 1-second technical attendance at local midnight, so that the daily undertime rules see the day. Technical attendances that produce no output are deleted.

Attendance: Process daily time rules and Attendance: Process weekly time rules (daily)

The crons of the source mixin, see When rules run.

Example

company.write({
    'auto_check_out': True,
    'auto_check_out_mode': 'tolerance',
    'auto_check_out_tolerance': 2,
})
attendance = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 22, 6, 0),  # yesterday, 8:00 in Brussels
})
env['hr.attendance']._cron_auto_check_out()
attendance.check_out, attendance.out_mode, attendance.worked_hours
# (datetime.datetime(2026, 10, 22, 16, 0), 'auto_check_out', 10.0): 8 hours + 2

# absence detection with a daily undertime rule
unpaid = env['hr.work.entry.type'].search([
    ('code', '=', '158.00'), ('country_id.code', '=', 'US'),
])
env['hr.time.rule'].create({
    'name': 'Daily undertime',
    'company_id': company.id,
    'threshold_operator': 'less_than',
    'work_entry_type_id': unpaid.id,
})
company.absence_management = True
bob = env['hr.employee'].create({
    'name': 'Bob Example',
    'tz': 'UTC',
    'date_version': date(2026, 1, 1),
    'contract_date_start': date(2026, 1, 1),
})
env['hr.attendance']._cron_absence_detection()
env['hr.attendance'].search([('employee_id', '=', bob.id)]).mapped(lambda a: (
    a.check_in, a.check_out, a.in_mode, a.work_entry_type_id.code, a.time_rule_id.name))
# [(datetime.datetime(2026, 10, 22, 8, 0, 1), datetime.datetime(2026, 10, 22, 16, 0),
#   'manual', '158.00', 'Daily undertime'),
#  (datetime.datetime(2026, 10, 22, 0, 0), datetime.datetime(2026, 10, 22, 0, 0, 1),
#   'technical', '002.00', False)]

Overtime and time off: hr_holidays_attendance

The bridge module hr_holidays_attendance (auto-installed with hr_attendance and hr_holidays) adds, besides the work entries described above:

Compensatory allocations

A rule with leave_compensation_rate (Allocate %) and allocation_type_id (a type requiring an allocation) credits hours * rate / hours_per_day days to an open-ended (date_to = False) validated allocation of the employee, created if needed; an undertime rule deducts them. Undertime that would overdraw the allocation is dropped entirely (no output, no deduction). Each credit is logged in hr.time.rule.allocation.log on the attendance that holds the time: the reclassified source, or the output attendance.

Reversal

Deleting that attendance, changing its span or employee, or setting a validated attendance back to draft or refused reverses its logged credits (refusing a source also refuses its outputs) (write()); re-running the rules credits the new values. A reversal that would bring the allocation below what was already taken raises a ValidationError.

Public holidays

Creating, changing or deleting a public holiday (resource.calendar.leaves without resource) re-runs the rules on the attendances of the affected employees (_retrigger_time_rules()): a public holiday has no expected hours, so the time worked on it becomes excess.

Accrual on worked hours

Accrual plan levels get the frequency Per Hour Worked (worked_hours): the accrued amount is prorated on the hours of the validated attendances of the period (_get_accrual_plan_level_work_entry_prorata()). It cannot be combined with an accrual at the start of the period.

The company attendance type (attendance_work_entry_type_id) is restricted to types that do not require an allocation.

Example

compensatory = env['hr.work.entry.type'].search([
    ('code', '=', 'LEAVE105'), ('country_id', '=', False),
])
compensatory.name, compensatory.requires_allocation  # ('Compensatory Time Off', True)
env['hr.time.rule'].create({
    'name': 'Daily overtime',
    'company_id': employee.company_id.id,
    'work_entry_type_id': overtime.id,
    'leave_compensation_rate': 1.0,  # 100% of the excess
    'allocation_type_id': compensatory.id,
})
attendance = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 5, 6, 0),    # 10 hours: 2 hours of overtime
    'check_out': datetime(2026, 10, 5, 16, 0),
})
allocation = env['hr.leave.allocation'].search([('employee_id', '=', employee.id)])
(allocation.work_entry_type_id.name, allocation.number_of_days, allocation.state,
 allocation.date_to)
# ('Compensatory Time Off', 0.25, 'validate', False): 2 hours / 8 hours per day
output = attendance.overtime_attendance_ids
env['hr.time.rule.allocation.log'].search([('allocation_id', '=', allocation.id)]).mapped(
    lambda log: (log.res_model, log.res_id == output.id, log.days))
# [('hr.attendance', True, 0.25)]
output.unlink()
allocation.number_of_days  # 0.0

env['resource.calendar.leaves'].create({
    'name': 'Public holiday',
    'company_id': employee.company_id.id,
    'date_from': datetime(2026, 10, 6, 22, 0),  # Wednesday in Brussels
    'date_to': datetime(2026, 10, 7, 21, 59, 59),
})
worked = env['hr.attendance'].create({
    'employee_id': employee.id,
    'check_in': datetime(2026, 10, 7, 6, 0),
    'check_out': datetime(2026, 10, 7, 14, 0),
})
worked.work_entry_type_id.code, worked.time_rule_id.name, allocation.number_of_days
# ('USOVERTIME150', 'Daily overtime', 1.0)

Extending

Check in and out

Override _attendance_action_change() (one employee, returns a notification or False). Extra values for the attendance are best passed through geo_information keys matching in_<key> / out_<key> fields. Add data to the kiosk and systray responses in the controller helpers _get_user_attendance_data and _get_employee_info_response (static methods: override them in a subclass of the controller and call super(SubClass, SubClass)).

Validation

_update_tolerance_state() implements the tolerance policy; _get_source_extra_fields_domain() decides which attendances the rules see.

Time rule outputs

_get_output_attendance_vals() for the values of output attendances, _apply_attendance_output() for side effects after the rules (keep returning the result of super()), and _get_write_source_extra_source_fields() to re-run the rules when another field changes.

Work entries

The hr.version hooks of Extending; hr_holidays_attendance overrides _get_version_work_entries_values() and passes its intervals to the other hooks through the context.

Pitfalls

  • check_in and check_out are naive UTC; date and every «day» (tolerance, automatic check out, hours today) are in the employee’s timezone.

  • _check_validity is skipped when the context has skip_time_rules, which the rule engine, the refusal and the automatic check out use. Code passing that context must keep the attendances consistent itself.

  • A source reclassified or cut by a rule cannot be extended over its outputs: the overlap constraint fails. Delete the outputs first.

  • Outputs are not deleted when their source changes, only flagged source_stale; code reading attendances (reports, payroll) must decide what to do with stale outputs.

  • Only validated attendances are rule sources and produce work entries: with manual_validation, nothing happens until an officer validates.

  • The time rules produce nothing for the days before contract_date_start.

  • hours_last_month* cover the current month, and total_overtime all the validated time rule outputs ever, whatever their type (an undertime output counts too).

  • Kiosk routes are public and run as superuser: protect the kiosk URL like a password, and check the company of every record received from the client in custom routes.

Reference

Original definition from hr_attendance

model odoo.addons.hr_attendance.models.hr_attendance.HrAttendance[fuente]
_name = hr.attendance
employee_id: int
Required

Name

Employee

Comodel

hr.employee

check_in: datetime.datetime
Required

Name

Check In

Default

2026-11-03 10:37:44

check_out: datetime.datetime
Name

Check Out

date: datetime.date
Required

Name

Date

Readonly

this field is not supposed to/cannot be set manually

worked_hours: float
Name

Worked Hours

Readonly

this field is not supposed to/cannot be set manually

break_duration: float
Name

Break Duration

Extra unpaid break duration (hours)

work_entry_type_id: int
Required

Name

Time Type

Comodel

hr.work.entry.type

state: str
Required

Name

Status

Readonly

this field is not supposed to/cannot be set manually

Selection

draft: Draft validated: Validated refused: Refused

Default

draft

in_mode: str
Name

Mode

Readonly

this field is not supposed to/cannot be set manually

Selection

kiosk: Kiosk systray: Systray manual: Manual technical: Technical

Default

manual

out_mode: str
Name

Out Mode

Readonly

this field is not supposed to/cannot be set manually

Selection

kiosk: Kiosk systray: Systray manual: Manual technical: Technical auto_check_out: Automatic Check-Out

Default

manual

time_rule_id: int
Name

Time Rule

Comodel

hr.time.rule

source_attendance_id: int
Name

Source Attendance

Comodel

hr.attendance

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

Overtime Attendance

Comodel

hr.attendance

source_stale: bool
Name

Source Stale

Default

False

create(vals_list)[fuente]

Create attendances, with an initial state derived from the validation policy.

Without state in the values, time rule outputs (time_rule_id or source_attendance_id set) are validated; other attendances are validated if the employee’s company has attendance_validation = 'no_validation', else draft. _update_tolerance_state() then applies the tolerance policy.

write(vals)[fuente]

Write vals, then flag the time rule outputs and validate within tolerance.

Moving an attendance to another employee requires being that employee, an attendance administrator or the employee’s attendance approver. When a field of _STALE_TRIGGER_FIELDS changes, the outputs that existed before the write (and that survive it) get source_stale = True. A new check_out (without state) re-applies the tolerance policy (_update_tolerance_state()).

Muestra

AccessError – if the user cannot assign the attendance to employee_id

action_validate()[fuente]

Validate the attendances, which runs the time rules on them.

action_refuse()[fuente]

Refuse the attendances and the records the time rules created from them (overtime_attendance_ids), without running the time rules (skip_time_rules).

action_reset_to_draft()[fuente]

Set the attendances back to draft.

action_mark_reviewed()[fuente]

Clear the source_stale flag, without re-running the time rules.

_check_validity()[fuente]

Check the attendance against the other attendances of the employee.

An employee has at most one open attendance (without check_out) and no overlapping attendances. Skipped when the context has skip_time_rules, i.e. for the writes of the time rule pipeline, the refusal and the automatic check-out.

Muestra

ValidationError – if the attendance overlaps another one or if another attendance of the employee is still open

_check_validity_check_in_check_out()[fuente]

Check that check_out is not earlier than check_in.

Muestra

ValidationError – if it is

_check_break_duration()[fuente]

Check that break_duration is positive, only set on closed attendances and not longer than the attendance.

Muestra

ValidationError – if one of these conditions is not met

_compute_worked_hours()[fuente]

Compute worked_hours: check_out - check_in in hours, minus break_duration.

The schedule (lunch break included) is not used. Open attendances get 0.

_update_tolerance_state()[fuente]

Validate the closed draft attendances within the tolerance of their company.

Only for the companies with attendance_validation = 'tolerance_validation', on the attendances not produced by a time rule: an attendance is validated when its duration (break included) differs from the expected hours of its local day by at most attendance_validation_tolerance, or when no hours are expected that day. As for action_validate(), writing the state runs the time rules on them.

_get_localized_times()[fuente]

Return check_in and check_out in the timezone of the employee’s version.

The attendance must be closed.

Devuelve

(check_in, check_out), naive local datetimes

Tipo del valor devuelto

tuple

_get_break_duration_within_period(start, stop)[fuente]

Return the part of the break durations of self falling within a period.

The break of each closed attendance is prorated to the share of the attendance that lies within [start, stop].

Parámetros
  • start (datetime) – naive local datetime (employee’s timezone)

  • stop (datetime) – naive local datetime

Devuelve

hours

Tipo del valor devuelto

float

_cron_auto_check_out()[fuente]

Cron: check out the forgotten open attendances, for the companies with auto_check_out, in tolerance and in specific_time mode.

_cron_auto_check_out_tolerance()[fuente]

Check out the open attendances exceeding the expected hours of their day plus the company’s auto_check_out_tolerance.

Applies to the companies in tolerance mode, to employees with a non flexible schedule. The expected hours are those of the schedule on the local day of the check-in (hr.employee._get_expected_attendances); the hours already worked that day are deducted. The check-out is set to the moment the limit was reached (at least one second after the check-in), with out_mode = 'auto_check_out', and a message is posted.

_cron_auto_check_out_specific_time()[fuente]

Check out the open attendances once the company’s auto_check_out_specific_time is reached.

Applies to the companies in specific_time mode. The check-out is the first occurrence of that hour (employee’s timezone) after the check-in, as soon as it is past, with out_mode = 'auto_check_out'; a message is posted.

_cron_absence_detection()[fuente]

Cron: create a validated 1-second technical attendance for each employee without attendance yesterday.

Only for the companies with absence_management, and the employees with a schedule whose current version is in contract yesterday. The attendance starts at local midnight and has the company’s attendance type, so that the daily undertime rules see the day and create output attendances for the missed hours. Technical attendances without any output are deleted afterwards.

_apply_record_output(rules, excess, deficit, active_iv=None)[fuente]

Apply the time rule results through hr.time.rule._apply_attendance_output.

_get_time_rule_deficit_occupied(employee_id, start_utc, period_end_utc)[fuente]

Return the spans of the attendances of the employee overlapping the period, refused ones excepted, so that undertime outputs are placed outside of them.

Tipo del valor devuelto

Intervals

_get_time_rule_output_vals(rule, df, dt, pp)[fuente]

Return the values of an output attendance, see hr.time.rule._get_output_attendance_vals.

Tipo del valor devuelto

dict

_get_time_rule_remainder_vals(df, dt)[fuente]

Return the values of a remainder attendance: employee_id, check_in, check_out and source_attendance_id.

Tipo del valor devuelto

dict

_get_source_extra_fields_domain()[fuente]

Only validated attendances are time rule sources.

Tipo del valor devuelto

list

_get_time_rule_break_hours()[fuente]

Return break_duration, which the time rules do not count as worked time.

Tipo del valor devuelto

float

_get_write_source_extra_source_fields()[fuente]

Writing work_entry_type_id, state or break_duration also runs the rules.

Tipo del valor devuelto

set[str]

get_kiosk_url()[fuente]

Return the public kiosk URL of the current company (base URL + kiosk key).

Tipo del valor devuelto

str

get_unusual_days(date_from, date_to=None)[fuente]

Return the unusual days of the employee of the context key employee_id, or else of the current user’s employee, see hr.employee._get_unusual_days.

Devuelve

{'YYYY-MM-DD': bool}

Tipo del valor devuelto

dict

Additional fields with hr_attendance

model odoo.addons.hr_attendance.models.hr_employee.HrEmployee[fuente]
attendance_manager_id: int
Name

Attendance Approver

Comodel

res.users

The user set in Attendance will access the attendance of the employee through the dedicated app and will be able to edit them.

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

Attendance

Comodel

hr.attendance

last_attendance_id: int
Name

Last Attendance

Readonly

this field is not supposed to/cannot be set manually

Comodel

hr.attendance

attendance_state: str
Name

Attendance Status

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

Selection

checked_out: Checked out checked_in: Checked in

hours_today: float
Name

Hours Today

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

hours_last_month: float
Name

Hours Last Month

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

hours_last_month_overtime: float
Name

Hours Last Month Overtime

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

total_overtime: float
Name

Total Overtime

Readonly

this field is not supposed to/cannot be set manually

Store

this field is there only for technical reasons

attendance_based: bool
Name

None

_attendance_action_change(geo_information=None, check_in_image_data=None)[fuente]

Check the employee in or out, now.

If the employee is not checked in, create an attendance; otherwise set check_out on the open attendance, unless the company uses single_check_in and the employee’s schedule is not flexible (then nothing is done). The presence state is sent on the bus.

Parámetros
  • geo_information (dict) – values written with an in_ (check-in) or out_ (check-out) prefix, e.g. {'mode': 'kiosk', 'latitude': ..., 'longitude': ..., 'location': ..., 'ip_address': ..., 'browser': ...}

  • check_in_image_data (dict) – {'image': bytes, 'type': str}, stored in in_image and posted on the attendance at check-in

Devuelve

False, or a notification {'type': 'warning', 'message': str} when the company requires a check-in picture and none was given

Muestra

UserError – if no open attendance is found to check out, or on a check-out from the systray (context is_from_systray_check_in_out) with single_check_in

_has_attendance_check_in_ability()[fuente]

Return whether the employee may check in and out from the systray (company’s attendance_from_systray).

Tipo del valor devuelto

bool

get_attendance_data_by_employee(date_start, date_stop)[fuente]

Return the hours of the attendances overlapping a period, per employee.

Hours are the full worked_hours of the overlapping attendances (not clipped to the period); overtime_hours sums the attendances produced by a time rule.

Parámetros
  • date_start (datetime) – naive UTC

  • date_stop (datetime) – naive UTC

Devuelve

{employee_id: {'worked_hours': float, 'overtime_hours': float, 'entries': [{'name': str, 'worked_hours': float, 'color': int}]}}, one entry per work entry type

Tipo del valor devuelto

dict

_compute_hours_today()[fuente]

Compute the hours worked today (employee’s timezone) until now, breaks deducted.

hours_today is the total, last_attendance_worked_hours the part of the last attendance, hours_previously_today the rest; today_attendance_ids are the attendances overlapping today.

_compute_hours_last_month()[fuente]

Compute the hours of the current month (despite the field names), in the employee’s timezone.

Only the closed attendances within the month count: hours_last_month sums those not produced by a time rule, hours_last_month_overtime those produced by one.

_compute_presence_state()[fuente]

Use the attendance state in the presence state, after the login.

A checked in employee (or an employee without user) is present, unless the employee has no user and is out of contract; a checked out employee within working hours and in contract is absent.

_notify_employee_presence_status()[fuente]

Send the presence state of the employee on the bus (hr.employee/presence).

action_archive()[fuente]

Archive the employees and check out their open attendances (now).

Additional fields with hr_attendance

model odoo.addons.hr_attendance.models.hr_version.HrVersion[fuente]
attendance_based: bool
Name

Attendance Based

When enabled, payslips are computed from badge records rather than the employee’s work schedule. Time off is always accounted for.

Additional fields with hr_attendance

model odoo.addons.hr_attendance.models.hr_time_rule.HrTimeRule[fuente]
_get_applicable_employees(employees)[fuente]

Also drop the employees without schedule for the rules based on the employee schedule (calendar_source = 'employee').

_get_output_attendance_vals(employee, check_in, check_out, source_attendance=None, accumulated_pp=frozenset({}))[fuente]

Return the creation values of an output attendance of the rule.

Parámetros
  • employee – hr.employee

  • check_in (datetime) – naive UTC

  • check_out (datetime) – naive UTC

  • source_attendance – the source hr.attendance

  • accumulated_pp (frozenset) – premium pay ids, used by overrides (hr_payroll)

Devuelve

employee_id, work_entry_type_id (the rule’s output type), check_in, check_out, source_attendance_id and time_rule_id

Tipo del valor devuelto

dict

_apply_attendance_output(excess, deficit, active_iv=None)[fuente]

Write the time rule results on attendances (hr.time.rule._apply_output).

hr_holidays_attendance extends it to credit or deduct allocations.

Devuelve

the result of _apply_output: (new_records, all_source_ids, excess_alloc, deficit_alloc)

Tipo del valor devuelto

tuple

Additional fields with hr_attendance

model odoo.addons.hr_attendance.models.res_company.ResCompany[fuente]
attendance_kiosk_mode: str
Name

Attendance Mode

Selection

barcode: Barcode / RFID barcode_manual: Barcode / RFID and Manual Selection manual: Manual Selection

Default

barcode_manual

attendance_kiosk_key: str
Name

Attendance Kiosk Key

Default

8cc8476c7adb4054a370ebdc99c882bb

attendance_kiosk_use_pin: bool
Name

Employee PIN Identification

attendance_from_systray: bool
Name

Attendance From Systray

Default

True

auto_check_out: bool
Name

Automatic Check Out

Default

False

auto_check_out_mode: str
Name

Auto Check Out Mode

Selection

tolerance: Tolerance specific_time: Specific Time

Default

tolerance

auto_check_out_tolerance: float
Name

Auto Check Out Tolerance

Default

2

auto_check_out_specific_time: float
Name

Auto Check Out Specific Time

Default

20.0

single_check_in: bool
Name

Single Check-In Attendance System

absence_management: bool
Name

Absence Management

Default

False

attendance_validation: str
Name

Attendance Validation

Selection

no_validation: Worked days are automatically approved manual_validation: Worked days require manual approval tolerance_validation: Worked days require approval if outside tolerance

Default

no_validation

attendance_validation_tolerance: float
Name

Validation Tolerance (Hours)

Default

0.0

attendance_work_entry_type_id: int
Name

Attendance Time Type

Comodel

hr.work.entry.type

Work entry type assigned to attendances and read by the time rule engine.

attendance_based: bool
Name

Attendance Based

Default

False

_regenerate_attendance_kiosk_key()[fuente]

Replace the kiosk key, which invalidates the current kiosk URL.

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.hr_attendance.HrAttendance[fuente]
write(vals)[fuente]

Reverse the allocation credits logged on the attendances before an invalidating write.

Credits are reversed when check_in, check_out or employee_id change (unless the context has skip_time_rules), or when a validated attendance goes back to draft or is refused. Re-running the rules credits the new values again.

Muestra

ValidationError – if the allocation was already used beyond its allowed balance

Reverse the allocation credits logged on the deleted attendances.

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.hr_version.HrVersion[fuente]
has_static_work_entries()[fuente]

Attendance-based versions have no static work entries.

Tipo del valor devuelto

bool

_get_attendance_intervals(start_dt, end_dt)[fuente]

Return empty intervals for the resources of attendance-based versions, the schedule intervals for the others.

Devuelve

{resource_id: Intervals}

Tipo del valor devuelto

dict

_get_version_work_entries_values(date_start, date_stop)[fuente]

Add the raw values of the validated, closed attendances of the period.

Each attendance gives a value of its own type, from check_in to check_out minus the break, with the key attendance_ids; where an attendance and a working time leave overlap, the lowest type sequence wins. The schedule part is computed by super() with three context keys ({resource_id: Intervals}, aware UTC):

  • knocked_day_intervals: for static versions, the local days with an attendance, removed from the schedule (_get_real_attendances());

  • time_on_intervals: the worked time (attendances and working time leaves), cut out of the absences (_get_valid_leave_intervals());

  • att_coverage_intervals: the attendance segments that won over a working time leave, cut out of that leave.

Parámetros
  • date_start (datetime) – naive or aware (UTC)

  • date_stop (datetime) – naive or aware (UTC)

Tipo del valor devuelto

list[dict]

_get_real_attendances(attendances, leaves, worked_leaves)[fuente]

Also remove the days with an attendance (context knocked_day_intervals).

_get_valid_leave_intervals(attendances, interval)[fuente]

Cut absences where the employee worked (context time_on_intervals) and working time leaves where an attendance won (context att_coverage_intervals).

Tipo del valor devuelto

list

_generate_work_entries_postprocess_adapt_to_calendar(vals)[fuente]

Values from attendances and from fully flexible versions keep their real duration.

_get_more_vals_attendance_interval(interval)[fuente]

Add ('attendance_ids', attendance) when the payload is an hr.attendance.

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.hr_time_rule.HrTimeRule[fuente]
_apply_attendance_output(excess, deficit, active_iv=None)[fuente]

Apply the results on attendances, then credit or deduct the allocations (hr.time.rule._apply_allocation_credits).

Deficits that would overdraw the allocation are dropped first (_filter_deficit_exceeding_allocation): they create neither output nor deduction.

Devuelve

the result of the base method, see hr.time.rule._apply_output

Tipo del valor devuelto

tuple

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.resource_calendar_leaves.ResourceCalendarLeaves[fuente]
_get_time_rule_affected()[fuente]

Return the employees affected by the public holidays of self.

A public holiday (leave without resource) affects the employees of its schedule, or else of its company.

Devuelve

[(employee, date_from, date_to)], aware UTC datetimes

Tipo del valor devuelto

list

_retrigger_time_rules()[fuente]

Re-run the time rules on the attendances of the employees affected by the public holidays of self; called on create, write (old and new values) and unlink.

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.hr_leave_allocation.HrLeaveAllocation[fuente]
_get_accrual_plan_level_work_entry_prorata(level, start_period, start_date, end_period, end_date)[fuente]

For worked_hours levels, return the hours of the validated attendances of the employee between start_date and end_date.

The attendance spans are united and clipped to the period; breaks are not deducted.

Tipo del valor devuelto

float

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.hr_leave_accrual_plan_level.HrLeaveAccrualLevel[fuente]
frequency: str
Name

Frequency

_check_worked_hours()[fuente]

Forbid the worked_hours frequency on levels accruing at the start of the period.

Muestra

ValidationError – if frequency = 'worked_hours' and accrued_gain_time = 'start'

Additional fields with hr_holidays_attendance

model odoo.addons.hr_holidays_attendance.models.hr_employee.HrEmployee[fuente]
get_attendance_data_by_employee(date_start, date_stop)[fuente]

Set overtime_hours to the sum of the time rule outputs overlapping the period.