EH HR Notify Engine
One send() call fans a notification across in-app, email and SMS, with per-user channel preferences and built-in duplicate suppression.
Why this module
EH HR Notify Engine
Send once, deliver everywhere
Feature modules pass a template code, a recipient (one user or a list) and a payload dict. The engine looks up the template, resolves the channels and dispatches. No module ever touches mail.mail, sms.sms or the bus directly, so delivery logic lives in one audited place instead of being copy-pasted across the platform.
Channels the way each person wants
Every user can store a per-template preference, or a single wildcard preference for all templates. A specific preference wins over the wildcard, which wins over the template default. The precedence is explicit and covered by automated tests, so a code that sorts oddly cannot accidentally let the wildcard override a deliberate choice.
Duplicates do not stack up
Pass a dedupe key and the engine suppresses a repeat of the same template, channel and key to the same user inside a 60 second window. A retried trigger or a double-fired event does not spam the recipient. The window is in-process and self-trimming, so it adds no tables and no cron.
Day in the life
A leave approval lands the right way
Time Off marks a request approved and calls the engine with the leave.approved template, the employee and a payload. The engine reads the employee's preference: they chose email for this template, so the wildcard SMS setting is ignored. The subject and body have the employee's name substituted from the payload, a mail.mail is queued and sent. The approver's manager triggers the same template a second time on a retry, but the dedupe key matches a send from forty seconds ago, so the duplicate is dropped. Nobody wrote a line of delivery code, and no one got notified twice.
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 per-template preference is resolved explicitly before the wildcard, not by sort order. A template code that sorts below the asterisk can no longer let the wildcard win by accident, a bug the precedence test pins down.
Duplicate suppression is keyed on (user, template code, channel, dedupe key) for 60 seconds. The cache lives in the process registry and self-trims past 5000 entries, so it never grows without bound and needs no table or scheduled job.
Each channel dispatches inside its own try block. If SMS raises, the exception is logged and email and in-app still go out. One broken channel cannot abort the rest of a multi-channel send.
SMS dispatches only when the core sms module is installed and the user has a mobile or phone number, otherwise it is skipped silently. Push and webhook look up a registered adapter service and skip cleanly if none is present, so the engine never errors on a channel it cannot deliver.
The model ACL grants every internal user CRUD, so a record rule restricts each user to their own preference rows while HR admins see all for support. Two isolation tests prove one user cannot read another user's channels.
An unrecognised channel string is logged as a warning and skipped rather than raising, and an empty preference falls back to in_app, so a malformed preference value degrades safely instead of breaking the send.
What is inside
Built to do the job, end to end.
- Models this module adds. eh.hr.notification.template (code, name, subject, body, sms and push fields, default channels) and eh.hr.notification.preference (per-user channels per template code or wildcard). Both carry a unique constraint expressed for Odoo 16 to 19.
- The send() service. A registered platform service, eh.hr.notification.engine, exposing send(template_code, recipient, payload, channels, dedupe_key). In-app posts to the user inbox via mail.message, email queues a mail.mail. It is plain Python, callable from a model trigger, a cron or an RPC endpoint.
- Delegation hooks, not bundled backends. SMS hands off to the core sms module when installed. Push and webhook call a separately registered adapter service if one exists and skip otherwise. This module ships the in-app and email channels and the routing for the rest.
- Templating and security. Templates render with lightweight {placeholder} substitution against the payload dict, returning the raw template if a key is missing rather than raising. Access is split between HR admin (full) and HR officer (read), with a record rule isolating per-user preferences.
Honest about the edges
What this does not do, so nothing surprises you.
- Bundled channels are in-app (via mail.message to the user inbox) and email (via mail.mail). SMS, push and webhook are delegation hooks: SMS needs the core sms module installed, and push and webhook require a separately registered adapter service that this module does not include. Without those, the respective channels skip silently.
- Templates render with simple Python {placeholder} substitution against the payload, not full QWeb expressions or conditionals. A missing placeholder returns the template unchanged rather than failing.
- Duplicate suppression is a 60 second in-process window held in the worker's registry. It is not shared across workers or persisted, so it deduplicates within a process and timeframe, not globally or across restarts. There is no rate-limit or quota throttle and no digest or batching feature.
- The preference model stores quiet-hours fields, but the engine does not yet read them, so quiet hours are recorded for downstream use rather than enforced at dispatch time.
- This module ships no menus, views, wizards or cron. It is an engine that other EH HR modules call, and it depends on eh_hr_core and the standard mail module.
- Email and SMS delivery depend on your outgoing mail server and SMS provider being configured in Odoo. The engine queues and sends through standard Odoo plumbing and does not provide its own transport.
odoo hr notification engine, odoo notification preferences, per-user notification channels odoo, in-app and email notifications odoo, odoo sms notification, webhook notification odoo, multi-channel hr alerts, duplicate notification suppression, notification template odoo, odoo 19 community hr, self-hosted odoo hr platform, hr notification service, channel abstract notifications, odoo mail notification module, eh hr platform
Please log in to comment on this module