| 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 | 7107 |
| Technical Name |
eh_hr_leave_pro |
| License | LGPL-3 |
| Website | https://erpheritage.com.au |
| Versions | 16.0 17.0 18.0 19.0 |
Leave Pro for Odoo HR
Ledger-based leave balances, policy-driven accrual, and a manager-then-officer approval chain that cannot be self-approved.
Why this module
Leave Pro for Odoo HR
The ledger is the balance
Balances are never overwritten in place. Every grant, consume, carryover, expiry, adjustment and reversal is an append-only row, summed by SQL on read. Rows are immutable except by a retention purge, so a balance is always a replayable, audit-grade history rather than a single mutable number.
Approval that cannot be gamed
Submitting opens a serial chain (line manager, then HR officer for long leave) declared in data, not code. The engine captures the real submitter before its sudo step, so a person who is the subject of a request, including a delegated submitter, is blocked from approving it. Steps carry escalation hours.
Accrual without hard-coded constants
Monthly accrual, year-boundary carryover capping and grant-age expiry all read their numbers from policy documents resolved per leave type. A mid-month hire earns a prorated first accrual, caps stop over-granting, and every cron grant carries a deterministic key so a re-run after a crash is safe.
Day in the life
From request to balance, one honest path
An employee submits annual leave from the desk or the mobile API. Duration counts only working days, skipping weekends and the company public-holiday calendar, and an eligibility check flags insufficient balance, overlap or a missing certificate before submission. The request moves draft to submitted and opens the manager-then-officer chain. On approval a single consume row hits the ledger, the request explodes into per-day rows, and an attendance bridge marks each day as leave. If the leave is later cancelled, a reversal row restores the balance and the projected time-off row is retracted. Nothing is edited in place; the trail stays 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.
Every accrual, grant, carryover and expiry row carries a deterministic correlation_id and the ledger enforces a UNIQUE constraint on it, so a cron that re-runs after a crash dedupes instead of double-granting.
Duration counts only the employee working week from their resource calendar and excludes the company public-holiday dates, so a leave that spans a weekend or a public holiday does not over-consume balance.
The approval engine records the true submitter before its sudo step and refuses any decision by a user who is a subject of the request, including a delegated submitter approving on the original employee's behalf.
Ledger rows reject writes from anyone but the platform admin and reject deletes outside the retention purge. Corrections are made by posting a reversal row, never by mutating history.
A mid-month hire earns only the worked fraction of the first month's accrual; someone hired after the accrual date earns zero for that period, and an existing employee earns the full amount.
Accrual reads the current ledger balance and grants only up to the policy cap, so a balance already at the cap accrues nothing and a partial grant fills exactly the remaining headroom.
Once a request is live (submitted onward) a database-checked constraint plus an overlap service refuse a second active request covering the same day, while drafts stay freely editable.
Cancelling an approved leave posts a reversal row, drops the per-day rows, recomputes the affected attendance window and retracts the projected core time-off record through a state every supported Odoo version permits a manager to delete.
The mobile submit endpoint requires an Idempotency-Key (header or body); re-sending the same key returns the existing request marked deduped rather than creating a duplicate.
Record rules scope requests, allocations and the ledger to self, team and company by group, leave-type codes are unique per company, and public holidays can be company-scoped or company-agnostic for a multi-country group.
What is inside
Built to do the job, end to end.
- Leave types and policy seams. Catalogue leave types (annual, sick, unpaid, parental, compassionate and more) with switches for requires-allocation, half-day, hourly, certificate-from-days and counts-against-attendance. Accrual, carryover, expiry and approval-chain codes are fields, so behaviour is data-driven per type.
- Balance ledger and services. An append-only eh.hr.leave.balance.ledger with kinds grant, consume, carryover, expiry, adjustment and reversal, read by a SQL-aggregating balance service and written only through it. A dedicated index on employee, type and date keeps balance reads fast.
- Requests, allocations and approvals. Leave requests and allocations sit on the platform workflow, approval, audit and company-aware mixins. State machines and the serial approval chains are declared in XML data; the request model owns only dates, duration, reason and evidence attachments.
- Accrual, carryover and expiry crons. Three daily crons drive monthly accrual on the 1st, carryover capping at the year boundary and grant-age expiry, each reading policy documents per leave type and emitting idempotent ledger rows. Standard policies (1.75 days per month, cap 30, carryover 5, expiry 12 months) ship as data you can edit.
- Attendance and time-off projection. Approved leave expands into per-day rows and bridges to the attendance day projection so days show as leave, while a projection service materialises a validated standard hr.leave row keyed to the request so the native Time Off calendar and reporting see the data. Cancellation retracts both.
- Mobile API and calendar UI. JSON endpoints under /api/hr/v1/leave expose leave types, balance matrix, pre-flight eligibility and idempotency-keyed submit for an employee app, alongside an OWL leave calendar and request drawer in the backend.
Honest about the edges
What this does not do, so nothing surprises you.
- Part of the EH HR Platform. It depends on eh_hr_core and the workflow, approval, policy and notification engines plus eh_hr_attendance_pro, so it is not a standalone drop-in onto stock Odoo HR alone.
- The shipped accrual, carryover and expiry policies (1.75 days per month, cap 30, carryover 5, expiry 12 months) are sensible defaults seeded as data. Country-specific statutory entitlements are not encoded; you adjust the policy documents to match your jurisdiction.
- Public holidays are entered or imported into the included holiday model. The module ships Australian state and territory region codes but does not bundle a populated holiday calendar for every country.
- Accrual and carryover crons act at period boundaries (accrual on the 1st of the month, carryover on 1 January). They are not arbitrary anniversary-date schedulers per employee.
- Approved leave is projected into the standard hr.leave time-off record for calendar and reporting. eh.hr.leave.request remains the source of record, so manage approvals here rather than in the native Time Off approval flow.
Odoo 19 leave management, Odoo Community HR leave, employee time off Odoo, leave accrual rules, leave balance ledger, annual leave tracking, sick leave management, carryover and expiry automation, half day leave Odoo, hourly leave Odoo, leave approval workflow, multi-company leave Odoo, public holiday leave exclusion, HR leave mobile API, leave allocation accrual cron
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