arca-sdk 1.0.4 → 1.1.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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ Todos los cambios notables de este proyecto se documentan en este archivo.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.1.0] — 2026-02-28
8
+
9
+ ### ✨ Nuevos Comprobantes (Vouchers)
10
+
11
+ Se expandió la funcionalidad del servicio de facturación (`WsfeService`) para cubrir el espectro completo de comprobantes básicos:
12
+
13
+ - **Notas de Crédito**: Agregados métodos `issueCreditNoteA()`, `issueCreditNoteB()` y `issueCreditNoteC()`.
14
+ - **Notas de Débito**: Agregados métodos `issueDebitNoteA()`, `issueDebitNoteB()` y `issueDebitNoteC()`.
15
+ - **Recibos**: Agregados métodos `issueReceiptA()`, `issueReceiptB()` y `issueReceiptC()`.
16
+ - **Comprobantes Asociados**: El SDK ahora genera correctamente el nodo `<ar:CbtesAsoc>` de forma obligatoria para emitir NC/ND, asegurando que la operación de contingencia (anulación total o parcial de una factura) respete el estándar del ente recaudador.
17
+
18
+ ---
19
+
7
20
  ## [1.0.4] — 2026-02-27
8
21
 
9
22
  ### 🐛 Bugfix — Timezone Handling
package/README.md CHANGED
@@ -127,9 +127,12 @@ console.log('QR:', result.qrUrl); // 'https://www.afip.gob.ar/fe/qr/
127
127
  |--------|-------------|---------------|
128
128
  | `issueSimpleReceipt()` | Ticket C | Monto total, sin detalle. Ideal para POS simple |
129
129
  | `issueReceipt()` | Ticket C + items | Con detalle de productos guardado localmente |
130
- | `issueInvoiceC()` | Factura C | Monotributistas a consumidor final |
130
+ | `issueInvoiceC()` | Factura C | Monotributistas a consumidor final / Empresas |
131
131
  | `issueInvoiceB()` | Factura B | Responsable Inscripto a consumidor final / Monotributo |
132
132
  | `issueInvoiceA()` | Factura A | Responsable Inscripto a Responsable Inscripto |
133
+ | `issueCreditNoteA/B/C()` | Nota de Crédito | Anulación/Devolución (Requiere asociar la factura original) |
134
+ | `issueDebitNoteA/B/C()` | Nota de Débito | Cobro extra/Penalidad (Requiere asociar la factura original) |
135
+ | `issueReceiptA/B/C()` | Recibo | Comprobante de pago (misma emisión que una factura) |
133
136
 
134
137
  ### ✅ Consultas disponibles
135
138
 
@@ -180,6 +183,26 @@ result.vat?.forEach(v => {
180
183
  });
181
184
  ```
182
185
 
186
+ ### Nota de Crédito (Anulando factura previa)
187
+
188
+ ```typescript
189
+ import { InvoiceType } from 'arca-sdk';
190
+
191
+ const result = await wsfe.issueCreditNoteC({
192
+ items: [
193
+ { description: 'Anulación de equipo defectuoso', quantity: 1, unitPrice: 45000 },
194
+ ],
195
+ // ⚠️ Obligatorio en NC/ND: especificar el comprobante original afectado
196
+ associatedInvoices: [{
197
+ type: InvoiceType.FACTURA_C, // La factura que estoy anulando
198
+ pointOfSale: 4,
199
+ invoiceNumber: 15302,
200
+ }],
201
+ });
202
+
203
+ console.log('CAE de anulación:', result.cae);
204
+ ```
205
+
183
206
  ### Consulta de Padrón A13
184
207
 
185
208
  ```typescript
@@ -270,6 +293,9 @@ try {
270
293
  }
271
294
  ```
272
295
 
296
+ ### 🚚 Acerca de los Remitos
297
+ > **¡Atención!** Este SDK implementa nativamente el servicio `WSFE` (Facturación Electrónica). Si tu negocio necesita emitir **Remitos Electrónicos Oficiales** para el traslado físico de mercaderías (Remitos Cárnicos, Azucareros, Harineros, etc.), tené en cuenta que la AFIP exige usar un webservice totalmente distinto llamado `WSREM` o similares. Estos servicios aún no están cubiertos por esta versión del SDK.
298
+
273
299
  ---
274
300
 
275
301
  ## Compatibilidad
package/dist/index.cjs CHANGED
@@ -507,8 +507,17 @@ var WsaaService = class {
507
507
  // src/types/wsfe.ts
508
508
  var InvoiceType = /* @__PURE__ */ ((InvoiceType2) => {
509
509
  InvoiceType2[InvoiceType2["FACTURA_A"] = 1] = "FACTURA_A";
510
+ InvoiceType2[InvoiceType2["NOTA_DEBITO_A"] = 2] = "NOTA_DEBITO_A";
511
+ InvoiceType2[InvoiceType2["NOTA_CREDITO_A"] = 3] = "NOTA_CREDITO_A";
512
+ InvoiceType2[InvoiceType2["RECIBO_A"] = 4] = "RECIBO_A";
510
513
  InvoiceType2[InvoiceType2["FACTURA_B"] = 6] = "FACTURA_B";
514
+ InvoiceType2[InvoiceType2["NOTA_DEBITO_B"] = 7] = "NOTA_DEBITO_B";
515
+ InvoiceType2[InvoiceType2["NOTA_CREDITO_B"] = 8] = "NOTA_CREDITO_B";
516
+ InvoiceType2[InvoiceType2["RECIBO_B"] = 9] = "RECIBO_B";
511
517
  InvoiceType2[InvoiceType2["FACTURA_C"] = 11] = "FACTURA_C";
518
+ InvoiceType2[InvoiceType2["NOTA_DEBITO_C"] = 12] = "NOTA_DEBITO_C";
519
+ InvoiceType2[InvoiceType2["NOTA_CREDITO_C"] = 13] = "NOTA_CREDITO_C";
520
+ InvoiceType2[InvoiceType2["RECIBO_C"] = 15] = "RECIBO_C";
512
521
  InvoiceType2[InvoiceType2["TICKET_A"] = 81] = "TICKET_A";
513
522
  InvoiceType2[InvoiceType2["TICKET_B"] = 82] = "TICKET_B";
514
523
  InvoiceType2[InvoiceType2["TICKET_C"] = 83] = "TICKET_C";
@@ -737,57 +746,93 @@ var WsfeService = class _WsfeService {
737
746
  return { ...cae, items: params.items };
738
747
  }
739
748
  /**
740
- * Emite una Factura C (consumidor final, sin discriminación de IVA).
749
+ * Emite una Factura A (Responsable Inscripto a Responsable Inscripto, con IVA discriminado).
750
+ * REQUIERE `vatRate` en todos los items.
741
751
  */
742
- async issueInvoiceC(params) {
743
- const total = round(calculateTotal(params.items));
744
- return this.issueDocument({
745
- type: 11 /* FACTURA_C */,
746
- concept: params.concept || 1 /* PRODUCTS */,
747
- total,
748
- date: params.date,
749
- buyer: {
750
- docType: 99 /* FINAL_CONSUMER */,
751
- docNumber: "0"
752
- },
753
- items: params.items
754
- });
752
+ async issueInvoiceA(params) {
753
+ return this.issueInvoiceWithVAT(1 /* FACTURA_A */, params);
755
754
  }
756
755
  /**
757
756
  * Emite una Factura B (con IVA discriminado).
758
757
  * REQUIERE `vatRate` en todos los items.
759
758
  */
760
759
  async issueInvoiceB(params) {
761
- this.validateItemsWithVAT(params.items);
762
- const includesVAT = params.includesVAT || false;
763
- const vatData = this.calculateVATByRate(params.items, includesVAT);
764
- return this.issueDocument({
765
- type: 6 /* FACTURA_B */,
766
- concept: params.concept || 1 /* PRODUCTS */,
767
- items: params.items,
768
- buyer: params.buyer,
769
- date: params.date,
770
- vatData,
771
- includesVAT
772
- });
760
+ return this.issueInvoiceWithVAT(6 /* FACTURA_B */, params);
773
761
  }
774
762
  /**
775
- * Emite una Factura A (Responsable Inscripto a Responsable Inscripto, con IVA discriminado).
776
- * REQUIERE `vatRate` en todos los items.
763
+ * Emite una Factura C (consumidor final, sin discriminación de IVA).
777
764
  */
778
- async issueInvoiceA(params) {
779
- this.validateItemsWithVAT(params.items);
780
- const includesVAT = params.includesVAT || false;
781
- const vatData = this.calculateVATByRate(params.items, includesVAT);
782
- return this.issueDocument({
783
- type: 1 /* FACTURA_A */,
784
- concept: params.concept || 1 /* PRODUCTS */,
785
- items: params.items,
786
- buyer: params.buyer,
787
- date: params.date,
788
- vatData,
789
- includesVAT
790
- });
765
+ async issueInvoiceC(params) {
766
+ return this.issueInvoiceWithoutVAT(11 /* FACTURA_C */, params);
767
+ }
768
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
769
+ // Recibos
770
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
771
+ /**
772
+ * Emite un Recibo A (con IVA discriminado).
773
+ */
774
+ async issueReceiptA(params) {
775
+ return this.issueInvoiceWithVAT(4 /* RECIBO_A */, params);
776
+ }
777
+ /**
778
+ * Emite un Recibo B (con IVA discriminado).
779
+ */
780
+ async issueReceiptB(params) {
781
+ return this.issueInvoiceWithVAT(9 /* RECIBO_B */, params);
782
+ }
783
+ /**
784
+ * Emite un Recibo C (sin discriminación de IVA).
785
+ */
786
+ async issueReceiptC(params) {
787
+ return this.issueInvoiceWithoutVAT(15 /* RECIBO_C */, params);
788
+ }
789
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
790
+ // Notas de Crédito
791
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
792
+ /**
793
+ * Emite una Nota de Crédito A.
794
+ * REQUIERE especificar la Factura A original en `associatedInvoices`.
795
+ */
796
+ async issueCreditNoteA(params) {
797
+ return this.issueInvoiceWithVAT(3 /* NOTA_CREDITO_A */, params);
798
+ }
799
+ /**
800
+ * Emite una Nota de Crédito B.
801
+ * REQUIERE especificar la Factura B original en `associatedInvoices`.
802
+ */
803
+ async issueCreditNoteB(params) {
804
+ return this.issueInvoiceWithVAT(8 /* NOTA_CREDITO_B */, params);
805
+ }
806
+ /**
807
+ * Emite una Nota de Crédito C.
808
+ * REQUIERE especificar la Factura C original en `associatedInvoices`.
809
+ */
810
+ async issueCreditNoteC(params) {
811
+ return this.issueInvoiceWithoutVAT(13 /* NOTA_CREDITO_C */, params);
812
+ }
813
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
814
+ // Notas de Débito
815
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
816
+ /**
817
+ * Emite una Nota de Débito A.
818
+ * REQUIERE especificar la Factura A original en `associatedInvoices`.
819
+ */
820
+ async issueDebitNoteA(params) {
821
+ return this.issueInvoiceWithVAT(2 /* NOTA_DEBITO_A */, params);
822
+ }
823
+ /**
824
+ * Emite una Nota de Débito B.
825
+ * REQUIERE especificar la Factura B original en `associatedInvoices`.
826
+ */
827
+ async issueDebitNoteB(params) {
828
+ return this.issueInvoiceWithVAT(7 /* NOTA_DEBITO_B */, params);
829
+ }
830
+ /**
831
+ * Emite una Nota de Débito C.
832
+ * REQUIERE especificar la Factura C original en `associatedInvoices`.
833
+ */
834
+ async issueDebitNoteC(params) {
835
+ return this.issueInvoiceWithoutVAT(12 /* NOTA_DEBITO_C */, params);
791
836
  }
792
837
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
793
838
  // Consultas
@@ -918,6 +963,63 @@ var WsfeService = class _WsfeService {
918
963
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
919
964
  // Métodos internos
920
965
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
966
+ /**
967
+ * Helper para emitir comprobantes tipo A/B que requieren IVA
968
+ */
969
+ async issueInvoiceWithVAT(type, params) {
970
+ this.validateItemsWithVAT(params.items);
971
+ this.validateAssociatedInvoices(type, params.associatedInvoices);
972
+ const includesVAT = params.includesVAT || false;
973
+ const vatData = this.calculateVATByRate(params.items, includesVAT);
974
+ return this.issueDocument({
975
+ type,
976
+ concept: params.concept || 1 /* PRODUCTS */,
977
+ items: params.items,
978
+ buyer: params.buyer,
979
+ associatedInvoices: params.associatedInvoices,
980
+ date: params.date,
981
+ vatData,
982
+ includesVAT
983
+ });
984
+ }
985
+ /**
986
+ * Helper para emitir comprobantes tipo C que no discriminan IVA
987
+ */
988
+ async issueInvoiceWithoutVAT(type, params) {
989
+ this.validateAssociatedInvoices(type, params.associatedInvoices);
990
+ const total = round(calculateTotal(params.items));
991
+ return this.issueDocument({
992
+ type,
993
+ concept: params.concept || 1 /* PRODUCTS */,
994
+ total,
995
+ date: params.date,
996
+ buyer: params.buyer || {
997
+ docType: 99 /* FINAL_CONSUMER */,
998
+ docNumber: "0"
999
+ },
1000
+ items: params.items,
1001
+ associatedInvoices: params.associatedInvoices
1002
+ });
1003
+ }
1004
+ /**
1005
+ * Validación obligatoria para NC/ND
1006
+ */
1007
+ validateAssociatedInvoices(type, associatedInvoices) {
1008
+ const needsAssociation = [
1009
+ 3 /* NOTA_CREDITO_A */,
1010
+ 2 /* NOTA_DEBITO_A */,
1011
+ 8 /* NOTA_CREDITO_B */,
1012
+ 7 /* NOTA_DEBITO_B */,
1013
+ 13 /* NOTA_CREDITO_C */,
1014
+ 12 /* NOTA_DEBITO_C */
1015
+ ].includes(type);
1016
+ if (needsAssociation && (!associatedInvoices || associatedInvoices.length === 0)) {
1017
+ throw new ArcaValidationError(
1018
+ "Las Notas de Cr\xE9dito y D\xE9bito requieren al menos un comprobante asociado.",
1019
+ { hint: "Debes enviar el arreglo `associatedInvoices` con la factura original a la cual haces referencia" }
1020
+ );
1021
+ }
1022
+ }
921
1023
  /**
922
1024
  * Método genérico interno para emitir cualquier tipo de comprobante.
923
1025
  */
@@ -942,6 +1044,7 @@ var WsfeService = class _WsfeService {
942
1044
  concept: request.concept,
943
1045
  date: request.date || /* @__PURE__ */ new Date(),
944
1046
  buyer: request.buyer,
1047
+ associatedInvoices: request.associatedInvoices,
945
1048
  net,
946
1049
  vat,
947
1050
  total,
@@ -1087,6 +1190,21 @@ var WsfeService = class _WsfeService {
1087
1190
  });
1088
1191
  vatXml += "\n </ar:Iva>";
1089
1192
  }
1193
+ let asocXml = "";
1194
+ if (params.associatedInvoices && params.associatedInvoices.length > 0) {
1195
+ asocXml = "<ar:CbtesAsoc>";
1196
+ params.associatedInvoices.forEach((asoc) => {
1197
+ asocXml += `
1198
+ <ar:CbteAsoc>
1199
+ <ar:Tipo>${asoc.type}</ar:Tipo>
1200
+ <ar:PtoVta>${asoc.pointOfSale}</ar:PtoVta>
1201
+ <ar:Nro>${asoc.invoiceNumber}</ar:Nro>
1202
+ ${asoc.cuit ? `<ar:Cuit>${asoc.cuit}</ar:Cuit>` : ""}
1203
+ ${asoc.date ? `<ar:CbteFch>${asoc.date.toISOString().split("T")[0].replace(/-/g, "")}</ar:CbteFch>` : ""}
1204
+ </ar:CbteAsoc>`;
1205
+ });
1206
+ asocXml += "\n </ar:CbtesAsoc>";
1207
+ }
1090
1208
  return `<?xml version="1.0" encoding="UTF-8"?>
1091
1209
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1092
1210
  xmlns:ar="http://ar.gov.afip.dif.FEV1/">
@@ -1120,6 +1238,7 @@ var WsfeService = class _WsfeService {
1120
1238
  <ar:ImpTrib>0.00</ar:ImpTrib>
1121
1239
  <ar:MonId>PES</ar:MonId>
1122
1240
  <ar:MonCotiz>1</ar:MonCotiz>
1241
+ ${asocXml}
1123
1242
  ${vatXml}
1124
1243
  </ar:FECAEDetRequest>
1125
1244
  </ar:FeDetReq>