Saltar al contenido principal

SIFEN — Eventos del emisor (Cancelación e Inutilización)

Fecha de cierre: 2026-04-10 Estado: Production-ready (ambos eventos probados contra SIFEN TEST) Tickets Jira: FAC-84 — Fase 1 (Cancelación) + Fase 2 (Inutilización)


Qué logramos

Pipeline completo de registro de eventos del emisor ante el WS sincrónico siRecepEvento de la DNIT:

EventoEndpointCódigo SIFENProtocolo test
CancelaciónPOST /api/sifen/eventos/cancelacion0600 — Evento registrado correctamente1089668
InutilizaciónPOST /api/sifen/eventos/inutilizacion0600 — Evento registrado correctamente1089669

Ambos con logging de auditoría en laravel.log (inicio, resultado, errores de validación, excepciones).


Cancelación de DTE

¿Qué es?

Invalida un DTE ya aprobado por SIFEN. La transacción no se concretó (error en datos del receptor, mercadería no entregada, servicio no realizado).

Reglas de negocio (Manual v150 §11.1.2, Tabla J)

ReglaValor
dTiGDENo se incluye — se infiere del nodo rGeVeCan
Plazo48h post-aprobación si es FE; 168h si es NCE/NDE/NRE/AFE
Estado requerido del DTEapproved
IdentificadorCDC de 44 dígitos
MotivoCampo libre 5–500 chars (mOtEve)
WSsiRecepEvento (sincrónico)
EfectoDTE pasa a estado cancelled en BD

Endpoint

POST /api/sifen/eventos/cancelacion
Authorization: Bearer <token>
Content-Type: application/json

{
"cdc": "01801260060001001000094322026040912850865440",
"motivo": "Cancelación por error en datos del receptor"
}

Respuesta exitosa:

{
"status": "approved",
"evento_id": 7,
"cod_res": "0600",
"msg_res": "Evento registrado correctamente",
"prot_aut": "1089668",
"cdc": "01801260060001001000094322026040912850865440",
"dte_id": 96,
"dte_estado": "cancelled"
}

Validaciones

  1. CDC debe existir en sifen_dtes con estado approved.
  2. Plazo: now() - last_sent_at <= 48h (FE) o <= 168h (otros).
  3. Motivo: 5–500 caracteres.
  4. CDC: exactamente 44 dígitos numéricos.

Pipeline interno

POST /api/sifen/eventos/cancelacion {cdc, motivo}
↓ CancelarEventoRequest (FormRequest)
↓ RegistrarCancelacionEventoAction::execute()
1. Validar DTE existe + approved + plazo OK
2. Crear SifenEvento(estado=pending)
3. SifenEventoXmlBuilder::buildCancelacion()
→ <gGroupGesEve><rGesEve><rEve Id="N"><gGroupTiEvt><rGeVeCan>
4. SifenEventoXmlDsigSigner::sign()
→ Reference URI="#N", Signature hijo de rGesEve
5. Storage::put('sifen/xml/eventos/{Y/m}/{id}.xml')
6. SifenSoapEnvelopeBuilder::buildEvento()
→ envelope SOAP con namespace default, embebido directo (sin CDATA)
7. SifenHttpClient::post(evento.wsdl, rEnviEventoDe)
8. Parsear dCodRes, dMsgRes, dProtAut
9. Update SifenEvento(estado=approved|rejected)
10. Si aprobado → SifenDte.estado = cancelled
↓ JsonResponse

Inutilización de rango de números de DE

¿Qué es?

Comunica a SIFEN que un rango de números de DE no fueron ni serán utilizados. Casos:

  • Saltos de numeración por error del sistema.
  • DEs rechazados por SIFEN cuyo ajuste modificó el CDC.
  • Decisión de la empresa de inutilizar un número.

Reglas de negocio (Manual v150 §11.1.1, Tabla J)

ReglaValor
dTiGDENo se incluye — se infiere del nodo rGeVeInu
Plazo15 primeros días del mes siguiente al hecho
Rango máximo1000 números secuenciales
RequisitoNingún DE del rango puede estar aprobado en SIFEN
MotivoCampo libre 5–500 chars
WSsiRecepEvento (sincrónico)
EfectoLos números quedan marcados como inutilizados en SIFEN

Endpoint

POST /api/sifen/eventos/inutilizacion
Authorization: Bearer <token>
Content-Type: application/json

{
"timbrado": 80126006,
"establecimiento": "001",
"punto_expedicion": "001",
"numero_inicio": "0004001",
"numero_fin": "0004010",
"tipo_documento": 1,
"motivo": "Saltos en numeración por reinicio del sistema de facturación"
}

Respuesta exitosa:

{
"status": "approved",
"evento_id": 8,
"cod_res": "0600",
"msg_res": "Evento registrado correctamente",
"prot_aut": "1089669",
"timbrado": "80126006",
"establecimiento": "001",
"punto_expedicion": "001",
"rango": "0004001 – 0004010",
"tipo_documento": 1
}

Validaciones

  1. numero_inicio ≤ numero_fin.
  2. Rango no supera 1000 números.
  3. Ningún DE aprobado o cancelado en el rango (query a sifen_dtes).
  4. No existe inutilización ya aprobada para el mismo rango/timbrado/est/punto.
  5. Todos los campos numéricos zero-padded: establecimiento (3), punto (3), números (7).
  6. tipo_documento entre 1 y 8 (1=FE, 2=FE export, 3=FE import, 4=AFE, 5=NCE, 6=NDE, 7=NRE, 8=CRE).
  7. Motivo: 5–500 caracteres.

Pipeline interno

POST /api/sifen/eventos/inutilizacion {timbrado, est, punto, rango, tipo_doc, motivo}
↓ InutilizarEventoRequest (FormRequest)
↓ RegistrarInutilizacionEventoAction::execute()
1. Validar rango (inicio ≤ fin, max 1000)
2. Verificar sin DEs aprobados en el rango
3. Verificar sin inutilización ya aprobada para el rango
4. Crear SifenEvento(estado=pending)
5. SifenEventoXmlBuilder::buildInutilizacion()
→ <gGroupGesEve><rGesEve><rEve Id="N"><gGroupTiEvt><rGeVeInu>
6. SifenEventoXmlDsigSigner::sign()
7. Storage::put() + SifenSoapEnvelopeBuilder::buildEvento()
8. SifenHttpClient::post(evento.wsdl, rEnviEventoDe)
9. Parsear respuesta + Update SifenEvento
↓ JsonResponse

Estructura XML del evento (validada contra SIFEN TEST)

Cancelación

<gGroupGesEve xmlns="http://ekuatia.set.gov.py/sifen/xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd">
<rGesEve xsi:schemaLocation="http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd">
<rEve Id="7">
<dFecFirma>2026-04-10T09:35:30</dFecFirma>
<dVerFor>150</dVerFor>
<gGroupTiEvt>
<rGeVeCan>
<Id>01801260060001001000100122026041017345886385</Id>
<mOtEve>Test completo con logging de auditoría</mOtEve>
</rGeVeCan>
</gGroupTiEvt>
</rEve>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<!-- Reference URI="#7" sobre rEve -->
</Signature>
</rGesEve>
</gGroupGesEve>

Inutilización

<gGroupGesEve xmlns="http://ekuatia.set.gov.py/sifen/xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd">
<rGesEve xsi:schemaLocation="http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd">
<rEve Id="8">
<dFecFirma>2026-04-10T09:35:30</dFecFirma>
<dVerFor>150</dVerFor>
<gGroupTiEvt>
<rGeVeInu>
<dNumTim>80126006</dNumTim>
<dEst>001</dEst>
<dPunExp>001</dPunExp>
<dNumIn>0004001</dNumIn>
<dNumFin>0004010</dNumFin>
<iTiDE>1</iTiDE>
<mOtEve>Test inutilización con logging</mOtEve>
</rGeVeInu>
</gGroupTiEvt>
</rEve>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<!-- Reference URI="#8" sobre rEve -->
</Signature>
</rGesEve>
</gGroupGesEve>

Envelope SOAP (igual para ambos)

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header/>
<env:Body>
<rEnviEventoDe xmlns="http://ekuatia.set.gov.py/sifen/xsd">
<dId>7</dId>
<dEvReg>{XML_DEL_EVENTO_FIRMADO}</dEvReg>
</rEnviEventoDe>
</env:Body>
</env:Envelope>

SOAP Action: rEnviEventoDe. Endpoint: evento.wsdl. Embebido DIRECTO (sin CDATA) — patrón Manual cap7 §7.2.2.2.


Endpoint de consulta

GET /api/sifen/dtes/{id} — DTE con eventos

Cuando un DTE tiene eventos asociados, el endpoint los incluye:

{
"id": 96,
"estado": "cancelled",
"cancelado": true,
"eventos": [
{
"id": 7,
"tipo": 1,
"tipo_descripcion": "Cancelación del DTE",
"estado": "approved",
"sifen_result_code": "0600",
"prot_aut": "1089668",
"motivo": "...",
"fecha_envio": "2026-04-10T09:35:30+00:00"
}
]
}

El flag cancelado: true permite al frontend renderizar watermark "CANCELADO" sobre el KuDE.

GET /api/sifen/eventos/{id} — Detalle de un evento

Retorna todos los campos del evento individual (tipo, cdc/rango, motivo, estado, cod_res, prot_aut, etc.).


Logging de auditoría (laravel.log)

Todos los eventos registran en laravel.log con prefijo [SIFEN] Evento <tipo>: para facilitar filtrado:

grep "[SIFEN] Evento" storage/logs/laravel.log

Niveles de log

NivelCuándoEjemplo
INFOInicio del registro[SIFEN] Evento Cancelación: iniciando registro. {evento_id:7, dte_id:96, cdc:..., motivo:...}
INFOResultado de SIFEN[SIFEN] Evento Cancelación: resultado. {estado:approved, cod_res:0600, prot_aut:1089668}
WARNINGValidación fallida[SIFEN] Evento Inutilización: rango ya inutilizado. {rango:0004001–0004010}
WARNINGCDC no encontrado[SIFEN] Evento Cancelación: CDC no encontrado. {cdc:...}
WARNINGDTE no aprobado[SIFEN] Evento Cancelación: DTE no aprobado. {dte_id:X, estado:Y}
WARNINGRango inválido/excede máx[SIFEN] Evento Inutilización: rango excede máximo. {cantidad:1500}
WARNINGDEs aprobados en rango[SIFEN] Evento Inutilización: DEs aprobados en el rango. {aprobados:3}
ERRORExcepción inesperada[SIFEN] Evento Cancelación: excepción. {exception:...}

Códigos de respuesta SIFEN para eventos

CódigoSignificado
0600Evento registrado correctamente (aprobado) — ambos tipos
0160XML mal formado (ver sección bugs)
4050–4099Validaciones específicas de Inutilización
0560–0579Mensaje de entrada del WS SiRecepEvento
0580–0599Control de llamada al WS SiRecepEvento

La Action acepta cualquier 06xx como aprobado por compatibilidad.


Bugs resueltos durante la implementación (FAC-84)

Cancelación — 4 bugs

#BugSíntomaFix
1Builder sin wrapper gGroupGesEve0160Agregar GDE000 como raíz
2Falta xmlns:xsi + xsi:schemaLocation0160Agregados en gGroupGesEve y rGesEve
3Campo dTiGDE sobrante0160Removido — se infiere del nodo rGeVeCan/rGeVeInu
4buildEvento() usaba CDATA + prefijo xsd:0160Namespace default + embebido directo (Manual cap7)

Inutilización — Sin bugs adicionales

La Fase 2 reutilizó toda la infraestructura corregida en Fase 1 (builder, signer, envelope). Funcionó al primer intento.


Tabla sifen_eventos (BD)

id BIGSERIAL PK
sifen_emitter_id FK → sifen_emitters
sifen_dte_id FK → sifen_dtes (nullable — inutilización no tiene DTE)
tipo SMALLINT (1=cancelación, 2=inutilización)
cdc VARCHAR(44) (solo cancelación)
timbrado VARCHAR(8) (solo inutilización)
establecimiento VARCHAR(3) (solo inutilización)
punto_expedicion VARCHAR(3) (solo inutilización)
numero_inicio VARCHAR(7) (solo inutilización)
numero_fin VARCHAR(7) (solo inutilización)
tipo_documento SMALLINT (solo inutilización, iTiDE 1-8)
motivo TEXT (5-500 chars)
xml_signed_path VARCHAR(255)
estado VARCHAR(20) — pending|sent|approved|rejected
sifen_result_code VARCHAR(4)
sifen_result_msg VARCHAR(500)
prot_aut VARCHAR(20) — protocolo de autorización SIFEN
fecha_envio TIMESTAMP
created_at TIMESTAMP
updated_at TIMESTAMP

Índices: dte_id, estado, tipo, cdc.


Archivos del módulo

ArchivoRol
app/Domains/Sifen/Enums/TipoEvento.phpEnum 1=CANCELACION, 2=INUTILIZACION
app/Domains/Sifen/DTOs/CancelacionEventoData.phpDTO readonly (cdc + motivo)
app/Domains/Sifen/DTOs/InutilizacionEventoData.phpDTO readonly (timbrado + rango + motivo)
app/Domains/Sifen/Models/SifenEvento.phpModelo Eloquent + Swagger schema
app/Domains/Sifen/Services/Xml/SifenEventoXmlBuilder.phpBuilder: buildCancelacion() + buildInutilizacion()
app/Domains/Sifen/Services/Signing/SifenEventoXmlDsigSigner.phpFirma XMLDSig sobre rEve
app/Domains/Sifen/Actions/RegistrarCancelacionEventoAction.phpOrquestador cancelación + logging
app/Domains/Sifen/Actions/RegistrarInutilizacionEventoAction.phpOrquestador inutilización + logging
app/Http/Requests/Sifen/CancelarEventoRequest.phpFormRequest validación cancelación
app/Http/Requests/Sifen/InutilizarEventoRequest.phpFormRequest validación inutilización
app/Http/Controllers/Api/SifenController.phpcancelarEvento() + inutilizarEvento() + showEvento()
app/Domains/Sifen/Services/Soap/SifenSoapEnvelopeBuilder.phpbuildEvento() — envelope sin CDATA
database/migrations/2026_04_09_140000_create_sifen_eventos_table.phpTabla sifen_eventos
routes/api.phpPOST /eventos/cancelacion + POST /eventos/inutilizacion + GET /eventos/{id}
scripts/tinker_test_cancelacion_evento.phpTest end-to-end cancelación

Referencias

  • Manual Técnico v150 — docs/pdf/Manual Técnico Versión 150.pdf
    • §7.2.2.2 — Namespace del envelope de eventos
    • §9.5 — WS siRecepEvento
    • §11.1.1 — Inutilización de número de DE
    • §11.1.2 — Cancelación de DTE
    • §11.5 — Estructura XML de los eventos (Schema 19)
    • §11.5.1 — Formato eventos emisor (rGeVeCan / rGeVeInu)
  • docs/manuales-md/manual_v150_cap11_eventos.md — Capítulo 11 en Markdown
  • docs/manuales-md/manual_v150_cap12_validaciones.md — Códigos 0600, 4050–4099
  • docs/guias-tecnicas/SIFEN_OPERATIVE_LOGIC.md §4.2 — Estructura XML cancelación (patrón validado)
  • docs/analisis-y-planes/PLAN_FAC84_EVENTOS_CANCELACION_INUTILIZACION.md — Plan original