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

Squashed 'hr_efficiency/' changes from 23b2f59..6da4b85

6da4b85 🔧 Fix: Corregir sincronización de fórmulas desde XML
390b44a feat: Implement dynamic indicator synchronization system
f366203 feat: Reorganize efficiency indicators and fix profitability formulas
38dfc42 Eliminar campos de agregación (_agg) del módulo HR Efficiency
942b304 feat: Major update to hr_efficiency module
d0368b0 Fix: Add post_init_hook to ensure inherited views persist after module updates
8160b96 fix: Solve dynamic indicators disappearing after module reinstall
3fb098f fix: Add post-install hook to preserve dynamic indicators after module reinstall
3683857 feat: Implement standard Odoo date filter like Sales module
dde7f54 fix: Use Odoo Planning's exact logic for allocated hours calculation
56f442b fix: Simplify planned hours calculation to match Odoo Planning behavior
e7c4baf fix: Filter planned hours to only count working days like Odoo Planning
748ca84 fix: Use flexible date range for planning slots to capture cross-month slots
cf8580f fix: Remove state restriction from planning slots query
2884b76 feat: Include dynamic indicators in new record creation criteria
0b5f340 fix: Include indicators with zero value in Overall Efficiency calculation
36502b5 fix: Remove duplicate menus from HR, keep only Planning menus
6908f03 fix: Move Efficiency Indicators menu to main Planning settings
6a166e1 feat: Implement dynamic filter labels like Sales app
763fad8 feat: Implement dynamic filters like Sales app
fcb9065 fix: Add dynamic current month filter and fix action configuration
134b2f2 feat: Add contract validation for expected_hours_to_date calculation
f678325 fix: Remove planned_hours dependency from expected_hours_to_date calculation
2bc2dfc fix: Correct expected_hours_to_date calculation logic
8339015 feat: Consider employee calendar for working days calculation
83167bc feat: Add expected_hours_to_date field with working days calculation
ffdd53e Initial commit: HR Efficiency module with dynamic fields and indicators
f4bea10 Initial commit
REVERT: 23b2f59 Actualizar HR Efficiency: Eliminar campos de agregación (_agg)
REVERT: 94f4b3c feat: Major update to hr_efficiency module - Added new fields (wage, utilization_rate, overhead, precio_por_hora) - Extended hr.employee and res.company models - Added profitability indicators - Updated formulas and views - Enhanced calculation logic
REVERT: 4c6fe8d fix: Add post-install hook to preserve dynamic indicators after module reinstall
REVERT: dc60c91 feat: Implement standard Odoo date filter like Sales module
REVERT: 26da5fa fix: Corregir cálculo de horas planificadas para slots que cruzan meses - Usar lógica de Odoo Planning nativo para calcular solo horas del período específico - Evitar contar horas completas de slots que cruzan meses
REVERT: 9f7f6e5 fix: Use Odoo Planning's exact logic for allocated hours calculation
REVERT: 128fb7a fix: Simplify planned hours calculation to match Odoo Planning behavior
REVERT: 4e74612 fix: Filter planned hours to only count working days like Odoo Planning
REVERT: 7907628 fix: Use flexible date range for planning slots to capture cross-month slots
REVERT: 80122ae fix: Remove state restriction from planning slots query
REVERT: 0d4b30e feat: Include dynamic indicators in new record creation criteria
REVERT: ee596eb fix: Include indicators with zero value in Overall Efficiency calculation
REVERT: f93fb9f feat: Add hr_efficiency module with latest updates
REVERT: 1ff377a Remove old hr_efficiency directory to prepare for subtree
REVERT: cbe7b89 Merge commit 'c4020d66a3f3eea08746325571e0d51ab15d5888' as 'hr_efficiency'
REVERT: c4020d6 Squashed 'hr_efficiency/' content from commit ffdd53e
REVERT: 4c4f7a5 update
REVERT: a54216c project_semaphore update
REVERT: 35b606f project semaphore
REVERT: 855d7e7 project semaphore add
REVERT: 9954fb5 remove project_semaphore
REVERT: 2cd0b17 restore delete semaphore
REVERT: f4592ee remove project_semaphore file
REVERT: 9449e5d project_semaphore master
REVERT: f5ce391 nuevo modulo project_semaphore
REVERT: de44555 sale_template_contract update
REVERT: 8ef44cc bloquear cambio de plantilla mensual si ya hay ordenes
REVERT: a3f9a87 template no mensual copia proyecto
REVERT: 55a2a1e Update ir_attachment.py
REVERT: e3b6720 upd Descarga masiva
REVERT: 3cf1f20 sale_template_contract updates
REVERT: a5bc338 sale_template_contract updates
REVERT: 1d5712f parametros para registro de horas en proyectos y ordenes de venta
REVERT: ed61dcb Actualizar project_task.py
REVERT: 4aedcd5 upd SAT
REVERT: 9c39021 Update account_edi_format.py
REVERT: 728e124 Update project_task.py
REVERT: d7810be upd Proyecto y subtareas
REVERT: e9cbda1 Update project_task.xml
REVERT: 97aafb7 Update project_task.xml
REVERT: 9d4ab04 Update project_task.xml
REVERT: d3b10f1 add Planeacion de proyecto
REVERT: e8790bf Update __manifest__.py
REVERT: 2a4e58c se agrega app whatsapp web con repo en git
REVERT: 034ebe4 Delete .gitmodules
REVERT: 3fdc544 Delete whatsapp_web
REVERT: ce5efaf upd Facturas
REVERT: 59aeabe Update account_payment.py
REVERT: 045672a gitmodule update
REVERT: 282af2a gitmodules update
REVERT: d10e009 whatsapp_web add
REVERT: b6d3125 remove .gitmodules
REVERT: 7b2a91e Eliminar submódulo whatsapp_web
REVERT: 65390eb ww updates
REVERT: 6761987 se agrega submódulo whatsapp web
REVERT: 2dbe6b3 Update __manifest__.py
REVERT: b0eb049 Update planning_slot.py
REVERT: 6745016 Update __manifest__.py
REVERT: 30a80aa upd
REVERT: d66370a Update account_payment.py
REVERT: 8eaa468 logs en planning_slot.py para identificar errores en horas
REVERT: 8d5f7ef logs en planning_slot.py para identificar errores en horas
REVERT: 97b6a4a se agrega gitignore y logs en planning_slot.py para identificar errores en horas
REVERT: 3299143 fix con zona horaria para calculo de dias
REVERT: 0adfc7c mejoras en calculo de dias disponibles en el mes para el recurso al modificar slot
REVERT: e80dd0a mejoras en calculo de dias disponibles en el mes para el recurso al modificar slot
REVERT: 3f66104 fix para contemplar hora al inicio y fin del mes
REVERT: f00a20a aplicacion m22_planning agrega columna real_working_days_count al modelo planning.slot y basado en el codigo de _compute_allocated_hours pone como fecha inicial el 1er día del mes de la fecha inicial del slot y fecha final el ultimo dia del mes de la fecha inicial
REVERT: 373ef4e Merge pull request #30 from M22TechConsulting/tc_import_taxes
REVERT: 88b15d6 [ADD] custom_import_layout: add taxes in tc
REVERT: 2b0c4eb Merge pull request #28 from M22TechConsulting/remove_taxes_prod
REVERT: 274d4f4 [ADD] remove taxes
REVERT: f453fcc Merge pull request #26 from M22TechConsulting/add_account_import
REVERT: e61c0e7 [ADD] account import lines
REVERT: cf06e88 Merge pull request #23 from M22TechConsulting/import_layaout_prod
REVERT: c8cf91a [ADD] custom_import_layout: add module
REVERT: 2132301 Merge pull request #21 from M22TechConsulting/upd_change_vat_in_partner
REVERT: 39dad4b [UPD] upd module
REVERT: acacbd2 Merge pull request #18 from M22TechConsulting/validate_rate_prod
REVERT: 1b1d3ef [UPD] custom_report_invoice
REVERT: 5f19cd0 Merge pull request #16 from M22TechConsulting/fix_rate
REVERT: 9b2efef [FIX] cutom_report_invoice: fix error in rate
REVERT: aa1fcfe Merge pull request #15 from M22TechConsulting/change_rate
REVERT: dfa3aac [ADD] custom_report_invoice: add exchange rate in prod
REVERT: cc1e577 Merge pull request #13 from M22TechConsulting/tc2
REVERT: e059461 [ADD] change_vat_in_partner: add partner in complement
REVERT: 1480014 Merge pull request #12 from M22TechConsulting/account_report_prod
REVERT: a217367 [ADD] custom_report_invoice: add module
REVERT: 8b50d91 [ADD] change_vat_in_partner: change addres native
REVERT: 5c04be2 Merge pull request #9 from M22TechConsulting/tc_change_vat_in_partner
REVERT: 850acdb [ADD] change_vat_in_partner: add module in prod
REVERT: 39be5d0 Add files via upload
REVERT: a0031e3 Update __manifest__.py
REVERT: 58c5a86 Add files via upload
REVERT: 4c59a6a Add files via upload
REVERT: a0be044 Add files via upload
REVERT: 6bd5dc4 Initial commit

git-subtree-dir: hr_efficiency
git-subtree-split: 6da4b85c6ebdba17bfa09079a62ea23c9bd71a23
root 5 месяцев назад
Родитель
Сommit
69332c1987

+ 26 - 8
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
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
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

+ 260 - 1
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,262 @@ 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 _load_indicator_from_xml(self, indicator_name):
+        """
+        Cargar datos del indicador desde el XML
+        """
+        try:
+            # Mapeo de indicadores con sus datos del XML
+            xml_indicators = {
+                'Planning Coverage': {
+                    'formula': '(planned_hours / available_hours) if available_hours > 0 else 0',
+                    'sequence': 10,
+                    '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)',
+                    'target_percentage': 95.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 65.0,
+                },
+                'Planned Utilization': {
+                    'formula': '(planned_billable_hours / available_hours) if available_hours > 0 else 0',
+                    'sequence': 20,
+                    '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)',
+                    'target_percentage': 80.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 60.0,
+                },
+                'Break-Even Hours Needed': {
+                    'formula': '(wage_overhead * (utilization_rate / 100)) / precio_por_hora if precio_por_hora > 0 else 0',
+                    'sequence': 30,
+                    '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.',
+                    'target_percentage': 0.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 65.0,
+                },
+                'Planned Profitability Coverage': {
+                    'formula': '(planned_billable_hours / ((wage_overhead * (utilization_rate / 100)) / precio_por_hora)) if wage_overhead > 0 and precio_por_hora > 0 else 0',
+                    'sequence': 40,
+                    '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)',
+                    'target_percentage': 100.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 100.0,
+                    'color_threshold_yellow': 85.0,
+                    'color_threshold_red': 70.0,
+                },
+                'Estimation Accuracy Plan Adherence': {
+                    'formula': '(total_actual_hours / planned_hours) if planned_hours > 0 else 0',
+                    'sequence': 50,
+                    'description': 'Precisión de la Estimación: ¿Qué tan acertada fue la planificación general vs. la realidad? Un valor cercano a 100% es ideal. (Total Horas Registradas / Total Horas Planeadas)',
+                    'target_percentage': 100.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 60.0,
+                },
+                'Billable Plan Compliance': {
+                    'formula': '(actual_billable_hours / planned_billable_hours) if planned_billable_hours > 0 else 0',
+                    'sequence': 60,
+                    'description': 'Cumplimiento del Plan Facturable: ¿Se cumplió con el objetivo específico de horas facturables? (Horas Facturables Registradas / Horas Facturables Planeadas)',
+                    'target_percentage': 100.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 65.0,
+                },
+                'Occupancy Rate': {
+                    'formula': '(total_actual_hours / available_hours) if available_hours > 0 else 0',
+                    'sequence': 70,
+                    '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)',
+                    'target_percentage': 95.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 60.0,
+                },
+                'Utilization Rate': {
+                    'formula': '(actual_billable_hours / available_hours) if available_hours > 0 else 0',
+                    'sequence': 80,
+                    'description': 'Tasa de Utilización: Mide qué tan "productivo" (generando ingresos) está el equipo. (Horas Facturables Registradas / Horas Disponibles)',
+                    'target_percentage': 80.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 60.0,
+                },
+                'Billability Rate': {
+                    'formula': '(actual_billable_hours / total_actual_hours) if total_actual_hours > 0 else 0',
+                    'sequence': 90,
+                    'description': 'Tasa de Facturabilidad: De todo el tiempo trabajado, ¿qué porcentaje fue facturable? (Horas Facturables Registradas / Total Horas Registradas)',
+                    'target_percentage': 85.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 85.0,
+                    'color_threshold_yellow': 75.0,
+                    'color_threshold_red': 65.0,
+                },
+                'Actual Profitability Achievement': {
+                    'formula': '(actual_billable_hours / ((wage_overhead * (utilization_rate / 100)) / precio_por_hora)) if wage_overhead > 0 and precio_por_hora > 0 else 0',
+                    'sequence': 100,
+                    '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)',
+                    'target_percentage': 100.0,
+                    'weight': 0.0,
+                    'color_threshold_green': 100.0,
+                    'color_threshold_yellow': 85.0,
+                    'color_threshold_red': 70.0,
+                },
+            }
+            
+            return xml_indicators.get(indicator_name, {})
+            
+        except Exception as e:
+            _logger.error(f"Error cargando indicador {indicator_name} desde XML: {e}")
+            return {}
+
+    @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:
+                    # PASO 1: Actualizar el indicador desde XML (fórmulas, secuencias, etc.)
+                    # Cargar el indicador desde XML para obtener la versión correcta
+                    xml_indicator = self._load_indicator_from_xml(indicator.name)
+                    if xml_indicator:
+                        # Actualizar indicador con datos del XML
+                        update_vals = {}
+                        if indicator.formula != xml_indicator.get('formula'):
+                            update_vals['formula'] = xml_indicator.get('formula')
+                        if indicator.sequence != xml_indicator.get('sequence'):
+                            update_vals['sequence'] = xml_indicator.get('sequence')
+                        if indicator.description != xml_indicator.get('description'):
+                            update_vals['description'] = xml_indicator.get('description')
+                        if indicator.target_percentage != xml_indicator.get('target_percentage'):
+                            update_vals['target_percentage'] = xml_indicator.get('target_percentage')
+                        if indicator.weight != xml_indicator.get('weight'):
+                            update_vals['weight'] = xml_indicator.get('weight')
+                        if indicator.color_threshold_green != xml_indicator.get('color_threshold_green'):
+                            update_vals['color_threshold_green'] = xml_indicator.get('color_threshold_green')
+                        if indicator.color_threshold_yellow != xml_indicator.get('color_threshold_yellow'):
+                            update_vals['color_threshold_yellow'] = xml_indicator.get('color_threshold_yellow')
+                        if indicator.color_threshold_red != xml_indicator.get('color_threshold_red'):
+                            update_vals['color_threshold_red'] = xml_indicator.get('color_threshold_red')
+                        
+                        if update_vals:
+                            indicator.write(update_vals)
+                            _logger.info(f"✅ Actualizado indicador: {indicator.name}")
+                            if 'formula' in update_vals:
+                                _logger.info(f"   Nueva fórmula: {update_vals['formula'][:50]}...")
+                    
+                    # PASO 2: 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
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
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
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
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"/>