@devlas/dte-sii 2.22.0 → 2.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/SiiPortalAuth.js +30 -0
- package/dte-sii.d.ts +108 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -606,6 +606,16 @@ SiiPortalAuth.limpiarSesionCache() // borra todas
|
|
|
606
606
|
El **límite de sesiones nunca se reintenta** (cada intento empeora el bloqueo) y se distingue por
|
|
607
607
|
`err.code === 'SII_LIMITE_SESIONES'`.
|
|
608
608
|
|
|
609
|
+
Dos códigos más indican un trámite pendiente del contribuyente, no un problema de la
|
|
610
|
+
librería ni de la conexión:
|
|
611
|
+
|
|
612
|
+
| `err.code` | Qué significa | Qué hacer |
|
|
613
|
+
|---|---|---|
|
|
614
|
+
| `CERTIFICADO_NO_HABILITADO` | El certificado nunca se habilitó como método de autenticación. El SII responde 200 con un `alert()` de JavaScript, no con un error HTTP. No se reintenta. | En sii.cl: Clave Tributaria, Cambiar clave, y habilitar la autenticación con Certificado Digital. |
|
|
615
|
+
| `EMPRESA_NO_AUTORIZADA` | La empresa no está autorizada a operar en ese ambiente. Aparece al leer los datos del contribuyente. | Terminar la certificación (en producción no queda autorizada antes) o correr la Postulación/Enrolamiento. |
|
|
616
|
+
|
|
617
|
+
`CafSolicitor.solicitar()` devuelve `EMPRESA_NO_AUTORIZADA` en `errorCode` por el mismo motivo.
|
|
618
|
+
|
|
609
619
|
### SiiSession: sesiones HTTP autenticadas
|
|
610
620
|
|
|
611
621
|
```javascript
|
package/SiiPortalAuth.js
CHANGED
|
@@ -821,7 +821,37 @@ class SiiPortalAuth {
|
|
|
821
821
|
* El HTML tiene filas <tr><td>Label</td><td> Valor</td></tr>
|
|
822
822
|
* @private
|
|
823
823
|
*/
|
|
824
|
+
/**
|
|
825
|
+
* "el Contribuyente no está autorizado para operar en esta modalidad" — la empresa nunca
|
|
826
|
+
* quedó postulada (y/o enrolada) como emisor electrónico ante el SII, así que ad_empresa2
|
|
827
|
+
* ni siquiera devuelve la tabla de datos que `_parsearTablaEmpresa` espera: devuelve esta
|
|
828
|
+
* página de rechazo. Mismo patrón y mismo código de error que `CafSolicitor.esEmpresaNoAutorizada`
|
|
829
|
+
* (`EMPRESA_NO_AUTORIZADA`) — ahí ya existía para otro flujo, acá faltaba. Sin esto caía en
|
|
830
|
+
* el genérico "no se encontraron datos de resolución", que no dice qué falta. Caso real
|
|
831
|
+
* verificado el 2026-09-10 (RUT anonimizado en los tests).
|
|
832
|
+
*/
|
|
833
|
+
static esEmpresaNoAutorizada(html) {
|
|
834
|
+
const texto = String(html || '').replace(/<[^>]+>/g, ' ');
|
|
835
|
+
// El SII usa dos sujetos distintos para el mismo rechazo, segun la pagina: "la empresa
|
|
836
|
+
// no esta autorizada" (ver CafSolicitor.esEmpresaNoAutorizada) y "el Contribuyente no
|
|
837
|
+
// esta autorizado" (ad_empresa2, caso real 2026-09-10) -- distinto genero tambien
|
|
838
|
+
// (autorizada/autorizado), por eso el [oa] al final en vez de repetir todo el patron.
|
|
839
|
+
return /(empresa|contribuyente)\s+no\s+est.{0,8}\s*autorizad[oa]\s+para\s+operar/i.test(texto);
|
|
840
|
+
}
|
|
841
|
+
|
|
824
842
|
static _parsearTablaEmpresa(html) {
|
|
843
|
+
if (SiiPortalAuth.esEmpresaNoAutorizada(html)) {
|
|
844
|
+
const err = new Error(
|
|
845
|
+
'SiiPortalAuth: la empresa no está autorizada para operar en esta modalidad. ' +
|
|
846
|
+
'No es un problema de datos ni de sesión: el SII no reconoce a la empresa como ' +
|
|
847
|
+
'emisor electrónico en este ambiente. Las dos causas habituales son que todavía ' +
|
|
848
|
+
'no complete la certificación (en producción no queda autorizada hasta entonces) ' +
|
|
849
|
+
'o que nunca se haya corrido la Postulación/Enrolamiento.'
|
|
850
|
+
);
|
|
851
|
+
err.code = 'EMPRESA_NO_AUTORIZADA';
|
|
852
|
+
throw err;
|
|
853
|
+
}
|
|
854
|
+
|
|
825
855
|
const datos = {};
|
|
826
856
|
const decode = s => s
|
|
827
857
|
.replace(/<[^>]+>/g, '')
|
package/dte-sii.d.ts
CHANGED
|
@@ -606,6 +606,60 @@ export class EnviadorSII {
|
|
|
606
606
|
consultarEstadoDte(params: object): Promise<object>;
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
+
/** Estado de un DTE recibido según su último evento en el Registro de Aceptación/Reclamo. */
|
|
610
|
+
export type EstadoReceptorDte = 'aceptada' | 'acuse_recibo' | 'reclamada' | 'sin_accion';
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Acción a registrar sobre un DTE recibido. ACD acepta el contenido, ERM otorga recibo de
|
|
614
|
+
* mercaderías o servicios, RCD reclama el contenido, RFP y RFT reclaman falta parcial o
|
|
615
|
+
* total de mercaderías.
|
|
616
|
+
*/
|
|
617
|
+
export type AccionReclamoDte = 'ACD' | 'ERM' | 'RCD' | 'RFP' | 'RFT';
|
|
618
|
+
|
|
619
|
+
export interface EventoReclamoDte {
|
|
620
|
+
codEvento: string;
|
|
621
|
+
descEvento: string;
|
|
622
|
+
rutResponsable: string;
|
|
623
|
+
dvResponsable: string;
|
|
624
|
+
fechaEvento: string;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** Respuesta del web service, con `codResp` numérico y `descResp` en texto, tal como los entrega el SII. */
|
|
628
|
+
export interface RespuestaReclamoDte {
|
|
629
|
+
codResp: number;
|
|
630
|
+
descResp: string;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Cliente del web service de Registro de Aceptación/Reclamo de DTE (WSRECLAMO).
|
|
635
|
+
* Se exporta desde index.js.
|
|
636
|
+
*/
|
|
637
|
+
export class WsReclamo {
|
|
638
|
+
constructor(
|
|
639
|
+
certificado: Certificado,
|
|
640
|
+
ambiente: 'certificacion' | 'produccion',
|
|
641
|
+
options?: { useTokenCache?: boolean },
|
|
642
|
+
);
|
|
643
|
+
/** Historial de eventos de un DTE recibido. */
|
|
644
|
+
listarEventosHistDoc(
|
|
645
|
+
rutEmisor: number | string, dvEmisor: string, tipoDoc: number | string, folio: number | string,
|
|
646
|
+
): Promise<RespuestaReclamoDte & { eventos: EventoReclamoDte[] }>;
|
|
647
|
+
/** Estado resumido según el último evento. 'sin_accion' cuando ningún evento lo determina. */
|
|
648
|
+
consultarEstadoReceptor(
|
|
649
|
+
rutEmisor: number | string, dvEmisor: string, tipoDoc: number | string, folio: number | string,
|
|
650
|
+
): Promise<EstadoReceptorDte>;
|
|
651
|
+
/** Registra una acción sobre un DTE recibido. Lanza si `accionDoc` no es una acción válida. */
|
|
652
|
+
ingresarAceptacion(
|
|
653
|
+
rutEmisor: number | string, dvEmisor: string, tipoDoc: number | string, folio: number | string,
|
|
654
|
+
accionDoc: AccionReclamoDte,
|
|
655
|
+
): Promise<RespuestaReclamoDte>;
|
|
656
|
+
/**
|
|
657
|
+
* Descarta el token SOAP en memoria de esta instancia. No limpia el caché compartido de
|
|
658
|
+
* tokens, que expira por TTL.
|
|
659
|
+
*/
|
|
660
|
+
invalidarToken(): void;
|
|
661
|
+
}
|
|
662
|
+
|
|
609
663
|
// ============================================
|
|
610
664
|
// FOLIO MANAGEMENT
|
|
611
665
|
// ============================================
|
|
@@ -808,11 +862,32 @@ export interface CafSolicitarOptions {
|
|
|
808
862
|
soloConsultarTope?: boolean;
|
|
809
863
|
}
|
|
810
864
|
|
|
865
|
+
/**
|
|
866
|
+
* Códigos que puede traer `CafSolicitarResult.errorCode`.
|
|
867
|
+
*
|
|
868
|
+
* Lista cerrada. test/contrato-estaticos-dts.test.js la compara contra los literales que
|
|
869
|
+
* emite `solicitar()` en las dos direcciones: si el código agrega o saca un valor sin
|
|
870
|
+
* actualizar esto, el test falla.
|
|
871
|
+
*/
|
|
872
|
+
export type CafSolicitarErrorCode =
|
|
873
|
+
| 'TIMBRAJE_BLOQUEADO'
|
|
874
|
+
| 'MAX_AUTOR_INSUFICIENTE'
|
|
875
|
+
| 'MAX_AUTOR_EXCEEDED'
|
|
876
|
+
| 'RANGO_YA_AUTORIZADO'
|
|
877
|
+
| 'EMPRESA_NO_AUTORIZADA'
|
|
878
|
+
| 'USUARIO_SIN_PERMISO'
|
|
879
|
+
| 'NO_AUTORIZADO_INGRESAR_OPCION'
|
|
880
|
+
| 'REQUIERE_TRAMITE_PRESENCIAL'
|
|
881
|
+
| 'VERIFICACION_ACTIVIDADES_PENDIENTE'
|
|
882
|
+
| 'SESSION_EXPIRED'
|
|
883
|
+
| 'WAAP_BLOCKED'
|
|
884
|
+
| 'UNKNOWN';
|
|
885
|
+
|
|
811
886
|
export interface CafSolicitarResult {
|
|
812
887
|
success: boolean;
|
|
813
888
|
xml?: string;
|
|
814
889
|
error?: string;
|
|
815
|
-
errorCode?:
|
|
890
|
+
errorCode?: CafSolicitarErrorCode;
|
|
816
891
|
folioDesde?: number;
|
|
817
892
|
folioHasta?: number;
|
|
818
893
|
}
|
|
@@ -841,6 +916,38 @@ export class CafSolicitor {
|
|
|
841
916
|
* **No devuelve el XML ni null**, devuelve un objeto con `success`.
|
|
842
917
|
*/
|
|
843
918
|
reobtenerCaf(tipoDte: number, rango: RangoReobtenible): Promise<ReobtenerRangoResult>;
|
|
919
|
+
/** true si la página del portal es el aviso "NO SE AUTORIZA TIMBRAJE". */
|
|
920
|
+
static esBloqueoTimbraje(html: string): boolean;
|
|
921
|
+
/**
|
|
922
|
+
* Motivo que el SII escribe en la página de bloqueo de timbraje, aislado del resto para
|
|
923
|
+
* mostrarlo tal cual. null si no lo encuentra; nunca lanza.
|
|
924
|
+
*/
|
|
925
|
+
static extraerMotivoBloqueoTimbraje(html: string): string | null;
|
|
926
|
+
/** true si el portal responde que no está autorizado para ingresar a esa opción. */
|
|
927
|
+
static esNoAutorizadoIngresarOpcion(html: string): boolean;
|
|
928
|
+
/** true si el SII exige presentarse en una oficina: trámite presencial. */
|
|
929
|
+
static esRequiereTramitePresencial(html: string): boolean;
|
|
930
|
+
/** true si la página dice que la empresa no está autorizada para operar. */
|
|
931
|
+
static esEmpresaNoAutorizada(html: string): boolean;
|
|
932
|
+
/**
|
|
933
|
+
* true si la página es uno de los rechazos que no se resuelven reintentando: bloqueo de
|
|
934
|
+
* timbraje, trámite presencial, empresa no autorizada, sin acceso a la opción, usuario
|
|
935
|
+
* sin permiso o verificación de actividades pendiente.
|
|
936
|
+
*/
|
|
937
|
+
static esRechazoDuro(html: string): boolean;
|
|
938
|
+
/** true si el usuario del certificado no tiene permiso o autorización en la empresa. */
|
|
939
|
+
static esUsuarioSinPermiso(html: string): boolean;
|
|
940
|
+
/**
|
|
941
|
+
* Texto visible de un HTML del portal: sin etiquetas, scripts ni estilos, con las
|
|
942
|
+
* entidades de vocales acentuadas pasadas a vocal simple y recortado a `max` caracteres
|
|
943
|
+
* (400 por defecto). Lo usan los detectores de arriba.
|
|
944
|
+
*/
|
|
945
|
+
static textoVisible(html: string, max?: number): string;
|
|
946
|
+
/**
|
|
947
|
+
* Cierra con logout todas las sesiones SII en caché. Conviene llamarlo al apagar el
|
|
948
|
+
* proceso, para no dejar sesiones que cuenten contra el límite de sesiones del SII.
|
|
949
|
+
*/
|
|
950
|
+
static closeAllSessions(): Promise<void>;
|
|
844
951
|
}
|
|
845
952
|
|
|
846
953
|
// ============================================
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devlas/dte-sii",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"description": "Facturación y boletas electrónicas para el SII de Chile. Genera, timbra, firma y envía DTEs, libros electrónicos y automatiza la certificación.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "dte-sii.d.ts",
|
|
@@ -38,11 +38,10 @@
|
|
|
38
38
|
"bwip-js": "^4.11.4",
|
|
39
39
|
"dotenv": "^17.3.1",
|
|
40
40
|
"fast-xml-parser": "^5.3.3",
|
|
41
|
+
"form-data": "^4.0.6",
|
|
41
42
|
"got": "^11.8.6",
|
|
42
43
|
"node-forge": "^1.3.3",
|
|
43
44
|
"pdf-lib": "^1.17.1",
|
|
44
|
-
"soap": "^1.6.3",
|
|
45
|
-
"xml-c14n": "^0.0.6",
|
|
46
45
|
"xml-crypto": "^6.1.2"
|
|
47
46
|
},
|
|
48
47
|
"files": [
|