dgii-ts 0.1.2 → 0.2.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.en.md +39 -3
- package/README.md +40 -3
- package/dist/bulk.cjs +8 -2
- package/dist/bulk.d.cts +48 -5
- package/dist/bulk.d.ts +48 -5
- package/dist/bulk.js +7 -1
- package/dist/{chunk-5FZ4F5UR.cjs → chunk-6ZRPVQPP.cjs} +20 -15
- package/dist/{chunk-HRRYZUKI.js → chunk-7ILLRYE4.js} +114 -11
- package/dist/{chunk-JBKAE66C.cjs → chunk-DLB5RJ7E.cjs} +16 -16
- package/dist/{chunk-BQPH3JDZ.cjs → chunk-FRM5XREF.cjs} +124 -21
- package/dist/chunk-H42U4GI5.js +49 -0
- package/dist/{chunk-DMGDJEUY.js → chunk-IRQX4WNQ.js} +8 -1
- package/dist/{chunk-ROJUYPIK.js → chunk-JV4HWHYP.js} +11 -6
- package/dist/{chunk-53A7IG4Y.js → chunk-KW4ZV6FJ.js} +1 -1
- package/dist/{chunk-7RJWFPCZ.cjs → chunk-NASWEFHN.cjs} +8 -1
- package/dist/chunk-OSGHCQFA.cjs +49 -0
- package/dist/{chunk-43LGK4YP.js → chunk-QTN5ZT4P.js} +2 -2
- package/dist/{chunk-TZC6RSWL.js → chunk-RTI3CLHV.js} +1 -48
- package/dist/{chunk-SUSDEKID.cjs → chunk-UW6W2JHW.cjs} +1 -48
- package/dist/{chunk-FZ6QDTC4.cjs → chunk-VIZWYFHP.cjs} +62 -11
- package/dist/{chunk-WY6V4Y7S.cjs → chunk-WMFHPNFP.cjs} +3 -3
- package/dist/{chunk-KIS7MVIW.js → chunk-Z72T6CZG.js} +61 -10
- package/dist/client.cjs +7 -6
- package/dist/client.d.cts +8 -3
- package/dist/client.d.ts +8 -3
- package/dist/client.js +6 -5
- package/dist/errors.cjs +4 -2
- package/dist/errors.d.cts +10 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.js +3 -1
- package/dist/index-BPvMDXya.d.cts +101 -0
- package/dist/index-BPvMDXya.d.ts +101 -0
- package/dist/index.cjs +16 -10
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -9
- package/dist/scraping.cjs +5 -4
- package/dist/scraping.d.cts +14 -3
- package/dist/scraping.d.ts +14 -3
- package/dist/scraping.js +4 -3
- package/dist/soap.cjs +4 -4
- package/dist/soap.d.cts +1 -1
- package/dist/soap.d.ts +1 -1
- package/dist/soap.js +3 -3
- package/dist/validators.cjs +4 -2
- package/dist/validators.d.cts +1 -1
- package/dist/validators.d.ts +1 -1
- package/dist/validators.js +5 -3
- package/package.json +2 -2
- package/dist/index-BE6Wfozd.d.cts +0 -63
- package/dist/index-BE6Wfozd.d.ts +0 -63
package/README.en.md
CHANGED
|
@@ -135,9 +135,41 @@ const result = await client.getContribuyente('131098193');
|
|
|
135
135
|
|
|
136
136
|
```typescript
|
|
137
137
|
const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
138
|
-
// { rnc: '131098193', ncf: 'B0100000001',
|
|
138
|
+
// { valid: true, rnc: '131098193', ncf: 'B0100000001', nombreComercial: '...' }
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
### Validate an e-NCF online
|
|
142
|
+
|
|
143
|
+
For an e-NCF (E-series) DGII requires the buyer's RNC. Without it DGII
|
|
144
|
+
answers with a required-field message and `getNCF` throws
|
|
145
|
+
`DgiiServiceError`. The security code is optional.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const ecfResult = await client.getNCF('101010632', 'E310125217173', {
|
|
149
|
+
rncComprador: '131262414',
|
|
150
|
+
codigoSeguridad: 'KrOLI0',
|
|
151
|
+
});
|
|
152
|
+
// {
|
|
153
|
+
// valid: true,
|
|
154
|
+
// rnc: '101010632',
|
|
155
|
+
// ncf: 'E310125217173',
|
|
156
|
+
// rncComprador: '131262414',
|
|
157
|
+
// codigoSeguridad: 'KrOLI0',
|
|
158
|
+
// estado: 'Aceptado',
|
|
159
|
+
// montoTotal: 230677.74,
|
|
160
|
+
// totalItbis: 35188.13,
|
|
161
|
+
// fechaEmision: '2026-02-11',
|
|
162
|
+
// fechaFirma: '2026-02-11',
|
|
163
|
+
// }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`valid: true` means DGII found the document, not that it is accepted:
|
|
167
|
+
check `estado`. The fields `rncComprador`, `codigoSeguridad`, `estado`,
|
|
168
|
+
`montoTotal`, `totalItbis`, `fechaEmision` and `fechaFirma` are only
|
|
169
|
+
filled for the E-series, and `nombreComercial` is not filled for that
|
|
170
|
+
series. Dates are strings exactly as DGII renders them. The options are
|
|
171
|
+
ignored for a B-series NCF.
|
|
172
|
+
|
|
141
173
|
## API reference
|
|
142
174
|
|
|
143
175
|
### Validators
|
|
@@ -155,7 +187,7 @@ const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
|
155
187
|
| --- | --- |
|
|
156
188
|
| `DgiiClient` | Scraping + SOAP fallback, circuit breaker, retry |
|
|
157
189
|
| `client.getContribuyente(rnc)` | Looks up taxpayer data by RNC |
|
|
158
|
-
| `client.getNCF(rnc, ncf)` | Validates a fiscal receipt against DGII |
|
|
190
|
+
| `client.getNCF(rnc, ncf, options?)` | Validates a fiscal receipt against DGII (`options` for e-NCF) |
|
|
159
191
|
|
|
160
192
|
### Scraping
|
|
161
193
|
|
|
@@ -163,7 +195,7 @@ const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
|
163
195
|
| --- | --- |
|
|
164
196
|
| `ScrapingClient` | Queries DGII's ASP.NET pages |
|
|
165
197
|
| `client.getContribuyente(rnc)` | Looks up taxpayer data by RNC |
|
|
166
|
-
| `client.getNCF(rnc, ncf)` | Validates a fiscal receipt |
|
|
198
|
+
| `client.getNCF(rnc, ncf, options?)` | Validates a fiscal receipt (`options` for e-NCF) |
|
|
167
199
|
|
|
168
200
|
### SOAP client (deprecated)
|
|
169
201
|
|
|
@@ -180,6 +212,10 @@ const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
|
180
212
|
| `downloadBulkFile(options)` | Downloads DGII\_RNC.zip to the given directory |
|
|
181
213
|
| `parseBulkFile(options)` | Parses the taxpayer TXT file |
|
|
182
214
|
|
|
215
|
+
`parseBulkFile` throws `BulkFormatError` if the file doesn't match the
|
|
216
|
+
expected 11-column layout (for example, if DGII changes the format). It
|
|
217
|
+
accepts an `encoding` option (defaults to `latin1`).
|
|
218
|
+
|
|
183
219
|
## Architecture
|
|
184
220
|
|
|
185
221
|
```text
|
package/README.md
CHANGED
|
@@ -138,9 +138,42 @@ const result = await client.getContribuyente('131098193');
|
|
|
138
138
|
|
|
139
139
|
```typescript
|
|
140
140
|
const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
141
|
-
// { rnc: '131098193', ncf: 'B0100000001',
|
|
141
|
+
// { valid: true, rnc: '131098193', ncf: 'B0100000001', nombreComercial: '...' }
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
### Validar e-NCF en línea
|
|
145
|
+
|
|
146
|
+
Para un e-NCF (serie E) la DGII exige el RNC del comprador. Sin él
|
|
147
|
+
responde con un mensaje de campo requerido y `getNCF` lanza
|
|
148
|
+
`DgiiServiceError`. El código de seguridad es opcional.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const ecfResult = await client.getNCF('101010632', 'E310125217173', {
|
|
152
|
+
rncComprador: '131262414',
|
|
153
|
+
codigoSeguridad: 'KrOLI0',
|
|
154
|
+
});
|
|
155
|
+
// {
|
|
156
|
+
// valid: true,
|
|
157
|
+
// rnc: '101010632',
|
|
158
|
+
// ncf: 'E310125217173',
|
|
159
|
+
// rncComprador: '131262414',
|
|
160
|
+
// codigoSeguridad: 'KrOLI0',
|
|
161
|
+
// estado: 'Aceptado',
|
|
162
|
+
// montoTotal: 230677.74,
|
|
163
|
+
// totalItbis: 35188.13,
|
|
164
|
+
// fechaEmision: '2026-02-11',
|
|
165
|
+
// fechaFirma: '2026-02-11',
|
|
166
|
+
// }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`valid: true` significa que la DGII encontró el comprobante, no que
|
|
170
|
+
esté aceptado: revisa `estado`. Los campos `rncComprador`,
|
|
171
|
+
`codigoSeguridad`, `estado`, `montoTotal`, `totalItbis`,
|
|
172
|
+
`fechaEmision` y `fechaFirma` solo se llenan para la serie E, y
|
|
173
|
+
`nombreComercial` no se llena para esa serie. Las fechas son strings
|
|
174
|
+
tal como la DGII las renderiza. Las opciones se ignoran para un NCF de
|
|
175
|
+
serie B.
|
|
176
|
+
|
|
144
177
|
## Referencia del API
|
|
145
178
|
|
|
146
179
|
### Validadores
|
|
@@ -158,7 +191,7 @@ const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
|
158
191
|
| --- | --- |
|
|
159
192
|
| `DgiiClient` | Cliente con scraping + SOAP fallback, circuit breaker y retry |
|
|
160
193
|
| `client.getContribuyente(rnc)` | Consulta datos de un contribuyente por RNC |
|
|
161
|
-
| `client.getNCF(rnc, ncf)` | Valida un comprobante fiscal contra la DGII |
|
|
194
|
+
| `client.getNCF(rnc, ncf, options?)` | Valida un comprobante fiscal contra la DGII (`options` para e-NCF) |
|
|
162
195
|
|
|
163
196
|
### Scraping
|
|
164
197
|
|
|
@@ -166,7 +199,7 @@ const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
|
166
199
|
| --- | --- |
|
|
167
200
|
| `ScrapingClient` | Consulta páginas ASP.NET de la DGII |
|
|
168
201
|
| `client.getContribuyente(rnc)` | Consulta contribuyente por RNC |
|
|
169
|
-
| `client.getNCF(rnc, ncf)` | Valida comprobante fiscal |
|
|
202
|
+
| `client.getNCF(rnc, ncf, options?)` | Valida comprobante fiscal (`options` para e-NCF) |
|
|
170
203
|
|
|
171
204
|
### Cliente SOAP (deprecated)
|
|
172
205
|
|
|
@@ -183,6 +216,10 @@ const ncfResult = await client.getNCF('131098193', 'B0100000001');
|
|
|
183
216
|
| `downloadBulkFile(options)` | Descarga DGII\_RNC.zip |
|
|
184
217
|
| `parseBulkFile(options)` | Parsea el archivo TXT |
|
|
185
218
|
|
|
219
|
+
`parseBulkFile` lanza `BulkFormatError` si el archivo no coincide con el
|
|
220
|
+
layout de 11 columnas esperado (por ejemplo, si la DGII cambia el formato).
|
|
221
|
+
Acepta `encoding` en las opciones (`latin1` por defecto).
|
|
222
|
+
|
|
186
223
|
## Arquitectura
|
|
187
224
|
|
|
188
225
|
```text
|
package/dist/bulk.cjs
CHANGED
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
var _chunkVIZWYFHPcjs = require('./chunk-VIZWYFHP.cjs');
|
|
6
7
|
require('./chunk-QH4BK5X3.cjs');
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
var _chunkNASWEFHNcjs = require('./chunk-NASWEFHN.cjs');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
|
|
11
|
-
exports.DGII_BULK_URL =
|
|
17
|
+
exports.BulkFormatError = _chunkNASWEFHNcjs.BulkFormatError; exports.DGII_BULK_URL = _chunkVIZWYFHPcjs.DGII_BULK_URL; exports.DGII_ESTADOS = _chunkVIZWYFHPcjs.DGII_ESTADOS; exports.downloadBulkFile = _chunkVIZWYFHPcjs.downloadBulkFile; exports.parseBulkFile = _chunkVIZWYFHPcjs.parseBulkFile;
|
package/dist/bulk.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { B as BulkContribuyente } from './index-
|
|
1
|
+
import { B as BulkContribuyente } from './index-BPvMDXya.cjs';
|
|
2
|
+
export { BulkFormatError } from './errors.cjs';
|
|
2
3
|
|
|
3
4
|
interface DownloadOptions {
|
|
4
5
|
/** Directorio de destino para el archivo ZIP */
|
|
@@ -9,8 +10,27 @@ interface DownloadOptions {
|
|
|
9
10
|
interface ParseOptions {
|
|
10
11
|
/** Ruta al archivo TXT extraído del ZIP */
|
|
11
12
|
filePath: string;
|
|
13
|
+
/**
|
|
14
|
+
* Codificación del archivo. La DGII publica el TXT en latin-1
|
|
15
|
+
* (ISO-8859-1), que es el valor por defecto. Permite anularla si una
|
|
16
|
+
* futura versión del archivo llega en otra codificación (p. ej. UTF-8).
|
|
17
|
+
*/
|
|
18
|
+
encoding?: BufferEncoding;
|
|
12
19
|
}
|
|
13
20
|
declare const DGII_BULK_URL: string;
|
|
21
|
+
/**
|
|
22
|
+
* Valores conocidos de la columna `estado` (col 9) en el archivo masivo,
|
|
23
|
+
* verificados contra ~778k filas en producción. El parser usa este
|
|
24
|
+
* vocabulario para detectar un cambio de layout: si ninguna fila de una
|
|
25
|
+
* muestra trae un `estado` reconocido, el formato de la DGII cambió.
|
|
26
|
+
*/
|
|
27
|
+
declare const DGII_ESTADOS: readonly ["ACTIVO", "SUSPENDIDO", "DADO DE BAJA", "CESE TEMPORAL", "ANULADO", "RECHAZADO"];
|
|
28
|
+
/**
|
|
29
|
+
* Valor de la columna `estado` del archivo masivo: uno de
|
|
30
|
+
* {@link DGII_ESTADOS}. Vocabulario completo de la DGII, distinto del
|
|
31
|
+
* `estado` binario (ACTIVO/INACTIVO) que exponen el scraping y el SOAP.
|
|
32
|
+
*/
|
|
33
|
+
type DgiiEstado = (typeof DGII_ESTADOS)[number];
|
|
14
34
|
|
|
15
35
|
/**
|
|
16
36
|
* Descarga el archivo masivo DGII_RNC.zip desde el portal de la DGII.
|
|
@@ -20,11 +40,34 @@ declare const DGII_BULK_URL: string;
|
|
|
20
40
|
declare function downloadBulkFile(options: DownloadOptions): Promise<string>;
|
|
21
41
|
|
|
22
42
|
/**
|
|
23
|
-
* Parsea el archivo TXT de contribuyentes
|
|
43
|
+
* Parsea el archivo TXT de contribuyentes extraído de DGII_RNC.zip.
|
|
44
|
+
*
|
|
45
|
+
* El archivo usa delimitador pipe (|) con 11 columnas y viene codificado
|
|
46
|
+
* en latin-1 (ISO-8859-1); leerlo como UTF-8 corrompe los nombres con
|
|
47
|
+
* ñ/acentos (p. ej. MUÑOZ, PEÑA). Usa {@link ParseOptions.encoding} para
|
|
48
|
+
* anular la codificación si una futura versión del archivo cambia.
|
|
49
|
+
*
|
|
50
|
+
* Layout verificado contra el archivo en producción (~778k filas):
|
|
51
|
+
*
|
|
52
|
+
* col 0 rnc (100% dígitos)
|
|
53
|
+
* col 1 nombre (razón social)
|
|
54
|
+
* col 2 nombreComercial
|
|
55
|
+
* col 3 actividad
|
|
56
|
+
* col 4-7 reservadas/vacías en el archivo actual
|
|
57
|
+
* col 8 fechaConstitucion (dd/mm/yyyy)
|
|
58
|
+
* col 9 estado (ACTIVO / SUSPENDIDO / DADO DE BAJA / ...)
|
|
59
|
+
* col 10 regimen (NORMAL / RST / PST)
|
|
60
|
+
*
|
|
61
|
+
* `estado` se normaliza a mayúsculas para que coincida con
|
|
62
|
+
* {@link DGII_ESTADOS} sin importar la capitalización de la fuente.
|
|
24
63
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
64
|
+
* Las filas cuyo número de columnas no sea exactamente 11 se descartan por
|
|
65
|
+
* malformadas (formato viejo más corto, o una columna añadida/insertada).
|
|
66
|
+
* Si el archivo trae filas pero la mayoría de la muestra no trae un
|
|
67
|
+
* `estado` reconocido, se lanza {@link BulkFormatError}: un cambio de
|
|
68
|
+
* layout de la DGII debe fallar ruidosamente en vez de devolver `estado`
|
|
69
|
+
* vacío o mapeado a la columna equivocada en silencio.
|
|
27
70
|
*/
|
|
28
71
|
declare function parseBulkFile(options: ParseOptions): Promise<BulkContribuyente[]>;
|
|
29
72
|
|
|
30
|
-
export { BulkContribuyente, DGII_BULK_URL, type DownloadOptions, type ParseOptions, downloadBulkFile, parseBulkFile };
|
|
73
|
+
export { BulkContribuyente, DGII_BULK_URL, DGII_ESTADOS, type DgiiEstado, type DownloadOptions, type ParseOptions, downloadBulkFile, parseBulkFile };
|
package/dist/bulk.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { B as BulkContribuyente } from './index-
|
|
1
|
+
import { B as BulkContribuyente } from './index-BPvMDXya.js';
|
|
2
|
+
export { BulkFormatError } from './errors.js';
|
|
2
3
|
|
|
3
4
|
interface DownloadOptions {
|
|
4
5
|
/** Directorio de destino para el archivo ZIP */
|
|
@@ -9,8 +10,27 @@ interface DownloadOptions {
|
|
|
9
10
|
interface ParseOptions {
|
|
10
11
|
/** Ruta al archivo TXT extraído del ZIP */
|
|
11
12
|
filePath: string;
|
|
13
|
+
/**
|
|
14
|
+
* Codificación del archivo. La DGII publica el TXT en latin-1
|
|
15
|
+
* (ISO-8859-1), que es el valor por defecto. Permite anularla si una
|
|
16
|
+
* futura versión del archivo llega en otra codificación (p. ej. UTF-8).
|
|
17
|
+
*/
|
|
18
|
+
encoding?: BufferEncoding;
|
|
12
19
|
}
|
|
13
20
|
declare const DGII_BULK_URL: string;
|
|
21
|
+
/**
|
|
22
|
+
* Valores conocidos de la columna `estado` (col 9) en el archivo masivo,
|
|
23
|
+
* verificados contra ~778k filas en producción. El parser usa este
|
|
24
|
+
* vocabulario para detectar un cambio de layout: si ninguna fila de una
|
|
25
|
+
* muestra trae un `estado` reconocido, el formato de la DGII cambió.
|
|
26
|
+
*/
|
|
27
|
+
declare const DGII_ESTADOS: readonly ["ACTIVO", "SUSPENDIDO", "DADO DE BAJA", "CESE TEMPORAL", "ANULADO", "RECHAZADO"];
|
|
28
|
+
/**
|
|
29
|
+
* Valor de la columna `estado` del archivo masivo: uno de
|
|
30
|
+
* {@link DGII_ESTADOS}. Vocabulario completo de la DGII, distinto del
|
|
31
|
+
* `estado` binario (ACTIVO/INACTIVO) que exponen el scraping y el SOAP.
|
|
32
|
+
*/
|
|
33
|
+
type DgiiEstado = (typeof DGII_ESTADOS)[number];
|
|
14
34
|
|
|
15
35
|
/**
|
|
16
36
|
* Descarga el archivo masivo DGII_RNC.zip desde el portal de la DGII.
|
|
@@ -20,11 +40,34 @@ declare const DGII_BULK_URL: string;
|
|
|
20
40
|
declare function downloadBulkFile(options: DownloadOptions): Promise<string>;
|
|
21
41
|
|
|
22
42
|
/**
|
|
23
|
-
* Parsea el archivo TXT de contribuyentes
|
|
43
|
+
* Parsea el archivo TXT de contribuyentes extraído de DGII_RNC.zip.
|
|
44
|
+
*
|
|
45
|
+
* El archivo usa delimitador pipe (|) con 11 columnas y viene codificado
|
|
46
|
+
* en latin-1 (ISO-8859-1); leerlo como UTF-8 corrompe los nombres con
|
|
47
|
+
* ñ/acentos (p. ej. MUÑOZ, PEÑA). Usa {@link ParseOptions.encoding} para
|
|
48
|
+
* anular la codificación si una futura versión del archivo cambia.
|
|
49
|
+
*
|
|
50
|
+
* Layout verificado contra el archivo en producción (~778k filas):
|
|
51
|
+
*
|
|
52
|
+
* col 0 rnc (100% dígitos)
|
|
53
|
+
* col 1 nombre (razón social)
|
|
54
|
+
* col 2 nombreComercial
|
|
55
|
+
* col 3 actividad
|
|
56
|
+
* col 4-7 reservadas/vacías en el archivo actual
|
|
57
|
+
* col 8 fechaConstitucion (dd/mm/yyyy)
|
|
58
|
+
* col 9 estado (ACTIVO / SUSPENDIDO / DADO DE BAJA / ...)
|
|
59
|
+
* col 10 regimen (NORMAL / RST / PST)
|
|
60
|
+
*
|
|
61
|
+
* `estado` se normaliza a mayúsculas para que coincida con
|
|
62
|
+
* {@link DGII_ESTADOS} sin importar la capitalización de la fuente.
|
|
24
63
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
64
|
+
* Las filas cuyo número de columnas no sea exactamente 11 se descartan por
|
|
65
|
+
* malformadas (formato viejo más corto, o una columna añadida/insertada).
|
|
66
|
+
* Si el archivo trae filas pero la mayoría de la muestra no trae un
|
|
67
|
+
* `estado` reconocido, se lanza {@link BulkFormatError}: un cambio de
|
|
68
|
+
* layout de la DGII debe fallar ruidosamente en vez de devolver `estado`
|
|
69
|
+
* vacío o mapeado a la columna equivocada en silencio.
|
|
27
70
|
*/
|
|
28
71
|
declare function parseBulkFile(options: ParseOptions): Promise<BulkContribuyente[]>;
|
|
29
72
|
|
|
30
|
-
export { BulkContribuyente, DGII_BULK_URL, type DownloadOptions, type ParseOptions, downloadBulkFile, parseBulkFile };
|
|
73
|
+
export { BulkContribuyente, DGII_BULK_URL, DGII_ESTADOS, type DgiiEstado, type DownloadOptions, type ParseOptions, downloadBulkFile, parseBulkFile };
|
package/dist/bulk.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DGII_BULK_URL,
|
|
3
|
+
DGII_ESTADOS,
|
|
3
4
|
downloadBulkFile,
|
|
4
5
|
parseBulkFile
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z72T6CZG.js";
|
|
6
7
|
import "./chunk-D26S6EXD.js";
|
|
8
|
+
import {
|
|
9
|
+
BulkFormatError
|
|
10
|
+
} from "./chunk-IRQX4WNQ.js";
|
|
7
11
|
export {
|
|
12
|
+
BulkFormatError,
|
|
8
13
|
DGII_BULK_URL,
|
|
14
|
+
DGII_ESTADOS,
|
|
9
15
|
downloadBulkFile,
|
|
10
16
|
parseBulkFile
|
|
11
17
|
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkDLB5RJ7Ecjs = require('./chunk-DLB5RJ7E.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkFRM5XREFcjs = require('./chunk-FRM5XREF.cjs');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
var
|
|
12
|
+
var _chunkNASWEFHNcjs = require('./chunk-NASWEFHN.cjs');
|
|
13
13
|
|
|
14
14
|
// src/client/circuit-breaker.ts
|
|
15
15
|
var DEFAULT_CIRCUIT_BREAKER_OPTIONS = /* @__PURE__ */ Object.freeze({
|
|
@@ -39,7 +39,7 @@ var ConsecutiveBreaker = class {
|
|
|
39
39
|
async execute(fn) {
|
|
40
40
|
const currentState = this.state;
|
|
41
41
|
if (currentState === "OPEN") {
|
|
42
|
-
throw new (0,
|
|
42
|
+
throw new (0, _chunkNASWEFHNcjs.DgiiServiceError)(
|
|
43
43
|
"Circuito abierto: estrategia deshabilitada temporalmente"
|
|
44
44
|
);
|
|
45
45
|
}
|
|
@@ -89,9 +89,9 @@ var DEFAULT_RETRY_OPTIONS = /* @__PURE__ */ Object.freeze({
|
|
|
89
89
|
maxDelayMs: 1e4
|
|
90
90
|
});
|
|
91
91
|
function isRetryableError(error) {
|
|
92
|
-
if (error instanceof
|
|
93
|
-
if (error instanceof
|
|
94
|
-
if (error instanceof
|
|
92
|
+
if (error instanceof _chunkNASWEFHNcjs.DgiiNotFoundError) return false;
|
|
93
|
+
if (error instanceof _chunkNASWEFHNcjs.DgiiConnectionError) return true;
|
|
94
|
+
if (error instanceof _chunkNASWEFHNcjs.DgiiServiceError) {
|
|
95
95
|
const code = error.statusCode;
|
|
96
96
|
if (code !== void 0 && code >= 500) return true;
|
|
97
97
|
return false;
|
|
@@ -123,10 +123,10 @@ async function withRetry(fn, options = DEFAULT_RETRY_OPTIONS) {
|
|
|
123
123
|
var DgiiClient = class {
|
|
124
124
|
constructor(options) {
|
|
125
125
|
const timeout = _optionalChain([options, 'optionalAccess', _ => _.timeout]);
|
|
126
|
-
this._scraping = new (0,
|
|
126
|
+
this._scraping = new (0, _chunkFRM5XREFcjs.ScrapingClient)(
|
|
127
127
|
timeout ? { timeout } : void 0
|
|
128
128
|
);
|
|
129
|
-
this._soap = new (0,
|
|
129
|
+
this._soap = new (0, _chunkDLB5RJ7Ecjs.DgiiSoapClient)(
|
|
130
130
|
timeout ? { timeout } : void 0
|
|
131
131
|
);
|
|
132
132
|
this._scrapingBreaker = new ConsecutiveBreaker(
|
|
@@ -150,11 +150,16 @@ var DgiiClient = class {
|
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
|
-
* Valida un comprobante fiscal
|
|
153
|
+
* Valida un comprobante fiscal contra la DGII.
|
|
154
|
+
*
|
|
155
|
+
* @param rnc - RNC del emisor
|
|
156
|
+
* @param ncf - NCF o e-NCF a validar
|
|
157
|
+
* @param options - Datos adicionales del e-NCF (`rncComprador`,
|
|
158
|
+
* `codigoSeguridad`). Se ignoran cuando `ncf` no es un e-NCF válido.
|
|
154
159
|
*/
|
|
155
|
-
async getNCF(rnc, ncf) {
|
|
160
|
+
async getNCF(rnc, ncf, options) {
|
|
156
161
|
return this._executeWithFallback(
|
|
157
|
-
(client) => client.getNCF(rnc, ncf)
|
|
162
|
+
(client) => client.getNCF(rnc, ncf, options)
|
|
158
163
|
);
|
|
159
164
|
}
|
|
160
165
|
async _executeWithFallback(operation) {
|
|
@@ -167,7 +172,7 @@ var DgiiClient = class {
|
|
|
167
172
|
)
|
|
168
173
|
);
|
|
169
174
|
} catch (error) {
|
|
170
|
-
if (error instanceof
|
|
175
|
+
if (error instanceof _chunkNASWEFHNcjs.DgiiNotFoundError) throw error;
|
|
171
176
|
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
172
177
|
}
|
|
173
178
|
if (this._soapFallback) {
|
|
@@ -179,11 +184,11 @@ var DgiiClient = class {
|
|
|
179
184
|
)
|
|
180
185
|
);
|
|
181
186
|
} catch (error) {
|
|
182
|
-
if (error instanceof
|
|
187
|
+
if (error instanceof _chunkNASWEFHNcjs.DgiiNotFoundError) throw error;
|
|
183
188
|
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
184
189
|
}
|
|
185
190
|
}
|
|
186
|
-
throw new (0,
|
|
191
|
+
throw new (0, _chunkNASWEFHNcjs.AllStrategiesFailedError)(
|
|
187
192
|
`Todas las estrategias de consulta fallaron. \xDAltimo error: ${_nullishCoalesce(_optionalChain([errors, 'access', _6 => _6[errors.length - 1], 'optionalAccess', _7 => _7.message]), () => ( "desconocido"))}`,
|
|
188
193
|
errors
|
|
189
194
|
);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateEcf
|
|
3
|
+
} from "./chunk-H42U4GI5.js";
|
|
1
4
|
import {
|
|
2
5
|
wrapFetchError
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KW4ZV6FJ.js";
|
|
4
7
|
import {
|
|
5
8
|
collapseSpaces,
|
|
6
9
|
stripNonDigits
|
|
@@ -8,7 +11,7 @@ import {
|
|
|
8
11
|
import {
|
|
9
12
|
DgiiNotFoundError,
|
|
10
13
|
DgiiServiceError
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-IRQX4WNQ.js";
|
|
12
15
|
|
|
13
16
|
// src/scraping/endpoints.ts
|
|
14
17
|
var DGII_RNC_URL = "https://dgii.gov.do/app/WebApps/ConsultasWeb2/ConsultasWeb/consultas/rnc.aspx";
|
|
@@ -21,10 +24,24 @@ var FORM_FIELDS = /* @__PURE__ */ Object.freeze({
|
|
|
21
24
|
rncSubmit: "ctl00$cphMain$btnBuscarPorRNC",
|
|
22
25
|
ncfRncInput: "ctl00$cphMain$txtRNC",
|
|
23
26
|
ncfInput: "ctl00$cphMain$txtNCF",
|
|
24
|
-
ncfSubmit: "ctl00$cphMain$btnConsultar"
|
|
27
|
+
ncfSubmit: "ctl00$cphMain$btnConsultar",
|
|
28
|
+
// Campos adicionales para e-NCF (serie E)
|
|
29
|
+
ncfRncCompradorInput: "ctl00$cphMain$txtRncComprador",
|
|
30
|
+
ncfCodigoSeguridadInput: "ctl00$cphMain$txtCodigoSeg"
|
|
25
31
|
});
|
|
26
32
|
|
|
27
33
|
// src/scraping/html-parser.ts
|
|
34
|
+
var NCF_INVALID_PHRASES = /* @__PURE__ */ Object.freeze([
|
|
35
|
+
"no es v",
|
|
36
|
+
"no se encuentra",
|
|
37
|
+
"no existe",
|
|
38
|
+
// Respuesta de la DGII a un e-NCF con código de seguridad incorrecto
|
|
39
|
+
"no encontrado"
|
|
40
|
+
]);
|
|
41
|
+
function isKnownInvalidMessage(message) {
|
|
42
|
+
const normalized = message.toLowerCase();
|
|
43
|
+
return NCF_INVALID_PHRASES.some((phrase) => normalized.includes(phrase));
|
|
44
|
+
}
|
|
28
45
|
function extractViewStateTokens(html) {
|
|
29
46
|
const viewState = extractInputValue(html, "__VIEWSTATE");
|
|
30
47
|
const viewStateGenerator = extractInputValue(html, "__VIEWSTATEGENERATOR");
|
|
@@ -91,7 +108,7 @@ function parseNcfHtml(html) {
|
|
|
91
108
|
const infoEnd = html.indexOf("</span>", infoStart);
|
|
92
109
|
if (infoEnd !== -1) {
|
|
93
110
|
const infoBlock = html.slice(infoStart, infoEnd);
|
|
94
|
-
if (
|
|
111
|
+
if (isKnownInvalidMessage(infoBlock)) {
|
|
95
112
|
return { valid: false, rnc: "", ncf: "", nombreComercial: void 0 };
|
|
96
113
|
}
|
|
97
114
|
}
|
|
@@ -121,6 +138,58 @@ function parseNcfHtml(html) {
|
|
|
121
138
|
nombreComercial: nombreComercial ? collapseSpaces(nombreComercial) : void 0
|
|
122
139
|
};
|
|
123
140
|
}
|
|
141
|
+
function parseEcfHtml(html) {
|
|
142
|
+
const info = extractSpanValue(html, "cphMain_lblInformacion");
|
|
143
|
+
if (info && isKnownInvalidMessage(info)) {
|
|
144
|
+
return { valid: false, rnc: "", ncf: "" };
|
|
145
|
+
}
|
|
146
|
+
if (info) {
|
|
147
|
+
throw new DgiiServiceError(`La DGII respondi\xF3: ${info}`);
|
|
148
|
+
}
|
|
149
|
+
if (!html.includes("cphMain_PResultadoFE")) {
|
|
150
|
+
throw new DgiiServiceError(
|
|
151
|
+
"Formato de respuesta HTML inesperado: panel de resultado e-NCF no encontrado"
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const rncEmisor = extractSpanValue(html, "cphMain_lblrncemisor");
|
|
155
|
+
const ncf = extractSpanValue(html, "cphMain_lblencf");
|
|
156
|
+
if (!rncEmisor && !ncf) {
|
|
157
|
+
throw new DgiiServiceError(
|
|
158
|
+
"Formato de respuesta HTML inesperado: panel e-NCF sin emisor ni e-NCF"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const rncComprador = extractSpanValue(html, "cphMain_lblrnccomprador");
|
|
162
|
+
const codigoSeguridad = extractSpanValue(html, "cphMain_lblCodSeguridad");
|
|
163
|
+
const estado = extractSpanValue(html, "cphMain_lblEstadoFe");
|
|
164
|
+
const fechaEmision = extractSpanValue(html, "cphMain_lblFechaEmision");
|
|
165
|
+
const fechaFirma = extractSpanValue(html, "cphMain_lblFechaFirma");
|
|
166
|
+
return {
|
|
167
|
+
valid: true,
|
|
168
|
+
rnc: stripNonDigits(rncEmisor),
|
|
169
|
+
ncf: collapseSpaces(ncf),
|
|
170
|
+
rncComprador: rncComprador ? stripNonDigits(rncComprador) : void 0,
|
|
171
|
+
codigoSeguridad: codigoSeguridad || void 0,
|
|
172
|
+
estado: estado || void 0,
|
|
173
|
+
montoTotal: parseAmount(extractSpanValue(html, "cphMain_lblMontoTotal")),
|
|
174
|
+
totalItbis: parseAmount(extractSpanValue(html, "cphMain_lblTotalItbis")),
|
|
175
|
+
fechaEmision: fechaEmision || void 0,
|
|
176
|
+
fechaFirma: fechaFirma || void 0
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function parseAmount(raw) {
|
|
180
|
+
if (!raw) return void 0;
|
|
181
|
+
const value = Number(raw.replace(/,/g, ""));
|
|
182
|
+
return Number.isFinite(value) ? value : void 0;
|
|
183
|
+
}
|
|
184
|
+
function extractSpanValue(html, spanId) {
|
|
185
|
+
const idx = html.indexOf(`id="${spanId}"`);
|
|
186
|
+
if (idx === -1) return "";
|
|
187
|
+
const tagEnd = html.indexOf(">", idx);
|
|
188
|
+
if (tagEnd === -1) return "";
|
|
189
|
+
const closeTag = html.indexOf("</span>", tagEnd);
|
|
190
|
+
if (closeTag === -1) return "";
|
|
191
|
+
return decodeHtmlEntities(stripHtmlTags(html.slice(tagEnd + 1, closeTag)));
|
|
192
|
+
}
|
|
124
193
|
function extractInputValue(html, name) {
|
|
125
194
|
let idx = html.indexOf(`id="${name}"`);
|
|
126
195
|
if (idx === -1) {
|
|
@@ -201,26 +270,60 @@ var ScrapingClient = class {
|
|
|
201
270
|
return parseContribuyenteHtml(html);
|
|
202
271
|
}
|
|
203
272
|
/**
|
|
204
|
-
* Valida un comprobante fiscal
|
|
273
|
+
* Valida un comprobante fiscal contra la DGII.
|
|
274
|
+
*
|
|
275
|
+
* Soporta NCF (serie B) y e-NCF (serie E). Para un e-NCF la DGII
|
|
276
|
+
* exige el RNC del comprador; sin él responde con un mensaje de campo
|
|
277
|
+
* requerido y este método lanza `DgiiServiceError`.
|
|
278
|
+
*
|
|
279
|
+
* @param rnc - RNC del emisor
|
|
280
|
+
* @param ncf - NCF o e-NCF a validar
|
|
281
|
+
* @param options - Datos adicionales del e-NCF (`rncComprador`,
|
|
282
|
+
* `codigoSeguridad`). Se ignoran cuando `ncf` no es un e-NCF válido.
|
|
205
283
|
*/
|
|
206
|
-
async getNCF(rnc, ncf) {
|
|
284
|
+
async getNCF(rnc, ncf, options) {
|
|
207
285
|
if (typeof rnc !== "string" || rnc.trim() === "") {
|
|
208
286
|
throw new DgiiServiceError("El par\xE1metro rnc es requerido");
|
|
209
287
|
}
|
|
210
288
|
if (typeof ncf !== "string" || ncf.trim() === "") {
|
|
211
289
|
throw new DgiiServiceError("El par\xE1metro ncf es requerido");
|
|
212
290
|
}
|
|
291
|
+
const rncComprador = options?.rncComprador;
|
|
292
|
+
if (rncComprador !== void 0 && (typeof rncComprador !== "string" || rncComprador.trim() === "")) {
|
|
293
|
+
throw new DgiiServiceError(
|
|
294
|
+
"La opci\xF3n rncComprador debe ser un string no vac\xEDo"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const codigoSeguridad = options?.codigoSeguridad;
|
|
298
|
+
if (codigoSeguridad !== void 0 && (typeof codigoSeguridad !== "string" || codigoSeguridad.trim() === "")) {
|
|
299
|
+
throw new DgiiServiceError(
|
|
300
|
+
"La opci\xF3n codigoSeguridad debe ser un string no vac\xEDo"
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const isEcf = validateEcf(ncf).valid;
|
|
213
304
|
const tokens = await this._fetchTokens(this._ncfUrl);
|
|
214
|
-
const
|
|
305
|
+
const fields = [
|
|
215
306
|
[FORM_FIELDS.viewState, tokens.viewState],
|
|
216
307
|
[FORM_FIELDS.viewStateGenerator, tokens.viewStateGenerator],
|
|
217
308
|
[FORM_FIELDS.eventValidation, tokens.eventValidation],
|
|
218
309
|
[FORM_FIELDS.ncfRncInput, rnc.trim()],
|
|
219
|
-
[FORM_FIELDS.ncfInput, ncf.trim()]
|
|
220
|
-
|
|
221
|
-
|
|
310
|
+
[FORM_FIELDS.ncfInput, ncf.trim()]
|
|
311
|
+
];
|
|
312
|
+
if (isEcf) {
|
|
313
|
+
if (rncComprador !== void 0) {
|
|
314
|
+
fields.push([FORM_FIELDS.ncfRncCompradorInput, rncComprador.trim()]);
|
|
315
|
+
}
|
|
316
|
+
if (codigoSeguridad !== void 0) {
|
|
317
|
+
fields.push([
|
|
318
|
+
FORM_FIELDS.ncfCodigoSeguridadInput,
|
|
319
|
+
codigoSeguridad.trim()
|
|
320
|
+
]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
fields.push([FORM_FIELDS.ncfSubmit, "Buscar"]);
|
|
324
|
+
const body = buildFormBody(fields);
|
|
222
325
|
const html = await this._post(this._ncfUrl, body);
|
|
223
|
-
return parseNcfHtml(html);
|
|
326
|
+
return isEcf ? parseEcfHtml(html) : parseNcfHtml(html);
|
|
224
327
|
}
|
|
225
328
|
async _fetchTokens(url) {
|
|
226
329
|
let response;
|