arca-sdk 0.4.0 → 1.0.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/dist/index.cjs CHANGED
@@ -32,14 +32,15 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ArcaAuthError: () => ArcaAuthError,
34
34
  ArcaError: () => ArcaError,
35
+ ArcaNetworkError: () => ArcaNetworkError,
35
36
  ArcaValidationError: () => ArcaValidationError,
36
- Concepto: () => Concepto,
37
+ BillingConcept: () => BillingConcept,
38
+ InvoiceType: () => InvoiceType,
37
39
  PadronService: () => PadronService,
38
- TipoComprobante: () => TipoComprobante,
39
- TipoDocumento: () => TipoDocumento,
40
+ TaxIdType: () => TaxIdType,
40
41
  WsaaService: () => WsaaService,
41
42
  WsfeService: () => WsfeService,
42
- generarUrlQR: () => generarUrlQR
43
+ generateQRUrl: () => generateQRUrl
43
44
  });
44
45
  module.exports = __toCommonJS(index_exports);
45
46
 
@@ -68,10 +69,11 @@ function getPadronEndpoint(environment) {
68
69
 
69
70
  // src/types/common.ts
70
71
  var ArcaError = class extends Error {
71
- constructor(message, code, details) {
72
+ constructor(message, code, details, hint) {
72
73
  super(message);
73
74
  this.code = code;
74
75
  this.details = details;
76
+ this.hint = hint;
75
77
  this.name = "ArcaError";
76
78
  }
77
79
  };
@@ -486,67 +488,127 @@ var WsaaService = class {
486
488
  };
487
489
 
488
490
  // src/types/wsfe.ts
489
- var TipoComprobante = /* @__PURE__ */ ((TipoComprobante2) => {
490
- TipoComprobante2[TipoComprobante2["FACTURA_A"] = 1] = "FACTURA_A";
491
- TipoComprobante2[TipoComprobante2["FACTURA_B"] = 6] = "FACTURA_B";
492
- TipoComprobante2[TipoComprobante2["FACTURA_C"] = 11] = "FACTURA_C";
493
- TipoComprobante2[TipoComprobante2["TICKET_A"] = 81] = "TICKET_A";
494
- TipoComprobante2[TipoComprobante2["TICKET_B"] = 82] = "TICKET_B";
495
- TipoComprobante2[TipoComprobante2["TICKET_C"] = 83] = "TICKET_C";
496
- return TipoComprobante2;
497
- })(TipoComprobante || {});
498
- var Concepto = /* @__PURE__ */ ((Concepto2) => {
499
- Concepto2[Concepto2["PRODUCTOS"] = 1] = "PRODUCTOS";
500
- Concepto2[Concepto2["SERVICIOS"] = 2] = "SERVICIOS";
501
- Concepto2[Concepto2["PRODUCTOS_Y_SERVICIOS"] = 3] = "PRODUCTOS_Y_SERVICIOS";
502
- return Concepto2;
503
- })(Concepto || {});
504
- var TipoDocumento = /* @__PURE__ */ ((TipoDocumento2) => {
505
- TipoDocumento2[TipoDocumento2["CUIT"] = 80] = "CUIT";
506
- TipoDocumento2[TipoDocumento2["CUIL"] = 86] = "CUIL";
507
- TipoDocumento2[TipoDocumento2["CDI"] = 87] = "CDI";
508
- TipoDocumento2[TipoDocumento2["LE"] = 89] = "LE";
509
- TipoDocumento2[TipoDocumento2["LC"] = 90] = "LC";
510
- TipoDocumento2[TipoDocumento2["CI_EXTRANJERA"] = 91] = "CI_EXTRANJERA";
511
- TipoDocumento2[TipoDocumento2["PASAPORTE"] = 94] = "PASAPORTE";
512
- TipoDocumento2[TipoDocumento2["CI_BUENOS_AIRES"] = 95] = "CI_BUENOS_AIRES";
513
- TipoDocumento2[TipoDocumento2["CI_POLICIA_FEDERAL"] = 96] = "CI_POLICIA_FEDERAL";
514
- TipoDocumento2[TipoDocumento2["DNI"] = 96] = "DNI";
515
- TipoDocumento2[TipoDocumento2["CONSUMIDOR_FINAL"] = 99] = "CONSUMIDOR_FINAL";
516
- return TipoDocumento2;
517
- })(TipoDocumento || {});
491
+ var InvoiceType = /* @__PURE__ */ ((InvoiceType2) => {
492
+ InvoiceType2[InvoiceType2["FACTURA_A"] = 1] = "FACTURA_A";
493
+ InvoiceType2[InvoiceType2["FACTURA_B"] = 6] = "FACTURA_B";
494
+ InvoiceType2[InvoiceType2["FACTURA_C"] = 11] = "FACTURA_C";
495
+ InvoiceType2[InvoiceType2["TICKET_A"] = 81] = "TICKET_A";
496
+ InvoiceType2[InvoiceType2["TICKET_B"] = 82] = "TICKET_B";
497
+ InvoiceType2[InvoiceType2["TICKET_C"] = 83] = "TICKET_C";
498
+ return InvoiceType2;
499
+ })(InvoiceType || {});
500
+ var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
501
+ BillingConcept2[BillingConcept2["PRODUCTS"] = 1] = "PRODUCTS";
502
+ BillingConcept2[BillingConcept2["SERVICES"] = 2] = "SERVICES";
503
+ BillingConcept2[BillingConcept2["PRODUCTS_AND_SERVICES"] = 3] = "PRODUCTS_AND_SERVICES";
504
+ return BillingConcept2;
505
+ })(BillingConcept || {});
506
+ var TaxIdType = /* @__PURE__ */ ((TaxIdType2) => {
507
+ TaxIdType2[TaxIdType2["CUIT"] = 80] = "CUIT";
508
+ TaxIdType2[TaxIdType2["CUIL"] = 86] = "CUIL";
509
+ TaxIdType2[TaxIdType2["CDI"] = 87] = "CDI";
510
+ TaxIdType2[TaxIdType2["LE"] = 89] = "LE";
511
+ TaxIdType2[TaxIdType2["LC"] = 90] = "LC";
512
+ TaxIdType2[TaxIdType2["FOREIGN_ID"] = 91] = "FOREIGN_ID";
513
+ TaxIdType2[TaxIdType2["PASSPORT"] = 94] = "PASSPORT";
514
+ TaxIdType2[TaxIdType2["BUENOS_AIRES_ID"] = 95] = "BUENOS_AIRES_ID";
515
+ TaxIdType2[TaxIdType2["NATIONAL_POLICE_ID"] = 96] = "NATIONAL_POLICE_ID";
516
+ TaxIdType2[TaxIdType2["DNI"] = 96] = "DNI";
517
+ TaxIdType2[TaxIdType2["FINAL_CONSUMER"] = 99] = "FINAL_CONSUMER";
518
+ return TaxIdType2;
519
+ })(TaxIdType || {});
518
520
 
519
521
  // src/utils/calculations.ts
520
- function calcularSubtotal(items, incluyeIva = false) {
522
+ function calculateSubtotal(items, includesVAT = false) {
521
523
  return items.reduce((sum, item) => {
522
- let precioNeto = item.precioUnitario;
523
- if (incluyeIva && item.alicuotaIva) {
524
- precioNeto = item.precioUnitario / (1 + item.alicuotaIva / 100);
524
+ let netPrice = item.unitPrice;
525
+ if (includesVAT && item.vatRate) {
526
+ netPrice = item.unitPrice / (1 + item.vatRate / 100);
525
527
  }
526
- return sum + item.cantidad * precioNeto;
528
+ return sum + item.quantity * netPrice;
527
529
  }, 0);
528
530
  }
529
- function calcularIVA(items, incluyeIva = false) {
531
+ function calculateVAT(items, includesVAT = false) {
530
532
  return items.reduce((sum, item) => {
531
- const alicuota = item.alicuotaIva || 0;
532
- let precioNeto = item.precioUnitario;
533
- if (incluyeIva && alicuota) {
534
- precioNeto = item.precioUnitario / (1 + alicuota / 100);
533
+ const rate = item.vatRate || 0;
534
+ let netPrice = item.unitPrice;
535
+ if (includesVAT && rate) {
536
+ netPrice = item.unitPrice / (1 + rate / 100);
535
537
  }
536
- const subtotalNeto = item.cantidad * precioNeto;
537
- return sum + subtotalNeto * alicuota / 100;
538
+ const netSubtotal = item.quantity * netPrice;
539
+ return sum + netSubtotal * rate / 100;
538
540
  }, 0);
539
541
  }
540
- function calcularTotal(items, incluyeIva = false) {
541
- if (incluyeIva) {
542
- return items.reduce((sum, item) => sum + item.cantidad * item.precioUnitario, 0);
542
+ function calculateTotal(items, includesVAT = false) {
543
+ if (includesVAT) {
544
+ return items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
543
545
  }
544
- const subtotal = calcularSubtotal(items, false);
545
- const iva = calcularIVA(items, false);
546
- return subtotal + iva;
546
+ const subtotal = calculateSubtotal(items, false);
547
+ const vat = calculateVAT(items, false);
548
+ return subtotal + vat;
547
549
  }
548
- function redondear(valor) {
549
- return Math.round(valor * 100) / 100;
550
+ function round(value) {
551
+ return Math.round(value * 100) / 100;
552
+ }
553
+
554
+ // src/utils/qr.ts
555
+ function generateQRUrl(caeResponse, issuerCUIT, total, buyer) {
556
+ const cleanCUIT = issuerCUIT.replace(/\D/g, "");
557
+ const cleanCAE = caeResponse.cae.replace(/\D/g, "");
558
+ const rawDate = caeResponse.date;
559
+ const formattedDate = rawDate.length === 8 ? `${rawDate.substring(0, 4)}-${rawDate.substring(4, 6)}-${rawDate.substring(6, 8)}` : rawDate;
560
+ const docType = buyer?.docType || 99 /* FINAL_CONSUMER */;
561
+ const docNumber = buyer?.docNumber ? buyer.docNumber.replace(/\D/g, "") : "0";
562
+ const qrData = {
563
+ ver: 1,
564
+ fecha: formattedDate,
565
+ cuit: Number(cleanCUIT),
566
+ ptoVta: Number(caeResponse.pointOfSale),
567
+ tipoCmp: Number(caeResponse.invoiceType),
568
+ nroCmp: Number(caeResponse.invoiceNumber),
569
+ importe: Number(parseFloat(total.toFixed(2))),
570
+ moneda: "PES",
571
+ ctz: 1
572
+ };
573
+ if (docType !== 99 /* FINAL_CONSUMER */ || Number(docNumber) > 0) {
574
+ qrData.tipoDocRec = Number(docType);
575
+ qrData.nroDocRec = Number(docNumber);
576
+ }
577
+ qrData.tipoCodAut = "E";
578
+ qrData.codAut = Number(cleanCAE);
579
+ const jsonString = JSON.stringify(qrData);
580
+ const base64 = typeof Buffer !== "undefined" ? Buffer.from(jsonString).toString("base64") : btoa(jsonString);
581
+ return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;
582
+ }
583
+
584
+ // src/constants/errors.ts
585
+ var ARCA_ERROR_HINTS = {
586
+ // === Autenticación WSAA ===
587
+ 501: "El certificado puede haber expirado o la relaci\xF3n CUIT/Servicio no est\xE1 habilitada en el portal de ARCA.",
588
+ 502: "El ticket de acceso (TA) es inv\xE1lido o ya expir\xF3. Hac\xE9 wsaa.login() nuevamente.",
589
+ 503: "Error interno del servidor de autenticaci\xF3n de ARCA. Reintent\xE1 en unos minutos.",
590
+ 1e3: "El CUIT informado no es v\xE1lido o no corresponde al certificado usado.",
591
+ 1001: "El servicio solicitado no existe o el certificado no tiene autorizaci\xF3n para usarlo.",
592
+ 1003: "El TRA (Ticket de Requerimiento de Acceso) tiene un formato inv\xE1lido.",
593
+ 1005: "El TRA ya expir\xF3 antes de ser presentado. Verific\xE1 la hora del sistema.",
594
+ // === WSFE — Puntos de venta y configuración ===
595
+ 10048: "El punto de venta no est\xE1 dado de alta en ARCA. Dalo de alta como Webservice en el portal.",
596
+ 10049: "El punto de venta no est\xE1 activo o est\xE1 bloqueado.",
597
+ // === WSFE — Comprobantes y montos ===
598
+ 10015: "Factura B: El importe supera el l\xEDmite para consumidores finales an\xF3nimos. Identific\xE1 al comprador con CUIT/DNI.",
599
+ 10016: "El CUIT informado como receptor no es v\xE1lido o no existe en el Padr\xF3n.",
600
+ 600: "No se pudo autorizar el comprobante. Revis\xE1 el campo `observations` en la respuesta para m\xE1s detalle.",
601
+ 601: "El comprobante ya fue autorizado anteriormente. No emitas dos veces el mismo n\xFAmero.",
602
+ 602: "El n\xFAmero de comprobante es inv\xE1lido o no es el correcto seg\xFAn el \xFAltimo autorizado.",
603
+ // === WSFE — IVA ===
604
+ 10043: "La al\xEDcuota de IVA informada no existe o es incorrecta. Us\xE1 3 (0%), 4 (10.5%), 5 (21%) o 6 (27%).",
605
+ 10044: "El importe de IVA no cuadra con la base imponible \xD7 al\xEDcuota.",
606
+ // === Padrón ===
607
+ PADRON_ERROR: "El servicio de Padr\xF3n suele ser inestable en homologaci\xF3n. Reintent\xE1 en unos minutos.",
608
+ CUIT_NOT_FOUND: "El CUIT consultado no existe en el Padr\xF3n de ARCA."
609
+ };
610
+ function getArcaHint(code) {
611
+ return ARCA_ERROR_HINTS[code];
550
612
  }
551
613
 
552
614
  // src/services/wsfe.ts
@@ -560,35 +622,24 @@ var WsfeService = class _WsfeService {
560
622
  if (!config.ticket || !config.ticket.token) {
561
623
  throw new ArcaValidationError(
562
624
  "Ticket WSAA requerido. Ejecut\xE1 wsaa.login() primero.",
563
- { hint: "El ticket se obtiene del servicio WSAA" }
625
+ { hint: "El ticket se obtiene del servicio WsaaService" }
564
626
  );
565
627
  }
566
- if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {
628
+ if (!config.pointOfSale || config.pointOfSale < 1 || config.pointOfSale > 9999) {
567
629
  throw new ArcaValidationError(
568
630
  "Punto de venta inv\xE1lido: debe ser un n\xFAmero entre 1 y 9999",
569
- { puntoVenta: config.puntoVenta }
631
+ { pointOfSale: config.pointOfSale }
570
632
  );
571
633
  }
572
634
  }
635
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
636
+ // Estado de los servidores
637
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
573
638
  /**
574
- * Emite un Ticket C de forma simple (solo total)
575
- * Tipo de comprobante: 83
576
- */
577
- async emitirTicketCSimple(params) {
578
- return this.emitirComprobante({
579
- tipo: 83 /* TICKET_C */,
580
- concepto: params.concepto || 1 /* PRODUCTOS */,
581
- total: params.total,
582
- fecha: params.fecha,
583
- comprador: {
584
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
585
- nroDocumento: "0"
586
- }
587
- });
588
- }
589
- /**
590
- * Verifica el estado de los servidores de ARCA (FEDummy)
591
- * No requiere autenticación ni instancia configurada.
639
+ * Verifica el estado de los servidores de ARCA (FEDummy).
640
+ * No requiere autenticación. Útil para health checks.
641
+ *
642
+ * @param environment Ambiente a consultar (default: 'homologacion')
592
643
  */
593
644
  static async checkStatus(environment = "homologacion") {
594
645
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
@@ -619,164 +670,265 @@ var WsfeService = class _WsfeService {
619
670
  throw new ArcaError("Respuesta FEDummy inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
620
671
  }
621
672
  return {
622
- AppServer: data.AppServer,
623
- DbServer: data.DbServer,
624
- AuthServer: data.AuthServer
673
+ appServer: data.AppServer,
674
+ dbServer: data.DbServer,
675
+ authServer: data.AuthServer
625
676
  };
626
677
  }
627
678
  /**
628
- * Verifica el estado de los servidores de ARCA (FEDummy)
629
- * No requiere autenticación.
679
+ * Verifica el estado de los servidores de ARCA.
680
+ * Versión de instancia — usa el ambiente configurado.
630
681
  */
631
682
  async checkStatus() {
632
683
  return _WsfeService.checkStatus(this.config.environment);
633
684
  }
685
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
686
+ // Emisión de comprobantes
687
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
634
688
  /**
635
- * Emite un Ticket C completo (con detalle de items)
636
- * Los items no se envían a ARCA, pero se retornan en la respuesta.
689
+ * Emite un Ticket C simple (solo monto total, sin detalle de items).
690
+ * Ideal para registros mínimos, como una app móvil de punto de venta.
637
691
  */
638
- async emitirTicketC(params) {
639
- const total = redondear(calcularTotal(params.items));
640
- const cae = await this.emitirComprobante({
641
- tipo: 83 /* TICKET_C */,
642
- concepto: params.concepto || 1 /* PRODUCTOS */,
692
+ async issueSimpleReceipt(params) {
693
+ return this.issueDocument({
694
+ type: 83 /* TICKET_C */,
695
+ concept: params.concept || 1 /* PRODUCTS */,
696
+ total: params.total,
697
+ date: params.date,
698
+ buyer: {
699
+ docType: 99 /* FINAL_CONSUMER */,
700
+ docNumber: "0"
701
+ }
702
+ });
703
+ }
704
+ /**
705
+ * Emite un Ticket C con detalle de items.
706
+ * Los items se guardan en la respuesta pero no se envían a ARCA.
707
+ */
708
+ async issueReceipt(params) {
709
+ const total = round(calculateTotal(params.items));
710
+ const cae = await this.issueDocument({
711
+ type: 83 /* TICKET_C */,
712
+ concept: params.concept || 1 /* PRODUCTS */,
643
713
  total,
644
- fecha: params.fecha,
645
- comprador: {
646
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
647
- nroDocumento: "0"
714
+ date: params.date,
715
+ buyer: {
716
+ docType: 99 /* FINAL_CONSUMER */,
717
+ docNumber: "0"
648
718
  }
649
719
  });
650
- return {
651
- ...cae,
652
- items: params.items
653
- };
720
+ return { ...cae, items: params.items };
654
721
  }
655
722
  /**
656
- * Emite una Factura B (monotributo a responsable inscripto)
657
- * REQUIERE detalle de items con IVA discriminado
723
+ * Emite una Factura C (consumidor final, sin discriminación de IVA).
658
724
  */
659
- async emitirFacturaB(params) {
660
- this.validateItemsWithIVA(params.items);
661
- const incluyeIva = params.incluyeIva || false;
662
- const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);
663
- return this.emitirComprobante({
664
- tipo: 6 /* FACTURA_B */,
665
- concepto: params.concepto || 1 /* PRODUCTOS */,
666
- items: params.items,
667
- comprador: params.comprador,
668
- fecha: params.fecha,
669
- ivaData,
670
- incluyeIva
725
+ async issueInvoiceC(params) {
726
+ const total = round(calculateTotal(params.items));
727
+ return this.issueDocument({
728
+ type: 11 /* FACTURA_C */,
729
+ concept: params.concept || 1 /* PRODUCTS */,
730
+ total,
731
+ date: params.date,
732
+ buyer: {
733
+ docType: 99 /* FINAL_CONSUMER */,
734
+ docNumber: "0"
735
+ },
736
+ items: params.items
671
737
  });
672
738
  }
673
739
  /**
674
- * Emite una Factura A (RI a RI)
675
- * REQUIERE detalle de items con IVA discriminado
740
+ * Emite una Factura B (con IVA discriminado).
741
+ * REQUIERE `vatRate` en todos los items.
676
742
  */
677
- async emitirFacturaA(params) {
678
- this.validateItemsWithIVA(params.items);
679
- const incluyeIva = params.incluyeIva || false;
680
- const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);
681
- return this.emitirComprobante({
682
- tipo: 1 /* FACTURA_A */,
683
- concepto: params.concepto || 1 /* PRODUCTOS */,
743
+ async issueInvoiceB(params) {
744
+ this.validateItemsWithVAT(params.items);
745
+ const includesVAT = params.includesVAT || false;
746
+ const vatData = this.calculateVATByRate(params.items, includesVAT);
747
+ return this.issueDocument({
748
+ type: 6 /* FACTURA_B */,
749
+ concept: params.concept || 1 /* PRODUCTS */,
684
750
  items: params.items,
685
- comprador: params.comprador,
686
- fecha: params.fecha,
687
- ivaData,
688
- incluyeIva
751
+ buyer: params.buyer,
752
+ date: params.date,
753
+ vatData,
754
+ includesVAT
689
755
  });
690
756
  }
691
757
  /**
692
- * Valida que todos los items tengan alícuota IVA definida
758
+ * Emite una Factura A (Responsable Inscripto a Responsable Inscripto, con IVA discriminado).
759
+ * REQUIERE `vatRate` en todos los items.
693
760
  */
694
- validateItemsWithIVA(items) {
695
- const sinIva = items.filter(
696
- (item) => item.alicuotaIva === void 0 || item.alicuotaIva === null
697
- );
698
- if (sinIva.length > 0) {
699
- throw new ArcaValidationError(
700
- "Esta operaci\xF3n requiere IVA discriminado en todos los items",
701
- {
702
- itemsSinIva: sinIva.map((i) => i.descripcion),
703
- hint: "Agreg\xE1 alicuotaIva a cada item (21, 10.5, 27, o 0)"
704
- }
705
- );
706
- }
761
+ async issueInvoiceA(params) {
762
+ this.validateItemsWithVAT(params.items);
763
+ const includesVAT = params.includesVAT || false;
764
+ const vatData = this.calculateVATByRate(params.items, includesVAT);
765
+ return this.issueDocument({
766
+ type: 1 /* FACTURA_A */,
767
+ concept: params.concept || 1 /* PRODUCTS */,
768
+ items: params.items,
769
+ buyer: params.buyer,
770
+ date: params.date,
771
+ vatData,
772
+ includesVAT
773
+ });
707
774
  }
775
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
776
+ // Consultas
777
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
708
778
  /**
709
- * Calcula IVA agrupado por alícuota
710
- * ARCA requiere esto para Factura B/A
779
+ * Consulta un comprobante ya emitido (FECompConsultar).
780
+ *
781
+ * @param type Tipo de comprobante
782
+ * @param invoiceNumber Número de comprobante
711
783
  */
712
- calcularIVAPorAlicuota(items, incluyeIva = false) {
713
- const porAlicuota = /* @__PURE__ */ new Map();
714
- items.forEach((item) => {
715
- const alicuota = item.alicuotaIva || 0;
716
- let precioNeto = item.precioUnitario;
717
- if (incluyeIva && alicuota) {
718
- precioNeto = item.precioUnitario / (1 + alicuota / 100);
719
- }
720
- const base = item.cantidad * precioNeto;
721
- const importe = base * alicuota / 100;
722
- const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };
723
- porAlicuota.set(alicuota, {
724
- base: actual.base + base,
725
- importe: actual.importe + importe
726
- });
784
+ async getInvoice(type, invoiceNumber) {
785
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
786
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
787
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
788
+ <soapenv:Header/>
789
+ <soapenv:Body>
790
+ <ar:FECompConsultar>
791
+ <ar:Auth>
792
+ <ar:Token>${this.config.ticket.token}</ar:Token>
793
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
794
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
795
+ </ar:Auth>
796
+ <ar:FeCompConsReq>
797
+ <ar:CbteTipo>${type}</ar:CbteTipo>
798
+ <ar:CbteNro>${invoiceNumber}</ar:CbteNro>
799
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
800
+ </ar:FeCompConsReq>
801
+ </ar:FECompConsultar>
802
+ </soapenv:Body>
803
+ </soapenv:Envelope>`;
804
+ const endpoint = getWsfeEndpoint(this.config.environment);
805
+ const response = await callArcaApi(endpoint, {
806
+ method: "POST",
807
+ headers: {
808
+ "Content-Type": "text/xml; charset=utf-8",
809
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECompConsultar"
810
+ },
811
+ body: soapRequest,
812
+ timeout: this.config.timeout
727
813
  });
728
- return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({
729
- alicuota,
730
- baseImponible: redondear(valores.base),
731
- importe: redondear(valores.importe)
732
- }));
814
+ if (!response.ok) {
815
+ throw new ArcaError(`Error HTTP al consultar comprobante: ${response.status}`, "HTTP_ERROR");
816
+ }
817
+ const responseXml = await response.text();
818
+ const result = parseXml(responseXml);
819
+ const data = result?.Envelope?.Body?.FECompConsultarResponse?.FECompConsultarResult;
820
+ if (!data) {
821
+ throw new ArcaError("Respuesta FECompConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
822
+ }
823
+ if (data.Errors) {
824
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
825
+ const code = error?.Code || "UNKNOWN";
826
+ throw new ArcaError(
827
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
828
+ "ARCA_ERROR",
829
+ data.Errors,
830
+ getArcaHint(code)
831
+ );
832
+ }
833
+ const det = data.ResultGet;
834
+ return {
835
+ invoiceType: Number(det.CbteTipo),
836
+ pointOfSale: Number(det.PtoVta),
837
+ invoiceNumber: Number(det.CbteDesde),
838
+ date: String(det.CbteFch),
839
+ concept: Number(det.Concepto),
840
+ docType: Number(det.DocTipo),
841
+ docNumber: Number(det.DocNro),
842
+ total: Number(det.ImpTotal),
843
+ net: Number(det.ImpNeto),
844
+ vat: Number(det.ImpIVA),
845
+ cae: String(det.CodAutorizacion),
846
+ caeExpiry: String(det.FchVto),
847
+ result: det.Resultado
848
+ };
733
849
  }
734
850
  /**
735
- * Emite una Factura C (consumidor final)
736
- * Forma simplificada sin especificar comprador
851
+ * Lista los puntos de venta habilitados para el CUIT autenticado (FEParamGetPtosVenta).
737
852
  */
738
- async emitirFacturaC(params) {
739
- const total = redondear(calcularTotal(params.items));
740
- return this.emitirComprobante({
741
- tipo: 11 /* FACTURA_C */,
742
- concepto: params.concepto || 1 /* PRODUCTOS */,
743
- total,
744
- fecha: params.fecha,
745
- comprador: {
746
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
747
- nroDocumento: "0"
853
+ async getPointsOfSale() {
854
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
855
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
856
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
857
+ <soapenv:Header/>
858
+ <soapenv:Body>
859
+ <ar:FEParamGetPtosVenta>
860
+ <ar:Auth>
861
+ <ar:Token>${this.config.ticket.token}</ar:Token>
862
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
863
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
864
+ </ar:Auth>
865
+ </ar:FEParamGetPtosVenta>
866
+ </soapenv:Body>
867
+ </soapenv:Envelope>`;
868
+ const endpoint = getWsfeEndpoint(this.config.environment);
869
+ const response = await callArcaApi(endpoint, {
870
+ method: "POST",
871
+ headers: {
872
+ "Content-Type": "text/xml; charset=utf-8",
873
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FEParamGetPtosVenta"
748
874
  },
749
- items: params.items
875
+ body: soapRequest,
876
+ timeout: this.config.timeout
750
877
  });
878
+ if (!response.ok) {
879
+ throw new ArcaError(`Error HTTP al consultar puntos de venta: ${response.status}`, "HTTP_ERROR");
880
+ }
881
+ const responseXml = await response.text();
882
+ const result = parseXml(responseXml);
883
+ const data = result?.Envelope?.Body?.FEParamGetPtosVentaResponse?.FEParamGetPtosVentaResult;
884
+ if (!data) {
885
+ throw new ArcaError("Respuesta FEParamGetPtosVenta inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
886
+ }
887
+ if (data.Errors) {
888
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
889
+ throw new ArcaError(`Error ARCA: ${error?.Msg || "Error desconocido"}`, "ARCA_ERROR", data.Errors);
890
+ }
891
+ const raw = data.ResultGet?.PtoVenta;
892
+ if (!raw) return [];
893
+ const list = Array.isArray(raw) ? raw : [raw];
894
+ return list.map((pv) => ({
895
+ number: Number(pv.Nro),
896
+ type: String(pv.EmisionTipo),
897
+ isBlocked: pv.Bloqueado === "S",
898
+ blockedSince: pv.FchBaja ? String(pv.FchBaja) : void 0
899
+ }));
751
900
  }
901
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
902
+ // Métodos internos
903
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
752
904
  /**
753
- * Emite un comprobante (método genérico interno)
905
+ * Método genérico interno para emitir cualquier tipo de comprobante.
754
906
  */
755
- async emitirComprobante(request) {
756
- const nroComprobante = await this.obtenerProximoNumero(request.tipo);
907
+ async issueDocument(request) {
908
+ const invoiceNumber = await this.getNextInvoiceNumber(request.type);
757
909
  let total = request.total || 0;
758
- let subtotal = total;
759
- let iva = 0;
910
+ let net = total;
911
+ let vat = 0;
760
912
  if (request.items && request.items.length > 0) {
761
- const incluyeIva = request.incluyeIva || false;
762
- subtotal = redondear(calcularSubtotal(request.items, incluyeIva));
763
- iva = redondear(calcularIVA(request.items, incluyeIva));
764
- total = redondear(calcularTotal(request.items, incluyeIva));
913
+ const includesVAT = request.includesVAT || false;
914
+ net = round(calculateSubtotal(request.items, includesVAT));
915
+ vat = round(calculateVAT(request.items, includesVAT));
916
+ total = round(calculateTotal(request.items, includesVAT));
765
917
  }
766
918
  if (total <= 0) {
767
919
  throw new ArcaValidationError("El monto total debe ser mayor a 0");
768
920
  }
769
- const soapRequest = this.buildCAESolicitarRequest({
770
- tipo: request.tipo,
771
- puntoVenta: this.config.puntoVenta,
772
- nroComprobante,
773
- concepto: request.concepto,
774
- fecha: request.fecha || /* @__PURE__ */ new Date(),
775
- comprador: request.comprador,
776
- subtotal,
777
- iva,
921
+ const soapRequest = this.buildCAERequest({
922
+ type: request.type,
923
+ pointOfSale: this.config.pointOfSale,
924
+ invoiceNumber,
925
+ concept: request.concept,
926
+ date: request.date || /* @__PURE__ */ new Date(),
927
+ buyer: request.buyer,
928
+ net,
929
+ vat,
778
930
  total,
779
- ivaData: request.ivaData
931
+ vatData: request.vatData
780
932
  });
781
933
  const endpoint = getWsfeEndpoint(this.config.environment);
782
934
  const response = await callArcaApi(endpoint, {
@@ -797,17 +949,19 @@ var WsfeService = class _WsfeService {
797
949
  }
798
950
  const responseXml = await response.text();
799
951
  const result = await this.parseCAEResponse(responseXml);
952
+ const qrUrl = generateQRUrl(result, this.config.cuit, total, request.buyer);
800
953
  return {
801
954
  ...result,
802
955
  items: request.items,
803
- iva: request.ivaData
956
+ vat: request.vatData,
957
+ qrUrl
804
958
  };
805
959
  }
806
960
  /**
807
- * Obtiene el próximo número de comprobante disponible
961
+ * Obtiene el próximo número de comprobante disponible (FECompUltimoAutorizado + 1)
808
962
  */
809
- async obtenerProximoNumero(tipo) {
810
- const soapRequest = this.buildProximoNumeroRequest(tipo);
963
+ async getNextInvoiceNumber(type) {
964
+ const soapRequest = this.buildLastInvoiceRequest(type);
811
965
  const endpoint = getWsfeEndpoint(this.config.environment);
812
966
  const response = await callArcaApi(endpoint, {
813
967
  method: "POST",
@@ -819,32 +973,102 @@ var WsfeService = class _WsfeService {
819
973
  timeout: this.config.timeout
820
974
  });
821
975
  if (!response.ok) {
822
- throw new ArcaError(`Error HTTP al consultar pr\xF3ximo n\xFAmero: ${response.status}`, "HTTP_ERROR");
976
+ throw new ArcaError(`Error HTTP al consultar \xFAltimo comprobante: ${response.status}`, "HTTP_ERROR");
823
977
  }
824
978
  const responseXml = await response.text();
825
979
  const result = parseXml(responseXml);
826
980
  const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;
827
981
  if (data?.Errors) {
828
982
  const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
829
- throw new ArcaError(`Error ARCA: ${error?.Msg || "Error desconocido"}`, "ARCA_ERROR", data.Errors);
983
+ const code = error?.Code || "UNKNOWN";
984
+ throw new ArcaError(
985
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
986
+ "ARCA_ERROR",
987
+ data.Errors,
988
+ getArcaHint(code)
989
+ );
990
+ }
991
+ const lastNumber = data?.CbteNro;
992
+ return typeof lastNumber === "number" ? lastNumber + 1 : 1;
993
+ }
994
+ /**
995
+ * Valida que todos los items tengan alícuota IVA definida
996
+ */
997
+ validateItemsWithVAT(items) {
998
+ const missingVAT = items.filter(
999
+ (item) => item.vatRate === void 0 || item.vatRate === null
1000
+ );
1001
+ if (missingVAT.length > 0) {
1002
+ throw new ArcaValidationError(
1003
+ "Esta operaci\xF3n requiere `vatRate` en todos los items",
1004
+ {
1005
+ itemsMissingVAT: missingVAT.map((i) => i.description),
1006
+ hint: "Agreg\xE1 vatRate a cada item (21, 10.5, 27, o 0)"
1007
+ }
1008
+ );
830
1009
  }
831
- const nro = data?.CbteNro;
832
- return typeof nro === "number" ? nro + 1 : 1;
833
1010
  }
834
- buildCAESolicitarRequest(params) {
835
- const fechaStr = params.fecha.toISOString().split("T")[0].replace(/-/g, "");
836
- let ivaXml = "";
837
- if (params.ivaData && params.ivaData.length > 0) {
838
- ivaXml = "<ar:Iva>";
839
- params.ivaData.forEach((aliquot) => {
840
- ivaXml += `
1011
+ /**
1012
+ * Calcula el IVA agrupado por alícuota (requerido por ARCA para Factura A/B)
1013
+ */
1014
+ calculateVATByRate(items, includesVAT = false) {
1015
+ const byRate = /* @__PURE__ */ new Map();
1016
+ items.forEach((item) => {
1017
+ const rate = item.vatRate || 0;
1018
+ let netPrice = item.unitPrice;
1019
+ if (includesVAT && rate) {
1020
+ netPrice = item.unitPrice / (1 + rate / 100);
1021
+ }
1022
+ const base = item.quantity * netPrice;
1023
+ const amount = base * rate / 100;
1024
+ const current = byRate.get(rate) || { base: 0, amount: 0 };
1025
+ byRate.set(rate, {
1026
+ base: current.base + base,
1027
+ amount: current.amount + amount
1028
+ });
1029
+ });
1030
+ return Array.from(byRate.entries()).map(([rate, values]) => ({
1031
+ rate,
1032
+ taxBase: round(values.base),
1033
+ amount: round(values.amount)
1034
+ }));
1035
+ }
1036
+ /**
1037
+ * Mapea alícuota % al código interno de ARCA
1038
+ */
1039
+ getVATCode(percentage) {
1040
+ const map = {
1041
+ 0: 3,
1042
+ 10.5: 4,
1043
+ 21: 5,
1044
+ 27: 6
1045
+ };
1046
+ const code = map[percentage];
1047
+ if (code === void 0) {
1048
+ throw new ArcaValidationError(
1049
+ `Al\xEDcuota IVA inv\xE1lida: ${percentage}%`,
1050
+ {
1051
+ validRates: [0, 10.5, 21, 27],
1052
+ hint: "Us\xE1 una de las al\xEDcuotas oficiales de Argentina"
1053
+ }
1054
+ );
1055
+ }
1056
+ return code;
1057
+ }
1058
+ buildCAERequest(params) {
1059
+ const dateStr = params.date.toISOString().split("T")[0].replace(/-/g, "");
1060
+ let vatXml = "";
1061
+ if (params.vatData && params.vatData.length > 0) {
1062
+ vatXml = "<ar:Iva>";
1063
+ params.vatData.forEach((entry) => {
1064
+ vatXml += `
841
1065
  <ar:AlicIva>
842
- <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>
843
- <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>
844
- <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>
1066
+ <ar:Id>${this.getVATCode(entry.rate)}</ar:Id>
1067
+ <ar:BaseImp>${entry.taxBase.toFixed(2)}</ar:BaseImp>
1068
+ <ar:Importe>${entry.amount.toFixed(2)}</ar:Importe>
845
1069
  </ar:AlicIva>`;
846
1070
  });
847
- ivaXml += "\n </ar:Iva>";
1071
+ vatXml += "\n </ar:Iva>";
848
1072
  }
849
1073
  return `<?xml version="1.0" encoding="UTF-8"?>
850
1074
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
@@ -860,26 +1084,26 @@ var WsfeService = class _WsfeService {
860
1084
  <ar:FeCAEReq>
861
1085
  <ar:FeCabReq>
862
1086
  <ar:CantReg>1</ar:CantReg>
863
- <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>
864
- <ar:CbteTipo>${params.tipo}</ar:CbteTipo>
1087
+ <ar:PtoVta>${params.pointOfSale}</ar:PtoVta>
1088
+ <ar:CbteTipo>${params.type}</ar:CbteTipo>
865
1089
  </ar:FeCabReq>
866
1090
  <ar:FeDetReq>
867
1091
  <ar:FECAEDetRequest>
868
- <ar:Concepto>${params.concepto}</ar:Concepto>
869
- <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>
870
- <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>
871
- <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>
872
- <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>
873
- <ar:CbteFch>${fechaStr}</ar:CbteFch>
1092
+ <ar:Concepto>${params.concept}</ar:Concepto>
1093
+ <ar:DocTipo>${params.buyer?.docType || 99}</ar:DocTipo>
1094
+ <ar:DocNro>${params.buyer?.docNumber || 0}</ar:DocNro>
1095
+ <ar:CbteDesde>${params.invoiceNumber}</ar:CbteDesde>
1096
+ <ar:CbteHasta>${params.invoiceNumber}</ar:CbteHasta>
1097
+ <ar:CbteFch>${dateStr}</ar:CbteFch>
874
1098
  <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>
875
1099
  <ar:ImpTotConc>0.00</ar:ImpTotConc>
876
- <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>
1100
+ <ar:ImpNeto>${params.net.toFixed(2)}</ar:ImpNeto>
877
1101
  <ar:ImpOpEx>0.00</ar:ImpOpEx>
878
- <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>
1102
+ <ar:ImpIVA>${params.vat.toFixed(2)}</ar:ImpIVA>
879
1103
  <ar:ImpTrib>0.00</ar:ImpTrib>
880
1104
  <ar:MonId>PES</ar:MonId>
881
1105
  <ar:MonCotiz>1</ar:MonCotiz>
882
- ${ivaXml}
1106
+ ${vatXml}
883
1107
  </ar:FECAEDetRequest>
884
1108
  </ar:FeDetReq>
885
1109
  </ar:FeCAEReq>
@@ -887,29 +1111,7 @@ var WsfeService = class _WsfeService {
887
1111
  </soapenv:Body>
888
1112
  </soapenv:Envelope>`;
889
1113
  }
890
- /**
891
- * Mapea alícuota % a código ARCA
892
- */
893
- getCodigoAlicuota(porcentaje) {
894
- const mapa = {
895
- 0: 3,
896
- 10.5: 4,
897
- 21: 5,
898
- 27: 6
899
- };
900
- const codigo = mapa[porcentaje];
901
- if (!codigo) {
902
- throw new ArcaValidationError(
903
- `Al\xEDcuota IVA inv\xE1lida: ${porcentaje}%`,
904
- {
905
- alicuotasValidas: [0, 10.5, 21, 27],
906
- hint: "Usar una de las al\xEDcuotas oficiales de Argentina"
907
- }
908
- );
909
- }
910
- return codigo;
911
- }
912
- buildProximoNumeroRequest(tipo) {
1114
+ buildLastInvoiceRequest(type) {
913
1115
  return `<?xml version="1.0" encoding="UTF-8"?>
914
1116
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
915
1117
  xmlns:ar="http://ar.gov.afip.dif.FEV1/">
@@ -921,8 +1123,8 @@ var WsfeService = class _WsfeService {
921
1123
  <ar:Sign>${this.config.ticket.sign}</ar:Sign>
922
1124
  <ar:Cuit>${this.config.cuit}</ar:Cuit>
923
1125
  </ar:Auth>
924
- <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>
925
- <ar:CbteTipo>${tipo}</ar:CbteTipo>
1126
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1127
+ <ar:CbteTipo>${type}</ar:CbteTipo>
926
1128
  </ar:FECompUltimoAutorizado>
927
1129
  </soapenv:Body>
928
1130
  </soapenv:Envelope>`;
@@ -935,27 +1137,33 @@ var WsfeService = class _WsfeService {
935
1137
  }
936
1138
  if (data.Errors) {
937
1139
  const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
938
- throw new ArcaError(`Error ARCA: ${error?.Msg || "Error desconocido"}`, "ARCA_ERROR", data.Errors);
1140
+ const code = error?.Code || "UNKNOWN";
1141
+ throw new ArcaError(
1142
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1143
+ "ARCA_ERROR",
1144
+ data.Errors,
1145
+ getArcaHint(code)
1146
+ );
939
1147
  }
940
1148
  const cab = data.FeCabResp;
941
1149
  const det = Array.isArray(data.FeDetResp.FECAEDetResponse) ? data.FeDetResp.FECAEDetResponse[0] : data.FeDetResp.FECAEDetResponse;
942
1150
  if (!det) {
943
1151
  throw new ArcaError("Respuesta WSFE incompleta: falta detalle del comprobante", "PARSE_ERROR");
944
1152
  }
945
- const observaciones = [];
1153
+ const observations = [];
946
1154
  if (det.Observaciones) {
947
1155
  const obsArray = Array.isArray(det.Observaciones.Obs) ? det.Observaciones.Obs : [det.Observaciones.Obs];
948
- obsArray.forEach((o) => observaciones.push(o.Msg));
1156
+ obsArray.forEach((o) => observations.push(o.Msg));
949
1157
  }
950
1158
  return {
951
- tipoComprobante: cab.CbteTipo,
952
- puntoVenta: cab.PtoVta,
953
- nroComprobante: Number(det.CbteDesde),
954
- fecha: det.CbteFch,
1159
+ invoiceType: cab.CbteTipo,
1160
+ pointOfSale: cab.PtoVta,
1161
+ invoiceNumber: Number(det.CbteDesde),
1162
+ date: det.CbteFch,
955
1163
  cae: String(det.CAE),
956
- vencimientoCae: String(det.CAEFchVto),
957
- resultado: det.Resultado,
958
- observaciones: observaciones.length > 0 ? observaciones : void 0
1164
+ caeExpiry: String(det.CAEFchVto),
1165
+ result: det.Resultado,
1166
+ observations: observations.length > 0 ? observations : void 0
959
1167
  };
960
1168
  }
961
1169
  };
@@ -977,12 +1185,12 @@ var PadronService = class {
977
1185
  });
978
1186
  }
979
1187
  /**
980
- * Consulta los datos de una persona por CUIT
981
- *
982
- * @param idPersona CUIT a consultar
983
- * @returns Datos de la persona o error
1188
+ * Consulta los datos de un contribuyente por CUIT
1189
+ *
1190
+ * @param taxId CUIT a consultar (11 dígitos sin guiones)
1191
+ * @returns Datos del contribuyente o mensaje de error
984
1192
  */
985
- async getPersona(idPersona) {
1193
+ async getTaxpayer(taxId) {
986
1194
  const ticket = await this.wsaa.login();
987
1195
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
988
1196
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
@@ -993,7 +1201,7 @@ var PadronService = class {
993
1201
  <token>${ticket.token}</token>
994
1202
  <sign>${ticket.sign}</sign>
995
1203
  <cuitRepresentada>${this.config.cuit}</cuitRepresentada>
996
- <idPersona>${idPersona}</idPersona>
1204
+ <idPersona>${taxId}</idPersona>
997
1205
  </a13:getPersona>
998
1206
  </soapenv:Body>
999
1207
  </soapenv:Envelope>`;
@@ -1003,14 +1211,13 @@ var PadronService = class {
1003
1211
  headers: {
1004
1212
  "Content-Type": "text/xml; charset=utf-8",
1005
1213
  "SOAPAction": ""
1006
- // A13 no suele requerir SOAPAction específica en el header
1007
1214
  },
1008
1215
  body: soapRequest,
1009
1216
  timeout: this.config.timeout || 15e3
1010
1217
  });
1011
1218
  if (!response.ok) {
1012
1219
  throw new ArcaNetworkError(
1013
- `Error HTTP al comunicarse con Padron A13: ${response.status}`,
1220
+ `Error HTTP al comunicarse con Padr\xF3n A13: ${response.status}`,
1014
1221
  { status: response.status }
1015
1222
  );
1016
1223
  }
@@ -1018,7 +1225,7 @@ var PadronService = class {
1018
1225
  return this.parseResponse(xml);
1019
1226
  }
1020
1227
  /**
1021
- * Parsear la respuesta XML de getPersona
1228
+ * Parsea la respuesta XML de getPersona
1022
1229
  */
1023
1230
  parseResponse(xml) {
1024
1231
  const parser = new import_fast_xml_parser2.XMLParser({
@@ -1028,13 +1235,13 @@ var PadronService = class {
1028
1235
  const result = parser.parse(xml);
1029
1236
  const body = result.Envelope?.Body;
1030
1237
  if (!body) {
1031
- throw new ArcaError("Respuesta de Padr\xF3n inv\xE1lida: No se encontr\xF3 Body", "PADRON_ERROR");
1238
+ throw new ArcaError("Respuesta del Padr\xF3n inv\xE1lida: Body no encontrado", "PADRON_ERROR");
1032
1239
  }
1033
1240
  const response = body.getPersonaResponse?.personaReturn;
1034
1241
  if (!response) {
1035
1242
  const fault = body.Fault;
1036
1243
  if (fault) {
1037
- return { error: fault.faultstring || "Error desconocido en AFIP" };
1244
+ return { error: fault.faultstring || "Error desconocido en ARCA" };
1038
1245
  }
1039
1246
  return { error: "No se encontraron datos para el CUIT informado" };
1040
1247
  }
@@ -1045,84 +1252,80 @@ var PadronService = class {
1045
1252
  if (!p) {
1046
1253
  return { error: "CUIT no encontrado" };
1047
1254
  }
1048
- const persona = {
1049
- idPersona: Number(p.idPersona),
1050
- tipoPersona: p.tipoPersona,
1051
- nombre: p.nombre,
1052
- apellido: p.apellido,
1053
- razonSocial: p.razonSocial,
1054
- estadoClave: p.estadoClave,
1055
- domicilio: this.mapDomicilios(p.domicilio),
1056
- descripcionActividadPrincipal: p.descripcionActividadPrincipal,
1057
- esInscriptoIVA: this.checkImpuesto(p, 30),
1255
+ const taxpayer = {
1256
+ taxId: Number(p.idPersona),
1257
+ personType: p.tipoPersona,
1258
+ firstName: p.nombre,
1259
+ lastName: p.apellido,
1260
+ companyName: p.razonSocial,
1261
+ status: p.estadoClave,
1262
+ addresses: this.mapAddresses(p.domicilio),
1263
+ activities: this.mapActivities(p.actividad),
1264
+ taxes: this.mapTaxRecords(p.impuesto),
1265
+ mainActivity: p.descripcionActividadPrincipal,
1266
+ isVATRegistered: this.hasTaxId(p, 30),
1058
1267
  // 30 = IVA
1059
- esMonotributista: this.checkImpuesto(p, 20),
1268
+ isMonotax: this.hasTaxId(p, 20),
1060
1269
  // 20 = Monotributo
1061
- esExento: this.checkImpuesto(p, 32)
1062
- // 32 = Exento
1270
+ isVATExempt: this.hasTaxId(p, 32)
1271
+ // 32 = IVA Exento
1063
1272
  };
1064
- return { persona };
1273
+ return { taxpayer };
1065
1274
  }
1066
- mapDomicilios(d) {
1067
- if (!d) return [];
1068
- const list = Array.isArray(d) ? d : [d];
1069
- return list.map((item) => ({
1070
- direccion: item.direccion,
1071
- localidad: item.localidad,
1072
- codPostal: item.codPostal,
1073
- idProvincia: Number(item.idProvincia),
1074
- descripcionProvincia: item.descripcionProvincia,
1075
- tipoDomicilio: item.tipoDomicilio
1275
+ mapAddresses(raw) {
1276
+ if (!raw) return [];
1277
+ return this.toArray(raw).map((item) => ({
1278
+ street: item.direccion,
1279
+ city: item.localidad,
1280
+ postalCode: item.codPostal,
1281
+ provinceId: Number(item.idProvincia),
1282
+ province: item.descripcionProvincia,
1283
+ type: item.tipoDomicilio
1076
1284
  }));
1077
1285
  }
1078
- checkImpuesto(p, id) {
1079
- const impuestos = p.impuesto;
1080
- if (!impuestos) return false;
1081
- const list = Array.isArray(impuestos) ? impuestos : [impuestos];
1082
- return list.some((i) => Number(i.idImpuesto) === id);
1286
+ mapActivities(raw) {
1287
+ if (!raw) return [];
1288
+ return this.toArray(raw).map((item) => ({
1289
+ id: Number(item.idActividad),
1290
+ description: item.descripcion,
1291
+ order: Number(item.orden),
1292
+ period: Number(item.periodo)
1293
+ }));
1083
1294
  }
1084
- };
1085
-
1086
- // src/utils/qr.ts
1087
- function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
1088
- const cleanCuit = cuitEmisor.replace(/\D/g, "");
1089
- const cleanCae = caeResponse.cae.replace(/\D/g, "");
1090
- const fDate = caeResponse.fecha;
1091
- const fechaFormat = fDate.length === 8 ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}` : fDate;
1092
- const docTipo = comprador?.tipoDocumento || 99 /* CONSUMIDOR_FINAL */;
1093
- const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\D/g, "") : "0";
1094
- const qrObj = {
1095
- ver: 1,
1096
- fecha: fechaFormat,
1097
- cuit: Number(cleanCuit),
1098
- ptoVta: Number(caeResponse.puntoVenta),
1099
- tipoCmp: Number(caeResponse.tipoComprobante),
1100
- nroCmp: Number(caeResponse.nroComprobante),
1101
- importe: Number(parseFloat(total.toFixed(2))),
1102
- moneda: "PES",
1103
- ctz: 1
1104
- };
1105
- if (docTipo !== 99 /* CONSUMIDOR_FINAL */ || Number(docNro) > 0) {
1106
- qrObj.tipoDocRec = Number(docTipo);
1107
- qrObj.nroDocRec = Number(docNro);
1295
+ mapTaxRecords(raw) {
1296
+ if (!raw) return [];
1297
+ return this.toArray(raw).map((item) => ({
1298
+ id: Number(item.idImpuesto),
1299
+ description: item.descripcion,
1300
+ period: Number(item.periodo)
1301
+ }));
1108
1302
  }
1109
- qrObj.tipoCodAut = "E";
1110
- qrObj.codAut = Number(cleanCae);
1111
- const jsonString = JSON.stringify(qrObj);
1112
- let base64 = typeof Buffer !== "undefined" ? Buffer.from(jsonString).toString("base64") : btoa(jsonString);
1113
- return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;
1114
- }
1303
+ hasTaxId(p, id) {
1304
+ const taxes = p.impuesto;
1305
+ if (!taxes) return false;
1306
+ return this.toArray(taxes).some((i) => Number(i.idImpuesto) === id);
1307
+ }
1308
+ /**
1309
+ * Normaliza un valor que puede ser un objeto único o un array (comportamiento de fast-xml-parser)
1310
+ */
1311
+ toArray(data) {
1312
+ if (data === void 0 || data === null) return [];
1313
+ if (Array.isArray(data)) return data;
1314
+ return [data];
1315
+ }
1316
+ };
1115
1317
  // Annotate the CommonJS export names for ESM import in node:
1116
1318
  0 && (module.exports = {
1117
1319
  ArcaAuthError,
1118
1320
  ArcaError,
1321
+ ArcaNetworkError,
1119
1322
  ArcaValidationError,
1120
- Concepto,
1323
+ BillingConcept,
1324
+ InvoiceType,
1121
1325
  PadronService,
1122
- TipoComprobante,
1123
- TipoDocumento,
1326
+ TaxIdType,
1124
1327
  WsaaService,
1125
1328
  WsfeService,
1126
- generarUrlQR
1329
+ generateQRUrl
1127
1330
  });
1128
1331
  //# sourceMappingURL=index.cjs.map