Friday, July 24, 2026
1 change · 19.0
Enhancements to existing features
The leave attendance report has been optimized so large HR datasets can be processed in minutes instead of timing out or running for many hours. This improves reliability for HR reporting and time-off ledger views, especially for companies with many employees, calendars, and attendance records.
Original PR description
The view took 800+ minutes to run in pg after timeout from the UI, due to correlated LATERAL subqueries and join conditions. Data: | Name | Count | |---|---| | Employees | 4 699 | | hr_version | 3…
The view took 800+ minutes to run in pg after timeout from the UI, due to correlated LATERAL subqueries and join conditions. Data: | Name | Count | |---|---| | Employees | 4 699 | | hr_version | 3 015 (6 004 total, avg 1.28/employee) | | Calendars | 114 (86 active) | | Calendar slots | 857 | | Validated leaves | 9 560 | | Attendances in range | 175 011 | | Max calendars in one company | 42 | For 1 year of data, we have 4 699 x 365 days = ~1.7M base rows This PR includes: - rca CTE: the original query sorted 857 resource_calendar_attendance rows to deduplicate working schedule slots on every iteration of the innermost subquery. Since the result never changes, it is now computed once as a CTE and reused throughout the query - leave_working_days CTE: the original query recalculated how many working days each leave spans on every base row, resulting in O(employees x days x leaves x leave_days) complexity. This pre-computes it once per (leave, calendar). The fix narrows it to the employee's own calendar via hr_version - daily_leave_hours CTE: the original query looked up and summed leave hours individually for each of the 1.7M base rows. Since the result only depends on (employee, day, calendar, company) and not on other columns of the base row, it is now pre-aggregated once as a CTE. The main query then looks up the result with a join instead of recalculating it per row - blocked_days CTE: the original joined resource_calendar_leaves using date range check and OR condition on calendar_id. PostgreSQL matches on company_id, then filter all holiday rows per company against every base row. Expanding holiday date ranges into one row per day upfront to match on (company_id, day) instead. Splitting into blocked_days_cal and blocked_days_global removes the OR condition, so both joins use only equality conditions and are fully hashable Benchmark: | |Before|After| |----|----|----| |hr_leave_attendance_report view | Timeout (pg stat: ~8xx mins ++) | ~10mins | |Timeoff ledger query | Timeout | ~10 mins | Plan: [Before](https://explain.dalibo.com/plan/936721375b2d7c19) | [After](https://explain.dalibo.com/plan/794c4f49eghg8c70) Related ticket: opw-5885500 opw-6254001 --- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr