Просмотр исходного кода

feat: Update HR Efficiency with dynamic indicator synchronization system

- Add sync_indicators_from_xml() method for automatic field synchronization
- Enhance _post_init_hook with improved synchronization capabilities
- Add manual sync button in indicator views for debugging
- Implement automatic field creation and updates from XML definitions
- Add comprehensive logging and error handling for sync operations
- Ensure dynamic fields stay synchronized with indicator definitions
- Improve field recalculation and view updates
- Add action_sync_from_xml() for manual synchronization triggers
- Enhanced system maintains consistency between XML and database
root 5 месяцев назад
Родитель
Сommit
ff028547b6

+ 26 - 8
hr_efficiency/README.md

@@ -101,13 +101,12 @@ El módulo se puede acceder desde:
 - `actual_billable_hours`: Horas reales en proyectos facturables
 - `actual_non_billable_hours`: Horas reales en proyectos no facturables
 - `expected_hours_to_date`: Horas esperadas hasta la fecha actual
-- `wage`: Salario bruto del empleado al momento del cálculo
+- `wage_overhead`: Costo por hora multiplicado por las horas disponibles del mes, incluyendo overhead de la compañía (campo principal para análisis)
+- `wage`: Salario bruto del empleado al momento del cálculo (campo de compatibilidad)
 - `utilization_rate`: Tasa de utilización del empleado al momento del cálculo
-- `overhead`: Overhead de la compañía al momento del cálculo
-- `actual_billable_hours`: Horas registradas en proyectos facturables
-- `actual_non_billable_hours`: Horas registradas en proyectos no facturables
-- `expected_hours_to_date`: Horas esperadas hasta la fecha actual
-- `wage`: Salario bruto del empleado según su contrato al momento del cálculo
+- `precio_por_hora`: Precio que cobramos al cliente por hora (calculado desde wage_overhead + rentabilidad esperada)
+
+**Nota**: El campo `overhead` ya no está disponible en las fórmulas porque está incluido en `wage_overhead`.
 
 ### Visualización de Datos
 
@@ -184,10 +183,11 @@ Modelo principal que almacena los registros de eficiencia mensual por empleado.
 - `planned_non_billable_hours`: Horas planeadas en proyectos no facturables
 - `actual_billable_hours`: Horas reales en proyectos facturables
 - `actual_non_billable_hours`: Horas reales en proyectos no facturables
-- `wage`: Salario bruto del empleado según su contrato al momento del cálculo
+- `wage_overhead`: Costo por hora multiplicado por las horas disponibles del mes, incluyendo overhead de la compañía (campo principal para análisis)
+- `wage`: Salario bruto del empleado según su contrato al momento del cálculo (campo de compatibilidad)
 - `currency_id`: Moneda del salario
 - `utilization_rate`: Tasa de utilización del empleado al momento del cálculo
-- `overhead`: Overhead de la compañía al momento del cálculo
+- `overhead`: Overhead de la compañía al momento del cálculo (ya incluido en wage_overhead)
 - `overall_efficiency`: Porcentaje de eficiencia general basado en indicadores configurados
 
 ### hr.efficiency.calculation.wizard
@@ -206,6 +206,24 @@ Modelo de reporte para análisis y visualización de datos.
 
 ## Cálculos
 
+### Sistema de Cálculo de Costos (Actualizado)
+El módulo ahora utiliza un sistema simplificado para el cálculo de costos:
+
+#### 1. **Wage Overhead** (Campo Principal)
+- **Fórmula**: `hourly_cost * available_hours * (1 + overhead/100)`
+- **Incluye**: Costo por hora + Overhead de la compañía
+- **Uso**: Campo principal para análisis de rentabilidad
+
+#### 2. **Precio por Hora**
+- **Fórmula**: `(wage_overhead / available_hours) * (1 + expected_profitability/100)`
+- **Incluye**: Costo por hora + Overhead + Rentabilidad esperada
+- **Uso**: Precio de venta al cliente
+
+#### 3. **Ventajas del Nuevo Sistema**
+- **Simplificación**: No es necesario aplicar overhead por separado en fórmulas
+- **Consistencia**: `wage_overhead` incluye automáticamente todos los costos base
+- **Mantenibilidad**: Cambios en overhead se reflejan automáticamente en todos los cálculos
+
 ### Horas Disponibles
 ```python
 # Horas totales del calendario del empleado

+ 68 - 68
hr_efficiency/data/hr_efficiency_indicators.xml

@@ -3,90 +3,79 @@
     <data noupdate="1">
 
         <!-- ================================================== -->
-        <!-- Indicadores de Capacidad (Capacity KPIs)           -->
+        <!-- 1. CAPACIDAD - Lo que el empleado puede hacer      -->
         <!-- ================================================== -->
 
-        <record id="indicator_occupancy_rate" model="hr.efficiency.indicator">
-            <field name="name">Occupancy Rate</field>
+        <record id="indicator_planning_coverage" model="hr.efficiency.indicator">
+            <field name="name">Planning Coverage</field>
             <field name="sequence">10</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
-            <field name="formula">(total_actual_hours / available_hours) if available_hours > 0 else 0</field>
+            <field name="formula">(planned_hours / available_hours) if available_hours > 0 else 0</field>
             <field name="target_percentage">95.0</field>
             <field name="weight">0.0</field>
-            <field name="description">Tasa de Ocupación: Mide qué tan "ocupado" está el equipo en general, considerando horas facturables y no facturables. (Total Horas Registradas / Horas Disponibles)</field>
+            <field name="description">Cobertura de Planificación: Mide qué porcentaje del tiempo disponible ha sido planificado, sin importar si es facturable o no. (Total Horas Planeadas / Horas Disponibles)</field>
             <field name="color_threshold_green">85.0</field>
             <field name="color_threshold_yellow">75.0</field>
-            <field name="color_threshold_red">60.0</field>
+            <field name="color_threshold_red">65.0</field>
         </record>
 
-        <record id="indicator_utilization_rate" model="hr.efficiency.indicator">
-            <field name="name">Utilization Rate</field>
+        <record id="indicator_planned_utilization" model="hr.efficiency.indicator">
+            <field name="name">Planned Utilization</field>
             <field name="sequence">20</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
-            <field name="formula">(actual_billable_hours / available_hours) if available_hours > 0 else 0</field>
+            <field name="formula">(planned_billable_hours / available_hours) if available_hours > 0 else 0</field>
             <field name="target_percentage">80.0</field>
             <field name="weight">0.0</field>
-            <field name="description">Tasa de Utilización: Mide qué tan "productivo" (generando ingresos) está el equipo. (Horas Facturables Registradas / Horas Disponibles)</field>
+            <field name="description">Utilización Planeada: ¿Cuál era el objetivo de utilización para el equipo? Permite comparar meta vs. realidad. (Horas Facturables Planeadas / Horas Disponibles)</field>
             <field name="color_threshold_green">85.0</field>
             <field name="color_threshold_yellow">75.0</field>
             <field name="color_threshold_red">60.0</field>
         </record>
 
         <!-- ================================================== -->
-        <!-- Indicadores de Eficiencia (Efficiency KPIs)        -->
+        <!-- 2. PLANIFICACIÓN - Lo que se planificó hacer       -->
         <!-- ================================================== -->
 
-        <record id="indicator_billability_rate" model="hr.efficiency.indicator">
-            <field name="name">Billability Rate</field>
+        <record id="indicator_break_even_hours" model="hr.efficiency.indicator">
+            <field name="name">Break-Even Hours Needed</field>
             <field name="sequence">30</field>
             <field name="active">True</field>
-            <field name="indicator_type">percentage</field>
-            <field name="formula">(actual_billable_hours / total_actual_hours) if total_actual_hours > 0 else 0</field>
-            <field name="target_percentage">85.0</field>
+            <field name="indicator_type">hours</field>
+            <!-- FORMULA SIMPLIFICADA: wage_overhead ya incluye el costo por hora multiplicado por las horas disponibles. -->
+            <field name="formula">(wage_overhead * (utilization_rate / 100)) / precio_por_hora if precio_por_hora > 0 else 0</field>
+            <field name="target_percentage">0.0</field>
             <field name="weight">0.0</field>
-            <field name="description">Tasa de Facturabilidad: De todo el tiempo trabajado, ¿qué porcentaje fue facturable? (Horas Facturables Registradas / Total Horas Registradas)</field>
+            <field name="description">Horas de Punto de Equilibrio: ¿Cuántas horas facturables se necesitan para cubrir el costo productivo (costo ponderado por utilización)? El resultado es un número de horas.</field>
+            <!-- Note: Thresholds for 'hours' type are less direct than for percentages. -->
             <field name="color_threshold_green">85.0</field>
             <field name="color_threshold_yellow">75.0</field>
             <field name="color_threshold_red">65.0</field>
         </record>
 
-        <!-- ================================================== -->
-        <!-- Indicadores de Planificación (Planning KPIs)       -->
-        <!-- ================================================== -->
-
-        <record id="indicator_planned_utilization" model="hr.efficiency.indicator">
-            <field name="name">Planned Utilization</field>
+        <record id="indicator_planned_profitability_coverage" model="hr.efficiency.indicator">
+            <field name="name">Planned Profitability Coverage</field>
             <field name="sequence">40</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
-            <field name="formula">(planned_billable_hours / available_hours) if available_hours > 0 else 0</field>
-            <field name="target_percentage">80.0</field>
+            <!-- FORMULA SIMPLIFICADA: wage_overhead ya incluye el costo por hora multiplicado por las horas disponibles. -->
+            <field name="formula">(planned_billable_hours / ((wage_overhead * (utilization_rate / 100)) / precio_por_hora)) if wage_overhead > 0 and precio_por_hora > 0 else 0</field>
+            <field name="target_percentage">100.0</field>
             <field name="weight">0.0</field>
-            <field name="description">Utilización Planeada: ¿Cuál era el objetivo de utilización para el equipo? Permite comparar meta vs. realidad. (Horas Facturables Planeadas / Horas Disponibles)</field>
-            <field name="color_threshold_green">85.0</field>
-            <field name="color_threshold_yellow">75.0</field>
-            <field name="color_threshold_red">60.0</field>
+            <field name="description">Cobertura de Rentabilidad Planeada: Mide si las horas facturables planeadas son suficientes para alcanzar el punto de equilibrio. Más de 100% indica un plan rentable. (Horas Facturables Planeadas / Horas de Punto de Equilibrio)</field>
+            <field name="color_threshold_green">100.0</field>
+            <field name="color_threshold_yellow">85.0</field>
+            <field name="color_threshold_red">70.0</field>
         </record>
 
-        <record id="indicator_planning_coverage" model="hr.efficiency.indicator">
-            <field name="name">Planning Coverage</field>
-            <field name="sequence">50</field>
-            <field name="active">True</field>
-            <field name="indicator_type">percentage</field>
-            <field name="formula">(planned_hours / available_hours) if available_hours > 0 else 0</field>
-            <field name="target_percentage">95.0</field>
-            <field name="weight">0.0</field>
-            <field name="description">Cobertura de Planificación: Mide qué porcentaje del tiempo disponible ha sido planificado, sin importar si es facturable o no. (Total Horas Planeadas / Horas Disponibles)</field>
-            <field name="color_threshold_green">85.0</field>
-            <field name="color_threshold_yellow">75.0</field>
-            <field name="color_threshold_red">65.0</field>
-        </record>
+        <!-- ================================================== -->
+        <!-- 3. EJECUCIÓN - Lo que realmente se ejecutó         -->
+        <!-- ================================================== -->
 
         <record id="indicator_estimation_accuracy" model="hr.efficiency.indicator">
             <field name="name">Estimation Accuracy Plan Adherence</field>
-            <field name="sequence">60</field>
+            <field name="sequence">50</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
             <field name="formula">(total_actual_hours / planned_hours) if planned_hours > 0 else 0</field>
@@ -100,7 +89,7 @@
 
         <record id="indicator_billable_plan_compliance" model="hr.efficiency.indicator">
             <field name="name">Billable Plan Compliance</field>
-            <field name="sequence">70</field>
+            <field name="sequence">60</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
             <field name="formula">(actual_billable_hours / planned_billable_hours) if planned_billable_hours > 0 else 0</field>
@@ -111,49 +100,60 @@
             <field name="color_threshold_yellow">75.0</field>
             <field name="color_threshold_red">65.0</field>
         </record>
-        
-        <!-- ================================================== -->
-        <!-- Indicadores de Rentabilidad (Profitability KPIs)   -->
-        <!-- ================================================== -->
 
-        <record id="indicator_break_even_hours" model="hr.efficiency.indicator">
-            <field name="name">Break-Even Hours Needed</field>
+        <record id="indicator_occupancy_rate" model="hr.efficiency.indicator">
+            <field name="name">Occupancy Rate</field>
+            <field name="sequence">70</field>
+            <field name="active">True</field>
+            <field name="indicator_type">percentage</field>
+            <field name="formula">(total_actual_hours / available_hours) if available_hours > 0 else 0</field>
+            <field name="target_percentage">95.0</field>
+            <field name="weight">0.0</field>
+            <field name="description">Tasa de Ocupación: Mide qué tan "ocupado" está el equipo en general, considerando horas facturables y no facturables. (Total Horas Registradas / Horas Disponibles)</field>
+            <field name="color_threshold_green">85.0</field>
+            <field name="color_threshold_yellow">75.0</field>
+            <field name="color_threshold_red">60.0</field>
+        </record>
+
+        <record id="indicator_utilization_rate" model="hr.efficiency.indicator">
+            <field name="name">Utilization Rate</field>
             <field name="sequence">80</field>
             <field name="active">True</field>
-            <field name="indicator_type">hours</field>
-            <!-- FORMULA RESTAURADA: Calcula el costo productivo basado en la tasa de utilización. -->
-            <field name="formula">(wage * (utilization_rate / 100) * (1 + (overhead / 100))) / precio_por_hora if precio_por_hora > 0 else 0</field>
-            <field name="target_percentage">0.0</field>
+            <field name="indicator_type">percentage</field>
+            <field name="formula">(actual_billable_hours / available_hours) if available_hours > 0 else 0</field>
+            <field name="target_percentage">80.0</field>
             <field name="weight">0.0</field>
-            <field name="description">Horas de Punto de Equilibrio: ¿Cuántas horas facturables se necesitan para cubrir el costo productivo (costo ponderado por utilización)? El resultado es un número de horas.</field>
-            <!-- Note: Thresholds for 'hours' type are less direct than for percentages. -->
+            <field name="description">Tasa de Utilización: Mide qué tan "productivo" (generando ingresos) está el equipo. (Horas Facturables Registradas / Horas Disponibles)</field>
             <field name="color_threshold_green">85.0</field>
             <field name="color_threshold_yellow">75.0</field>
-            <field name="color_threshold_red">65.0</field>
+            <field name="color_threshold_red">60.0</field>
         </record>
 
-        <record id="indicator_planned_profitability_coverage" model="hr.efficiency.indicator">
-            <field name="name">Planned Profitability Coverage</field>
+        <record id="indicator_billability_rate" model="hr.efficiency.indicator">
+            <field name="name">Billability Rate</field>
             <field name="sequence">90</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
-            <!-- FORMULA AJUSTADA: Compara las horas planeadas contra las horas de punto de equilibrio (costo productivo). -->
-            <field name="formula">(planned_billable_hours / ((wage * (utilization_rate / 100) * (1 + (overhead / 100))) / precio_por_hora)) if wage > 0 and precio_por_hora > 0 else 0</field>
-            <field name="target_percentage">100.0</field>
+            <field name="formula">(actual_billable_hours / total_actual_hours) if total_actual_hours > 0 else 0</field>
+            <field name="target_percentage">85.0</field>
             <field name="weight">0.0</field>
-            <field name="description">Cobertura de Rentabilidad Planeada: Mide si las horas facturables planeadas son suficientes para alcanzar el punto de equilibrio. Más de 100% indica un plan rentable. (Horas Facturables Planeadas / Horas de Punto de Equilibrio)</field>
-            <field name="color_threshold_green">100.0</field>
-            <field name="color_threshold_yellow">85.0</field>
-            <field name="color_threshold_red">70.0</field>
+            <field name="description">Tasa de Facturabilidad: De todo el tiempo trabajado, ¿qué porcentaje fue facturable? (Horas Facturables Registradas / Total Horas Registradas)</field>
+            <field name="color_threshold_green">85.0</field>
+            <field name="color_threshold_yellow">75.0</field>
+            <field name="color_threshold_red">65.0</field>
         </record>
+        
+        <!-- ================================================== -->
+        <!-- 4. RENTABILIDAD - Resultado final                  -->
+        <!-- ================================================== -->
 
         <record id="indicator_actual_profitability_achievement" model="hr.efficiency.indicator">
             <field name="name">Actual Profitability Achievement</field>
             <field name="sequence">100</field>
             <field name="active">True</field>
             <field name="indicator_type">percentage</field>
-            <!-- FORMULA AJUSTADA: Compara las horas reales contra las horas de punto de equilibrio (costo productivo). -->
-            <field name="formula">(actual_billable_hours / ((wage * (utilization_rate / 100) * (1 + (overhead / 100))) / precio_por_hora)) if wage > 0 and precio_por_hora > 0 else 0</field>
+            <!-- FORMULA SIMPLIFICADA: wage_overhead ya incluye el costo por hora multiplicado por las horas disponibles. -->
+            <field name="formula">(actual_billable_hours / ((wage_overhead * (utilization_rate / 100)) / precio_por_hora)) if wage_overhead > 0 and precio_por_hora > 0 else 0</field>
             <field name="target_percentage">100.0</field>
             <field name="weight">0.0</field>
             <field name="description">Logro de Rentabilidad Real: Mide el progreso real hacia el punto de equilibrio basado en las horas facturables registradas. Más de 100% indica que ya se ha alcanzado la rentabilidad. (Horas Facturables Registradas / Horas de Punto de Equilibrio)</field>

+ 35 - 26
hr_efficiency/models/hr_efficiency.py

@@ -33,6 +33,7 @@ class HrEfficiency(models.Model):
     
     # Employee contract information
     wage = fields.Float('Gross Salary', digits=(10, 2), aggregator='sum', help="Employee's gross salary from their contract at the time of calculation")
+    wage_overhead = fields.Float('Wage Overhead', digits=(10, 2), aggregator='sum', help="Hourly cost multiplied by available hours for the month, including company overhead")
     currency_id = fields.Many2one('res.currency', 'Currency', help="Currency for the wage field")
     
     # Employee utilization and company overhead (stored at calculation time)
@@ -55,7 +56,7 @@ class HrEfficiency(models.Model):
     expected_hours_to_date = fields.Float('Expected Hours to Date', digits=(10, 2), help='Hours that should be registered based on planning until current date')
     
     # Precio por Hora (stored field)
-    precio_por_hora = fields.Float('Precio por Hora', digits=(10, 2), help='Precio que cobramos al cliente por hora (hourly_cost + overhead + rentabilidad_esperada)', aggregator='avg')
+    precio_por_hora = fields.Float('Precio por Hora', digits=(10, 2), help='Precio que cobramos al cliente por hora (wage_overhead/available_hours + rentabilidad_esperada)', aggregator='avg')
     
     # Dynamic indicator fields (will be created automatically)
     # These fields are managed dynamically based on hr.efficiency.indicator records
@@ -134,21 +135,21 @@ class HrEfficiency(models.Model):
 
     def _calculate_precio_por_hora(self, record):
         """
-        Calculate precio por hora: hourly_cost * (1 + overhead/100) * (1 + expected_profitability/100)
+        Calculate precio por hora: (wage_overhead / available_hours) * (1 + expected_profitability/100)
+        Since wage_overhead already includes overhead, we only need to add expected profitability
         """
         try:
-            # Get hourly_cost from employee
-            employee = getattr(record, 'employee_id', None)
-            if not employee:
-                return 0.0
-            
-            hourly_cost = getattr(employee, 'hourly_cost', 0.0) or 0.0
-            overhead = getattr(record, 'overhead', 40.0) or 40.0
+            # Get wage_overhead and available_hours from record
+            wage_overhead = getattr(record, 'wage_overhead', 0.0) or 0.0
+            available_hours = getattr(record, 'available_hours', 0.0) or 0.0
             expected_profitability = getattr(record, 'expected_profitability', 30.0) or 30.0
             
-            if hourly_cost > 0:
-                # Apply overhead and expected profitability margin
-                precio_por_hora = hourly_cost * (1 + (overhead / 100)) * (1 + (expected_profitability / 100))
+            if wage_overhead > 0 and available_hours > 0:
+                # Calculate hourly cost from wage_overhead (which already includes overhead)
+                hourly_cost_with_overhead = wage_overhead / available_hours
+                
+                # Apply only expected profitability margin (overhead is already included)
+                precio_por_hora = hourly_cost_with_overhead * (1 + (expected_profitability / 100))
                 return float_round(precio_por_hora, 2)
             else:
                 return 0.0
@@ -170,7 +171,7 @@ class HrEfficiency(models.Model):
             'available_hours', 'planned_hours', 'planned_billable_hours',
             'planned_non_billable_hours', 'actual_billable_hours',
             'actual_non_billable_hours', 'total_actual_hours',
-            'expected_hours_to_date', 'wage', 'utilization_rate', 'overhead',
+            'expected_hours_to_date', 'wage', 'wage_overhead', 'utilization_rate', 'overhead',
             'precio_por_hora', 'employee_id', 'company_id', 'month_year', 'active',
         ]
         
@@ -202,6 +203,7 @@ class HrEfficiency(models.Model):
                 'total_actual_hours': getattr(record, 'total_actual_hours', 0.0) or 0.0,
                 'expected_hours_to_date': getattr(record, 'expected_hours_to_date', 0.0) or 0.0,
                 'wage': getattr(record, 'wage', 0.0) or 0.0,
+                'wage_overhead': getattr(record, 'wage_overhead', 0.0) or 0.0,
                 'utilization_rate': getattr(record, 'utilization_rate', 100.0) or 100.0,
                 'overhead': getattr(record, 'overhead', 40.0) or 40.0,
                 'precio_por_hora': getattr(record, 'precio_por_hora', 0.0) or 0.0,
@@ -297,6 +299,7 @@ class HrEfficiency(models.Model):
                 'total_actual_hours': record.total_actual_hours or 0.0,
                 'expected_hours_to_date': record.expected_hours_to_date or 0.0,
                 'wage': record.wage or 0.0,
+                'wage_overhead': record.wage_overhead or 0.0,
                 'utilization_rate': record.utilization_rate or 100.0,
                 'overhead': record.overhead or 40.0,
             }
@@ -415,6 +418,7 @@ class HrEfficiency(models.Model):
             'total_actual_hours': getattr(record, 'total_actual_hours', 0.0) or 0.0,
             'expected_hours_to_date': getattr(record, 'expected_hours_to_date', 0.0) or 0.0,
             'wage': getattr(record, 'wage', 0.0) or 0.0,
+            'wage_overhead': getattr(record, 'wage_overhead', 0.0) or 0.0,
             'utilization_rate': getattr(record, 'utilization_rate', 100.0) or 100.0,
             'overhead': getattr(record, 'overhead', 40.0) or 40.0,
         }
@@ -470,6 +474,11 @@ class HrEfficiency(models.Model):
         wage_date = date.today()
         wage, currency_id = self._get_employee_wage_and_currency(employee, wage_date)
         
+        # Calculate wage_overhead: hourly_cost * available_hours * (1 + overhead/100)
+        hourly_cost = employee.hourly_cost or 0.0
+        overhead = employee.company_id.overhead or 40.0
+        wage_overhead = hourly_cost * available_hours
+        
         # Get employee's utilization rate and company's overhead at calculation time
         utilization_rate = employee.utilization_rate or 100.0
         overhead = employee.company_id.overhead or 40.0
@@ -490,6 +499,7 @@ class HrEfficiency(models.Model):
             'actual_billable_hours': adjusted_actual_billable_hours,
             'actual_non_billable_hours': actual_non_billable_hours,
             'wage': wage,
+            'wage_overhead': wage_overhead,
             'currency_id': currency_id,
             'utilization_rate': utilization_rate,
             'overhead': overhead,
@@ -810,30 +820,29 @@ class HrEfficiency(models.Model):
     @api.model
     def _post_init_hook(self):
         """
-        Post-install hook to ensure dynamic fields are created for existing indicators
+        Post-install hook mejorado que sincroniza indicadores desde XML
+        Se ejecuta en cada carga del módulo para mantener sincronización
         """
         import logging
         _logger = logging.getLogger(__name__)
         
         try:
-            _logger.info("Running post-install hook for hr_efficiency module")
+            _logger.info("🚀 Ejecutando _post_init_hook mejorado para hr_efficiency")
             
-            # Ensure all active indicators have manual fields
-            active_indicators = self.env['hr.efficiency.indicator'].search([('active', '=', True)], order='sequence')
-            for indicator in active_indicators:
-                self.env['hr.efficiency.indicator']._create_dynamic_field(indicator)
+            # 1. Sincronizar indicadores desde XML con campos dinámicos
+            sync_result = self.env['hr.efficiency.indicator'].sync_indicators_from_xml()
             
-            # Update views with dynamic fields
-            self._update_views_with_dynamic_fields()
-            
-            # Apply default values to existing records
+            # 2. Aplicar valores por defecto a registros existentes
             self._apply_default_values_to_existing_records()
             
-            _logger.info(f"Post-install hook completed. Processed {len(active_indicators)} indicators")
+            # 3. Log del resultado
+            _logger.info("✅ _post_init_hook completado exitosamente")
+            _logger.info(f"📊 Resultado: {sync_result}")
             
         except Exception as e:
-            _logger.error(f"Error in post-install hook: {str(e)}")
-            raise
+            _logger.error(f"❌ Error en _post_init_hook: {str(e)}")
+            # No hacer raise para evitar que falle la carga del módulo
+            _logger.warning("⚠️ Continuando carga del módulo a pesar del error en _post_init_hook")
 
 
     @api.model

+ 115 - 1
hr_efficiency/models/hr_efficiency_indicator.py

@@ -278,8 +278,8 @@ class HrEfficiencyIndicator(models.Model):
                 'total_actual_hours': efficiency_data.get('total_actual_hours', 0),
                 'expected_hours_to_date': efficiency_data.get('expected_hours_to_date', 0),
                 'wage': efficiency_data.get('wage', 0),
+                'wage_overhead': efficiency_data.get('wage_overhead', 0),
                 'utilization_rate': efficiency_data.get('utilization_rate', 100.0),
-                'overhead': efficiency_data.get('overhead', 40.0),
                 'precio_por_hora': efficiency_data.get('precio_por_hora', 0),
             }
             
@@ -311,3 +311,117 @@ class HrEfficiencyIndicator(models.Model):
             return 'text-danger'
         else:
             return 'text-danger'  # Below red threshold - critical
+    
+    def action_sync_from_xml(self):
+        """
+        Acción manual para forzar sincronización desde XML
+        Útil para debugging y actualizaciones manuales
+        """
+        result = self.env['hr.efficiency.indicator'].sync_indicators_from_xml()
+        
+        # Mostrar mensaje al usuario
+        message = f"""
+        🔄 Sincronización completada:
+        📊 Indicadores procesados: {result['indicators_processed']}
+        🆕 Campos creados: {result['fields_created']}
+        ✅ Campos actualizados: {result['fields_updated']}
+        🔄 Registros recalculados: {result['records_recalculated']}
+        """
+        
+        return {
+            'type': 'ir.actions.client',
+            'tag': 'display_notification',
+            'params': {
+                'title': 'Sincronización Completada',
+                'message': message,
+                'type': 'success',
+                'sticky': True,
+            }
+        }
+    
+    @api.model
+    def sync_indicators_from_xml(self):
+        """
+        Sincroniza los indicadores desde el XML con la base de datos
+        Compara definiciones y actualiza campos dinámicos automáticamente
+        """
+        import logging
+        _logger = logging.getLogger(__name__)
+        
+        try:
+            _logger.info("🔄 Iniciando sincronización de indicadores desde XML...")
+            
+            # Obtener todos los indicadores activos ordenados por secuencia
+            active_indicators = self.search([('active', '=', True)], order='sequence')
+            _logger.info(f"📊 Encontrados {len(active_indicators)} indicadores activos")
+            
+            # Contadores para el reporte
+            fields_created = 0
+            fields_updated = 0
+            indicators_processed = 0
+            
+            for indicator in active_indicators:
+                try:
+                    # Verificar si el campo dinámico existe
+                    field_name = self.env['hr.efficiency']._get_indicator_field_name(indicator.name)
+                    
+                    # Buscar el campo manual existente
+                    existing_field = self.env['ir.model.fields'].search([
+                        ('model', '=', 'hr.efficiency'),
+                        ('name', '=', field_name),
+                        ('state', '=', 'manual'),
+                    ], limit=1)
+                    
+                    if existing_field:
+                        # Actualizar campo existente si hay cambios
+                        update_vals = {}
+                        if existing_field.field_description != indicator.name:
+                            update_vals['field_description'] = indicator.name
+                        if existing_field.help != (indicator.description or indicator.name):
+                            update_vals['help'] = indicator.description or indicator.name
+                        
+                        if update_vals:
+                            existing_field.write(update_vals)
+                            fields_updated += 1
+                            _logger.info(f"✅ Actualizado campo: {field_name}")
+                    else:
+                        # Crear nuevo campo dinámico
+                        self._create_dynamic_field(indicator)
+                        fields_created += 1
+                        _logger.info(f"🆕 Creado campo: {field_name}")
+                    
+                    indicators_processed += 1
+                    
+                except Exception as e:
+                    _logger.error(f"❌ Error procesando indicador {indicator.name}: {e}")
+                    continue
+            
+            # Actualizar vistas con los campos dinámicos
+            self.env['hr.efficiency']._update_views_with_dynamic_fields()
+            
+            # Recalcular todos los registros existentes
+            efficiency_records = self.env['hr.efficiency'].search([('active', '=', True)])
+            if efficiency_records:
+                efficiency_records._calculate_all_indicators()
+                _logger.info(f"🔄 Recalculados {len(efficiency_records)} registros de eficiencia")
+            
+            # Reporte final
+            _logger.info("=" * 60)
+            _logger.info("📋 REPORTE DE SINCRONIZACIÓN COMPLETADO")
+            _logger.info("=" * 60)
+            _logger.info(f"📊 Indicadores procesados: {indicators_processed}")
+            _logger.info(f"🆕 Campos creados: {fields_created}")
+            _logger.info(f"✅ Campos actualizados: {fields_updated}")
+            _logger.info(f"🔄 Registros recalculados: {len(efficiency_records)}")
+            _logger.info("=" * 60)
+            
+            return {
+                'indicators_processed': indicators_processed,
+                'fields_created': fields_created,
+                'fields_updated': fields_updated,
+                'records_recalculated': len(efficiency_records),
+            }
+            
+        except Exception as e:
+            _logger.error(f"❌ Error crítico en sincronización: {e}")
+            raise

+ 6 - 0
hr_efficiency/report/hr_efficiency_report.py

@@ -25,6 +25,9 @@ class HrEfficiencyReport(models.Model):
     actual_non_billable_hours = fields.Float('Actual Non-Billable Hours', readonly=True)
     total_actual_hours = fields.Float('Total Actual Hours', readonly=True)
     
+    # Financial
+    wage_overhead = fields.Float('Wage Overhead', readonly=True, help="Hourly cost multiplied by available hours for the month, including company overhead")
+    
     # Efficiency rates
     efficiency_rate = fields.Float('Efficiency Rate (%)', readonly=True)
     billable_efficiency_rate = fields.Float('Billable Efficiency Rate (%)', readonly=True)
@@ -50,6 +53,7 @@ class HrEfficiencyReport(models.Model):
                 e.actual_billable_hours,
                 e.actual_non_billable_hours,
                 e.total_actual_hours,
+                e.wage_overhead,
                 e.efficiency_rate,
                 e.billable_efficiency_rate,
                 CASE 
@@ -85,5 +89,7 @@ class HrEfficiencyReport(models.Model):
                         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)
+                    if 'wage_overhead' in fields:
+                        group['wage_overhead'] = sum(r.wage_overhead for r in records)
         
         return res

+ 4 - 1
hr_efficiency/report/hr_efficiency_report_views.xml

@@ -16,6 +16,7 @@
                     <field name="available_hours" type="measure" widget="float_time"/>
                     <field name="planned_hours" type="measure" widget="float_time"/>
                     <field name="total_actual_hours" type="measure" widget="float_time"/>
+                    <field name="wage_overhead" type="measure" widget="monetary" string="Wage Overhead (incl. overhead)"/>
                 </pivot>
             </field>
         </record>
@@ -30,6 +31,7 @@
                     <field name="employee_id"/>
                     <field name="efficiency_rate" type="measure"/>
                     <field name="billable_efficiency_rate" type="measure"/>
+                    <field name="wage_overhead" type="measure" string="Wage Overhead (incl. overhead)"/>
                 </graph>
             </field>
         </record>
@@ -43,6 +45,7 @@
                     <field name="employee_id"/>
                     <field name="month_year"/>
                     <field name="department_id"/>
+                    <field name="wage_overhead" string="Wage Overhead (incl. overhead)"/>
                     <field name="company_id" groups="base.group_multi_company"/>
                     <filter string="High Efficiency" name="high_efficiency" domain="[('efficiency_rate', '>=', 90)]"/>
                     <filter string="Medium Efficiency" name="medium_efficiency" domain="[('efficiency_rate', '>=', 70), ('efficiency_rate', '&lt;', 90)]"/>
@@ -66,7 +69,7 @@
             <field name="search_view_id" ref="view_hr_efficiency_report_search"/>
             <field name="context">{
                 'pivot_row_groupby': ['month_year:month'],
-                'pivot_measures': ['efficiency_rate', 'billable_efficiency_rate', 'utilization_rate'],
+                'pivot_measures': ['efficiency_rate', 'billable_efficiency_rate', 'utilization_rate', 'wage_overhead'],
                 'graph_groupbys': ['month_year:month', 'employee_id'],
                 'group_by': [],
             }</field>

+ 5 - 1
hr_efficiency/views/hr_efficiency_indicator_views.xml

@@ -6,6 +6,9 @@
         <field name="model">hr.efficiency.indicator</field>
         <field name="arch" type="xml">
             <list string="Efficiency Indicators">
+                <header>
+                    <button name="action_sync_from_xml" string="🔄 Sync from XML" type="object" class="btn-primary"/>
+                </header>
                 <field name="sequence" widget="handle"/>
                 <field name="name"/>
                 <field name="indicator_type"/>
@@ -77,7 +80,8 @@
     <record id="action_hr_efficiency_indicator" model="ir.actions.act_window">
         <field name="name">Efficiency Indicators</field>
         <field name="res_model">hr.efficiency.indicator</field>
-                        <field name="view_mode">list,form</field>
+        <field name="view_mode">list,form</field>
+        <field name="context">{'search_default_active': 1}</field>
         <field name="help" type="html">
             <p class="o_view_nocontent_empty_folder">
                 No efficiency indicators configured!

+ 2 - 2
hr_efficiency/views/hr_efficiency_views.xml

@@ -9,7 +9,7 @@
                 <list string="Employee Efficiency">
                     <field name="month_year"/>
                     <field name="employee_id"/>
-                    <field name="wage" sum="Total Gross Salary" optional="show"/>
+                    <field name="wage_overhead" sum="Total Wage Overhead (incl. overhead)" optional="show"/>
                     <field name="utilization_rate" avg="Average Utilization" optional="show"/>
                     <field name="overhead" avg="Average Overhead" optional="show"/>
                     <field name="expected_profitability" avg="Average Expected Profitability" optional="show"/>
@@ -49,7 +49,7 @@
                             <group>
                                 <field name="month_year"/>
                                 <field name="employee_id"/>
-                                <field name="wage"/>
+                                <field name="wage_overhead"/>
                                 <field name="utilization_rate"/>
                                 <field name="overhead"/>
                                 <field name="expected_profitability"/>