Plataforma técnica · Orchestrator
DJ 1879 Service
Orchestrator Declaraciones Dj 1879
DJ1879Service genera la Declaración Jurada 1879 anual: el contribuyente reporta al SII todas las retenciones sobre honorarios pagados a terceros durante el año (Art. 42 N°2 y Art. 48 LIR). Lee remuneraciones.honorarios, aplica corrección monetaria a cada retención mensual y consolida por receptor (RUT prestador).
Vive en domain/declaraciones_juradas/ pero se agrupa con las demás declaraciones por afinidad de flujo y UI.
Operaciones
Section titled “Operaciones”| Método | Para |
|---|---|
generar(ctx, año) | Genera (o regenera borrador) la DJ del año. Emite dj:1879_generada. |
getByAño(ctx, año) | Lookup del header por año tributario. |
getWithDetalle(ctx, id) | Header + array de detalles por receptor. |
list(ctx) | Lista todas las DJ del tenant. |
validar(ctx, id) | Transición BORRADOR → VALIDADO. Emite dj:1879_status_cambiado. |
generateCSV(ctx, declaracionId) | Exporta formato F1879 (18 columnas, ; separator, sin headers). |
Flujo de generar(año)
Section titled “Flujo de generar(año)”flowchart TB
IN["generar(año)"]
TX["withTransaction"]
EX["findByAño(año)"]
CHK{"existe y estado != BORRADOR?"}
ERR1["Error 'Ya existe DJ en estado X'"]
DEL["si es BORRADOR: delete()"]
DECL["getDatosDeclarante()"]
HON["getHonorariosParaDJ(año)"]
HCHK{"honorarios.length === 0?"}
ERR2["Error 'No hay honorarios en año'"]
FCT["CommonDataService<br/>.getCorreccionMonetaria(año)"]
MAP["buildFactoresMap<br/>(filtra mes_destino=12,<br/>default factor=1)"]
CONS["consolidarPorReceptor<br/>(agrupa por RUT, calcula<br/>retencionActualizada = round(monto × factor))"]
TOT["calcularTotales"]
HDR["insertHeader(declarante + totales)"]
DET["por cada receptor:<br/>insertDetalle(correlativo, monto + detalle mensual)"]
EMIT["outbox.queue<br/>(dj:1879_generada)"]
COMMIT["COMMIT"]
IN --> TX --> EX --> CHK
CHK -- sí --> ERR1
CHK -- no --> DEL --> DECL --> HON --> HCHK
HCHK -- sí --> ERR2
HCHK -- no --> FCT --> MAP --> CONS --> TOT --> HDR --> DET --> EMIT --> COMMIT Corrección monetaria — buildFactoresMap
Section titled “Corrección monetaria — buildFactoresMap”Los honorarios pagados a lo largo del año se actualizan a diciembre del mismo año tributario aplicando el factor de corrección monetaria del SII (variación IPC mes-a-diciembre).
// Filtra solo factores con mes_destino = 12 (actualización a diciembre)for (const f of factoresRaw) { if (f.mes_destino === 12 && f.mes_origen >= 1 && f.mes_origen <= 12) { map.set(f.mes_origen, Number(f.factor_aplicable) || 1); }}
// Default: factor 1 para meses sin datofor (let m = 1; m <= 12; m++) { if (!map.has(m)) map.set(m, 1);}| Mes origen | Factor (ejemplo) | Significado |
|---|---|---|
| 1 (enero) | 1.038 | Una retención de 103.800. |
| 6 (junio) | 1.018 | Retención de junio sube ~1.8%. |
| 12 (dic.) | 1.000 | Diciembre no se ajusta (es el mes destino). |
consolidarPorReceptor — algoritmo
Section titled “consolidarPorReceptor — algoritmo”Recorre todos los honorarios del año y agrupa por rut_prestador. Para cada uno acumula:
| Campo | Cálculo |
|---|---|
meses | Set de meses con boleta (sin duplicados aunque haya varias en el mismo mes). |
retencionSinActualizar | Suma del monto_retencion directo. |
retencionActualizada | Suma del round(monto_retencion × factor[mes]). |
montoBrutoTotal | Suma del monto_bruto. |
detalleMensual[] | Un row por boleta: { mes, monto_bruto, retencion_sin_actualizar, factor_actualizacion, retencion_actualizada }. |
Output ordenado por RUT ascendente.
Cálculo de totales
Section titled “Cálculo de totales”totales = { retencionSinActualizar: Σ receptor.retencionSinActualizar, retencionHonorarios: Σ receptor.retencionActualizada, retencionDirectores: 0, // pendiente — se completará cuando se identifiquen directores};retencionDirectores (Art. 48 LIR) está hardcoded a 0 — el sistema aún no distingue directores de honorarios regulares. Cuando se agregue, debería leerse de un flag en honorarios o en empleados.
validar(ctx, id)
Section titled “validar(ctx, id)”Transición simple:
async validar(ctx, id) {return this.withTransaction(ctx, async (client, outbox) => { const dj = await DJ1879Repository.findById(client, id); if (!dj) throw new Error(`DJ 1879 ${id} no encontrada`); if (dj.estado === 'VALIDADO') return; // no-op idempotente
await DJ1879Repository.updateEstado(client, id, 'VALIDADO'); outbox.queue('dj:1879_status_cambiado', { declaracionId: id, anio: Number(dj.año_tributario), estado: 'VALIDADO', prevEstado: dj.estado, });});}No hay validación de negocio adicional — solo marca como validado. El estado DECLARADO se aplicaría externamente (no hay método para ello en este service; se haría con un updateEstado manual).
generateCSV(ctx, declaracionId) — formato SII F1879
Section titled “generateCSV(ctx, declaracionId) — formato SII F1879”Formato: 18 columnas, separador ;, sin encabezados, sin comillas, sin decimales (todos los montos redondeados con Math.round).
| Col | Ancho | Contenido |
|---|---|---|
| C1 | 8(N) | RUT sin DV. |
| C2 | 12(N) | Retención honorarios Art. 42 N°2 actualizada. |
| C3 | 12(N) | Retención directores Art. 48 actualizada (hoy siempre 0). |
| C4–C15 | 1(C) | Meses Ene–Dic: X si tuvo renta ese mes, vacío si no. |
| C16 | 12(N) | Monto Isla de Pascua (hoy siempre 0). |
| C17 | 12(N) | Retención 3% préstamos (hoy siempre 0). |
| C18 | 7(N) | Número de certificado (numero_certificado o numero_correlativo). |
Ejemplo de línea (1 receptor con honorarios en abril y agosto):
12345678;540000;0;;;X;;;X;;;;;;0;0;1stripDV — helper
Section titled “stripDV — helper”Acepta 12345678-9, 12.345.678-K, 12345678-9 y devuelve 12345678. Toupper + remover puntos + cortar en el guión.
Estados
Section titled “Estados”BORRADOR → VALIDADO → DECLARADO (manual)BORRADOR: por default trasgenerar. Permite regenerar (sobrescribe).VALIDADO: requiere llamarvalidar(). Bloquea regeneración (generarlanza si existe DJ no-borrador).DECLARADO: no hay método dedicado; se aplica conupdateEstadodirecto o por flujo manual.
Hooks emitidos
Section titled “Hooks emitidos”| Evento | Cuándo | Payload |
|---|---|---|
dj:1879_generada | generar post-COMMIT | { declaracionId, anio, totalReceptores, totalRetencionHonorarios } |
dj:1879_status_cambiado | validar cuando cambia de estado | { declaracionId, anio, estado, prevEstado } |
Endpoints
Section titled “Endpoints”| Método | Ruta | Service call |
|---|---|---|
POST | /api/declaraciones-juradas/1879/generar/:año | generar(ctx, año) |
GET | /api/declaraciones-juradas/1879 | list(ctx) |
GET | /api/declaraciones-juradas/1879/año/:año | getByAño(ctx, año) |
GET | /api/declaraciones-juradas/1879/:id | getWithDetalle(ctx, id) |
POST | /api/declaraciones-juradas/1879/:id/validar | validar(ctx, id) |
GET | /api/declaraciones-juradas/1879/:id/csv | generateCSV(ctx, id) (text/csv) |
Gotchas
Section titled “Gotchas”| Caso | Comportamiento |
|---|---|
| Año sin honorarios | Lanza Error('No hay honorarios registrados en X para generar DJ 1879'). |
DJ existe en VALIDADO/DECLARADO | Lanza Error('Ya existe DJ 1879 para X en estado Y'). |
DJ existe en BORRADOR | Se elimina y se regenera limpia. |
| Factor de corrección faltante en un mes | Usa factor 1 (sin actualización) y continúa. |
Honorario con monto_retencion = 0 | Se incluye igual en el detalle mensual; suma 0 al total. |
| Misma persona con boletas en múltiples meses | Aparece UNA vez en detalle, con array de meses + detalle mensual. |