| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Attendances (hr_attendance)
• Discuss (mail) • Employees (hr) |
| Community Apps Dependencies | Show |
| Lines of code | 4934 |
| Technical Name |
eh_hr_attendance_access_control |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Versions | 16.0 17.0 18.0 19.0 |
Door Access Control
Turn a face match at the kiosk into a door unlock, with per-employee zone permissions and a full audit trail.
Why this module
Door Access Control
One protocol, any hardware
The Odoo side speaks a single thing: an HTTP webhook. It POSTs action, employee, zone, reason, and auto-relock seconds as JSON. Customers with relay, Wiegand, MQTT, or proprietary boards put a one-page bridge script in front of the hardware. No driver lock-in, no per-vendor module to maintain.
An audit row, always
Allowed, denied for no permission, denied by the reader, reader error, no zone for the site, no reader for the zone: each path writes an append-only eh.hr.access.event with the reader's response text and milliseconds elapsed. Events are read-only, company-scoped, and swept on a retention cron, not deleted by hand.
Fire and forget by design
The kiosk JS wraps the existing face-match call and fires the unlock as a side effect. The visible success screen never waits on door hardware, so a slow or offline reader does not stall the queue. The unlock outcome lands in the audit list where a manager can see it.
Day in the life
A contractor badges in at the loading dock.
A contractor walks up to the dock kiosk and the camera matches their face. The kiosk shows its usual success screen at once. In the background the suite resolves the dock site to its access zone, checks that the contractor holds an active permission for that zone and that today falls inside their valid-from and valid-to window, then POSTs an unlock to the dock reader's webhook with a four second timeout and a five second auto-relock instruction. The bridge service flips the relay and returns 200. An access event is written: outcome allowed, the reader's response captured, 180 ms elapsed. Next door, a former employee whose permission lapsed yesterday matches at the front kiosk; the success screen still shows for attendance, but the access check fails the date window, no webhook is called, and a denied event is logged for the manager to review. Every row stays inside the company that owns the kiosk.
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.
A permission is honoured only when active and inside its valid-from / valid-to range, evaluated in the record's timezone. A row dated to end yesterday denies today and logs a denied event rather than silently unlocking.
Access events are read-only with set-null relations: even an admin cannot edit an outcome, and deleting a zone, reader, or employee blanks the link but preserves the historical row. Cleanup happens only through the retention cron.
The retention sweep caps each run at a batch limit across companies, honours each company's own retention-days setting, and reports progress through the framework's _commit_progress so a long purge cannot run away or starve other crons.
Zones, readers, permissions, and events all carry a global per-company record rule, and the unlock endpoint rejects an employee whose company does not match the kiosk's company with a 404 before any reader is called.
The public trigger endpoint requires a valid active device token (401 otherwise), is rate-limited per token or IP, validates that employee_id is an integer (400), and confirms the employee exists in the device's company before touching hardware.
The webhook call maps a 2xx to allowed, a 4xx to reader-denied, and a 5xx, timeout, or network error to reader-error, capturing up to 1000 characters of response. A missing requests library degrades to a logged error, not a crash.
Zone codes are constrained to URL-safe characters and unique per company; a reader's webhook URL must start with http:// or https://; a permission is unique per employee and zone. Bad config is refused at save, not discovered at the door.
What is inside
Built to do the job, end to end.
- Access zones and readers. eh.hr.access.zone names a controlled space tied to one or more kiosk sites; eh.hr.access.reader holds the bridge webhook URL, an optional bearer token (visible only to HR admins), an HTTP timeout, and auto-relock seconds. A reader test button lets you verify the bridge is reachable during commissioning.
- Per-employee zone permissions. eh.hr.access.permission grants one employee access to one zone, with optional valid-from and valid-to dates and an active flag. Permissions surface on the employee form, and a unique constraint stops duplicate rows for the same employee and zone.
- Append-only event log. eh.hr.access.event records every unlock attempt with its outcome, reader response, elapsed milliseconds, and the employee, zone, reader, site, and device involved. Rows are read-only and company-scoped, with a daily retention sweep governed by a per-company retention-days setting.
- Kiosk trigger endpoint and JS hook. A token-authenticated, rate-limited /eh_hr/kiosk/access/trigger endpoint resolves the device's zone, validates the permission, calls the reader, and records the event. A small JS hook wraps the existing face-match call and fires the trigger only on a successful match, without blocking the kiosk's success screen.
Honest about the edges
What this does not do, so nothing surprises you.
- Ships one reader protocol only: an HTTP webhook. Relay, Wiegand, MQTT, and proprietary boards need a small bridge service (a one-page script) that exposes an HTTP endpoint. No native serial or board driver is included.
- When a site maps to more than one zone, the first active zone wins; multi-zone-per-site routing is not yet implemented. Within a zone, the first active reader is used.
- Permissions gate on an active flag and a date window only. There is no time-of-day schedule, weekday calendar, anti-passback, or occupancy limit.
- The unlock is fire-and-forget: the kiosk shows its success screen regardless of whether the door opened. The outcome is visible only in the audit log, not as a hard gate on entry.
- Requires the ERP Heritage attendance base, face kiosk, and core modules. It extends an existing kiosk flow and is not a standalone access-control product.
- The Python requests library must be available on the server for the webhook call. There is no offline queue: if Odoo or the bridge is unreachable at match time, that attempt is logged as a reader error.
Odoo 19 door access control, Odoo Community access control, face recognition door unlock Odoo, biometric door access Odoo, attendance access control, hr_attendance door integration, kiosk face match unlock, Wiegand bridge Odoo, relay door unlock webhook, per-employee zone permissions, access audit trail Odoo, webhook door reader, physical access control HR, auto relock door Odoo
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.
Please log in to comment on this module