| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Employees (hr)
• Time Off (hr_holidays) • Discuss (mail) • Attendances (hr_attendance) • Calendar (calendar) |
| Community Apps Dependencies | Show |
| Lines of code | 7091 |
| Technical Name |
eh_hr_leave_pro |
| License | LGPL-3 |
| Website | https://erpheritage.com.au |
| Versions | 16.0 17.0 18.0 19.0 |
EH HR Leave Pro
An event-sourced leave engine where the balance is a ledger, approvals are data, and every grant is idempotent.
Why this module
EH HR Leave Pro
The ledger is the source of truth
Every grant, consume, carryover, expiry, adjustment and reversal is a row in an append-only ledger. The balance is their sum, never an edited counter, so the number always reconciles to events you can read back. Rows cannot be edited except by an admin and cannot be deleted except by the retention service.
Crons you can run twice without harm
Accrual, carryover and expiry are keyed by a deterministic correlation id and deduped by a unique database constraint. A re-run after a crash, or a double-fire of the daily cron, cannot double-grant. Accrual also respects a per-type cap and prorates a mid-month hire's first period.
It only bills the days actually worked
Duration counts working days from the employee resource calendar and subtracts the company public-holiday calendar, so a request across a weekend or a holiday does not over-consume balance. Half-day requests bill 0.5 of a single working day; hourly types bill the hour window.
Day in the life
From request to balance, with nothing fudged
An employee opens the request drawer, picks annual leave across a week that contains a public holiday, and the duration shows only the working days. Eligibility flags a short-notice warning and blocks the submit if the balance is short or the dates overlap an existing request. On submit the request enters the serial chain: the line manager has 48 hours before it escalates, then an officer step for long leave. The manager cannot approve a request they themselves raised on someone's behalf. On approval the engine writes a single consume row to the ledger, expands the request into per-day rows, sets the attendance day status to leave, and projects a validated row into the native Time Off calendar. Cancel after approval and a reversal row restores the balance and retracts the projection.
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.
Accrual, carryover and expiry write ledger rows under a deterministic correlation id deduped by a unique constraint, so a re-run after a crash or a double cron fire never double-grants.
The real submitter is captured before the approval engine escalates to superuser and is recorded as submitted_by, so a delegated submitter is barred from approving their own request.
Balance ledger rows reject write() unless the user is an HR admin and reject unlink() unless the retention purge context is set; corrections are made by posting a reversal row, not by editing history.
Duration excludes non-working weekdays from the resource calendar and the company public-holiday dates, so leave spanning a weekend or holiday does not over-consume balance.
A live request (submitted onward) cannot overlap another active request for the same employee; the check is enforced both as a model constraint and in the pre-submit eligibility service while drafts stay freely editable.
Accrual stops at the policy cap by computing current balance headroom, and a mid-month hire earns only the worked fraction of the first period.
Leave types can require an evidence attachment from a configured number of consecutive days; submit is refused until a certificate is attached.
Leave types, holidays, requests and ledger rows are company-scoped via the company-aware mixin and record rules, so a multi-entity group keeps separate calendars and balances.
Approved requests materialise a validated row in core Time Off keyed to the request; cancellation retracts it, and rows created directly in core Time Off are never touched.
What is inside
Built to do the job, end to end.
- Leave types and policies. Catalogue records for annual, sick, unpaid, parental and compassionate leave seeded out of the box, each carrying flags for allocation, half-day, hourly and certificate thresholds, plus policy codes for accrual, carryover and expiry so behaviour is data-driven rather than hard-coded.
- The balance ledger. An append-only event log of grant, consume, carryover, expiry, adjustment and reversal rows. Current balance is read by summing ledger deltas up to a date in a single aggregate query, with a composite index on employee, type and time.
- Requests and allocations. Requests own dates, duration, half-day or hourly granularity, reason and evidence attachments. Allocations grant entitlement manually or via accrual. Both run on declarative workflow and approval engines, not per-record Python state machines.
- Accrual, carryover and expiry crons. Three daily crons read per-type policy documents: accrual grants on the period boundary with cap and proration, carryover forfeits balance above the limit at the year boundary, and expiry reverses the unused portion of allocations past their grant window.
- Approval chains and escalation. A serial line-manager then HR-officer chain declared as data, with per-step escalation hours (48 then 72) and a per-type chain override so, for example, sick leave can skip the manager step. Self-approval is forbidden at the engine.
- Calendar and attendance integration. Approved leave expands into per-day rows that set the attendance day status to leave through a bridge service, and projects a validated row into the native Odoo Time Off calendar so core reporting stays accurate. A mobile JSON API exposes types, balance, eligibility and idempotent submit.
Honest about the edges
What this does not do, so nothing surprises you.
- Built for Odoo 16 Community and depends on the EH HR Platform engines (eh_hr_core and the workflow, approval, policy and notification engines) plus eh_hr_attendance_pro; it is not a standalone replacement for the core Time Off app.
- Seeded accrual, carryover and expiry policies are sensible defaults (1.75 days per month capped at 30, 5 days carryover, 12 month expiry) and are meant to be tuned per organisation; they are not statutory entitlement rules for any jurisdiction.
- Public-holiday seeding ships region codes for Australian states and territories as the holiday model, but holiday dates themselves are entered by you; no national holiday list is pre-populated.
- Accrual periods cover month, week and year boundaries; arbitrary anniversary or pay-cycle accrual schedules are not modelled.
- Proration applies to a monthly mid-period hire only; other proration rules are not computed.
- The mobile surface is a JSON API for types, balance, eligibility and submit, not a packaged mobile app.
Odoo 16 leave management, Odoo leave app, annual leave Odoo, sick leave tracking, leave accrual Odoo, leave balance ledger, time off management Odoo 16, leave approval workflow, carryover and expiry, half day leave, hourly leave Odoo, public holiday leave, multi company leave, employee leave request, leave allocation, absence management
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