consultame-sdk 1.2.1 → 1.2.3

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 CHANGED
@@ -5,8 +5,8 @@ que se necesitan para interactuar con **SIFEN** (Sistema Integrado de Facturaci
5
5
  Nacional de Paraguay – SET).
6
6
 
7
7
  La librería **no** hace las peticiones HTTP al web service de SIFEN: su responsabilidad es
8
- **construir los sobres XML** (consulta de RUC, consulta de lote, consulta de documento y
9
- evento de nominación de un documento ya emitido),
8
+ **construir los sobres XML** (consulta de RUC, consulta de lote, consulta de documento,
9
+ envío de lote, y los eventos de nominación y cancelación de un documento ya emitido),
10
10
  **extraer el certificado y la llave privada** desde un archivo de firma digital `.p12`/`.pfx`,
11
11
  **firmar digitalmente (XMLDSig)** el Documento Electrónico generado y **generar el código QR**
12
12
  que se incrusta en el documento electrónico ya firmado.
@@ -27,6 +27,8 @@ Con esa salida, la aplicación consumidora envía los mensajes por su cuenta.
27
27
  - [`generateDocument`](#generatedocumentgeneratedocumentoelectronicodto)
28
28
  - [`signedXml`](#signedxmlxml-tag-responsedigitalsign)
29
29
  - [`nominationInvoice`](#nominationinvoicenominationinvoicedto)
30
+ - [`generateBatch`](#generatebatchgeneratebatchdto)
31
+ - [`cancelInvoice`](#cancelinvoicecancelinvoicedto)
30
32
  - [DTOs (contratos de entrada)](#dtos-contratos-de-entrada)
31
33
  - [Funciones utilitarias internas](#funciones-utilitarias-internas)
32
34
  - [Arquitectura del proyecto](#arquitectura-del-proyecto)
@@ -186,6 +188,19 @@ const xmlNominacion = sifen.nominationInvoice({
186
188
  digitoVerificado: "1",
187
189
  razonSocial: "Cliente Ejemplo S.R.L.",
188
190
  });
191
+
192
+ // 9) Armar el sobre de envío de lote (rEnvioLote) con el/los documento(s) ya preparados
193
+ const xmlLoteEnvio = sifen.generateBatch({
194
+ id: 5,
195
+ xml: xmlConQr, // contenido que va dentro de <xDE>
196
+ });
197
+
198
+ // 10) Cancelar un documento ya emitido (evento de cancelación, sin firmar)
199
+ const xmlCancelacion = sifen.cancelInvoice({
200
+ id: 6,
201
+ cdc: "01800123456001001000000012023052212345678901",
202
+ motivo: "Error en los datos de la operación",
203
+ });
189
204
  ```
190
205
 
191
206
  ---
@@ -775,6 +790,113 @@ try {
775
790
 
776
791
  ---
777
792
 
793
+ ### `generateBatch(generateBatchDto)`
794
+
795
+ Genera el sobre SOAP `rEnvioLote` que envuelve el contenido que la aplicación quiere enviar a
796
+ SIFEN como **lote** de documentos electrónicos.
797
+
798
+ > ⚠️ La librería **no** valida, comprime ni codifica el contenido: `xml` se inserta tal cual
799
+ > dentro de `<xDE>`. Si el web service de SIFEN espera ese contenido comprimido/codificado,
800
+ > la aplicación consumidora debe prepararlo antes de llamar a este método.
801
+
802
+ | Parámetro | Tipo | Descripción |
803
+ | --------- | -------- | ---------------------------------------------------- |
804
+ | `id` | `number` | Identificador de la petición (`dId`). |
805
+ | `xml` | `string` | Contenido a incluir en `<xDE>` (documento/s del lote). |
806
+
807
+ **Retorna:** `string` — XML SOAP. A diferencia de las consultas, **no pasa por `normalizeXML`**:
808
+ el sobre conserva los saltos de línea y la indentación de la plantilla.
809
+
810
+ ```ts
811
+ const xml = sifen.generateBatch({ id: 5, xml: xmlDocumentoFirmado });
812
+ ```
813
+
814
+ <details>
815
+ <summary>Ejemplo de salida</summary>
816
+
817
+ ```xml
818
+ <?xml version="1.0" encoding="UTF-8"?>
819
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
820
+ <env:Header/>
821
+ <env:Body>
822
+ <rEnvioLote xmlns="http://ekuatia.set.gov.py/sifen/xsd">
823
+ <dId>5</dId>
824
+ <xDE>...</xDE>
825
+ </rEnvioLote>
826
+ </env:Body>
827
+ </env:Envelope>
828
+ ```
829
+
830
+ </details>
831
+
832
+ ---
833
+
834
+ ### `cancelInvoice(cancelInvoiceDto)`
835
+
836
+ Genera el sobre SOAP `rEnviEventoDe` con el **evento de cancelación** (`rGeVeCan`) de un
837
+ documento electrónico ya emitido, identificado por su **CDC**.
838
+
839
+ > ⚠️ El XML devuelto **no está firmado**: la aplicación consumidora debe firmar el evento y
840
+ > enviarlo a SIFEN por su cuenta.
841
+
842
+ | Parámetro | Tipo | Descripción |
843
+ | --------- | -------- | ------------------------------------------------------------ |
844
+ | `id` | `number` | Identificador de la petición (`dId`). |
845
+ | `cdc` | `string` | CDC del documento a cancelar (`Id` de `rGeVeCan`). |
846
+ | `motivo` | `string` | Motivo de la cancelación (`mOtEve`). |
847
+
848
+ **Retorna:** `string` — XML SOAP con `dFecFirma` tomado de la fecha/hora actual y `dVerFor` = `150`.
849
+ El evento interno (`dEvReg`) va normalizado en una sola línea, pero el sobre externo conserva los
850
+ saltos de línea de la plantilla.
851
+
852
+ > A diferencia de [`nominationInvoice`](#nominationinvoicenominationinvoicedto), este método
853
+ > **no valida el DTO** (longitud del CDC o del motivo) y el atributo `Id` de `rEve` se emite
854
+ > con valor fijo `1`; `id` solo se usa como `dId`.
855
+
856
+ ```ts
857
+ const xml = sifen.cancelInvoice({
858
+ id: 6,
859
+ cdc: "01800123456001001000000012023052212345678901",
860
+ motivo: "Error en los datos de la operación",
861
+ });
862
+ ```
863
+
864
+ <details>
865
+ <summary>Ejemplo de salida (formateada para lectura)</summary>
866
+
867
+ ```xml
868
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
869
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
870
+ <env:Header/>
871
+ <env:Body>
872
+ <rEnviEventoDe xmlns="http://ekuatia.set.gov.py/sifen/xsd">
873
+ <dId>6</dId>
874
+ <dEvReg>
875
+ <gGroupGesEve xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
876
+ xsi:schemaLocation="http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd">
877
+ <rGesEve>
878
+ <rEve Id="1">
879
+ <dFecFirma>2026-11-05T17:57:03</dFecFirma>
880
+ <dVerFor>150</dVerFor>
881
+ <gGroupTiEvt>
882
+ <rGeVeCan>
883
+ <Id>01800123456001001000000012023052212345678901</Id>
884
+ <mOtEve>Error en los datos de la operación</mOtEve>
885
+ </rGeVeCan>
886
+ </gGroupTiEvt>
887
+ </rEve>
888
+ </rGesEve>
889
+ </gGroupGesEve>
890
+ </dEvReg>
891
+ </rEnviEventoDe>
892
+ </env:Body>
893
+ </env:Envelope>
894
+ ```
895
+
896
+ </details>
897
+
898
+ ---
899
+
778
900
  ## DTOs (contratos de entrada)
779
901
 
780
902
  Todos los DTOs de consulta extienden `GenerateIdDto`, que aporta el campo `id`.
@@ -819,6 +941,15 @@ interface NominationInvoiceDto extends GenerateIdDto {
819
941
  razonSocial: string;
820
942
  // ...y los campos opcionales del receptor (ruc, direccion, idDistricto, email, etc.)
821
943
  }
944
+
945
+ interface GenerateBatchDto extends GenerateIdDto {
946
+ xml: string; // contenido que se inserta en <xDE>
947
+ }
948
+
949
+ interface CancelInvoiceDto extends GenerateIdDto {
950
+ cdc: string; // CDC del documento a cancelar
951
+ motivo: string; // motivo de la cancelación
952
+ }
822
953
  ```
823
954
 
824
955
  Enums disponibles en [`src/shared/utils/dictionary.utils.ts`](src/shared/utils/dictionary.utils.ts):
@@ -835,15 +966,10 @@ enum LINK_SIFEN {
835
966
  }
836
967
  ```
837
968
 
838
- DTOs ya definidos pero **aún sin implementación** en la persistencia (ver
839
- [roadmap](#estado--roadmap)):
969
+ DTO ya definido pero **aún sin implementación** en la persistencia (ver
970
+ [roadmap](#estado--roadmap)); se importa por ruta y no está en el barrel `dtos/index.ts`:
840
971
 
841
972
  ```ts
842
- interface CancelInvoiceDto extends GenerateIdDto {
843
- cdc: string;
844
- motivo: string;
845
- }
846
-
847
973
  interface InvalidationInvoiceDto extends GenerateIdDto {
848
974
  timbrado: string;
849
975
  establecimiento: number;
@@ -879,7 +1005,9 @@ No forman parte de la API pública, pero es útil conocerlas:
879
1005
  | `generateConsultDocumentXml` | Arma el string XML de consulta de documento por CDC. |
880
1006
  | `generateConsultBatchXml` | Arma el string XML de consulta de lote. |
881
1007
  | `generateNominationInvoiceXml` | Valida el `NominationInvoiceDto` y arma el sobre `rEnviEventoDe` del evento de nominación con `xml2js.Builder`, ya normalizado. |
882
- | `convertToFormatDateSET` | Formatea una `Date` como fecha/hora SIFEN (usada para `dFecFirma` del evento). |
1008
+ | `generateSendBatchXml` | Arma el sobre `rEnvioLote` con el contenido recibido dentro de `<xDE>` (sin normalizar). |
1009
+ | `generateCancelInvoiceXml` | Arma el sobre `rEnviEventoDe` del evento de cancelación (`rGeVeCan`) con `xml2js.Builder`; evento interno normalizado, sin firmar. |
1010
+ | `convertToFormatDateSET` | Formatea una `Date` como fecha/hora SIFEN (usada para `dFecFirma` de los eventos). |
883
1011
  | `normalizeXML` | Colapsa el XML a una sola línea: elimina saltos, tabulaciones y espacios entre etiquetas. |
884
1012
  | `readDigitalSign` | Lee el `.p12` con `node-forge` y extrae certificado + llave privada en PEM, emparejando el certificado correcto por módulo RSA cuando hay varios. |
885
1013
  | `signXml` | Firma un XML (XMLDSig) con `xml-crypto`: `enveloped-signature` + `exc-c14n`, digest SHA-256, firma RSA-SHA256. _(async)_ |
@@ -947,6 +1075,10 @@ mantener estado intermedio (`jsonFactura`) mientras se arma cada sección del XM
947
1075
  delegando en `generateNominationInvoiceXml`, que valida el DTO y arma el evento con
948
1076
  `xml2js.Builder` (a diferencia de las consultas simples, que son strings de plantilla).
949
1077
 
1078
+ `cancelInvoice` (`CancelDocumentUseCase` → `SifenPersistence.cancelationInvoice`, nombre del
1079
+ puerto) y `generateBatch` (`GenerateBatchUseCase` → `SifenPersistence.generateBatch`) siguen el
1080
+ mismo flujo, delegando en `generateCancelInvoiceXml` y `generateSendBatchXml` respectivamente.
1081
+
950
1082
  **Por qué esta separación:**
951
1083
 
952
1084
  - **`domain`** no depende de nada externo: define _qué_ se hace (casos de uso) y el
@@ -991,6 +1123,7 @@ consultame-sdk/
991
1123
  │ │ │ ├── digitalSign.dto.ts
992
1124
  │ │ │ ├── generationQr.dto.ts
993
1125
  │ │ │ ├── nominationInvoice.dto.ts
1126
+ │ │ │ ├── generateBatch.dto.ts
994
1127
  │ │ │ ├── cancelDocument.dto.ts
995
1128
  │ │ │ └── invalidationInvoice.dto.ts
996
1129
  │ │ └── services/
@@ -1008,6 +1141,8 @@ consultame-sdk/
1008
1141
  │ │ ├── generateQr.useCase.ts
1009
1142
  │ │ ├── generateDocument.useCase.ts
1010
1143
  │ │ ├── nominationInvoice.useCase.ts
1144
+ │ │ ├── generateBatch.useCase.ts
1145
+ │ │ ├── cancelDocument.useCase.ts # (importado por ruta, no está en el barrel)
1011
1146
  │ │ └── signedXml.useCase.ts
1012
1147
  │ │
1013
1148
  │ └── infrastructure/ # Detalles concretos (ADAPTADORES)
@@ -1049,16 +1184,17 @@ Funcionalidades **implementadas**:
1049
1184
  Crédito/Débito y Remisión (`generateDocument`)
1050
1185
  - ✅ Firma digital XMLDSig del Documento Electrónico (`signedXml`)
1051
1186
  - ✅ Evento de nominación de un documento emitido (`nominationInvoice`)
1187
+ - ✅ Sobre de envío de lote (`generateBatch`)
1188
+ - ✅ Evento de cancelación de un documento emitido (`cancelInvoice`)
1052
1189
 
1053
1190
  Definidas pero **pendientes de implementación** (hoy lanzan `Method not implemented`
1054
1191
  o aún no están expuestas en `SifenService`):
1055
1192
 
1056
- - ⏳ Cancelación de documento (`cancelationInvoice` / `CancelInvoiceDto`)
1057
1193
  - ⏳ Inutilización de documentos (`invalidationInvoice` / `InvalidationInvoiceDto`)
1058
1194
 
1059
1195
  ---
1060
1196
 
1061
1197
  > **Nota:** esta librería genera insumos (XML, credenciales, firma XMLDSig y el QR) para SIFEN,
1062
1198
  > pero no realiza el envío HTTP de los documentos — eso queda a cargo de la aplicación
1063
- > consumidora. El evento que devuelve `nominationInvoice` tampoco viene firmado. `generateQr` **requiere** que el XML ya venga firmado (con `signedXml` o por
1199
+ > consumidora. Los eventos que devuelven `nominationInvoice` y `cancelInvoice` tampoco vienen firmados. `generateQr` **requiere** que el XML ya venga firmado (con `signedXml` o por
1064
1200
  > cualquier otro medio), porque toma el `DigestValue` de la firma para calcular el hash del QR.
package/dist/index.js CHANGED
@@ -131,6 +131,30 @@ var NominationInvoiceUseCase = class {
131
131
  }
132
132
  };
133
133
 
134
+ // src/sifen/domain/useCases/generateBatch.useCase.ts
135
+ var GenerateBatchUseCase = class {
136
+ constructor(sifenRepository) {
137
+ this.sifenRepository = sifenRepository;
138
+ }
139
+ sifenRepository;
140
+ execute(generateBatchDto) {
141
+ const batch = this.sifenRepository.generateBatch(generateBatchDto);
142
+ return batch;
143
+ }
144
+ };
145
+
146
+ // src/sifen/domain/useCases/cancelDocument.useCase.ts
147
+ var CancelDocumentUseCase = class {
148
+ constructor(sifenRepository) {
149
+ this.sifenRepository = sifenRepository;
150
+ }
151
+ sifenRepository;
152
+ execute(cancelInvoiceDto) {
153
+ const xml = this.sifenRepository.cancelationInvoice(cancelInvoiceDto);
154
+ return xml;
155
+ }
156
+ };
157
+
134
158
  // src/shared/utils/dictionary.utils.ts
135
159
  var TIPOS_DOCUMENTOS = [
136
160
  {
@@ -8168,6 +8192,19 @@ var generateConsultBatchXml = (consultBatchDto) => {
8168
8192
  </env:Envelope>
8169
8193
  `;
8170
8194
  };
8195
+ var generateSendBatchXml = (generateBatchDto) => {
8196
+ return `<?xml version="1.0" encoding="UTF-8"?>
8197
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
8198
+ <env:Header/>
8199
+ <env:Body>
8200
+ <rEnvioLote xmlns="http://ekuatia.set.gov.py/sifen/xsd">
8201
+ <dId>${generateBatchDto.id}</dId>
8202
+ <xDE>${generateBatchDto.xml}</xDE>
8203
+ </rEnvioLote>
8204
+ </env:Body>
8205
+ </env:Envelope>
8206
+ `;
8207
+ };
8171
8208
  var validateNominationInvoice = (dto) => {
8172
8209
  if (!/^\d{44}$/.test(dto.cdc)) {
8173
8210
  throw new Error("El CDC debe tener 44 d\xEDgitos");
@@ -8491,6 +8528,48 @@ var signXml = async (xml, tag, responseDigitalSign) => {
8491
8528
  throw error;
8492
8529
  }
8493
8530
  };
8531
+ var generateCancelInvoiceXml = (cancelInvocieDto) => {
8532
+ const json = {};
8533
+ json.gGroupGesEve = {};
8534
+ json.gGroupGesEve.rGesEve = {};
8535
+ json.gGroupGesEve.$ = {};
8536
+ json.gGroupGesEve.$["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
8537
+ json.gGroupGesEve.$["xsi:schemaLocation"] = "http://ekuatia.set.gov.py/sifen/xsd siRecepEvento_v150.xsd";
8538
+ json.gGroupGesEve.rGesEve.rEve = {};
8539
+ json.gGroupGesEve.rGesEve.rEve.$ = {};
8540
+ json.gGroupGesEve.rGesEve.rEve.$.Id = 1;
8541
+ json.gGroupGesEve.rGesEve.rEve.dFecFirma = convertToFormatDateSET(/* @__PURE__ */ new Date());
8542
+ json.gGroupGesEve.rGesEve.rEve.dVerFor = 150;
8543
+ json.gGroupGesEve.rGesEve.rEve.gGroupTiEvt = {};
8544
+ json.gGroupGesEve.rGesEve.rEve.gGroupTiEvt.rGeVeCan = {
8545
+ Id: cancelInvocieDto.cdc,
8546
+ mOtEve: cancelInvocieDto.motivo
8547
+ };
8548
+ const builder = new xml2js__namespace.Builder({
8549
+ xmldec: {
8550
+ version: "1.0",
8551
+ encoding: "UTF-8",
8552
+ standalone: false
8553
+ }
8554
+ });
8555
+ let xml = builder.buildObject(json);
8556
+ xml = normalizeXML(xml);
8557
+ xml = xml.replace(
8558
+ '<?xml version="1.0" encoding="UTF-8" standalone="no"?>',
8559
+ ""
8560
+ );
8561
+ return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
8562
+ <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
8563
+ <env:Header/>
8564
+ <env:Body>
8565
+ <rEnviEventoDe xmlns="http://ekuatia.set.gov.py/sifen/xsd">
8566
+ <dId>${cancelInvocieDto.id}</dId>
8567
+ <dEvReg>${xml}</dEvReg>
8568
+ </rEnviEventoDe>
8569
+ </env:Body>
8570
+ </env:Envelope>
8571
+ `;
8572
+ };
8494
8573
 
8495
8574
  // src/shared/clases/detalleDocumento.ts
8496
8575
  var DetalleDocumento = class {
@@ -10110,7 +10189,7 @@ var SifenPersistence = class {
10110
10189
  return xmlFormated;
10111
10190
  }
10112
10191
  cancelationInvoice(cancelInvoiceDto) {
10113
- throw new Error("Method not implemented.");
10192
+ return generateCancelInvoiceXml(cancelInvoiceDto);
10114
10193
  }
10115
10194
  invalidationInvoice(invalidationInvoiceDto) {
10116
10195
  try {
@@ -10167,6 +10246,13 @@ var SifenPersistence = class {
10167
10246
  throw error;
10168
10247
  }
10169
10248
  }
10249
+ generateBatch(generateBatchDto) {
10250
+ try {
10251
+ return generateSendBatchXml(generateBatchDto);
10252
+ } catch (error) {
10253
+ throw error;
10254
+ }
10255
+ }
10170
10256
  };
10171
10257
 
10172
10258
  // src/sifen/applications/services/sifen.service.ts
@@ -10179,6 +10265,8 @@ var SifenService = class {
10179
10265
  generateDocumentUseCase;
10180
10266
  signedXmlUseCase;
10181
10267
  nominationInvoiceUseCase;
10268
+ generateBatchUseCase;
10269
+ cancelDocumentUseCase;
10182
10270
  sifenRepository;
10183
10271
  constructor() {
10184
10272
  this.sifenRepository = new SifenPersistence();
@@ -10198,6 +10286,10 @@ var SifenService = class {
10198
10286
  this.nominationInvoiceUseCase = new NominationInvoiceUseCase(
10199
10287
  this.sifenRepository
10200
10288
  );
10289
+ this.generateBatchUseCase = new GenerateBatchUseCase(this.sifenRepository);
10290
+ this.cancelDocumentUseCase = new CancelDocumentUseCase(
10291
+ this.sifenRepository
10292
+ );
10201
10293
  }
10202
10294
  consultRuc(consultRucDto) {
10203
10295
  const ruc = this.consultRucUseCase.execute(consultRucDto);
@@ -10245,6 +10337,14 @@ var SifenService = class {
10245
10337
  throw error;
10246
10338
  }
10247
10339
  }
10340
+ generateBatch(generateBatchDto) {
10341
+ const xml = this.generateBatchUseCase.execute(generateBatchDto);
10342
+ return xml;
10343
+ }
10344
+ cancelInvoice(cancelInvocieDto) {
10345
+ const xml = this.cancelDocumentUseCase.execute(cancelInvocieDto);
10346
+ return xml;
10347
+ }
10248
10348
  };
10249
10349
 
10250
10350
  exports.SifenService = SifenService;