Plataforma técnica · Orchestrator
Permission Service
Remuneraciones Permisos Licencias
PermissionService administra solicitudes de permisos, licencias médicas y otras ausencias justificadas. Cada solicitud tiene un tipo (que define la regla de pago), un estado en máquina de transiciones, flags de comportamiento (goce de sueldo, parcialidad por horas, recuperable, descuenta vacaciones) y respaldo documental opcional.
El service aplica reglas de coherencia importantes: si la solicitud es parcial, exige hora_desde y hora_hasta con hora_hasta > hora_desde; si no es parcial, fuerza las horas a null para evitar inconsistencias. Si es recuperable, exige que la fecha_recuperacion sea posterior al fin del permiso.
Ubicación
Section titled “Ubicación”Directoryorchestrator/src/domain/permissions/
- PermissionService.ts
- PermissionRepository.ts
- types.ts
Tabla: remuneraciones.permisos. Vista enriquecida usa join contra empleados y departamentos.
API pública
Section titled “API pública”| Método | Firma | Resultado |
|---|---|---|
getById | (ctx, id) => Promise<ServiceResult<PermissionRow>> | Devuelve fila enriquecida o notFound("Permiso", id). |
list | (ctx, filter: PermissionListFilter) => Promise<ServiceResult<...>> | scope === "list" usa findAll(filter) con totales y summary; sin scope devuelve listRecent(pool). |
create | (ctx, data: PermissionInput) => Promise<ServiceResult<void>> | Aplica defaults y reglas de parcialidad. |
update | (ctx, id, data: PermissionUpdateInput) => Promise<ServiceResult<void>> | Update parcial con merge contra el estado actual. |
delete | (ctx, id) => Promise<ServiceResult<void>> | notFound("Permiso", id) si no afecta filas. |
type PermissionStatus = | "SOLICITADO" | "APROBADO" | "RECHAZADO" | "CANCELADO" | "USADO";
interface PermissionInput { empleado_id: string; // obligatorio fecha_desde: string; // obligatorio (YYYY-MM-DD) fecha_hasta: string; // obligatorio tipo_permiso: string; // obligatorio (catálogo libre: MEDICO, MATRIMONIO, ...) motivo: string; // obligatorio
hora_desde?: string | null; hora_hasta?: string | null; subtipo_permiso?: string | null; es_parcial?: boolean; estado?: PermissionStatus; // default SOLICITADO requiere_documento?: boolean; documento_adjunto_url?: string | null; numero_documento?: string | null; es_con_goce_sueldo?: boolean; // default true descuenta_vacaciones?: boolean; es_recuperable?: boolean; fecha_recuperacion?: string | null; comentario_aprobacion?: string | null; observaciones?: string | null;}Validaciones en create
Section titled “Validaciones en create”| Campo | Regla |
|---|---|
empleado_id, fecha_desde, fecha_hasta, tipo_permiso, motivo | Obligatorios. validateRequired. |
estado (opcional) | ∈ ["SOLICITADO", "APROBADO", "RECHAZADO", "CANCELADO", "USADO"]. |
fecha_hasta >= fecha_desde | "La fecha hasta debe ser mayor o igual a fecha desde". |
fecha_recuperacion > fecha_hasta | Solo si es_recuperable && fecha_recuperacion. |
es_parcial = true → hora_desde && hora_hasta | "Los permisos parciales requieren hora desde y hora hasta". |
es_parcial = true → hora_hasta > hora_desde | "La hora hasta debe ser mayor que la hora desde". |
es_parcial = false | Forzar hora_desde = null y hora_hasta = null. |
Defaults aplicados
Section titled “Defaults aplicados”Antes de persistir, el service completa:
{ estado: data.estado || "SOLICITADO", es_parcial: !!data.es_parcial, requiere_documento: !!data.requiere_documento, es_con_goce_sueldo: data.es_con_goce_sueldo !== undefined ? !!data.es_con_goce_sueldo : true, descuenta_vacaciones: !!data.descuenta_vacaciones, es_recuperable: !!data.es_recuperable, created_by: ctx.userId,}es_con_goce_sueldo es el único default true; el resto de los flags son false si no se envían.
Update parcial
Section titled “Update parcial”update lee la fila actual con findById y la usa como base para resolver los campos que no vienen en el data. Lógica clave:
const has = (k) => Object.prototype.hasOwnProperty.call(data, k);const finalParcial = has("es_parcial") ? !!data.es_parcial : current.es_parcial;let finalHoraDesde = has("hora_desde") ? (data.hora_desde ?? "") : null;let finalHoraHasta = has("hora_hasta") ? (data.hora_hasta ?? "") : null;if (!finalParcial) { finalHoraDesde = ""; finalHoraHasta = ""; }Si la fila pasa de es_parcial=true a false, las horas se vacían explícitamente. La validación de fechas se hace con merge de data + current para evitar inconsistencias parciales.
Estados y flags
Section titled “Estados y flags”| Estado | Significado |
|---|---|
SOLICITADO | Pendiente de aprobación. No impacta asistencia ni payroll. |
APROBADO | Aprobado por RRHH/jefatura. Impacta la grilla de AttendanceService y, según flags, los cálculos de payroll. |
RECHAZADO | Negado. No impacta. |
CANCELADO | Anulado por el solicitante. No impacta. |
USADO | Aprobado y consumido por el periodo liquidado. Estado final. |
| Flag | Efecto en payroll |
|---|---|
es_con_goce_sueldo = true | Mantiene remuneración. No descuenta. |
es_con_goce_sueldo = false | Descuenta proporcional al periodo afectado. |
es_parcial = true | Descuenta sólo las horas indicadas, no el día completo. |
descuenta_vacaciones = true | Rebaja saldo de feriado (política empresa). |
es_recuperable = true | El trabajador recupera las horas en fecha_recuperacion. No descuenta si se cumple. |
requiere_documento = true | Exige documento_adjunto_url antes de aprobar (regla de UI). |
Errores
Section titled “Errores”| Mensaje | Cuándo |
|---|---|
"<campo> es requerido" | Falta alguno de los 5 obligatorios. |
"Estado inválido: <valor>" | estado fuera del enum. |
"La fecha hasta debe ser mayor o igual a fecha desde" | Rango invertido. |
"La fecha de recuperación debe ser posterior a la fecha hasta" | Recuperable con fecha mal puesta. |
"Los permisos parciales requieren hora desde y hora hasta" | es_parcial=true sin horas. |
"La hora hasta debe ser mayor que la hora desde" | Horas invertidas. |
notFound("Permiso", id) | update o delete con id inexistente. |
Consumidores
Section titled “Consumidores”| Service | Cómo consume |
|---|---|
AttendanceService | Cuando un permiso pasa a APROBADO, la grilla del mes refleja tipo_ausencia en los días afectados. |
PayrollEngine | Lee permisos aprobados del periodo para aplicar descuento por días sin goce, mantener pago en con goce, o calcular subsidio en licencia médica. |
VacationService | Si descuenta_vacaciones=true, el saldo de feriado se rebaja al aprobarse el permiso. |