[FIX] hr_holidays: calculate the number of days left for the report

Steps to reproduce:
-------------------
- create a time off type;
- create two 5 days allocations for an employee;
- validate them;
- create a leave of 8 days for the same employee;
- be in debug mode;
- go to employee form view;
- in action menu, click on "Time off Analysis by Employee and Time Off Type".

Issue:
------
The number of days remaining ("left") for the employee and leave type is -6 instead of 2.

Cause:
------
The query that generates the report is in two parts

```
Find the number of remaining leaves by subtracting leave taken from allocations
(model: hr_leave_allocation and hr_leave) ==> will generate the "left" column

UNION ALL

Find the number of leaves already taken
(model: hr_leave) ==> will generate the "taken" and "planned" columns
```

In the part that generates the "left" column, we relate leaves to allocations.
As there is no "unique" link between allocations and leaves,
we can only link them via employee and type.

We are therefore obliged to use aggregation methods.

The measure that returns negative numbers is:
```sql
CASE
    WHEN request.number_of_days > 0 THEN allocation.number_of_days - request.number_of_days
    ELSE allocation.number_of_days
END as number_of_days
```

It should be read as follows:
If there are validated or planned leaves, the number of days remaining
for an allocation is the number of days of this allocation minus
the SUM of the days of leaves (validated or planned) for this allocations
(same employee and type).

For example:
2 allocations of 5 days and a leave of 8 days,
we will have (5 - 8) + (5 - 8) = (-3) + (-3) = -6 days remaining ==> this is wrong
In this example, we want: (5 + 5) - 8 = 2 days remaining

Solution:
---------
As it is not possible to link a leave to a specific allocation,
we have to make difference between sums like this:

days left = [SUM(days of allocations) - SUM(days of leaves)]_(for one employee AND one type)

The frontend will sum these days left.
This difference must therefore be calculated only once.
By default, we use allocation with the smallest id.

The result is:
days left frontend = [day left_(for min id) + 0 * (nbr allocation - 1)]_(for one employee AND one type)

opw-3366530

closes odoo/odoo#130781

X-original-commit: a273148cf931f220cc4252c638ae14890f889c9e
Signed-off-by: Sofie Gvaladze (sgv) <sgv@odoo.com>
Signed-off-by: Thomas Lefebvre (thle) <thle@odoo.com>
This commit is contained in:
Thomas Lefebvre (thle)
2023-08-04 06:56:33 +02:00
parent 1453b42fa4
commit ef47fd0d7d
@@ -51,9 +51,10 @@ class LeaveReport(models.Model):
FROM (SELECT
allocation.employee_id as employee_id,
employee.active as active_employee,
CASE
WHEN request.number_of_days > 0 THEN allocation.number_of_days - request.number_of_days
ELSE allocation.number_of_days
CASE
WHEN allocation.id = min_allocation_id.min_id
THEN aggregate_allocation.number_of_days - aggregate_leave.number_of_days
ELSE 0
END as number_of_days,
allocation.department_id as department_id,
allocation.holiday_status_id as leave_type,
@@ -64,10 +65,25 @@ class LeaveReport(models.Model):
allocation.employee_company_id as company_id
FROM hr_leave_allocation as allocation
INNER JOIN hr_employee as employee ON (allocation.employee_id = employee.id)
LEFT JOIN
(SELECT holiday_status_id, employee_id, sum(number_of_days) as number_of_days
FROM hr_leave GROUP BY holiday_status_id, employee_id) request
on (allocation.employee_id=request.employee_id and allocation.holiday_status_id = request.holiday_status_id)
/* Obtain the minimum id for a given employee and type of leave */
LEFT JOIN
(SELECT employee_id, holiday_status_id, min(id) as min_id
FROM hr_leave_allocation GROUP BY employee_id, holiday_status_id) min_allocation_id
on (allocation.employee_id=min_allocation_id.employee_id and allocation.holiday_status_id=min_allocation_id.holiday_status_id)
/* Obtain the sum of allocations */
LEFT JOIN
(SELECT employee_id, holiday_status_id, sum(number_of_days) as number_of_days
FROM hr_leave_allocation GROUP BY employee_id, holiday_status_id) aggregate_allocation
on (allocation.employee_id=aggregate_allocation.employee_id and allocation.holiday_status_id=aggregate_allocation.holiday_status_id)
/* Obtain the sum of requested leaves (validated and confirmed) */
LEFT JOIN
(SELECT employee_id, holiday_status_id, sum(number_of_days) as number_of_days
FROM hr_leave GROUP BY employee_id, holiday_status_id) aggregate_leave
on (allocation.employee_id=aggregate_leave.employee_id and allocation.holiday_status_id = aggregate_leave.holiday_status_id)
UNION ALL SELECT
request.employee_id as employee_id,
employee.active as active_employee,