| 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 |
HR Leave Pro
Event-sourced leave balances, data-driven approval chains, and idempotent accrual that survives a crash and a re-run.
Why this module
HR Leave Pro
The ledger is the balance
Every grant, consume, carryover, expiry, and reversal is an immutable ledger row. The current balance is the SQL sum of those rows, so balances can be rebuilt and replayed for audit. Non-admin writes are refused; corrections happen as reversal rows, never edits.
Crons you can run twice
Accrual, carryover, and expiry each write with a deterministic correlation_id protected by a UNIQUE constraint. A cron that crashes mid-run and re-fires does not double-grant. The mobile submit endpoint dedupes the same way via an Idempotency-Key.
Only working days count
Duration counts the employee's working weekdays from their resource calendar and skips company public holidays, so a Monday-to-Friday leave over a holiday is not over-charged. Half days are supported on a single day, AM or PM.
Day in the life
From request to balance, the whole path
An employee submits annual leave from their portal or the mobile API. Eligibility runs first: it blocks on insufficient balance, an overlapping request, or a missing certificate, and warns on short notice. On submit the request enters a data-driven workflow and an approval chain routes it to the line manager, then to an HR officer for longer leave, with escalation hours set per step. The person who submitted it cannot approve it. On approval a consume row hits the balance ledger, the request expands into per-day rows that flip attendance to leave, and a native Time Off row is projected so the core calendar and reporting see it. Cancel after approval and a reversal row restores the balance and retracts the projection. Meanwhile the daily accrual cron grants the monthly entitlement, prorated for anyone hired mid-month, capped at the policy ceiling, and the year-boundary sweep forfeits anything above the carryover limit.
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, grant, carryover, and expiry rows carry deterministic correlation_ids backed by a UNIQUE constraint, so a cron that dies after partial work and re-runs dedupes instead of double-granting.
The mobile submit endpoint requires an Idempotency-Key (header or body); a repeat call returns the existing request marked deduped rather than creating a second one.
Duration counts only working weekdays from the employee's resource calendar and excludes company public holidays, so a span covering a weekend or public holiday does not over-consume balance. A weekend-only request is zero days.
The real submitter is captured before the engine elevates to superuser, so a delegated submitter, even an officer, is blocked from approving a request that concerns them.
The balance ledger refuses non-admin writes and refuses deletes outside the retention purge; the only way to undo an entry is a compensating reversal row.
Leave types, public holidays, and the company-aware mixin scope records per company, and record rules grant self, team, and company-wide visibility to employee, manager, and officer respectively.
Projection only manages rows it created (eh_managed); rows entered directly in core Time Off are never touched, and edits to managed rows write a tamper-evident audit event.
The first accrual is prorated to the worked fraction of the hire month, so a 16th-of-a-30-day-month hire earns half the period's grant rather than a full one.
Accrual checks remaining headroom against the policy cap and grants only up to it, and the year-boundary carryover sweep forfeits the excess above the carryover limit with a ledger row.
What is inside
Built to do the job, end to end.
- Append-only balance ledger. eh.hr.leave.balance.ledger records grant, consume, carryover, expiry, adjustment, and reversal events. Writes are blocked for non-admins, deletes are blocked outside the retention purge, and a UNIQUE correlation_id dedupes re-runs. Balances are summed in SQL with a composite index on employee, type, and date.
- Data-driven workflow and approval. The state machine (draft, submitted, approved, refused, cancelled) lives in XML, not Python. Approval chains are serial steps with dynamic line-manager resolution, officer groups, and per-step escalation hours, and any leave type can override its chain code.
- Accrual, carryover, and expiry services. Three daily crons read policy documents through the policy engine: monthly or weekly or yearly accrual with proration and a cap, a year-boundary carryover sweep, and an expiry sweep based on months from grant. Each is period-guarded so off-day runs are no-ops.
- Working-day duration engine. Duration honours the employee's resource calendar working weekdays and the company public holiday calendar, supports AM or PM half days on a single day, and exposes hourly granularity for leave types that allow it.
- Native Time Off projection. Approved requests materialise an idempotent hr.leave row keyed to the request so the core calendar and reporting see native data, retracted cleanly on cancellation, with manually created Time Off rows left untouched.
- Public holiday calendar and mobile API. A single public holiday model, company-scoped with AU state and territory regions, feeds both leave and award interpretation. A JSON API exposes leave types, the balance matrix, eligibility pre-flight, and idempotent submit for mobile clients.
Honest about the edges
What this does not do, so nothing surprises you.
- Built on the EH HR Platform: it requires eh_hr_core, the workflow, approval, policy, and notification engines, and eh_hr_attendance_pro. It is not a standalone time-off app.
- Concurrency safety comes from deterministic correlation_ids and database UNIQUE constraints plus the cron runner's own transaction, not from advisory locks or explicit savepoints in this module.
- Seeded standard policies are sensible defaults (1.75 days per month, cap 30, carryover 5, expiry 12 months from grant); they are data records you are expected to edit per your own rules.
- Accrual periods are month, week, or year. Other cadences would need a policy and service extension.
- Public holiday regions are Australian states and territories; other countries use the national region or company-scoped entries.
- Targets Odoo 17 Community. The codebase carries cross-version shims, but this listing covers the Odoo 17 build.
Odoo 17 leave management, Odoo employee time off, leave accrual engine Odoo, leave approval workflow, annual leave sick leave tracking, half day leave Odoo, leave balance ledger, public holiday calendar Odoo, carryover and expiry rules, multi-company HR leave, leave allocation Odoo 17, mobile leave API, self service leave request, working day leave calculation
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