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