Saltar al contenido
dte-sdk

El ejecutor

runCertification hace todo el lado HTML de la certificación en el orden que espera el SII, y se reanuda desde un almacén: córrelo de nuevo tras una corrección y continúa donde lo indica el estado guardado, sin repetir lo que ya está hecho. Debajo, cada paso es una función que puedes llamar por separado (sendTestSet, declareTestSets, runBooks…), y lo mismo cada operación del portal (downloadTestSet, declareProgress, readProgress…).

Nada de esto se ha ejecutado todavía contra los portales. Las pruebas usan páginas sintéticas, así que cada forma de página sigue sin comprobarse. En tu primera corrida con una empresa inscrita, conserva los artefactos que escribe el almacén (ahí queda cada respuesta del portal) como fixtures. Ver Estado.

Dos cosas no se pueden deshacer. Descargar de nuevo el set de pruebas lo regenera en el SII y deja sin efecto todo lo enviado para el anterior. La declaración de cumplimiento es la declaración jurada del representante legal: el ejecutor la hace solo con declareCompliance: true. Sin esa opción la corrida termina en la detención compliance.

Uso básico#

TypeScript
import { readFileSync } from 'node:fs'
import { createSiiClient, loadCertificateFromPkcs12 } from 'dte-sdk'
import { createDirectoryFolioRegistry } from 'dte-sdk/node'
import { createPortalClient, createDirectoryCertificationStore, runCertification } from 'dte-sdk/portal'
import { readPrintedSamples } from 'dte-sdk/pdf'
 
const certificate = loadCertificateFromPkcs12(readFileSync('firma.pfx'), process.env.PFX_PASSWORD!)
const companyRut = '76543210-3'
 
const deps = {
  portal: createPortalClient({ environment: 'certification', certificate, companyRut }),
  sii: createSiiClient({ environment: 'certification', certificate, companyRut }),
  store: createDirectoryCertificationStore({ dir: './certification' }), // state.json y los artefactos; guarda llaves de CAF
  registry: createDirectoryFolioRegistry({ dir: './folios' }),
  issuer: { rut: companyRut, businessName: 'Comercial Andina SpA', address: 'Av. Los Aromos 1540' },
  certificate,
  // receiver: el receptor de prueba 66666666-6 por defecto
  // resolution: número 0 con la fecha UTC del día en que parte la corrida, por defecto
  onProgress: (event) => console.log(event.step),
}
 
// Primera corrida: descarga el set de pruebas, obtiene los folios, envía y declara los cuatro sets,
// los libros, la simulación, responde el intercambio, y se detiene hasta que se entreguen las muestras
const first = await runCertification(deps)
first.stopped // 'printedSamples' cuando todo salió bien
 
// Segunda corrida: sube las muestras y se detiene en `compliance`: la declaración jurada necesita un permiso explícito
const second = await runCertification(deps, { samples: () => readPrintedSamples('./certification/samples') })
second.stopped // 'compliance'
 
// Tercera corrida, cuando ya leíste las páginas que guardó el almacén y estás seguro
const third = await runCertification(deps, { declareCompliance: true })
third.stopped // 'done'

Córrelo de nuevo tras cualquier detención que no sea done. Un rechazo, un bloqueo o un timeout vuelven como datos (stopped, detail) con el estado guardado, así que puedes leerlos, corregir la causa y volver a correr. Un error lanzado por el portal, el SII o los folios se propaga después de guardar el estado, con un evento CERT_ERROR.

Dependencias#

runCertification(dependencies, options) recibe un CertificationDependencies. Cada función de paso recibe el mismo objeto.

CampoSignificado
portalPortalClient en el ambiente de certificación (createPortalClient)
siiSiiClient en el ambiente de certificación (createSiiClient)
storeEl CertificationStore donde viven el estado y los artefactos
registryEl registro de folios: createDirectoryFolioRegistry (dte-sdk/node) o createMemoryFolioRegistry
issuerLa empresa: rut, businessName y address son obligatorios. El giro, el código de actividad, la comuna y la ciudad tienen valores por defecto
certificateEl certificado de firma
receiverOpcional, campo por campo: por defecto el receptor de prueba
resolution{ number, date } opcional: por defecto número 0 y la fecha UTC del día de la corrida
nowReloj. Las fechas que la corrida escribe para el SII son fechas de Chile de este reloj
sleepFunción de espera. Por defecto las esperas reales; inyecta una para probar sin esperar
onProgressRecibe cada CertificationProgressEvent
contactEl contacto (name, mail, phone) que nombran las respuestas del intercambio

Opciones y resultado#

OpciónSignificado
untilUna CertificationStage: detenerse al completar esa etapa
optionalSetsLos sets opcionales a marcar al descargar, como códigos SETnn. Por defecto DEFAULT_OPTIONAL_SETS
declareComplianceHacer la declaración de cumplimiento. Apagado por defecto
exchangeSetXmlEl set de intercambio, cuando lo descargaste a mano
samplesLos PDF a subir, o una función que los entrega (readPrintedSamples). Sin ella la corrida se detiene en printedSamples

runCertification devuelve { state, stopped, detail? }: el estado tal como quedó guardado, por qué se detuvo, y una palabra o frase sobre el porqué.

stoppedSignificado
doneSe hizo la declaración de cumplimiento
complianceTodo está hecho hasta la declaración, que espera declareCompliance: true
printedSamplesLas muestras esperan la opción samples
exchangeNo se pudo descargar el set de intercambio, o la empresa no está en la etapa de intercambio. detail dice cuál: ver Intercambio y muestras
untilLa etapa en que pediste detenerte está completa
rejectedEl SII o el portal rechazó: una declaración que no se tomó, un set o la simulación rechazados, una revisión rechazada, o un portal que no avanzó más allá de los libros. detail trae su frase
blockedEl portal dice que la postulación no está en la etapa (las muestras, o la declaración final). detail trae su frase
timedOutUna espera se agotó sin respuesta
incompleteNo se pudo enviar un libro obligatorio

El orden de la corrida#

1

Sets de pruebas

El set de pruebas se descarga una sola vez (cuando el estado no tiene plan), se parsea y se planifica. La corrida obtiene los folios que necesita y luego emite y envía cada set que trae el plan y que el estado aún no envió, en el orden basic, guide, exempt, purchase. Declara los cuatro track id y espera la aprobación.

2

Libros

Los libros de compras, ventas y guías (y el de compras para exentos cuando el set de pruebas lo trajo) se arman, envían, declaran y esperan. Con los tres libros obligatorios enviados, la corrida pide al portal la etapa siguiente.

3

Simulación

Folios nuevos, cada caso emitido de nuevo en un sobre, enviado, declarado y confirmado.

4

Intercambio

El set de intercambio se descarga (o se entrega), se responde con tres archivos firmados, se suben y se confirma.

5

Muestras impresas

Los PDF de samples se suben como una revisión, que se envía y se lee de nuevo. Sin samples la corrida se detiene aquí.

6

Cumplimiento

La declaración de cumplimiento, solo con declareCompliance: true.

until se revisa entre etapas: la corrida devuelve until apenas la etapa guardada es posterior a la que pediste.

Reanudar desde el almacén#

El estado registra dónde va la corrida (stage), así que una corrida parte en la etapa que guardó. Dentro de ella nada se repite:

  • un set con track id no se vuelve a enviar, y el set de pruebas no se vuelve a descargar mientras el estado tenga un plan;
  • una simulación confirmada no se declara de nuevo, un intercambio confirmado no se responde de nuevo, y una revisión ya enviada no se vuelve a subir;
  • la fase de libros es la excepción: parte de cero en cada corrida, y qué libros están aprobados lo dice el portal, no lo que recuerda el estado. Ver La fase de libros.

Un error que detiene un paso deja el estado como lo guardó lo último que salió bien. Cada CAF obtenido se guarda de inmediato, así que un paso que termina corto de folios los deja en mano para el siguiente intento en vez de pedirlos otra vez al SII.

Cada paso por separado#

Cada paso recibe las dependencias y el estado y devuelve { state, outcome, detail? } con el estado que guardó. Úsalos después de que el SII rechazó un set con observaciones, para rehacer una pieza sin correr el resto:

TypeScript
let state = (await deps.store.load())!
state = (await sendTestSet(deps, state, { kind: 'basic' })).state
state = (await declareTestSets(deps, state)).state
PasoQué haceResultados (outcome)
obtainTestSetDescarga el set de pruebas, lo parsea, lo planifica y guarda el texto y el plan como artefactos. Descargar regenera el set en el SII: un estado que ya tenía plan parte de cero (todo lo enviado queda sin efecto), conservando solo sus CAF y folios usadosloaded, regenerated
prepareFoliosSe asegura de que la corrida tenga los folios que necesita cada tipo. Ver Foliosready (y los cafs con que emitir)
sendTestSetEmite un set con los folios del registro, guarda el sobre y cada documento como artefactos, lo sube, marca los folios enviados pase lo que pase, y registra el track id y la fecha de Chilesent. Lanza ante un rechazo
declareTestSetsDeclara los sets enviados hasta ahora con la fecha de hoy en Chile, y guarda cualquier diagnósticoUn DeclarationOutcome, más success
waitForTestSetsApprovalConsulta la página de avance hasta que los sets estén aprobadosapproved, rejected, timedOut
runBooksLa fase de librosapproved, incomplete, rejected, timedOut
advanceToNextStagePide al portal la etapa posterior a los librosadvanced, unchanged, failed, timedOut
sendSimulationObtiene folios nuevos (nunca los CAF de los sets), emite y envía la simulaciónsent
declareSimulationDeclara la simulación, salvo que el portal ya nombre el intercambio como la etapaUn DeclarationOutcome o skipped, más success
waitForSimulationApprovalConsulta hasta que la simulación esté aprobada y confirmadadone, confirmed, rejected, timedOut
runExchangeLa etapa de intercambiodone, alreadyDone, setUnavailable, notInExchange
uploadPrintedSamplesLa etapa de muestras impresassubmitted, alreadySubmitted, blocked, rejected
declareComplianceFinalUna petición más para avanzar, y luego la declaración de cumplimientodeclared, blocked

Detalles que importan cuando los llamas por separado:

  • sendTestSet lanza MISSING_FIELD (plan) sin el plan del set. Cuando la subida falla, igual marca los folios como enviados en el registro (un timeout pudo haber entregado el sobre, y un folio enviado dos veces rechaza el siguiente sobre) y emite SET_ERROR.
  • declareTestSets lanza MISSING_FIELD (sets) cuando no se envió nada.
  • sendSimulation y declareSimulation lanzan MISSING_FIELD (plan, simulation) cuando falta su entrada.
  • declareComplianceFinal lanza SII_PORTAL_UNEXPECTED_PAGE cuando la última página no confirma. Ver La detención en cumplimiento.
  • setFolioNeeds, simulationFolioNeeds, previousPeriod y nextFreePeriod son ayudantes exportados de los pasos de folios y de libros.

Las operaciones del portal que hay debajo#

Los pasos se construyen sobre estas funciones de dte-sdk/portal. Cada una recibe primero el PortalClient y, como último argumento, ajustes opcionales de la llamada.

OperaciónQué hace
downloadTestSet(portal, { optionalSets, download, previousAttentionNumber })Genera y descarga el set de pruebas. Devuelve su texto, su número de atención y si se regeneró. Con download: false solo lee la tabla de estados. Lanza SII_PORTAL_REJECTED con notEnrolled para una empresa no inscrita
queryCertificationSets(portal)Lee el estado de cada set y libro, y si los obligatorios están aprobados
declareProgress(portal, { entries })Declara track id y fechas. Ver Declaraciones
readProgress(portal)Lee la página de avance: la etapa, el estado de cada set y libro, y si la simulación espera su confirmación
advanceStage(portal)Pide la etapa siguiente: advanced, unchanged (el portal dice que ya está ahí) o failed, con la frase
listBookHistory(portal, { year })Lista los libros que el SII tiene para un año. Todo período listado está ocupado para un nuevo envío total
declareCompliance(portal)La declaración de cumplimiento: declared, blocked o unconfirmed, con la frase

El almacén#

Un CertificationStore es donde viven el estado y los artefactos de una corrida:

TypeScript
type CertificationStore = {
  load: () => Promise<CertificationState | null>
  save: (state: CertificationState) => Promise<void>
  writeArtefact: (name: string, content: string) => Promise<void>
  readArtefact: (name: string) => Promise<string | null>
  listArtefacts: () => Promise<readonly string[]>
}

Los nombres de artefacto son rutas relativas con / de separador, hechas solo de letras, dígitos, ., _ y -; cualquier otra cosa lanza INVALID_OPTION (name). createMemoryCertificationStore() lo guarda todo en memoria, para pruebas y corridas cortas. createDirectoryCertificationStore({ dir }) guarda state.json (escrito de forma atómica, con un archivo temporal y un renombre) y los artefactos como archivos, con una / en un nombre como subdirectorio. Un servicio puede implementar el tipo sobre su propio almacenamiento.

El estado es un secreto. Contiene los archivos CAF, cada uno con su llave privada. El almacén en directorio crea sus directorios privados para ti (modo 0700) y sus archivos ilegibles para otros (0600); un directorio que ya existe conserva su modo. Está pensado para un solo proceso. Mantén el directorio fuera del control de versiones.

createCertificationState(companyRut) crea un estado nuevo. El estado es un valor JSON simple; sus campos principales:

CampoContiene
stageDónde va la corrida: una CertificationStage
setText, attentionNumber, planEl set de pruebas descargado, su número de atención y el plan
setReadDateEl día UTC en que se leyó el set: su mes es el período base de los libros
cafsLos archivos CAF que tiene la corrida (con llaves privadas)
usedFoliosCada folio que usó la corrida
annulledRangesLos rangos que el SII dio por anulados o recibidos, por tipo de documento
sets, setsApprovedLos sets enviados (track id, fecha de Chile, instante, casos) y si están aprobados
booksEl período en uso, los períodos ocupados, los ajustes intentados, los totales acumulados y cada libro enviado
simulationLa simulación enviada, y si se confirmó
lastDeclarationLa última declaración hecha y cómo terminó
diagnosisLo que dijo el estado SOAP de un envío, por track id
exchange, samplesLas etapas de intercambio y de muestras impresas
blockedLa frase que bloqueó la declaración de cumplimiento

Los artefactos son lo que inspeccionas, y de lo que se arman las muestras impresas:

ArtefactoQué es
set-texto.txt, estructuras.jsonEl texto del set de pruebas y el plan
sets-prueba/envio-set-basico.xml, …-guia.xml, …-exenta.xml, …-compra.xmlEl sobre de cada set
sets-prueba/dtes/dte-<tipo>-<folio>.xmlCada documento de los sets (SET_ARTEFACT_NAMES nombra los sobres)
libro-ventas.xml, libro-compras.xml, libro-guias.xml, libro-compras-exentos.xmlLos libros (BOOK_ARTEFACT_NAMES)
simulacion/envio-simulacion.xml, simulacion/dtes/…La simulación
declaracion-sets-<n>.html, declaracion-libros-<n>.html, declaracion-simulacion-<n>.html (y …-pe_avance2-<n>.html, …-estado-<clave>-<trackId>.xml)Cada página de declaración y cada respuesta del estado SOAP
intercambio/…, muestras/…Cada respuesta del portal del intercambio y de las muestras impresas, y las respuestas del intercambio

findEnvelopeFiles de dte-sdk/pdf encuentra por nombre los sobres de los sets y de la simulación dentro de este directorio, así que writePrintedSamples puede generar los PDF directamente desde él. Ver Intercambio y muestras.

Eventos de progreso#

onProgress recibe un CertificationProgressEvent por cada paso de la corrida. Los nombres están en CERTIFICATION_STEPS:

FaseEventos
Set de pruebasSETS_DOWNLOADING, SETS_LOADED, SETS_REGENERATED
FoliosCAF_REQUESTING y CAF_OK, cada uno con el type del documento
SetsSET_START (con set), SET_OK (con set y trackId), SET_ERROR (con set y error), SETS_DECLARING, SETS_DECLARED, SETS_APPROVED, SETS_REJECTED
EsperasPOLLING con attempt, max y label: sets, declaration, books o simulation
LibrosBOOKS_START, BOOK_SKIPPED, BOOK_SENDING, BOOK_OK (con trackId), BOOK_ERROR (con error), BOOK_PERIOD_RETRY (con period y attempt), BOOKS_DECLARING, BOOKS_DONE (con books y success)
AvanceADVANCE_WAITING, ADVANCE_DONE (con el texto de stage del portal)
SimulaciónSIM_START, SIM_SENDING, SIM_OK (con trackId), SIM_DECLARING, SIM_DONE
IntercambioINTERCAMBIO_START, INTERCAMBIO_DESCARGADO, INTERCAMBIO_DONE (con uploaded)
MuestrasMUESTRAS_UPLOADING (con count), MUESTRAS_DONE (con success)
FinCERT_DONE, CERT_ERROR (con error)

El avance final de declareComplianceFinal no lee ninguna etapa, así que no emite ningún evento.

Folios#

prepareFolios se asegura de que la corrida tenga los folios que necesita cada tipo, en el orden en que el SII los raciona. Nunca pide más de lo que necesitan los sets aún no enviados, porque folios autorizados sin usar hacen que el SII racione la próxima solicitud.

  1. Primero los CAF en mano. Un CAF vencido se omite, porque el SII rechaza sus folios. Los folios de esta corrida que el SII nunca recibió (un envío que falló) se devuelven al registro. Los folios que el SII ya recibió, de una corrida anterior o de otro software, se marcan como usados para saltarlos.
  2. Luego el cupo. Se lee el máximo del portal. Un rechazo o un fallo al leerlo no detiene: la corrida sigue sin tope. Un cupo bloqueado, o corto con folios sin usar en el SII, los reobtiene para toda la necesidad del tipo. Una respuesta completa cierra el tipo; una corta se conserva, en lugar de los CAF en mano, solo si descargó algo que cubre más que ellos.
  3. Luego una solicitud. Un máximo bloqueado no pide nada al portal. Si no, una solicitud exacta, o por tandas cuando el máximo está bajo la necesidad (tantas tandas como folios falten), cayendo a tandas cuando la exacta queda corta con un máximo que no es cero. Un bloqueo de la persona intenta la reobtención una vez antes que cualquier otra cosa.
  4. Anular es el último recurso. Solo cuando la negativa es racionamiento y el SII muestra más folios sin usar que los que tiene la corrida, se anulan rangos sin usar de los últimos 180 días: como máximo 10 por pasada, en hasta cuatro pasadas. Nunca toca un rango que la corrida descargó (los CAF del estado, incluidos los vencidos, los recién obtenidos y los que trajo una reobtención y la corrida no conservó). Los rangos que el SII reportó anulados se guardan en el estado (annulledRanges) y no se vuelven a intentar. Si se anuló alguno, se lee de nuevo el máximo y se repite la solicitud siguiéndolo. Una anulación que falla antes de que el SII responda cuenta como ninguna.

Si aún falta, lanza FOLIO_OUT_OF_RANGE con type, required, available, maxAuthorized, availableFolios, rejection y un reason (noUnusedFolios o unusedFoliosBlock). Cuando las tandas se detuvieron por una falla y no por la respuesta del SII, los CAF que obtuvieron ya están en el estado, y el paso lanza en cambio el código de esa falla, con type, operation: 'requestCafInBatches' y step (queryFolioQuota o requestCaf). Antes de la 0.5.0 el mismo código salía de las propias tandas, y los CAF que habían obtenido se perdían.

Una reobtención cuyas descargas se detuvieron por una falla, o una anulación cortada cuando el SII ya había respondido, termina el paso de la misma forma: lo que trajo la reobtención se conserva con la regla del paso 2, y lo que anuló la anulación va a annulledRanges; luego el paso lanza el código de la falla con type, operation (reobtainFolios o annulFolios), step y, cuando el SII rechazó, rejection. Antes de la 0.6.0 una anulación cortada contaba como ninguna y el paso terminaba con FOLIO_OUT_OF_RANGE, que leía una falla de red como racionamiento. El error lanzado, y el evento CERT_ERROR, traen solo el código y esas claves: el error original, con su reason, httpStatus y attempts, va al logger del cliente del portal como sii portal flow stopped, keeping what it holds, con sus textos tachados y sin lo que dijo una página ni su url.

Una solicitud que el SII responde con el formulario de solicitud otra vez, sin folios y sin motivo, termina con rejection: 'stampingUncertain'. No es un rechazo de racionamiento, así que por ella no se anula nada. Puede que el SII haya autorizado el rango: revisa los rangos que se pueden reobtener antes de volver a correr.

Una tanda posterior que el SII rechaza también conserva su tipo. Antes de la 0.7.0 cualquier rechazo del segundo viaje o de uno posterior, salvo stampingUncertain, salía de las tandas como quotaCapReached, que se lee como racionamiento: un rechazo de la empresa o de la opción llevaba al paso a anular folios, y un bloqueo de la persona no se levantaba primero. Ahora solo un folio por sobre el máximo después de una entrega (quotaExceeded) se lee como quotaCapReached, así que un rechazo posterior que no es racionamiento nunca lleva a anular, y un bloqueo de la persona se levanta primero reobteniendo, como en el primer viaje.

Anular no se puede deshacer y cuenta contra el cupo durante seis meses. El ejecutor lo hace solo como último recurso, cuando el SII raciona el tipo y muestra más folios sin usar que los que tiene la corrida. Ver Portal.

Los folios se comprueban contra el SII antes de emitir. Antes de emitir un set, se le pregunta al SII si ya tiene los folios que la corrida está por usar (como la empresa, hasta 30 consultas por CAF, el CAF más antiguo primero, hasta cubrir la necesidad); uno que el SII tiene se marca como usado. Un folio por el que el SII no puede responder se usa. Un rango obtenido de nuevo se usa solo a través de los folios que el SII mostró libres: todo otro folio de él se marca como usado apenas se guarda el CAF, así el registro cuenta exactamente los libres. El registro entrega su folio libre más bajo, no el siguiente al último. sendTestSet y sendSimulation marcan cada folio como enviado en el registro sea cual sea el resultado del envío.

Declaraciones#

declareProgress(portal, { entries }) declara envíos en el formulario de avance del SII (pe_avance2). Una entrada es { trackId, date } con la fecha como AAAA-MM-DD; las claves son las filas del formulario (basic, guide, exempt, purchase, los libros, simulation).

  • Del formulario se leen los campos ocultos y el índice del campo de cada fila, buscada por su etiqueta (los índices se mueven al aprobarse sets). Una página sin formulario no envía nada y el resultado es declined.
  • Se envían todos los campos NUM_ENV, FEC_ENV, SET y EST hasta TOTREG en el orden del formulario, con los valores que ya trae la página: el track id sin ceros a la izquierda y la fecha como dd-mm-aaaa. Una fila sin campo (aprobada, o en revisión) no agrega nada y el formulario se envía igual.
  • La página se lee de nuevo para verificar. La declaración queda en pie cuando ningún campo declarado volvió vacío, o alguna fila muestra EN REVISION. Una fila que quedó vacía se pregunta de nuevo en el ciclo.
  • Con cada track id va la fecha de hoy en Chile, sea cual sea el día en que se envió el sobre.
  • ENVIO CON ERRORES O REPAROS es un estado de un envío, no una falla de la declaración. La respuesta nombra las filas afectadas (contentErrors); la declaración puede seguir en pie. La corrida pregunta una vez el estado SOAP de esos envíos y guarda la respuesta como diagnóstico.
outcomeSignificado
accepted, inReviewLa declaración quedó en pie
contentErrorsEl SII encuentra errores en algunos envíos (la declaración misma puede seguir en pie, ver success)
allRejectedToda fila declarada volvió vacía: el SII no tomó ninguna
notSavedAlgunas filas declaradas volvieron vacías
inconsistentUna fecha o número que el SII dice que no corresponde
declinedNo había formulario que enviar
failedUn envío que el portal no aceptó

El ciclo de la corrida (declareTestSets, declareSimulation y la fase de libros) espera 10 s y luego hace hasta 10 intentos separados por 5 s. Una declaración que queda en pie lo termina, salvo que la respuesta diga que el SII aún no procesó los envíos, que se pregunta de nuevo (una sesión perdida se lee así, y el intento siguiente vuelve a iniciar sesión). Los errores de contenido se preguntan de nuevo mientras los campos declarados vuelvan vacíos (el SII aún no vio los track id); con los campos en su lugar se diagnostican y el ciclo termina. Una declaración que no se guardó se pregunta de nuevo; toda fila declarada vacía termina el ciclo, salvo en la simulación, donde se reintenta porque el portal muestra la simulación sin declarar mientras aún procesa el track id. El último intento devuelve lo que obtuvo, y cada página va a los artefactos.

Envíos de la corrida#

Cada sobre y cada libro de la corrida pasa por una misma rutina de envío.

  • Reintentos. Un envío se repite solo ante una falla de red que nombra un socket reiniciado, una conexión que expiró o un host que no se resolvió: 8 intentos, 2 s creciendo ×1,8 hasta 15 s, sin jitter. Una petición que expiró en el reloj del propio cliente, una conexión rechazada y toda respuesta del SII no se reintentan.
  • Cómo se lee la respuesta. Solo STATUS 0 con un elemento TRACKID es un archivo enviado. STATUS 7 es un duplicado: con un elemento TRACKID se recupera el track id y el envío sigue como uno enviado, salvo que su primera línea de error nombre un rechazo de charset o de esquema. STATUS 99, y cualquier otra cosa, es un envío fallido: SII_UPLOAD_REJECTED con status y errors. Un duplicado sin track id, donde hace falta uno, es ese mismo error con duplicate: true.
  • Cómo se lee un estado. El estado de un envío se pregunta una vez, por el canal SOAP, sin reintentos. Los estados se clasifican por código: exitosos (EPR, RPR, LOK), intermedios (REC, SOK, FOK, PRD, CRT, DNK, PDR, LSO, 0, y los códigos de error propios del servicio de consulta, -1 a -14, que se leen como "pregunta de nuevo") y rechazados (RPT, RFR, VOF, RCT, RCH, RLV, RSC, LNC, LRH). Un estado que nunca respondió, o que no trae estado, no es ninguno de los tres.

La fase de libros#

runBooks arma, envía, declara y espera los libros.

  1. El período. Se lee año a año el historial de libros que tiene el SII (hasta dos años vacíos) y se marcan los períodos ocupados. El período es el mes UTC en que se leyó el set de pruebas cuando el set trajo un libro de compras, si no el mes UTC actual, o el primer mes libre bajo él, buscado de nuevo en cada corrida.
  2. Qué se envía. Un libro que el portal muestra como REVISADO CONFORME se omite. Los libros de compras, ventas y guías (y el de compras para exentos, cuando el plan lo trae) se arman, se guardan como artefactos y se suben. Un libro de compras que el SII ya tiene para el período va de nuevo como ajuste.
  3. Libros obligatorios. Sin los tres libros obligatorios (MANDATORY_BOOK_KEYS) enviados u omitidos, la fase termina incomplete. Es el único caso en que runBooks termina incomplete.
  4. Antes de declarar. Se lee el estado SOAP de los libros enviados durante cerca de un minuto. Un libro rechazado queda fuera de la espera.
  5. Declarar. Los libros se declaran junto con los cuatro sets. Mientras toda fila declarada vuelva vacía, como máximo 24 veces, todos los libros van de nuevo: como ajuste una vez por período cuando se guardan los totales del libro de ventas o del de compras, si no como envío total en el siguiente período libre.
  6. Esperar. A una declaración que queda en pie le sigue la espera de aprobación (40 consultas separadas por 15 s, el estado SOAP cada dos consultas, porque el portal muestra un sobre rechazado como declarado para siempre). Una fila que la página deja rechazada, cerrada o declarada y nunca revisada mantiene la búsqueda aunque sea de un libro que esta corrida no envía (un libro de compras para exentos que el set de pruebas no trajo): no se envía nada por ella, pero los libros se declaran otra vez y esa fila se espera, hasta 24 veces.

Cuando un período está ocupado, todos los libros van de nuevo como ajuste, también el libro de guías y el de compras para exentos. Los libros que la declaración nombra con ENVIO CON ERRORES O REPAROS, o que el estado SOAP rechaza, no se esperan sino que se envían de nuevo. Ninguna lectura del portal de esta fase detiene la corrida: una página que no se puede leer solo gasta su consulta.

Avanzar de etapa#

Con los tres libros obligatorios enviados, el ejecutor pide al portal la etapa siguiente estén o no aprobados los libros: decide el portal. advanceToNextStage lee la página de avance (hasta 30 veces, separadas por 10 s, mientras no se pueda leer), pide el avance de inmediato con la primera página que se lee, y luego lee la página de nuevo para saber la etapa. La etapa del estado pasa a la del portal cuando esa es posterior.

ResultadoSignificado
advancedEl portal avanzó
unchangedEl portal dice que ya está en ese paso
failedEl portal lo negó, o no se pudo hacer la petición. detail es su frase
timedOutLa página de avance no se pudo leer en 30 intentos

Nada de esto lanza por una respuesta del portal. Si el portal no avanzó y la etapa sigue siendo los libros, runCertification se detiene con rejected y la frase del portal.

Valores por defecto del operador#

El operador entrega menos de lo que necesita el SII, y la corrida completa el resto.

QuéValor por defecto
ReceptorDEFAULT_CERTIFICATION_RECEIVER: RUT 66666666-6, CLIENTE TEST, ACTIVIDADES VARIAS, AVENIDA PRINCIPAL 123, Santiago. Un campo que entregues gana, campo por campo; uno vacío toma el valor por defecto
EmisorDEFAULT_CERTIFICATION_ISSUER: giro ACTIVIDADES DE PROGRAMACION INFORMATICA, código de actividad 620200, comuna y ciudad Santiago. El RUT, la razón social y la dirección no tienen valor por defecto
ResoluciónNúmero 0 y la fecha UTC del día en que partió la corrida

La fecha de resolución por defecto solo es correcta el día en que el SII resolvió la postulación. Cualquier otro día, entrega resolution.date tú mismo. Una resolución omitida se fecha una sola vez, al partir la corrida, así que todos los sobres de la corrida llevan la misma fecha.

El giro de un receptor se corta en sus 40 caracteres en vez de rechazarse, y también el del emisor cuando el emisor es el receptor de sus propios documentos (el set de compra, los traslados internos, la simulación). Como emisor conserva sus 80.

Las esperas#

EsperaComportamiento
Aprobación de los sets5 consultas separadas por 10 s, durmiendo primero
Ciclo de declaraciónPrimero 10 s, luego hasta 10 intentos separados por 5 s
Aprobación de los libros40 consultas separadas por 15 s, durmiendo primero
Aprobación de la simulación30 consultas separadas por 10 s, consultando primero
Lectura de la página de avance para avanzarHasta 30 lecturas separadas por 10 s
Estado SOAP de los libros antes de declarar3 rondas separadas por 15 s, tras una primera espera de 15 s

Inyecta sleep y now en las dependencias para probar sin esperar. Una lectura que falla durante una espera solo gasta su intento.

La detención en cumplimiento#

Cuando las muestras impresas están enviadas, la etapa es compliance y la corrida se detiene con compliance hasta que pases declareCompliance: true. Entonces declareComplianceFinal pide al portal avanzar la etapa una vez más (un rechazo o un error solo va al log), y hace la declaración.

  • La declaración cuenta como hecha cuando la última página ya no muestra su formulario. Guarda la página que deja el almacén y revísala.
  • Un bloqueo (el portal dice que la certificación no ha terminado) es un dato: stopped: 'blocked', con la frase también en state.blocked.
  • Una última página que no confirma lanza SII_PORTAL_UNEXPECTED_PAGE.
  • Una declaración termina la corrida: la etapa es done y se emite CERT_DONE.

Solo el representante legal puede hacer la declaración, y no se puede deshacer. Antes, comprueba en el portal que la revisión de muestras impresas esté aprobada.

Catálogos#

ExportQué contiene
SET_KEYSLos cuatro sets en el orden en que se envían: basic, guide, exempt, purchase
BOOK_KEYSLos libros en el orden en que se envían: purchaseBook, salesBook, guideBook, exemptPurchaseBook
MANDATORY_BOOK_KEYSLos libros que toda certificación debe tener aprobados: purchaseBook, salesBook, guideBook
CERTIFICATION_STAGEStestSets, books, simulation, exchange, printedSamples, compliance, done
CERTIFICATION_STEPSLos nombres de los eventos de progreso
DEFAULT_OPTIONAL_SETSSET03 guías, SET06 facturas exentas, SET07 libro de ventas, SET08 libro de compras, SET09 libro de guías, SET15 libro de compras para contribuyentes exentos y SET72 el set de compra. El set básico (SET01) siempre se pide. Exportación y liquidación nunca se marcan
BOOK_FILE_NAMES, BOOK_ARTEFACT_NAMES, SET_ARTEFACT_NAMESEl nombre de archivo con que viaja cada libro, y los nombres de artefacto de los libros y de los sobres de los sets
PORTAL_STAGE_WORDSLas palabras con que el portal nombra cada etapa

Errores#

Los rechazos vuelven como datos. Todo lo demás es un DteError que se propaga después de guardar el estado: los códigos del portal (SII_PORTAL_REJECTED con un rejection, SII_PORTAL_SESSION_EXPIRED, SII_PORTAL_UNEXPECTED_PAGE, SII_PORTAL_CERT_NOT_ENABLED), los códigos de envío al SII (SII_UPLOAD_REJECTED), FOLIO_OUT_OF_RANGE y MISSING_FIELD. Ver Errores.

Próximos pasos#