[IMP] hr_payroll: Improve benefits conflict performance
This commit changes the way benefits overlaps are detected. Currently, all benefits are read from db and the overlap detection is done in Python. After this commit, use a single sql query to only retreive overlapping benefits. The query takes advantage of the postgresql range type `tsrange` and the intersection operator Performance improvements: Tests with 500 employees and benefits generated for 15 months (~300000 records in the table) Conflict detection over 1 month (~20000 records) => ~5X speedup (from more than 2s to less than 0.5s)
This commit is contained in:
committed by
Yannick Tivisse
parent
29633756b0
commit
74246d288f
@@ -85,20 +85,39 @@ class HrWorkEnrty(models.Model):
|
||||
|
||||
@api.model
|
||||
def _mark_conflicting_work_entries(self, start, stop):
|
||||
conflict = False
|
||||
domain = [
|
||||
('date_start', '<', stop),
|
||||
('date_stop', '>', start),
|
||||
]
|
||||
benefs = self.search(domain)
|
||||
work_entries_by_employee = itertools.groupby(benefs, lambda b: b.employee_id)
|
||||
for employee, benefs in work_entries_by_employee:
|
||||
intervals = Intervals(intervals=((b.date_start, b.date_stop, b) for b in benefs))
|
||||
for interval in intervals:
|
||||
if len(interval[2]) > 1:
|
||||
interval[2].write({'display_warning': True})
|
||||
conflict = True
|
||||
return conflict
|
||||
"""
|
||||
Set `display_warning` to True for overlapping benefits
|
||||
between two dates.
|
||||
Return True if overlapping benefits were detected.
|
||||
"""
|
||||
# Use the postgresql range type `tsrange` which is a range of timestamp
|
||||
# It supports the intersection operator (&&) useful to detect overlap.
|
||||
# use '()' to exlude the lower and upper bounds of the range.
|
||||
# Filter on date_start and date_stop (both indexed) in the EXISTS clause to
|
||||
# limit the resulting set size and fasten the query.
|
||||
query = """
|
||||
SELECT b1.id
|
||||
FROM hr_work_entry b1
|
||||
WHERE
|
||||
b1.date_start <= %s
|
||||
AND b1.date_stop >= %s
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM hr_work_entry b2
|
||||
WHERE
|
||||
b2.date_start <= %s
|
||||
AND b2.date_stop >= %s
|
||||
AND tsrange(b1.date_start, b1.date_stop, '()') && tsrange(b2.date_start, b2.date_stop, '()')
|
||||
AND b1.id <> b2.id
|
||||
AND b1.employee_id = b2.employee_id
|
||||
);
|
||||
"""
|
||||
self.env.cr.execute(query, (stop, start, stop, start))
|
||||
conflicts = [res.get('id') for res in self.env.cr.dictfetchall()]
|
||||
self.browse(conflicts).write({
|
||||
'display_warning': True,
|
||||
})
|
||||
return bool(conflicts)
|
||||
|
||||
@api.multi
|
||||
def _compute_conflicts_leaves_to_approve(self):
|
||||
|
||||
Reference in New Issue
Block a user