HR Attendance Pro
An append-only event log, a rebuildable day projection, and standard hr.attendance kept in sync, so payroll and reporting read native rows while integrity lives in an immutable chain.
Why this module
HR Attendance Pro
Events are immutable, days are derived
Every check-in, check-out, and break lands once in an append-only log guarded against edit and delete. Day totals are a projection computed by a service, not ORM triggers, so they rebuild deterministically from the events at any time.
Standard hr.attendance stays in sync
Paired sessions project into native hr.attendance rows tagged as managed, so payroll, reporting, and any module reading attendance just work. Rows created directly in standard Odoo are tagged unmanaged and never touched.
One ingest pipeline for every source
Kiosk, mobile, biometric webhook, manual, and bulk import all flow through a single validated, idempotent ingest path. Re-sending the same event is a no-op, so offline buffers and retried batches converge instead of duplicating.
Day in the life
A late biometric punch and a correction
An employee clocks in at a kiosk, then a biometric controller posts the same out punch twice after a network blip. The unique correlation id absorbs the duplicate, the day recomputes with rounding and grace-to-schedule applied, and late and overtime minutes settle. A missing check-out trips an anomaly that notifies the manager. The employee files a correction with a photo; once the approval chain clears, the apply step writes new events linked back to the request, and the nightly reconcile converges the trailing days.
Edge cases
The cases most modules quietly ignore.
In the shipped code today, each one a place where a cheaper module silently does the wrong thing.
Every event carries a correlation id under a UNIQUE constraint. Duplicate kiosk batches, retried webhooks, and re-applied corrections dedupe silently instead of creating phantom punches.
The event model blocks writes to all but a few operational fields and refuses unlink outside the retention purge context, so attendance history cannot be quietly edited or deleted through any surface.
Civil-date attribution anchors the tail of an overnight shift to the correct day, so a 2am clock-out is counted against the shift that started the previous evening rather than splitting across two days.
When the first clock-in falls within the in-grace of the roster start, or the last clock-out within the out-grace of the end, paid time snaps to the scheduled boundary; minute rounding then applies on top, with nearest, up, or down modes.
Per-employee dated shift assignments win over the company default, and the shortest range covering a day beats a long rotation, so a one-day override takes precedence with the latest start breaking ties.
The nightly cron recomputes the trailing days for every active employee in batches of 200 with a commit between batches, so a worker restart never loses progress and re-running converges rather than double-counting.
Corrections inherit the platform approval chain and workflow mixins, with capture roles cascading from employee to manager to officer to admin, so amendments are reviewed as declared data rather than ad hoc edits.
Kiosk tokens are short-lived HMAC-signed JWTs, the device directory listing is off by default and page size is capped server-side, and ingest is throttled per device, so a leaked token cannot bulk-pull the directory or flood events.
Events, days, devices, and geofences carry company_id with record rules, and device resolution refuses a kiosk whose company does not match the employee, keeping capture inside company boundaries.
Old events archive after a configurable window and, after a grace period, export to a CSV attachment before purge, so hot reads stay fast while the audit trail is preserved.
What is inside
Built to do the job, end to end.
- Immutable event log and derived day. eh.hr.attendance.event is append-only with covering indexes for day-window scans and a unique correlation id; eh.hr.attendance.day is a one-row-per-employee-per-day projection rebuilt by a compute service that resolves shift, policy thresholds, rounding, late, early-leave, and overtime.
- Capture surfaces and controllers. JSON endpoints for kiosk pair, token refresh, batched events, and a gated employee picker; a session-authenticated mobile endpoint with today and history; and an HMAC-verified biometric webhook. A CSV import service streams batches through the same ingest path.
- Kiosk, geofence, and offline. Paired kiosk devices with short-lived HS256 tokens, per-device throttle, and a quarantine and retire lifecycle; haversine point-in-fence geofence tagging; and an IndexedDB offline buffer in the kiosk OWL app that drains on reconnect.
- Corrections, anomalies, and crons. A workflowed correction model with evidence attachments and an approval chain that applies as new linked events; an anomaly service flagging missing punches, overlaps, double check-ins, geo mismatches, and excessive daily minutes with manager notifications; nightly reconcile and retention crons.
Honest about the edges
What this does not do, so nothing surprises you.
- This module depends on the EH HR Platform layer (eh_hr_core, the workflow, approval, policy, and notification engines, and eh_hr_compat) and is not a standalone add-on to plain Odoo. It targets Odoo 17 Community.
- Workflow states, approval chains, late and overtime thresholds, and notification dispatch are owned by the platform engines, not by this module. It owns capture, derivation, and surfaces only.
- Geofence matching is a linear haversine scan over active fences per company, suitable for typical fence counts; it is not a spatial index, and the off-shift anomaly check is intentionally conservative and currently returns no codes.
- Idempotency and convergence come from the unique correlation id plus dedupe-on-ingest and batch-committed reconcile, not from Postgres advisory locks; concurrent compute relies on the per-day unique constraint rather than row locking.
- Day status treats approved leave via the platform leave projection when present and falls back to core hr.leave otherwise; payroll rules, pay codes, and rate calculation are out of scope and consume the day projection downstream.
- Kiosk signing secrets and webhook secrets live in ir.config_parameter; encryption at rest depends on the operator wiring database-level encryption. Biometric and HRIS vendor adapters are HMAC webhook contracts, not bundled device drivers.
odoo 17 attendance, time and attendance odoo, employee time clock odoo, kiosk attendance odoo, biometric attendance odoo, geofence attendance, shift roster odoo, overtime tracking odoo, attendance import csv, hr.attendance sync, attendance corrections approval, event sourced attendance, late and early leave tracking, multi company attendance, attendance anomaly detection
Need this fitted to the way you work?
ERP Heritage delivers end to end Odoo work: Odoo Implementation, Customization and Development, Integration, Migration, Consultation, Support and Training. We help teams put this module into production, shape it to their process, and keep it running.
We work with businesses across Australia (Melbourne, Sydney, Brisbane, Perth, Adelaide, Canberra) and the Middle East (Dubai, Abu Dhabi, Riyadh, Jeddah, Doha, Kuwait City, Muscat). Start a conversation at erpheritage.com.au or email info@erpheritage.com.au.
Languages
Available in 19 languages
The interface ships translated out of the box. Switch language in Odoo and the fields, menus, and messages follow.
Please log in to comment on this module