Módulo de Vacaciones - Documentación Técnica¶
Descripción General¶
El Módulo de Vacaciones de Coati Payroll es un subsistema robusto, auditable y agnóstico al país, diseñado para gestionar el control completo de vacaciones de empleados. Este módulo está basado en principios de sistemas enterprise HRIS/Payroll y puede adaptarse a las regulaciones de cualquier país de América (LATAM, USA y Canadá).
Principios de Diseño¶
El módulo se basa en los siguientes principios fundamentales:
-
Vacaciones no son días, son un balance: Las vacaciones se gestionan como un balance contable que aumenta (acumulación) y disminuye (uso).
-
El balance se modifica solo por eventos: Todas las modificaciones al balance se realizan mediante eventos registrados en el libro mayor (ledger).
-
Las reglas no viven en código, viven en configuración: Las políticas de vacaciones son configurables y no requieren cambios en el código para adaptarse a diferentes legislaciones.
-
El sistema no decide legalidad, solo ejecuta políticas: El sistema ejecuta políticas configuradas; la interpretación legal es responsabilidad del usuario.
-
El motor debe ser determinista y reproducible: Dado el mismo conjunto de eventos, el sistema siempre produce el mismo balance.
-
Toda mutación debe dejar trazabilidad: Cada cambio en el balance deja un registro inmutable en el ledger.
Arquitectura del Sistema¶
Employee
└── VacationAccount (per policy)
├── current_balance
├── policy_id
└── VacationLedger[] (immutable audit trail)
├── ACCRUAL (earned)
├── USAGE (taken)
├── ADJUSTMENT (manual)
├── EXPIRATION (expired)
└── PAYOUT (paid out)
Entidades del Modelo¶
1. VacationPolicy (Política de Vacaciones)¶
Define cómo se acumulan, usan y vencen las vacaciones. Es completamente configurable por planilla (nómina) o empresa.
IMPORTANTE: Las políticas se asocian principalmente a Planillas (nóminas), no solo a empresas. Esto permite que una instalación tenga múltiples planillas con distintas reglas de vacaciones, incluso para distintos países en consolidados de empresas.
Campos Principales:
Identificación¶
codigo: Código único de la políticanombre: Nombre descriptivodescripcion: Descripción detalladaplanilla_id: Planilla asociada (recomendado, permite políticas específicas por nómina/país)empresa_id: Empresa asociada (opcional, para políticas globales que aplican a toda la empresa)
Configuración de Acumulación¶
accrual_method: Método de acumulaciónperiodic: Cantidad fija por períodoproportional: Basado en días/horas trabajadasseniority: Escalonado por antigüedadaccrual_rate: Tasa de acumulación (cantidad por período)accrual_frequency: Frecuencia de acumulaciónmonthly: Mensualbiweekly: Quincenalannual: Anualaccrual_basis: Base para cálculo proporcionaldays_worked: Días trabajadoshours_worked: Horas trabajadasmin_service_days: Días mínimos de servicio antes de acumularseniority_tiers: Niveles por antigüedad (JSON)
Límites de Balance¶
max_balance: Balance máximo permitidocarryover_limit: Máximo que puede traspasar al siguiente períodoallow_negative: Permitir balance negativo (vacaciones adelantadas)
Reglas de Vencimiento¶
expiration_rule: Cuándo vencen las vacacionesnever: Nunca vencenfiscal_year_end: Al fin del año fiscalanniversary: En el aniversario del empleadocustom_date: Fecha personalizadaexpiration_months: Meses después de acumulación antes de vencerexpiration_date: Fecha personalizada de vencimiento
Configuración de Uso¶
unit_type: Tipo de unidaddays: Díashours: Horascount_weekends: Incluir fines de semana en el cálculocount_holidays: Incluir feriados en el cálculopartial_units_allowed: Permitir fracciones de días/horasrounding_rule: Regla de redondeo (nearest,up,down)accrue_during_leave: Continuar acumulando durante vacacionespayout_on_termination: Pagar vacaciones al terminar relación laboral
Ejemplo de Configuración - Nicaragua:
policy_nicaragua = VacationPolicy(
codigo="NIC-STANDARD",
nombre="Política Nicaragua Estándar",
accrual_method="periodic",
accrual_rate=1.25, # 15 días al año / 12 meses
accrual_frequency="monthly",
unit_type="days",
count_weekends=True,
count_holidays=True,
payout_on_termination=True,
expiration_rule="anniversary",
expiration_months=12
)
Ejemplo de Configuración - USA:
policy_usa = VacationPolicy(
codigo="USA-HOURLY",
nombre="USA Hourly Accrual",
accrual_method="proportional",
accrual_basis="hours_worked",
accrual_rate=0.025, # 1 hora por cada 40 horas trabajadas
accrual_frequency="monthly",
unit_type="hours",
count_weekends=False,
count_holidays=False,
partial_units_allowed=True,
allow_negative=False,
payout_on_termination=True
)
Ejemplo de Configuración - Antigüedad:
policy_seniority = VacationPolicy(
codigo="LAT-SENIORITY",
nombre="Política por Antigüedad LATAM",
accrual_method="seniority",
accrual_frequency="annual",
unit_type="days",
seniority_tiers=[
{"years": 0, "rate": 10}, # 0-1 años: 10 días
{"years": 2, "rate": 15}, # 2-5 años: 15 días
{"years": 6, "rate": 20} # 6+ años: 20 días
],
payout_on_termination=True
)
2. VacationAccount (Cuenta de Vacaciones)¶
Representa el balance de vacaciones de un empleado bajo una política específica.
Campos Principales:
- empleado_id: ID del empleado
- policy_id: ID de la política aplicable
- current_balance: Balance actual (calculado desde el ledger)
- last_accrual_date: Fecha de la última acumulación
- activo: Estado de la cuenta
Regla de Oro: Nunca actualizar current_balance directamente sin registrar una entrada en VacationLedger.
3. VacationLedger (Libro Mayor de Vacaciones)¶
Registro inmutable de todos los movimientos de vacaciones. Es la fuente de verdad del sistema.
Campos Principales:
- account_id: ID de la cuenta
- empleado_id: ID del empleado (para consultas rápidas)
- fecha: Fecha del movimiento
- entry_type: Tipo de entrada
- accrual: Vacaciones ganadas
- usage: Vacaciones tomadas
- adjustment: Ajuste manual
- expiration: Vacaciones vencidas
- payout: Vacaciones pagadas (al terminar)
- quantity: Cantidad (positiva para adiciones, negativa para deducciones)
- source: Origen del movimiento (system, novelty, termination, manual)
- reference_id: ID de referencia (ej: ID de novedad)
- reference_type: Tipo de referencia
- observaciones: Notas adicionales
- balance_after: Balance después de este movimiento
Características:
- Inmutable: Una vez creado, nunca se modifica o elimina
- Auditable: Rastrea quién, cuándo y por qué se modificó el balance
- Determinista: balance = SUM(ledger.quantity)
4. VacationNovelty (Novedad/Solicitud de Vacaciones)¶
Representa una solicitud de vacaciones que afecta el balance cuando es aprobada.
Campos Principales:
- empleado_id: ID del empleado
- account_id: ID de la cuenta de vacaciones
- start_date: Fecha de inicio
- end_date: Fecha de fin
- units: Unidades solicitadas (días/horas)
- estado: Estado de la solicitud
- pendiente: Pendiente de aprobación
- aprobado: Aprobada
- rechazado: Rechazada
- disfrutado: Disfrutada/completada
- fecha_aprobacion: Fecha de aprobación
- aprobado_por: Usuario que aprobó
- ledger_entry_id: ID de la entrada en el ledger (cuando es aprobada)
- observaciones: Notas
- motivo_rechazo: Motivo del rechazo (si aplica)
Flujos de Trabajo¶
Flujo 1: Acumulación de Vacaciones¶
1. Job programado ejecuta motor de acumulación
2. Motor evalúa política de cada cuenta activa
3. Calcula cantidad a acumular según método
4. Genera entrada en VacationLedger (type=ACCRUAL)
5. Actualiza current_balance en VacationAccount
6. Actualiza last_accrual_date
Flujo 2: Solicitud y Aprobación de Vacaciones¶
1. Usuario crea VacationNovelty (estado=pendiente)
2. Sistema valida:
- Empleado tiene cuenta activa
- Balance suficiente (o policy.allow_negative=true)
3. Administrador revisa solicitud
4. Si aprueba:
a. Cambia estado a 'aprobado'
b. Genera VacationLedger (type=USAGE, quantity=-N)
c. Actualiza current_balance
d. Vincula ledger_entry_id
5. Si rechaza:
a. Cambia estado a 'rechazado'
b. Registra motivo_rechazo
Flujo 3: Ajuste Manual¶
1. Administrador crea ajuste manual
2. Sistema genera VacationLedger (type=ADJUSTMENT)
3. Actualiza current_balance
4. Registra observaciones y usuario
Flujo 4: Vencimiento de Vacaciones¶
1. Job programado evalúa políticas de vencimiento
2. Identifica vacaciones vencidas según expiration_rule
3. Genera VacationLedger (type=EXPIRATION, quantity=-N)
4. Actualiza current_balance
Flujo 5: Pago al Terminar Relación Laboral¶
1. Empleado es dado de baja
2. Si policy.payout_on_termination=true:
a. Genera VacationLedger (type=PAYOUT, quantity=-balance)
b. Balance queda en 0
c. Se integra con nómina para pago
Casos de Uso Cubiertos¶
✅ LATAM (Ejemplo: Nicaragua)¶
- Acumulación: 15 días calendario al año
- Frecuencia: Mensual (1.25 días/mes)
- Unidad: Días calendario
- Vencimiento: 12 meses después del aniversario
- Pago al terminar: Sí
✅ USA¶
- Acumulación: Por horas trabajadas
- Frecuencia: Mensual (basado en horas del mes)
- Unidad: Horas
- Vencimiento: Fin de año fiscal
- Fracciones: Permitidas
✅ Canadá¶
- Acumulación: Porcentaje del salario
- Frecuencia: Quincenal o mensual
- Unidad: Días u horas
- Pago al terminar: Sí
✅ Part-Time / Empleados por Horas¶
- Acumulación proporcional basada en horas trabajadas
- Configuración específica por tipo de empleado
✅ Vacaciones Adelantadas¶
- Configurar
allow_negative=trueen la política - El balance puede ser negativo
- Se descuenta en futuras acumulaciones
✅ Ajustes Manuales Auditables¶
- Todos los ajustes quedan registrados en el ledger
- Se registra usuario, fecha y motivo
Integración con Nómina¶
El módulo de vacaciones NO calcula dinero, solo gestiona unidades (días/horas).
La integración con nómina se realiza mediante:
- Vacaciones Tomadas: Se registran como novedades que pueden afectar el cálculo de la nómina
- Pago de Vacaciones al Terminar: El evento
PAYOUTgenera una novedad que se procesa en la nómina final - Provisión de Vacaciones: Se puede configurar como prestación patronal basada en el balance acumulado
Reportes y Consultas¶
Balance por Empleado¶
Historial Completo¶
Cálculo Verificable del Balance¶
SELECT SUM(quantity) FROM vacation_ledger WHERE account_id = ?
-- Debe ser igual a vacation_account.current_balance
Vacaciones por Vencer¶
SELECT va.*, vp.expiration_months
FROM vacation_account va
JOIN vacation_policy vp ON va.policy_id = vp.id
WHERE vp.expiration_rule != 'never'
AND va.last_accrual_date < DATEADD(MONTH, -vp.expiration_months, GETDATE())
Seguridad y Permisos¶
- Crear/Editar Políticas: Solo administradores
- Ver Cuentas: Usuarios con permisos de lectura
- Aprobar Solicitudes: Administradores y RRHH
- Crear Solicitudes: Usuarios con permisos de escritura
- Ajustes Manuales: Solo administradores
Ventajas del Diseño¶
✅ 100% agnóstico al país: Se adapta a cualquier legislación mediante configuración
✅ Auditable: Cada cambio queda registrado
✅ Reversible: Ajustes se registran, no se borran
✅ Extensible: Nuevas reglas se agregan sin cambiar código
✅ Compatible con plugins: Sistema de eventos permite extensiones
✅ No rompe nómina: Integración limpia con el módulo de nómina
Riesgos Evitados¶
❌ Hardcodear días legales
❌ Mezclar vacaciones con nómina
❌ Actualizar balances directamente
❌ No tener ledger (libro mayor)
❌ No modelar eventos
Próximas Mejoras¶
- Motor de acumulación automático (scheduled job)
- Notificaciones de vacaciones por vencer
- Integración con calendario empresarial
- Reportes avanzados y dashboards
- API REST para integraciones externas
- Importación masiva de balances iniciales
Conclusión¶
El Módulo de Vacaciones de Coati Payroll implementa un sistema robusto basado en políticas + eventos + ledger + motor de reglas, apto para cumplir con legislaciones de cualquier país de América sin necesidad de reescribir código.