Skip to content

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.

MétodoPara
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).
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 dato
for (let m = 1; m <= 12; m++) {
if (!map.has(m)) map.set(m, 1);
}
Mes origenFactor (ejemplo)Significado
1 (enero)1.038Una retención de 100.000eneneroseactualizaa100.000 en enero se actualiza a 103.800.
6 (junio)1.018Retención de junio sube ~1.8%.
12 (dic.)1.000Diciembre no se ajusta (es el mes destino).

Recorre todos los honorarios del año y agrupa por rut_prestador. Para cada uno acumula:

CampoCálculo
mesesSet de meses con boleta (sin duplicados aunque haya varias en el mismo mes).
retencionSinActualizarSuma del monto_retencion directo.
retencionActualizadaSuma del round(monto_retencion × factor[mes]).
montoBrutoTotalSuma del monto_bruto.
detalleMensual[]Un row por boleta: { mes, monto_bruto, retencion_sin_actualizar, factor_actualizacion, retencion_actualizada }.

Output ordenado por RUT ascendente.

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.

Transición simple:

validar
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).

ColAnchoContenido
C18(N)RUT sin DV.
C212(N)Retención honorarios Art. 42 N°2 actualizada.
C312(N)Retención directores Art. 48 actualizada (hoy siempre 0).
C4–C151(C)Meses Ene–Dic: X si tuvo renta ese mes, vacío si no.
C1612(N)Monto Isla de Pascua (hoy siempre 0).
C1712(N)Retención 3% préstamos (hoy siempre 0).
C187(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;1

Acepta 12345678-9, 12.345.678-K, 12345678-9 y devuelve 12345678. Toupper + remover puntos + cortar en el guión.

BORRADOR → VALIDADO → DECLARADO (manual)
  • BORRADOR: por default tras generar. Permite regenerar (sobrescribe).
  • VALIDADO: requiere llamar validar(). Bloquea regeneración (generar lanza si existe DJ no-borrador).
  • DECLARADO: no hay método dedicado; se aplica con updateEstado directo o por flujo manual.
EventoCuándoPayload
dj:1879_generadagenerar post-COMMIT{ declaracionId, anio, totalReceptores, totalRetencionHonorarios }
dj:1879_status_cambiadovalidar cuando cambia de estado{ declaracionId, anio, estado, prevEstado }
MétodoRutaService call
POST/api/declaraciones-juradas/1879/generar/:añogenerar(ctx, año)
GET/api/declaraciones-juradas/1879list(ctx)
GET/api/declaraciones-juradas/1879/año/:añogetByAño(ctx, año)
GET/api/declaraciones-juradas/1879/:idgetWithDetalle(ctx, id)
POST/api/declaraciones-juradas/1879/:id/validarvalidar(ctx, id)
GET/api/declaraciones-juradas/1879/:id/csvgenerateCSV(ctx, id) (text/csv)
CasoComportamiento
Año sin honorariosLanza Error('No hay honorarios registrados en X para generar DJ 1879').
DJ existe en VALIDADO/DECLARADOLanza Error('Ya existe DJ 1879 para X en estado Y').
DJ existe en BORRADORSe elimina y se regenera limpia.
Factor de corrección faltante en un mesUsa factor 1 (sin actualización) y continúa.
Honorario con monto_retencion = 0Se incluye igual en el detalle mensual; suma 0 al total.
Misma persona con boletas en múltiples mesesAparece UNA vez en detalle, con array de meses + detalle mensual.