arca-sdk 0.5.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
 
@@ -487,112 +488,124 @@ var WsaaService = class {
487
488
  };
488
489
 
489
490
  // src/types/wsfe.ts
490
- var TipoComprobante = /* @__PURE__ */ ((TipoComprobante2) => {
491
- TipoComprobante2[TipoComprobante2["FACTURA_A"] = 1] = "FACTURA_A";
492
- TipoComprobante2[TipoComprobante2["FACTURA_B"] = 6] = "FACTURA_B";
493
- TipoComprobante2[TipoComprobante2["FACTURA_C"] = 11] = "FACTURA_C";
494
- TipoComprobante2[TipoComprobante2["TICKET_A"] = 81] = "TICKET_A";
495
- TipoComprobante2[TipoComprobante2["TICKET_B"] = 82] = "TICKET_B";
496
- TipoComprobante2[TipoComprobante2["TICKET_C"] = 83] = "TICKET_C";
497
- return TipoComprobante2;
498
- })(TipoComprobante || {});
499
- var Concepto = /* @__PURE__ */ ((Concepto2) => {
500
- Concepto2[Concepto2["PRODUCTOS"] = 1] = "PRODUCTOS";
501
- Concepto2[Concepto2["SERVICIOS"] = 2] = "SERVICIOS";
502
- Concepto2[Concepto2["PRODUCTOS_Y_SERVICIOS"] = 3] = "PRODUCTOS_Y_SERVICIOS";
503
- return Concepto2;
504
- })(Concepto || {});
505
- var TipoDocumento = /* @__PURE__ */ ((TipoDocumento2) => {
506
- TipoDocumento2[TipoDocumento2["CUIT"] = 80] = "CUIT";
507
- TipoDocumento2[TipoDocumento2["CUIL"] = 86] = "CUIL";
508
- TipoDocumento2[TipoDocumento2["CDI"] = 87] = "CDI";
509
- TipoDocumento2[TipoDocumento2["LE"] = 89] = "LE";
510
- TipoDocumento2[TipoDocumento2["LC"] = 90] = "LC";
511
- TipoDocumento2[TipoDocumento2["CI_EXTRANJERA"] = 91] = "CI_EXTRANJERA";
512
- TipoDocumento2[TipoDocumento2["PASAPORTE"] = 94] = "PASAPORTE";
513
- TipoDocumento2[TipoDocumento2["CI_BUENOS_AIRES"] = 95] = "CI_BUENOS_AIRES";
514
- TipoDocumento2[TipoDocumento2["CI_POLICIA_FEDERAL"] = 96] = "CI_POLICIA_FEDERAL";
515
- TipoDocumento2[TipoDocumento2["DNI"] = 96] = "DNI";
516
- TipoDocumento2[TipoDocumento2["CONSUMIDOR_FINAL"] = 99] = "CONSUMIDOR_FINAL";
517
- return TipoDocumento2;
518
- })(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 || {});
519
520
 
520
521
  // src/utils/calculations.ts
521
- function calcularSubtotal(items, incluyeIva = false) {
522
+ function calculateSubtotal(items, includesVAT = false) {
522
523
  return items.reduce((sum, item) => {
523
- let precioNeto = item.precioUnitario;
524
- if (incluyeIva && item.alicuotaIva) {
525
- 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);
526
527
  }
527
- return sum + item.cantidad * precioNeto;
528
+ return sum + item.quantity * netPrice;
528
529
  }, 0);
529
530
  }
530
- function calcularIVA(items, incluyeIva = false) {
531
+ function calculateVAT(items, includesVAT = false) {
531
532
  return items.reduce((sum, item) => {
532
- const alicuota = item.alicuotaIva || 0;
533
- let precioNeto = item.precioUnitario;
534
- if (incluyeIva && alicuota) {
535
- 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);
536
537
  }
537
- const subtotalNeto = item.cantidad * precioNeto;
538
- return sum + subtotalNeto * alicuota / 100;
538
+ const netSubtotal = item.quantity * netPrice;
539
+ return sum + netSubtotal * rate / 100;
539
540
  }, 0);
540
541
  }
541
- function calcularTotal(items, incluyeIva = false) {
542
- if (incluyeIva) {
543
- 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);
544
545
  }
545
- const subtotal = calcularSubtotal(items, false);
546
- const iva = calcularIVA(items, false);
547
- return subtotal + iva;
546
+ const subtotal = calculateSubtotal(items, false);
547
+ const vat = calculateVAT(items, false);
548
+ return subtotal + vat;
548
549
  }
549
- function redondear(valor) {
550
- return Math.round(valor * 100) / 100;
550
+ function round(value) {
551
+ return Math.round(value * 100) / 100;
551
552
  }
552
553
 
553
554
  // src/utils/qr.ts
554
- function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
555
- const cleanCuit = cuitEmisor.replace(/\D/g, "");
556
- const cleanCae = caeResponse.cae.replace(/\D/g, "");
557
- const fDate = caeResponse.fecha;
558
- const fechaFormat = fDate.length === 8 ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}` : fDate;
559
- const docTipo = comprador?.tipoDocumento || 99 /* CONSUMIDOR_FINAL */;
560
- const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\D/g, "") : "0";
561
- const qrObj = {
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 = {
562
563
  ver: 1,
563
- fecha: fechaFormat,
564
- cuit: Number(cleanCuit),
565
- ptoVta: Number(caeResponse.puntoVenta),
566
- tipoCmp: Number(caeResponse.tipoComprobante),
567
- nroCmp: Number(caeResponse.nroComprobante),
564
+ fecha: formattedDate,
565
+ cuit: Number(cleanCUIT),
566
+ ptoVta: Number(caeResponse.pointOfSale),
567
+ tipoCmp: Number(caeResponse.invoiceType),
568
+ nroCmp: Number(caeResponse.invoiceNumber),
568
569
  importe: Number(parseFloat(total.toFixed(2))),
569
570
  moneda: "PES",
570
571
  ctz: 1
571
572
  };
572
- if (docTipo !== 99 /* CONSUMIDOR_FINAL */ || Number(docNro) > 0) {
573
- qrObj.tipoDocRec = Number(docTipo);
574
- qrObj.nroDocRec = Number(docNro);
573
+ if (docType !== 99 /* FINAL_CONSUMER */ || Number(docNumber) > 0) {
574
+ qrData.tipoDocRec = Number(docType);
575
+ qrData.nroDocRec = Number(docNumber);
575
576
  }
576
- qrObj.tipoCodAut = "E";
577
- qrObj.codAut = Number(cleanCae);
578
- const jsonString = JSON.stringify(qrObj);
579
- let base64 = typeof Buffer !== "undefined" ? Buffer.from(jsonString).toString("base64") : btoa(jsonString);
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);
580
581
  return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;
581
582
  }
582
583
 
583
584
  // src/constants/errors.ts
584
585
  var ARCA_ERROR_HINTS = {
585
- // Generales / Auth
586
- 501: "Error de autenticaci\xF3n: El certificado puede haber expirado o la relaci\xF3n CUIT/Servicio no est\xE1 dada de alta en la web de AFIP.",
587
- 502: "Error de autenticaci\xF3n: El ticket de acceso (TA) ya no es v\xE1lido o est\xE1 mal formado.",
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.",
588
590
  1e3: "El CUIT informado no es v\xE1lido o no corresponde al certificado usado.",
589
- // WSFE (Facturación)
590
- 10015: "Factura B: El importe total es superior al l\xEDmite para consumidores finales an\xF3nimos. Identific\xE1 al comprador.",
591
- 10016: "Factura C: El CUIT informado como receptor no es v\xE1lido.",
592
- 10048: 'Punto de Venta inv\xE1lido: Asegurate de que el punto de venta est\xE9 dado de alta como "Factuweb" o "Webservice" en AFIP.',
593
- 600: "No se pudo autorizar el comprobante. Revis\xE1 las observaciones para m\xE1s detalle.",
594
- // Padrón
595
- "PADRON_ERROR": "El servicio de Padr\xF3n suele ser inestable en homologaci\xF3n. Reintent\xE1 en unos minutos."
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."
596
609
  };
597
610
  function getArcaHint(code) {
598
611
  return ARCA_ERROR_HINTS[code];
@@ -609,35 +622,24 @@ var WsfeService = class _WsfeService {
609
622
  if (!config.ticket || !config.ticket.token) {
610
623
  throw new ArcaValidationError(
611
624
  "Ticket WSAA requerido. Ejecut\xE1 wsaa.login() primero.",
612
- { hint: "El ticket se obtiene del servicio WSAA" }
625
+ { hint: "El ticket se obtiene del servicio WsaaService" }
613
626
  );
614
627
  }
615
- if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {
628
+ if (!config.pointOfSale || config.pointOfSale < 1 || config.pointOfSale > 9999) {
616
629
  throw new ArcaValidationError(
617
630
  "Punto de venta inv\xE1lido: debe ser un n\xFAmero entre 1 y 9999",
618
- { puntoVenta: config.puntoVenta }
631
+ { pointOfSale: config.pointOfSale }
619
632
  );
620
633
  }
621
634
  }
635
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
636
+ // Estado de los servidores
637
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
622
638
  /**
623
- * Emite un Ticket C de forma simple (solo total)
624
- * Tipo de comprobante: 83
625
- */
626
- async emitirTicketCSimple(params) {
627
- return this.emitirComprobante({
628
- tipo: 83 /* TICKET_C */,
629
- concepto: params.concepto || 1 /* PRODUCTOS */,
630
- total: params.total,
631
- fecha: params.fecha,
632
- comprador: {
633
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
634
- nroDocumento: "0"
635
- }
636
- });
637
- }
638
- /**
639
- * Verifica el estado de los servidores de ARCA (FEDummy)
640
- * 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')
641
643
  */
642
644
  static async checkStatus(environment = "homologacion") {
643
645
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
@@ -668,164 +670,265 @@ var WsfeService = class _WsfeService {
668
670
  throw new ArcaError("Respuesta FEDummy inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
669
671
  }
670
672
  return {
671
- AppServer: data.AppServer,
672
- DbServer: data.DbServer,
673
- AuthServer: data.AuthServer
673
+ appServer: data.AppServer,
674
+ dbServer: data.DbServer,
675
+ authServer: data.AuthServer
674
676
  };
675
677
  }
676
678
  /**
677
- * Verifica el estado de los servidores de ARCA (FEDummy)
678
- * No requiere autenticación.
679
+ * Verifica el estado de los servidores de ARCA.
680
+ * Versión de instancia — usa el ambiente configurado.
679
681
  */
680
682
  async checkStatus() {
681
683
  return _WsfeService.checkStatus(this.config.environment);
682
684
  }
685
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
686
+ // Emisión de comprobantes
687
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
683
688
  /**
684
- * Emite un Ticket C completo (con detalle de items)
685
- * 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.
686
691
  */
687
- async emitirTicketC(params) {
688
- const total = redondear(calcularTotal(params.items));
689
- const cae = await this.emitirComprobante({
690
- tipo: 83 /* TICKET_C */,
691
- 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 */,
692
713
  total,
693
- fecha: params.fecha,
694
- comprador: {
695
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
696
- nroDocumento: "0"
714
+ date: params.date,
715
+ buyer: {
716
+ docType: 99 /* FINAL_CONSUMER */,
717
+ docNumber: "0"
697
718
  }
698
719
  });
699
- return {
700
- ...cae,
701
- items: params.items
702
- };
720
+ return { ...cae, items: params.items };
703
721
  }
704
722
  /**
705
- * Emite una Factura B (monotributo a responsable inscripto)
706
- * REQUIERE detalle de items con IVA discriminado
723
+ * Emite una Factura C (consumidor final, sin discriminación de IVA).
707
724
  */
708
- async emitirFacturaB(params) {
709
- this.validateItemsWithIVA(params.items);
710
- const incluyeIva = params.incluyeIva || false;
711
- const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);
712
- return this.emitirComprobante({
713
- tipo: 6 /* FACTURA_B */,
714
- concepto: params.concepto || 1 /* PRODUCTOS */,
715
- items: params.items,
716
- comprador: params.comprador,
717
- fecha: params.fecha,
718
- ivaData,
719
- 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
720
737
  });
721
738
  }
722
739
  /**
723
- * Emite una Factura A (RI a RI)
724
- * REQUIERE detalle de items con IVA discriminado
740
+ * Emite una Factura B (con IVA discriminado).
741
+ * REQUIERE `vatRate` en todos los items.
725
742
  */
726
- async emitirFacturaA(params) {
727
- this.validateItemsWithIVA(params.items);
728
- const incluyeIva = params.incluyeIva || false;
729
- const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);
730
- return this.emitirComprobante({
731
- tipo: 1 /* FACTURA_A */,
732
- 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 */,
733
750
  items: params.items,
734
- comprador: params.comprador,
735
- fecha: params.fecha,
736
- ivaData,
737
- incluyeIva
751
+ buyer: params.buyer,
752
+ date: params.date,
753
+ vatData,
754
+ includesVAT
738
755
  });
739
756
  }
740
757
  /**
741
- * 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.
742
760
  */
743
- validateItemsWithIVA(items) {
744
- const sinIva = items.filter(
745
- (item) => item.alicuotaIva === void 0 || item.alicuotaIva === null
746
- );
747
- if (sinIva.length > 0) {
748
- throw new ArcaValidationError(
749
- "Esta operaci\xF3n requiere IVA discriminado en todos los items",
750
- {
751
- itemsSinIva: sinIva.map((i) => i.descripcion),
752
- hint: "Agreg\xE1 alicuotaIva a cada item (21, 10.5, 27, o 0)"
753
- }
754
- );
755
- }
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
+ });
756
774
  }
775
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
776
+ // Consultas
777
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
757
778
  /**
758
- * Calcula IVA agrupado por alícuota
759
- * 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
760
783
  */
761
- calcularIVAPorAlicuota(items, incluyeIva = false) {
762
- const porAlicuota = /* @__PURE__ */ new Map();
763
- items.forEach((item) => {
764
- const alicuota = item.alicuotaIva || 0;
765
- let precioNeto = item.precioUnitario;
766
- if (incluyeIva && alicuota) {
767
- precioNeto = item.precioUnitario / (1 + alicuota / 100);
768
- }
769
- const base = item.cantidad * precioNeto;
770
- const importe = base * alicuota / 100;
771
- const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };
772
- porAlicuota.set(alicuota, {
773
- base: actual.base + base,
774
- importe: actual.importe + importe
775
- });
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
776
813
  });
777
- return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({
778
- alicuota,
779
- baseImponible: redondear(valores.base),
780
- importe: redondear(valores.importe)
781
- }));
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
+ };
782
849
  }
783
850
  /**
784
- * Emite una Factura C (consumidor final)
785
- * Forma simplificada sin especificar comprador
851
+ * Lista los puntos de venta habilitados para el CUIT autenticado (FEParamGetPtosVenta).
786
852
  */
787
- async emitirFacturaC(params) {
788
- const total = redondear(calcularTotal(params.items));
789
- return this.emitirComprobante({
790
- tipo: 11 /* FACTURA_C */,
791
- concepto: params.concepto || 1 /* PRODUCTOS */,
792
- total,
793
- fecha: params.fecha,
794
- comprador: {
795
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
796
- 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"
797
874
  },
798
- items: params.items
875
+ body: soapRequest,
876
+ timeout: this.config.timeout
799
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
+ }));
800
900
  }
901
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
902
+ // Métodos internos
903
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
801
904
  /**
802
- * Emite un comprobante (método genérico interno)
905
+ * Método genérico interno para emitir cualquier tipo de comprobante.
803
906
  */
804
- async emitirComprobante(request) {
805
- const nroComprobante = await this.obtenerProximoNumero(request.tipo);
907
+ async issueDocument(request) {
908
+ const invoiceNumber = await this.getNextInvoiceNumber(request.type);
806
909
  let total = request.total || 0;
807
- let subtotal = total;
808
- let iva = 0;
910
+ let net = total;
911
+ let vat = 0;
809
912
  if (request.items && request.items.length > 0) {
810
- const incluyeIva = request.incluyeIva || false;
811
- subtotal = redondear(calcularSubtotal(request.items, incluyeIva));
812
- iva = redondear(calcularIVA(request.items, incluyeIva));
813
- 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));
814
917
  }
815
918
  if (total <= 0) {
816
919
  throw new ArcaValidationError("El monto total debe ser mayor a 0");
817
920
  }
818
- const soapRequest = this.buildCAESolicitarRequest({
819
- tipo: request.tipo,
820
- puntoVenta: this.config.puntoVenta,
821
- nroComprobante,
822
- concepto: request.concepto,
823
- fecha: request.fecha || /* @__PURE__ */ new Date(),
824
- comprador: request.comprador,
825
- subtotal,
826
- 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,
827
930
  total,
828
- ivaData: request.ivaData
931
+ vatData: request.vatData
829
932
  });
830
933
  const endpoint = getWsfeEndpoint(this.config.environment);
831
934
  const response = await callArcaApi(endpoint, {
@@ -846,24 +949,19 @@ var WsfeService = class _WsfeService {
846
949
  }
847
950
  const responseXml = await response.text();
848
951
  const result = await this.parseCAEResponse(responseXml);
849
- const urlQr = generarUrlQR(
850
- result,
851
- this.config.cuit,
852
- total,
853
- request.comprador
854
- );
952
+ const qrUrl = generateQRUrl(result, this.config.cuit, total, request.buyer);
855
953
  return {
856
954
  ...result,
857
955
  items: request.items,
858
- iva: request.ivaData,
859
- urlQr
956
+ vat: request.vatData,
957
+ qrUrl
860
958
  };
861
959
  }
862
960
  /**
863
- * Obtiene el próximo número de comprobante disponible
961
+ * Obtiene el próximo número de comprobante disponible (FECompUltimoAutorizado + 1)
864
962
  */
865
- async obtenerProximoNumero(tipo) {
866
- const soapRequest = this.buildProximoNumeroRequest(tipo);
963
+ async getNextInvoiceNumber(type) {
964
+ const soapRequest = this.buildLastInvoiceRequest(type);
867
965
  const endpoint = getWsfeEndpoint(this.config.environment);
868
966
  const response = await callArcaApi(endpoint, {
869
967
  method: "POST",
@@ -875,7 +973,7 @@ var WsfeService = class _WsfeService {
875
973
  timeout: this.config.timeout
876
974
  });
877
975
  if (!response.ok) {
878
- 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");
879
977
  }
880
978
  const responseXml = await response.text();
881
979
  const result = parseXml(responseXml);
@@ -890,23 +988,87 @@ var WsfeService = class _WsfeService {
890
988
  getArcaHint(code)
891
989
  );
892
990
  }
893
- const nro = data?.CbteNro;
894
- return typeof nro === "number" ? nro + 1 : 1;
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
+ );
1009
+ }
1010
+ }
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
+ }));
895
1035
  }
896
- buildCAESolicitarRequest(params) {
897
- const fechaStr = params.fecha.toISOString().split("T")[0].replace(/-/g, "");
898
- let ivaXml = "";
899
- if (params.ivaData && params.ivaData.length > 0) {
900
- ivaXml = "<ar:Iva>";
901
- params.ivaData.forEach((aliquot) => {
902
- ivaXml += `
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 += `
903
1065
  <ar:AlicIva>
904
- <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>
905
- <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>
906
- <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>
907
1069
  </ar:AlicIva>`;
908
1070
  });
909
- ivaXml += "\n </ar:Iva>";
1071
+ vatXml += "\n </ar:Iva>";
910
1072
  }
911
1073
  return `<?xml version="1.0" encoding="UTF-8"?>
912
1074
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
@@ -922,26 +1084,26 @@ var WsfeService = class _WsfeService {
922
1084
  <ar:FeCAEReq>
923
1085
  <ar:FeCabReq>
924
1086
  <ar:CantReg>1</ar:CantReg>
925
- <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>
926
- <ar:CbteTipo>${params.tipo}</ar:CbteTipo>
1087
+ <ar:PtoVta>${params.pointOfSale}</ar:PtoVta>
1088
+ <ar:CbteTipo>${params.type}</ar:CbteTipo>
927
1089
  </ar:FeCabReq>
928
1090
  <ar:FeDetReq>
929
1091
  <ar:FECAEDetRequest>
930
- <ar:Concepto>${params.concepto}</ar:Concepto>
931
- <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>
932
- <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>
933
- <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>
934
- <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>
935
- <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>
936
1098
  <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>
937
1099
  <ar:ImpTotConc>0.00</ar:ImpTotConc>
938
- <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>
1100
+ <ar:ImpNeto>${params.net.toFixed(2)}</ar:ImpNeto>
939
1101
  <ar:ImpOpEx>0.00</ar:ImpOpEx>
940
- <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>
1102
+ <ar:ImpIVA>${params.vat.toFixed(2)}</ar:ImpIVA>
941
1103
  <ar:ImpTrib>0.00</ar:ImpTrib>
942
1104
  <ar:MonId>PES</ar:MonId>
943
1105
  <ar:MonCotiz>1</ar:MonCotiz>
944
- ${ivaXml}
1106
+ ${vatXml}
945
1107
  </ar:FECAEDetRequest>
946
1108
  </ar:FeDetReq>
947
1109
  </ar:FeCAEReq>
@@ -949,29 +1111,7 @@ var WsfeService = class _WsfeService {
949
1111
  </soapenv:Body>
950
1112
  </soapenv:Envelope>`;
951
1113
  }
952
- /**
953
- * Mapea alícuota % a código ARCA
954
- */
955
- getCodigoAlicuota(porcentaje) {
956
- const mapa = {
957
- 0: 3,
958
- 10.5: 4,
959
- 21: 5,
960
- 27: 6
961
- };
962
- const codigo = mapa[porcentaje];
963
- if (!codigo) {
964
- throw new ArcaValidationError(
965
- `Al\xEDcuota IVA inv\xE1lida: ${porcentaje}%`,
966
- {
967
- alicuotasValidas: [0, 10.5, 21, 27],
968
- hint: "Usar una de las al\xEDcuotas oficiales de Argentina"
969
- }
970
- );
971
- }
972
- return codigo;
973
- }
974
- buildProximoNumeroRequest(tipo) {
1114
+ buildLastInvoiceRequest(type) {
975
1115
  return `<?xml version="1.0" encoding="UTF-8"?>
976
1116
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
977
1117
  xmlns:ar="http://ar.gov.afip.dif.FEV1/">
@@ -983,8 +1123,8 @@ var WsfeService = class _WsfeService {
983
1123
  <ar:Sign>${this.config.ticket.sign}</ar:Sign>
984
1124
  <ar:Cuit>${this.config.cuit}</ar:Cuit>
985
1125
  </ar:Auth>
986
- <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>
987
- <ar:CbteTipo>${tipo}</ar:CbteTipo>
1126
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1127
+ <ar:CbteTipo>${type}</ar:CbteTipo>
988
1128
  </ar:FECompUltimoAutorizado>
989
1129
  </soapenv:Body>
990
1130
  </soapenv:Envelope>`;
@@ -1010,20 +1150,20 @@ var WsfeService = class _WsfeService {
1010
1150
  if (!det) {
1011
1151
  throw new ArcaError("Respuesta WSFE incompleta: falta detalle del comprobante", "PARSE_ERROR");
1012
1152
  }
1013
- const observaciones = [];
1153
+ const observations = [];
1014
1154
  if (det.Observaciones) {
1015
1155
  const obsArray = Array.isArray(det.Observaciones.Obs) ? det.Observaciones.Obs : [det.Observaciones.Obs];
1016
- obsArray.forEach((o) => observaciones.push(o.Msg));
1156
+ obsArray.forEach((o) => observations.push(o.Msg));
1017
1157
  }
1018
1158
  return {
1019
- tipoComprobante: cab.CbteTipo,
1020
- puntoVenta: cab.PtoVta,
1021
- nroComprobante: Number(det.CbteDesde),
1022
- fecha: det.CbteFch,
1159
+ invoiceType: cab.CbteTipo,
1160
+ pointOfSale: cab.PtoVta,
1161
+ invoiceNumber: Number(det.CbteDesde),
1162
+ date: det.CbteFch,
1023
1163
  cae: String(det.CAE),
1024
- vencimientoCae: String(det.CAEFchVto),
1025
- resultado: det.Resultado,
1026
- observaciones: observaciones.length > 0 ? observaciones : void 0
1164
+ caeExpiry: String(det.CAEFchVto),
1165
+ result: det.Resultado,
1166
+ observations: observations.length > 0 ? observations : void 0
1027
1167
  };
1028
1168
  }
1029
1169
  };
@@ -1045,12 +1185,12 @@ var PadronService = class {
1045
1185
  });
1046
1186
  }
1047
1187
  /**
1048
- * Consulta los datos de una persona por CUIT
1049
- *
1050
- * @param idPersona CUIT a consultar
1051
- * @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
1052
1192
  */
1053
- async getPersona(idPersona) {
1193
+ async getTaxpayer(taxId) {
1054
1194
  const ticket = await this.wsaa.login();
1055
1195
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1056
1196
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
@@ -1061,7 +1201,7 @@ var PadronService = class {
1061
1201
  <token>${ticket.token}</token>
1062
1202
  <sign>${ticket.sign}</sign>
1063
1203
  <cuitRepresentada>${this.config.cuit}</cuitRepresentada>
1064
- <idPersona>${idPersona}</idPersona>
1204
+ <idPersona>${taxId}</idPersona>
1065
1205
  </a13:getPersona>
1066
1206
  </soapenv:Body>
1067
1207
  </soapenv:Envelope>`;
@@ -1071,14 +1211,13 @@ var PadronService = class {
1071
1211
  headers: {
1072
1212
  "Content-Type": "text/xml; charset=utf-8",
1073
1213
  "SOAPAction": ""
1074
- // A13 no suele requerir SOAPAction específica en el header
1075
1214
  },
1076
1215
  body: soapRequest,
1077
1216
  timeout: this.config.timeout || 15e3
1078
1217
  });
1079
1218
  if (!response.ok) {
1080
1219
  throw new ArcaNetworkError(
1081
- `Error HTTP al comunicarse con Padron A13: ${response.status}`,
1220
+ `Error HTTP al comunicarse con Padr\xF3n A13: ${response.status}`,
1082
1221
  { status: response.status }
1083
1222
  );
1084
1223
  }
@@ -1086,7 +1225,7 @@ var PadronService = class {
1086
1225
  return this.parseResponse(xml);
1087
1226
  }
1088
1227
  /**
1089
- * Parsear la respuesta XML de getPersona
1228
+ * Parsea la respuesta XML de getPersona
1090
1229
  */
1091
1230
  parseResponse(xml) {
1092
1231
  const parser = new import_fast_xml_parser2.XMLParser({
@@ -1096,7 +1235,7 @@ var PadronService = class {
1096
1235
  const result = parser.parse(xml);
1097
1236
  const body = result.Envelope?.Body;
1098
1237
  if (!body) {
1099
- 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");
1100
1239
  }
1101
1240
  const response = body.getPersonaResponse?.personaReturn;
1102
1241
  if (!response) {
@@ -1113,82 +1252,80 @@ var PadronService = class {
1113
1252
  if (!p) {
1114
1253
  return { error: "CUIT no encontrado" };
1115
1254
  }
1116
- const persona = {
1117
- idPersona: Number(p.idPersona),
1118
- tipoPersona: p.tipoPersona,
1119
- nombre: p.nombre,
1120
- apellido: p.apellido,
1121
- razonSocial: p.razonSocial,
1122
- estadoClave: p.estadoClave,
1123
- domicilio: this.mapDomicilios(p.domicilio),
1124
- actividad: this.mapActividades(p.actividad),
1125
- impuesto: this.mapImpuestos(p.impuesto),
1126
- descripcionActividadPrincipal: p.descripcionActividadPrincipal,
1127
- 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),
1128
1267
  // 30 = IVA
1129
- esMonotributista: this.checkImpuesto(p, 20),
1268
+ isMonotax: this.hasTaxId(p, 20),
1130
1269
  // 20 = Monotributo
1131
- esExento: this.checkImpuesto(p, 32)
1132
- // 32 = Exento
1270
+ isVATExempt: this.hasTaxId(p, 32)
1271
+ // 32 = IVA Exento
1133
1272
  };
1134
- return { persona };
1273
+ return { taxpayer };
1135
1274
  }
1136
- mapDomicilios(d) {
1137
- if (!d) return [];
1138
- const list = this.ensureArray(d);
1139
- return list.map((item) => ({
1140
- direccion: item.direccion,
1141
- localidad: item.localidad,
1142
- codPostal: item.codPostal,
1143
- idProvincia: Number(item.idProvincia),
1144
- descripcionProvincia: item.descripcionProvincia,
1145
- 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
1146
1284
  }));
1147
1285
  }
1148
- mapActividades(a) {
1149
- if (!a) return [];
1150
- const list = this.ensureArray(a);
1151
- return list.map((item) => ({
1152
- idActividad: Number(item.idActividad),
1153
- descripcion: item.descripcion,
1154
- orden: Number(item.orden),
1155
- periodo: Number(item.periodo)
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)
1156
1293
  }));
1157
1294
  }
1158
- mapImpuestos(i) {
1159
- if (!i) return [];
1160
- const list = this.ensureArray(i);
1161
- return list.map((item) => ({
1162
- idImpuesto: Number(item.idImpuesto),
1163
- descripcion: item.descripcion,
1164
- periodo: Number(item.periodo)
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)
1165
1301
  }));
1166
1302
  }
1167
- checkImpuesto(p, id) {
1168
- const impuestos = p.impuesto;
1169
- if (!impuestos) return false;
1170
- const list = this.ensureArray(impuestos);
1171
- return list.some((i) => Number(i.idImpuesto) === id);
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);
1172
1307
  }
1173
1308
  /**
1174
- * Helper para normalizar la respuesta de XML que puede ser objeto único o array
1309
+ * Normaliza un valor que puede ser un objeto único o un array (comportamiento de fast-xml-parser)
1175
1310
  */
1176
- ensureArray(data) {
1311
+ toArray(data) {
1177
1312
  if (data === void 0 || data === null) return [];
1178
- return Array.isArray(data) ? data : [data];
1313
+ if (Array.isArray(data)) return data;
1314
+ return [data];
1179
1315
  }
1180
1316
  };
1181
1317
  // Annotate the CommonJS export names for ESM import in node:
1182
1318
  0 && (module.exports = {
1183
1319
  ArcaAuthError,
1184
1320
  ArcaError,
1321
+ ArcaNetworkError,
1185
1322
  ArcaValidationError,
1186
- Concepto,
1323
+ BillingConcept,
1324
+ InvoiceType,
1187
1325
  PadronService,
1188
- TipoComprobante,
1189
- TipoDocumento,
1326
+ TaxIdType,
1190
1327
  WsaaService,
1191
1328
  WsfeService,
1192
- generarUrlQR
1329
+ generateQRUrl
1193
1330
  });
1194
1331
  //# sourceMappingURL=index.cjs.map