# -*- coding: utf-8 -*- # 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 HrEfficiencyReport(models.Model): _name = "hr.efficiency.report" _description = "HR Efficiency Analysis Report" _auto = False month_year = fields.Char('Month Year', readonly=True) employee_id = fields.Many2one('hr.employee', 'Employee', readonly=True) company_id = fields.Many2one('res.company', 'Company', readonly=True) department_id = fields.Many2one('hr.department', 'Department', readonly=True) job_title = fields.Char('Job Title', readonly=True) # Hours available_hours = fields.Float('Available Hours', readonly=True) planned_hours = fields.Float('Planned Hours', readonly=True) planned_billable_hours = fields.Float('Planned Billable Hours', readonly=True) planned_non_billable_hours = fields.Float('Planned Non-Billable Hours', readonly=True) actual_billable_hours = fields.Float('Actual Billable Hours', readonly=True) actual_non_billable_hours = fields.Float('Actual Non-Billable Hours', readonly=True) total_actual_hours = fields.Float('Total Actual Hours', readonly=True) # Efficiency rates efficiency_rate = fields.Float('Efficiency Rate (%)', readonly=True) billable_efficiency_rate = fields.Float('Billable Efficiency Rate (%)', readonly=True) # Computed fields for analysis utilization_rate = fields.Float('Utilization Rate (%)', readonly=True, help="Percentage of available hours that were planned") billable_utilization_rate = fields.Float('Billable Utilization Rate (%)', readonly=True, help="Percentage of available hours that were planned on billable projects") @property def _table_query(self): return """ SELECT e.id, e.month_year, e.employee_id, e.company_id, emp.department_id, emp.job_title, e.available_hours, e.planned_hours, e.planned_billable_hours, e.planned_non_billable_hours, e.actual_billable_hours, e.actual_non_billable_hours, e.total_actual_hours, e.efficiency_rate, e.billable_efficiency_rate, CASE WHEN e.available_hours > 0 THEN (e.planned_hours / e.available_hours) * 100 ELSE 0 END AS utilization_rate, CASE WHEN e.available_hours > 0 THEN (e.planned_billable_hours / e.available_hours) * 100 ELSE 0 END AS billable_utilization_rate FROM hr_efficiency e LEFT JOIN hr_employee emp ON e.employee_id = emp.id """ @api.model def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): """ Override read_group to handle computed fields """ res = super().read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) # Recalculate computed fields for grouped data for group in res: if '__domain' in group: records = self.search(group['__domain']) if records: # Calculate averages for efficiency rates if 'efficiency_rate' in fields: group['efficiency_rate'] = sum(r.efficiency_rate for r in records) / len(records) if 'billable_efficiency_rate' in fields: group['billable_efficiency_rate'] = sum(r.billable_efficiency_rate for r in records) / len(records) if 'utilization_rate' in fields: group['utilization_rate'] = sum(r.utilization_rate for r in records) / len(records) if 'billable_utilization_rate' in fields: group['billable_utilization_rate'] = sum(r.billable_utilization_rate for r in records) / len(records) return res