99 lines
4.6 KiB
Python
99 lines
4.6 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.tools.sql import drop_view_if_exists, SQL
|
|
|
|
|
|
class PlanningAttendanceAnalysisReport(models.Model):
|
|
_name = 'planning.attendance.analysis.report'
|
|
|
|
_description = "Planning / Attendance Analysis"
|
|
_auto = False
|
|
_rec_name = "entry_date"
|
|
_order = "entry_date desc"
|
|
|
|
entry_date = fields.Date(readonly=True)
|
|
employee_id = fields.Many2one("hr.employee", string="Employee", readonly=True)
|
|
department_id = fields.Many2one("hr.department", string="Department", readonly=True)
|
|
company_id = fields.Many2one("res.company", string="Company", readonly=True)
|
|
effective_hours = fields.Float("Attendance Time", readonly=True)
|
|
planned_hours = fields.Float("Planned Time", readonly=True)
|
|
time_difference = fields.Float("Time Difference", readonly=True)
|
|
effective_costs = fields.Float("Attendance Cost", readonly=True)
|
|
planned_costs = fields.Float("Planned Cost", readonly=True)
|
|
cost_difference = fields.Float("Cost Difference", readonly=True)
|
|
|
|
def init(self):
|
|
query = """
|
|
SELECT MAX(id) AS id,
|
|
t.entry_date,
|
|
t.employee_id,
|
|
t.department_id,
|
|
t.company_id,
|
|
COALESCE(SUM(t.worked_hours), 0) AS effective_hours,
|
|
COALESCE(SUM(t.allocated_hours), 0) AS planned_hours,
|
|
COALESCE(SUM(t.worked_hours), 0) - COALESCE(SUM(t.allocated_hours), 0) AS time_difference,
|
|
NULLIF(COALESCE(SUM(t.worked_hours), 0) * t.hourly_cost, 0) AS effective_costs,
|
|
NULLIF(COALESCE(SUM(t.allocated_hours), 0) * t.hourly_cost, 0) AS planned_costs,
|
|
NULLIF((COALESCE(SUM(t.worked_hours), 0) - COALESCE(sum(t.allocated_hours), 0)) * t.hourly_cost, 0) AS cost_difference
|
|
FROM (
|
|
SELECT -A.id AS id,
|
|
A.check_in::date AS entry_date,
|
|
A.employee_id AS employee_id,
|
|
V.department_id AS department_id,
|
|
E.company_id AS company_id,
|
|
E.hourly_cost AS hourly_cost,
|
|
A.worked_hours AS worked_hours,
|
|
0.0 AS allocated_hours
|
|
FROM hr_attendance A
|
|
LEFT JOIN hr_employee E ON E.id = A.employee_id
|
|
LEFT JOIN (
|
|
SELECT DISTINCT ON (employee_id) *
|
|
FROM hr_version
|
|
ORDER BY employee_id, date_version DESC
|
|
) V ON V.employee_id = E.id
|
|
UNION ALL
|
|
SELECT S.id AS id,
|
|
d::date AS entry_date,
|
|
S.employee_id AS employee_id,
|
|
V.department_id AS department_id,
|
|
S.company_id AS company_id,
|
|
E.hourly_cost AS hourly_cost,
|
|
0.0 AS worked_hours,
|
|
(S.allocated_hours / NULLIF((S.end_datetime::date - S.start_datetime::date + 1), 0)) AS allocated_hours
|
|
FROM generate_series(
|
|
(SELECT min(start_datetime) FROM planning_slot)::date,
|
|
CURRENT_DATE,
|
|
'1 day'::interval
|
|
) d
|
|
LEFT JOIN planning_slot S
|
|
ON d::date >= S.start_datetime::date
|
|
AND d::date <= S.end_datetime::date
|
|
LEFT JOIN hr_employee E ON E.id = S.employee_id
|
|
LEFT JOIN (
|
|
SELECT DISTINCT ON (employee_id) *
|
|
FROM hr_version
|
|
ORDER BY employee_id, date_version DESC
|
|
) V ON V.employee_id = E.id
|
|
) AS t
|
|
WHERE t.employee_id IS NOT NULL
|
|
AND entry_date <= CURRENT_DATE
|
|
GROUP BY t.entry_date, t.employee_id, t.department_id, t.company_id, t.hourly_cost
|
|
ORDER BY t.entry_date, t.employee_id
|
|
"""
|
|
|
|
drop_view_if_exists(self.env.cr, self._table)
|
|
self.env.cr.execute(
|
|
SQL(
|
|
"""CREATE OR REPLACE VIEW %s AS (%s)""",
|
|
SQL.identifier(self._table),
|
|
SQL(query),
|
|
)
|
|
)
|
|
|
|
@api.model
|
|
def formatted_read_group(self, domain, groupby=(), aggregates=(), having=(), offset=0, limit=None, order=None) -> list[dict]:
|
|
if not order and groupby:
|
|
order = ', '.join(f"{spec} DESC" if spec.startswith('date:') else spec for spec in groupby)
|
|
return super().formatted_read_group(domain, groupby, aggregates, having=having, offset=offset, limit=limit, order=order)
|