Payroll Service
Orchestrator · Remuneraciones
ServicePayrollMotor de Cálculo
PayrollService es la pieza central del dominio de remuneraciones del Orchestrator. Coordina la hidratación de datos, la ejecución del motor de cálculo y la persistencia de la liquidación. El dominio separa el cálculo puro (PayrollEngine y sus calculadoras) del acceso a datos (PayrollRepository) y de la orquestación transaccional (PayrollService).
Estructura del Dominio
Section titled “Estructura del Dominio”Directorysrc/domain/payroll/
Directorycalculators/
- BaseSalaryCalculator.ts
- GratificationCalculator.ts
- HealthPlanCalculator.ts
- ProrrataCalculator.ts
- SocialLawsCalculator.ts
- TaxCalculator.ts
- PayrollEngine.ts
- PayrollInputBuilder.ts
- PayrollRepository.ts
- PayrollService.ts
- pdf-template.ts
- types.ts
- tests /
| Capa | Archivo | Rol |
|---|---|---|
| Service | PayrollService.ts | Capa de aplicación: valida, hidrata contexto, orquesta el cálculo, gestiona la transacción y los eventos. Extiende BaseService. |
| Engine | PayrollEngine.ts | Función pura de cálculo, sin dependencias de base de datos. calculate(input) es determinista. |
| Input Builder | PayrollInputBuilder.ts | Construcción fluida y validada del PayrollInput (~30 campos). |
| Repository | PayrollRepository.ts | Acceso a PostgreSQL: hidratación de contexto, persistencia y consultas de router. |
| Calculators | calculators/ | Reglas de negocio específicas, cada una aislada y testeable. |
| Types | types.ts | Tipos compartidos del dominio: ContractType, HealthMode, GratificationMode. |
Flujo de Cálculo
Section titled “Flujo de Cálculo”PayrollEngine.calculate(input: PayrollInput) ejecuta el cálculo en este orden:
-
Sueldo base prorrateado —
BaseSalaryCalculatorajusta el sueldo base por días trabajados sobre la base de cálculo (30 por defecto), con piso en el sueldo mínimo. -
Horas extra — se obtiene el valor hora con el factor del Art. 32 y se aplican los recargos según el tipo de hora extra registrada.
-
Gratificación —
GratificationCalculatoraplica el Art. 50 sobre el sueldo prorrateado más los bonos imponibles fijos. -
Total imponible — suma de haberes imponibles (sueldo, gratificación, bonos y horas extra); es la base de cotizaciones.
-
Leyes sociales —
SocialLawsCalculatorcalcula AFP, salud, AFC y aportes patronales respetando los topes imponibles. -
Impuesto único —
TaxCalculatoraplica la tabla del SII sobre la base tributable (imponible menos descuentos legales y APV). -
Haberes no imponibles —
ProrrataCalculatorprorratea colación y movilización por días trabajados. -
Descuentos y líquido — se suman descuentos legales, impuesto, anticipos, préstamos y descuentos voluntarios; el líquido es el total de haberes menos el total de descuentos.
Calculadoras
Section titled “Calculadoras”| Calculadora | Responsabilidad |
|---|---|
BaseSalaryCalculator | Sueldo base prorrateado por días trabajados, con piso en el sueldo mínimo. |
GratificationCalculator | Gratificación legal (Art. 50): modos 25 %, abono anual, exento o sin gratificación. |
SocialLawsCalculator | Leyes sociales: AFP, salud, AFC, SIS, mutual y aportes patronales, respetando topes. |
TaxCalculator | Impuesto único de segunda categoría sobre la base tributable. |
HealthPlanCalculator | Resolución de la cotización de salud: FONASA 7 % vs. ISAPRE (mayor entre 7 % y plan pactado). |
ProrrataCalculator | Prorrateo mensual de montos fijos (colación, movilización) según días trabajados. |
Métodos del Service
Section titled “Métodos del Service”| Método | Tipo | Descripción |
|---|---|---|
previewPayroll(ctx, contractId, period, overrides) | Lectura | Calcula una liquidación sin persistirla. |
generatePayroll(ctx, contractId, period, options) | Escritura | Calcula y persiste dentro de una transacción. Admite dryRun, force y overrides. |
deletePayroll(ctx, id, force) | Escritura | Elimina una liquidación. Bloqueada para estados APROBADA y PAGADA salvo force. |
updateFileUrl(ctx, id, url) | Escritura | Asocia la URL del PDF de liquidación. |
updateStatus(ctx, id, estado) | Escritura | Cambia el estado; al pasar a APROBADA encola el evento payroll:approved. |
El Service recibe siempre un ServiceContext (tenant, usuario, base de datos) y sigue el patrón de servicio de backend: validación de parámetros, transacciones con withTransaction y resultados tipados.
Reglas de Negocio Clave
Section titled “Reglas de Negocio Clave”Tasas de cesantía por tipo de contrato
Section titled “Tasas de cesantía por tipo de contrato”El Service ajusta las tasas del seguro de cesantía (AFC) según el tipo de contrato antes de construir el PayrollInput:
| Tipo de contrato | Trabajador | Empleador | Indemnización |
|---|---|---|---|
INDEFINIDO | 0,6 % | 2,4 % | — |
PLAZO_FIJO | — | 3,0 % | — |
CASA_PARTICULAR | — | 3,0 % | 1,11 % |
Cotización de salud
Section titled “Cotización de salud”HealthPlanCalculator resuelve la cotización: en FONASA siempre se descuenta el 7 % de la base imponible topeada; en ISAPRE se descuenta el mayor entre ese 7 % legal y el plan pactado (UF o CLP).
Topes imponibles
Section titled “Topes imponibles”AFP, salud y SIS comparten el tope de pensión; el seguro de cesantía usa un tope mayor. Los topes llegan en UF y se convierten a pesos con la UF del periodo liquidado.
Estados y Persistencia
Section titled “Estados y Persistencia”generatePayroll corre dentro de una transacción: hidrata el contexto, calcula con el Engine y persiste con el Repository. La liquidación queda en estado CALCULADA.
| Estado | Significado |
|---|---|
BORRADOR | Liquidación en preparación. |
CALCULADA | Resultado generado y persistido. |
APROBADA | Liquidación validada; emite el evento payroll:approved. |
PAGADA | Pago registrado. |
ANULADA | Liquidación dejada sin efecto. |
savePayroll hace upsert de la cabecera —única por empleado, año y mes— y reescribe el detalle por concepto. Una liquidación APROBADA o PAGADA no se sobrescribe ni elimina salvo force.
Ejemplo de Uso
Section titled “Ejemplo de Uso”import { PayrollService } from "@/domain/payroll/PayrollService";
const service = new PayrollService();
// Previsualización: calcula sin persistirconst preview = await service.previewPayroll( ctx, // ServiceContext: tenant, usuario, base de datos "contract-uuid-123", new Date("2026-05-01"), { horas_extra_50: 5 }, // overrides opcionales);
// Generación: calcula y persiste dentro de una transacciónconst generated = await service.generatePayroll( ctx, "contract-uuid-123", new Date("2026-05-01"), { dryRun: false, force: false },);