# MODELO DE DOMINIO: RRHH

**Módulos:** MGN-010 (RRHH Básico)
**Fecha:** 2025-11-24
**Referencia Odoo:** hr, hr_attendance, hr_holidays
**Referencia Gamilit:** hr_management schema

---

## Diagrama de Entidades (Texto UML)

```
[Employee]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - company_id: UUID (FK)
  - user_id: UUID (FK)
  - partner_id: UUID (FK)
  - full_name: String
  - employee_number: String
  - job_id: UUID (FK)
  - department_id: UUID (FK)
  - manager_id: UUID (FK self)
  - work_email: String
  - work_phone: String
  - hire_date: Date
  - status: ENUM (active, inactive)

  1 <----> * [Contract]
  1 <----> * [Attendance]
  1 <----> * [Leave]
  1 <----> * [Timesheet]

[Department]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - company_id: UUID (FK)
  - name: String
  - parent_id: UUID (FK self)
  - manager_id: UUID (FK employee)

  1 <----> * [Employee]

[Job]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - company_id: UUID (FK)
  - name: String
  - description: Text
  - department_id: UUID (FK)

  1 <----> * [Employee]

[Contract]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - employee_id: UUID (FK)
  - name: String
  - contract_type: ENUM (permanent, temporary, contractor)
  - job_id: UUID (FK)
  - wage: Decimal
  - date_start: Date
  - date_end: Date
  - status: ENUM (draft, running, expired, cancelled)

[Attendance]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - employee_id: UUID (FK)
  - check_in: Timestamp
  - check_out: Timestamp
  - worked_hours: Decimal

[LeaveType]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - company_id: UUID (FK)
  - name: String
  - code: String
  - allocation_type: ENUM (fixed, accrual, unlimited)
  - requires_approval: Boolean

  1 <----> * [LeaveAllocation]
  1 <----> * [Leave]

[LeaveAllocation]
  - id: UUID (PK)
  - employee_id: UUID (FK)
  - leave_type_id: UUID (FK)
  - days_allocated: Decimal
  - date_from: Date
  - date_to: Date

[Leave]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - employee_id: UUID (FK)
  - leave_type_id: UUID (FK)
  - date_from: Date
  - date_to: Date
  - days_requested: Decimal
  - status: ENUM (draft, pending_approval, approved, refused, cancelled)
  - approver_id: UUID (FK user)

[Timesheet]
  - id: UUID (PK)
  - tenant_id: UUID (FK)
  - employee_id: UUID (FK)
  - date: Date
  - hours: Decimal
  - analytic_account_id: UUID (FK)
  - task_id: UUID (FK)
  - description: String
  - status: ENUM (draft, approved)
```

## Entidades Principales

### 1. Employee (Empleado)
**Descripción:** Empleado de la empresa.

**Atributos:**
- `id`: UUID
- `user_id`: Usuario asociado (opcional)
- `partner_id`: Partner asociado
- `full_name`: Nombre completo
- `employee_number`: Número de empleado
- `job_id`: Puesto
- `department_id`: Departamento
- `manager_id`: Jefe directo
- `work_email`: Email laboral
- `work_phone`: Teléfono laboral
- `hire_date`: Fecha de ingreso
- `status`: active, inactive

**Relaciones:**
- 1 Employee → 1 User (opcional)
- 1 Employee → 1 Partner
- N Employees → 1 Department
- N Employees → 1 Job
- N Employees → 1 Manager (self-reference)

**Patrón Odoo:** hr.employee
**Nota:** Un empleado puede o no tener user_id (acceso al sistema)

### 2. Department (Departamento)
**Descripción:** Departamento organizacional.

**Atributos:**
- `id`: UUID
- `name`: Nombre
- `parent_id`: Departamento padre (jerarquía)
- `manager_id`: Jefe de departamento

**Relaciones:**
- 1 Department → N Employees
- 1 Department → N Subdepartments

**Patrón Odoo:** hr.department
**Jerarquía típica:**
- Dirección General
  - Administración
    - Finanzas
    - RRHH
  - Operaciones
    - Producción
    - Logística

### 3. Job (Puesto)
**Descripción:** Puesto de trabajo.

**Atributos:**
- `id`: UUID
- `name`: Nombre (ej: "Gerente de Ventas")
- `description`: Descripción del puesto
- `department_id`: Departamento

**Relaciones:**
- N Jobs → 1 Department
- 1 Job → N Employees

**Patrón Odoo:** hr.job

### 4. Contract (Contrato Laboral)
**Descripción:** Contrato de trabajo del empleado.

**Atributos:**
- `id`: UUID
- `employee_id`: Empleado
- `name`: Nombre del contrato
- `contract_type`: permanent, temporary, contractor
- `job_id`: Puesto contratado
- `wage`: Salario
- `date_start`: Fecha de inicio
- `date_end`: Fecha de fin (opcional)
- `status`: draft, running, expired, cancelled

**Relaciones:**
- N Contracts → 1 Employee
- N Contracts → 1 Job

**Patrón Odoo:** hr.contract
**Estados:**
- draft: Borrador
- running: Activo (date_start <= HOY <= date_end)
- expired: Expirado (date_end < HOY)
- cancelled: Cancelado

### 5. Attendance (Asistencia)
**Descripción:** Registro de entrada/salida de empleado.

**Atributos:**
- `id`: UUID
- `employee_id`: Empleado
- `check_in`: Hora de entrada
- `check_out`: Hora de salida
- `worked_hours`: Horas trabajadas (calculado)

**Relaciones:**
- N Attendances → 1 Employee

**Patrón Odoo:** hr.attendance
**Cálculo:** worked_hours = check_out - check_in (en horas)

### 6. LeaveType (Tipo de Ausencia)
**Descripción:** Tipo de ausencia/permiso.

**Atributos:**
- `id`: UUID
- `name`: Nombre (ej: "Vacaciones", "Incapacidad")
- `code`: Código
- `allocation_type`: fixed, accrual, unlimited
- `requires_approval`: Requiere aprobación

**Relaciones:**
- 1 LeaveType → N Leaves
- 1 LeaveType → N LeaveAllocations

**Patrón Odoo:** hr.leave.type
**Tipos comunes:**
- Vacaciones (fixed: X días al año)
- Incapacidad (unlimited)
- Permiso sin goce (requires_approval)
- Permiso con goce (requires_approval)

### 7. LeaveAllocation (Asignación de Ausencias)
**Descripción:** Días de ausencia asignados a empleado.

**Atributos:**
- `id`: UUID
- `employee_id`: Empleado
- `leave_type_id`: Tipo de ausencia
- `days_allocated`: Días asignados
- `date_from`: Fecha inicio de vigencia
- `date_to`: Fecha fin de vigencia

**Relaciones:**
- N LeaveAllocations → 1 Employee
- N LeaveAllocations → 1 LeaveType

**Patrón Odoo:** hr.leave.allocation
**Ejemplo:** 15 días de vacaciones por año

### 8. Leave (Ausencia/Permiso)
**Descripción:** Solicitud de ausencia de empleado.

**Atributos:**
- `id`: UUID
- `employee_id`: Empleado
- `leave_type_id`: Tipo de ausencia
- `date_from`: Fecha inicio
- `date_to`: Fecha fin
- `days_requested`: Días solicitados
- `status`: draft, pending_approval, approved, refused, cancelled
- `approver_id`: Usuario que aprobó

**Relaciones:**
- N Leaves → 1 Employee
- N Leaves → 1 LeaveType

**Patrón Odoo:** hr.leave
**Flujo:**
1. Empleado crea solicitud (draft)
2. Empleado envía a aprobación (pending_approval)
3. Manager aprueba/rechaza (approved/refused)

### 9. Timesheet (Hoja de Horas)
**Descripción:** Registro de horas trabajadas por proyecto/tarea.

**Atributos:**
- `id`: UUID
- `employee_id`: Empleado
- `date`: Fecha
- `hours`: Horas trabajadas
- `analytic_account_id`: Cuenta analítica (proyecto)
- `task_id`: Tarea (opcional)
- `description`: Descripción
- `status`: draft, approved

**Relaciones:**
- N Timesheets → 1 Employee
- N Timesheets → 1 AnalyticAccount (MGN-008)
- N Timesheets → 1 Task (MGN-011)

**Patrón Odoo:** account.analytic.line (tipo=timesheet)
**Integración:** Genera líneas analíticas en MGN-008

## Reglas de Negocio

### RN-HR-001: Un Empleado = Un Partner
- Todo empleado tiene partner_id (is_employee=true)
- Partner tiene datos personales: dirección, contactos, etc.

### RN-HR-002: Contrato Activo
- Empleado puede tener múltiples contratos
- Solo un contrato puede estar 'running' simultáneamente
- Contrato 'running' si date_start <= HOY <= date_end

### RN-HR-003: Check-in/Check-out
- Check-out no puede ser anterior a check-in
- Un empleado no puede tener check-in sin check-out pendiente
- worked_hours calculado automáticamente

### RN-HR-004: Saldo de Vacaciones
- Saldo = LeaveAllocation.days_allocated - SUM(Leave.days_requested WHERE approved)
- No permitir Leave si saldo insuficiente (excepto tipos unlimited)

### RN-HR-005: Aprobación de Ausencias
- Manager puede aprobar ausencias de su equipo
- RRHH puede aprobar todas las ausencias
- Empleado no puede aprobar sus propias ausencias

### RN-HR-006: Timesheet y Analítica
- Timesheet genera línea analítica en MGN-008
- Tipo de línea: 'expense' (costo de mano de obra)
- Monto = hours * employee hourly rate

### RN-HR-007: Organigrama
- Estructura jerárquica: Employee.manager_id
- Visualización en árbol
- Manager puede ver timesheets/ausencias de su equipo

## Casos de Uso Principales

1. **UC-HR-001:** RRHH crea empleado
2. **UC-HR-002:** RRHH asigna empleado a departamento y puesto
3. **UC-HR-003:** RRHH crea contrato de trabajo
4. **UC-HR-004:** Empleado registra check-in (entrada)
5. **UC-HR-005:** Empleado registra check-out (salida)
6. **UC-HR-006:** Empleado solicita vacaciones
7. **UC-HR-007:** Manager aprueba/rechaza solicitud de vacaciones
8. **UC-HR-008:** Empleado registra timesheet diario
9. **UC-HR-009:** Manager valida timesheet de empleado
10. **UC-HR-010:** RRHH consulta reporte de asistencias

## Validaciones y Constraints

```sql
-- Employee number único por company
UNIQUE (tenant_id, company_id, employee_number)

-- Check-out después de check-in
CHECK (check_out > check_in)

-- Worked hours >= 0
CHECK (worked_hours >= 0)

-- Leave date_to >= date_from
CHECK (date_to >= date_from)

-- Days requested > 0
CHECK (days_requested > 0)

-- Timesheet hours > 0
CHECK (hours > 0)

-- Contrato date_end >= date_start
CHECK (date_end IS NULL OR date_end >= date_start)

-- Wage >= 0
CHECK (wage >= 0)
```

## Índices Requeridos

```sql
CREATE INDEX idx_employees_department_id ON hr.employees(department_id);
CREATE INDEX idx_employees_manager_id ON hr.employees(manager_id);
CREATE INDEX idx_employees_user_id ON hr.employees(user_id);
CREATE INDEX idx_employees_status ON hr.employees(status);
CREATE INDEX idx_contracts_employee_id ON hr.contracts(employee_id);
CREATE INDEX idx_contracts_status ON hr.contracts(status);
CREATE INDEX idx_attendances_employee_id ON hr.attendances(employee_id);
CREATE INDEX idx_attendances_check_in ON hr.attendances(check_in);
CREATE INDEX idx_leaves_employee_id ON hr.leaves(employee_id);
CREATE INDEX idx_leaves_status ON hr.leaves(status);
CREATE INDEX idx_timesheets_employee_id ON hr.timesheets(employee_id);
CREATE INDEX idx_timesheets_date ON hr.timesheets(date);
```

## Integración con Otros Módulos

### Con MGN-001 (Fundamentos)
- Employee.user_id → User
- Empleado puede tener acceso al sistema

### Con MGN-003 (Catálogos)
- Employee.partner_id → Partner (is_employee=true)

### Con MGN-008 (Analítica)
- Timesheet genera líneas analíticas
- Costo de mano de obra por proyecto

### Con MGN-011 (Proyectos)
- Timesheet.task_id → Task
- Horas trabajadas por tarea

### Con MGN-014 (Mensajería)
- Notificaciones de ausencias aprobadas/rechazadas
- Recordatorios de timesheet pendiente

## Referencias
- [ALCANCE-POR-MODULO.md - MGN-010](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-010)
- [ADR-007: Database Design](../../adr/ADR-007-database-design.md)
- [odoo-hr-analysis.md](../../00-analisis-referencias/odoo/odoo-hr-analysis.md)
