EH HR Attendance Pro
Event-sourced attendance that proves itself.
Why this module
EH HR Attendance Pro
Truth lives in an append-only log
eh.hr.attendance.event is immutable: a write guard rejects edits to recorded fields and rows cannot be deleted except by the retention service. Amendments are expressed as new events through a correction workflow, so the original record always stands. A UNIQUE correlation_id makes every ingest idempotent, so a replayed kiosk batch or a retried webhook lands exactly once.
Native rows, no parallel system
Paired check-in and check-out events project into standard hr.attendance with an idempotency key, and stale managed rows are retired on recompute. Payroll, reporting, and any module reading hr.attendance just work. Rows created directly through standard Odoo are never touched, and out-of-pipeline edits to check-in, check-out, or employee are recorded in the audit chain.
Shifts and rules without code
Late, early-leave, and overtime thresholds resolve through the shared policy engine and merge with per-shift grace, rounding, and grace-to-schedule settings. Corrections move through a workflow and approval chain declared as data, with line-manager and HR-officer steps and escalation hours. An administrator changes how attendance behaves, not a developer.
Day in the life
From a tap on a kiosk to a payroll-ready day
An employee taps a kiosk, scans a fingerprint reader, or clocks in from the mobile app. The event is signed, idempotency-checked, throttled per device, timezone-tagged, geofence-matched, and written once to the immutable log. The day recomputes: it pairs sessions, snaps clock-ins within grace to the rostered shift, rounds boundaries, and derives worked, break, late, early-leave, and overtime minutes, honouring overnight shifts. Anomalies like a missing check-out are flagged and routed to the manager through the notification engine. The result projects into standard hr.attendance, and a nightly cron reconciles the trailing days so late biometric events converge. If a tap was wrong, the employee files a correction; once approved it applies as new events and the day recomputes itself, leaving the original log intact.
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.
Ingest dedupes on a UNIQUE correlation_id, so a retried webhook, a re-flushed offline kiosk buffer, or a re-imported CSV row creates the event once and returns the existing id rather than a duplicate.
Civil-date attribution pulls a wide event window and attributes the tail of an overnight shift to the correct day when the local time falls before the shift end, so a midnight-crossing shift counts as one day, not two.
A clock-in within the in-grace of the rostered start can snap to schedule, and every boundary can round nearest, up, or down to a set increment, so paid time is measured from the roster while the raw taps stay exact in the log.
A check-in with no matching check-out still projects as an open hr.attendance row and raises a missing_out anomaly, instead of being silently dropped.
Applying an approved correction is safe to repeat: the produced events embed the correction id in their correlation_id, so re-running the apply step writes nothing new.
Record rules scope events, days, and corrections to self, team, or company by group, and shift rosters are isolated per company, so a manager sees their team and an officer sees their companies, never the whole database.
Kiosk tokens are short-lived HMAC-SHA256 JWTs, the employee directory is gated behind a per-device flag and a server-capped page size, and endpoints are rate-limited in the database, so a leaked token cannot bulk-pull names and photos.
A retention cron archives events past the configured window and exports then purges after a grace period, while a nightly reconcile recomputes trailing days in batches with a commit between each, so a worker restart does not lose progress on a long run.
What is inside
Built to do the job, end to end.
- Models this module adds. eh.hr.attendance.event (immutable log), eh.hr.attendance.day (derived projection), eh.hr.attendance.correction (workflowed amendment), eh.hr.shift, eh.hr.shift.assignment (per-day roster), eh.hr.geofence, eh.hr.kiosk.device, and the eh.hr.attendance.import.wizard.
- Standard Odoo model it extends. hr.attendance, with added provenance and integrity fields (session key, managed flag, source, first and last event, geofence, device, anomaly codes). Standard fields are never redefined, and direct edits are written to the audit chain.
- Services and surfaces. Documented services for ingest, day compute, projection, geofence matching, anomaly detection, correction apply, kiosk session JWT, and bulk import. REST controllers for kiosk, mobile, and biometric webhook, plus OWL today board, manager pulse, kiosk app, and an IndexedDB offline buffer.
- Data and automation. Approval chain and workflow for corrections, attendance policy defaults, sequences, notification templates, security groups and record rules, a nightly reconcile cron, and a retention archive cron, all shipped as data.
Honest about the edges
What this does not do, so nothing surprises you.
- The kiosk, mobile, and biometric apps and devices are not included. This module provides the Odoo-side REST endpoints, kiosk web client, and a JWT and webhook signature scheme; you supply or build the hardware and client integrations.
- Geofence matching is a linear haversine scan over active fences per company, which suits typical site counts but is not a spatial index for very large fence sets.
- Workflow, approval chains, policy thresholds, and notification dispatch are provided by the shared EH HR Platform engines this module depends on, not reimplemented here.
- Kiosk signing secrets are stored through ir.config_parameter; at-rest encryption depends on the operator wiring up database-level encryption.
- Overtime, leave status, and payroll posting are surfaced from attendance days but final overtime approval and payroll runs live in their own platform modules.
- Tested on Odoo 16 to 19 Community; the 16 and 17 view layers are produced from a single authored source by a build step.
odoo attendance, odoo time clock, odoo kiosk attendance, odoo biometric attendance, odoo attendance geofence, odoo mobile check in, odoo shift roster, odoo overtime tracking, odoo attendance correction, odoo attendance audit trail, event sourced attendance, odoo 19 attendance, odoo community hr, self hosted attendance, odoo attendance import
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