consultame-sdk 1.2.2 → 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
@@ -143,6 +143,18 @@ var GenerateBatchUseCase = class {
143
143
  }
144
144
  };
145
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
+
146
158
  // src/shared/utils/dictionary.utils.ts
147
159
  var TIPOS_DOCUMENTOS = [
148
160
  {
@@ -8516,6 +8528,48 @@ var signXml = async (xml, tag, responseDigitalSign) => {
8516
8528
  throw error;
8517
8529
  }
8518
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
+ };
8519
8573
 
8520
8574
  // src/shared/clases/detalleDocumento.ts
8521
8575
  var DetalleDocumento = class {
@@ -10135,7 +10189,7 @@ var SifenPersistence = class {
10135
10189
  return xmlFormated;
10136
10190
  }
10137
10191
  cancelationInvoice(cancelInvoiceDto) {
10138
- throw new Error("Method not implemented.");
10192
+ return generateCancelInvoiceXml(cancelInvoiceDto);
10139
10193
  }
10140
10194
  invalidationInvoice(invalidationInvoiceDto) {
10141
10195
  try {
@@ -10212,6 +10266,7 @@ var SifenService = class {
10212
10266
  signedXmlUseCase;
10213
10267
  nominationInvoiceUseCase;
10214
10268
  generateBatchUseCase;
10269
+ cancelDocumentUseCase;
10215
10270
  sifenRepository;
10216
10271
  constructor() {
10217
10272
  this.sifenRepository = new SifenPersistence();
@@ -10232,6 +10287,9 @@ var SifenService = class {
10232
10287
  this.sifenRepository
10233
10288
  );
10234
10289
  this.generateBatchUseCase = new GenerateBatchUseCase(this.sifenRepository);
10290
+ this.cancelDocumentUseCase = new CancelDocumentUseCase(
10291
+ this.sifenRepository
10292
+ );
10235
10293
  }
10236
10294
  consultRuc(consultRucDto) {
10237
10295
  const ruc = this.consultRucUseCase.execute(consultRucDto);
@@ -10283,6 +10341,10 @@ var SifenService = class {
10283
10341
  const xml = this.generateBatchUseCase.execute(generateBatchDto);
10284
10342
  return xml;
10285
10343
  }
10344
+ cancelInvoice(cancelInvocieDto) {
10345
+ const xml = this.cancelDocumentUseCase.execute(cancelInvocieDto);
10346
+ return xml;
10347
+ }
10286
10348
  };
10287
10349
 
10288
10350
  exports.SifenService = SifenService;