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.js CHANGED
@@ -442,112 +442,124 @@ var WsaaService = class {
442
442
  };
443
443
 
444
444
  // src/types/wsfe.ts
445
- var TipoComprobante = /* @__PURE__ */ ((TipoComprobante2) => {
446
- TipoComprobante2[TipoComprobante2["FACTURA_A"] = 1] = "FACTURA_A";
447
- TipoComprobante2[TipoComprobante2["FACTURA_B"] = 6] = "FACTURA_B";
448
- TipoComprobante2[TipoComprobante2["FACTURA_C"] = 11] = "FACTURA_C";
449
- TipoComprobante2[TipoComprobante2["TICKET_A"] = 81] = "TICKET_A";
450
- TipoComprobante2[TipoComprobante2["TICKET_B"] = 82] = "TICKET_B";
451
- TipoComprobante2[TipoComprobante2["TICKET_C"] = 83] = "TICKET_C";
452
- return TipoComprobante2;
453
- })(TipoComprobante || {});
454
- var Concepto = /* @__PURE__ */ ((Concepto2) => {
455
- Concepto2[Concepto2["PRODUCTOS"] = 1] = "PRODUCTOS";
456
- Concepto2[Concepto2["SERVICIOS"] = 2] = "SERVICIOS";
457
- Concepto2[Concepto2["PRODUCTOS_Y_SERVICIOS"] = 3] = "PRODUCTOS_Y_SERVICIOS";
458
- return Concepto2;
459
- })(Concepto || {});
460
- var TipoDocumento = /* @__PURE__ */ ((TipoDocumento2) => {
461
- TipoDocumento2[TipoDocumento2["CUIT"] = 80] = "CUIT";
462
- TipoDocumento2[TipoDocumento2["CUIL"] = 86] = "CUIL";
463
- TipoDocumento2[TipoDocumento2["CDI"] = 87] = "CDI";
464
- TipoDocumento2[TipoDocumento2["LE"] = 89] = "LE";
465
- TipoDocumento2[TipoDocumento2["LC"] = 90] = "LC";
466
- TipoDocumento2[TipoDocumento2["CI_EXTRANJERA"] = 91] = "CI_EXTRANJERA";
467
- TipoDocumento2[TipoDocumento2["PASAPORTE"] = 94] = "PASAPORTE";
468
- TipoDocumento2[TipoDocumento2["CI_BUENOS_AIRES"] = 95] = "CI_BUENOS_AIRES";
469
- TipoDocumento2[TipoDocumento2["CI_POLICIA_FEDERAL"] = 96] = "CI_POLICIA_FEDERAL";
470
- TipoDocumento2[TipoDocumento2["DNI"] = 96] = "DNI";
471
- TipoDocumento2[TipoDocumento2["CONSUMIDOR_FINAL"] = 99] = "CONSUMIDOR_FINAL";
472
- return TipoDocumento2;
473
- })(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 || {});
474
474
 
475
475
  // src/utils/calculations.ts
476
- function calcularSubtotal(items, incluyeIva = false) {
476
+ function calculateSubtotal(items, includesVAT = false) {
477
477
  return items.reduce((sum, item) => {
478
- let precioNeto = item.precioUnitario;
479
- if (incluyeIva && item.alicuotaIva) {
480
- 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);
481
481
  }
482
- return sum + item.cantidad * precioNeto;
482
+ return sum + item.quantity * netPrice;
483
483
  }, 0);
484
484
  }
485
- function calcularIVA(items, incluyeIva = false) {
485
+ function calculateVAT(items, includesVAT = false) {
486
486
  return items.reduce((sum, item) => {
487
- const alicuota = item.alicuotaIva || 0;
488
- let precioNeto = item.precioUnitario;
489
- if (incluyeIva && alicuota) {
490
- 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);
491
491
  }
492
- const subtotalNeto = item.cantidad * precioNeto;
493
- return sum + subtotalNeto * alicuota / 100;
492
+ const netSubtotal = item.quantity * netPrice;
493
+ return sum + netSubtotal * rate / 100;
494
494
  }, 0);
495
495
  }
496
- function calcularTotal(items, incluyeIva = false) {
497
- if (incluyeIva) {
498
- 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);
499
499
  }
500
- const subtotal = calcularSubtotal(items, false);
501
- const iva = calcularIVA(items, false);
502
- return subtotal + iva;
500
+ const subtotal = calculateSubtotal(items, false);
501
+ const vat = calculateVAT(items, false);
502
+ return subtotal + vat;
503
503
  }
504
- function redondear(valor) {
505
- return Math.round(valor * 100) / 100;
504
+ function round(value) {
505
+ return Math.round(value * 100) / 100;
506
506
  }
507
507
 
508
508
  // src/utils/qr.ts
509
- function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
510
- const cleanCuit = cuitEmisor.replace(/\D/g, "");
511
- const cleanCae = caeResponse.cae.replace(/\D/g, "");
512
- const fDate = caeResponse.fecha;
513
- const fechaFormat = fDate.length === 8 ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}` : fDate;
514
- const docTipo = comprador?.tipoDocumento || 99 /* CONSUMIDOR_FINAL */;
515
- const docNro = comprador?.nroDocumento ? comprador.nroDocumento.replace(/\D/g, "") : "0";
516
- const qrObj = {
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
517
  ver: 1,
518
- fecha: fechaFormat,
519
- cuit: Number(cleanCuit),
520
- ptoVta: Number(caeResponse.puntoVenta),
521
- tipoCmp: Number(caeResponse.tipoComprobante),
522
- nroCmp: Number(caeResponse.nroComprobante),
518
+ fecha: formattedDate,
519
+ cuit: Number(cleanCUIT),
520
+ ptoVta: Number(caeResponse.pointOfSale),
521
+ tipoCmp: Number(caeResponse.invoiceType),
522
+ nroCmp: Number(caeResponse.invoiceNumber),
523
523
  importe: Number(parseFloat(total.toFixed(2))),
524
524
  moneda: "PES",
525
525
  ctz: 1
526
526
  };
527
- if (docTipo !== 99 /* CONSUMIDOR_FINAL */ || Number(docNro) > 0) {
528
- qrObj.tipoDocRec = Number(docTipo);
529
- qrObj.nroDocRec = Number(docNro);
527
+ if (docType !== 99 /* FINAL_CONSUMER */ || Number(docNumber) > 0) {
528
+ qrData.tipoDocRec = Number(docType);
529
+ qrData.nroDocRec = Number(docNumber);
530
530
  }
531
- qrObj.tipoCodAut = "E";
532
- qrObj.codAut = Number(cleanCae);
533
- const jsonString = JSON.stringify(qrObj);
534
- let base64 = typeof Buffer !== "undefined" ? Buffer.from(jsonString).toString("base64") : btoa(jsonString);
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
535
  return `https://www.afip.gob.ar/fe/qr/?p=${encodeURIComponent(base64)}`;
536
536
  }
537
537
 
538
538
  // src/constants/errors.ts
539
539
  var ARCA_ERROR_HINTS = {
540
- // Generales / Auth
541
- 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.",
542
- 502: "Error de autenticaci\xF3n: El ticket de acceso (TA) ya no es v\xE1lido o est\xE1 mal formado.",
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.",
543
544
  1e3: "El CUIT informado no es v\xE1lido o no corresponde al certificado usado.",
544
- // WSFE (Facturación)
545
- 10015: "Factura B: El importe total es superior al l\xEDmite para consumidores finales an\xF3nimos. Identific\xE1 al comprador.",
546
- 10016: "Factura C: El CUIT informado como receptor no es v\xE1lido.",
547
- 10048: 'Punto de Venta inv\xE1lido: Asegurate de que el punto de venta est\xE9 dado de alta como "Factuweb" o "Webservice" en AFIP.',
548
- 600: "No se pudo autorizar el comprobante. Revis\xE1 las observaciones para m\xE1s detalle.",
549
- // Padrón
550
- "PADRON_ERROR": "El servicio de Padr\xF3n suele ser inestable en homologaci\xF3n. Reintent\xE1 en unos minutos."
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."
551
563
  };
552
564
  function getArcaHint(code) {
553
565
  return ARCA_ERROR_HINTS[code];
@@ -564,35 +576,24 @@ var WsfeService = class _WsfeService {
564
576
  if (!config.ticket || !config.ticket.token) {
565
577
  throw new ArcaValidationError(
566
578
  "Ticket WSAA requerido. Ejecut\xE1 wsaa.login() primero.",
567
- { hint: "El ticket se obtiene del servicio WSAA" }
579
+ { hint: "El ticket se obtiene del servicio WsaaService" }
568
580
  );
569
581
  }
570
- if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {
582
+ if (!config.pointOfSale || config.pointOfSale < 1 || config.pointOfSale > 9999) {
571
583
  throw new ArcaValidationError(
572
584
  "Punto de venta inv\xE1lido: debe ser un n\xFAmero entre 1 y 9999",
573
- { puntoVenta: config.puntoVenta }
585
+ { pointOfSale: config.pointOfSale }
574
586
  );
575
587
  }
576
588
  }
589
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
590
+ // Estado de los servidores
591
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
577
592
  /**
578
- * Emite un Ticket C de forma simple (solo total)
579
- * Tipo de comprobante: 83
580
- */
581
- async emitirTicketCSimple(params) {
582
- return this.emitirComprobante({
583
- tipo: 83 /* TICKET_C */,
584
- concepto: params.concepto || 1 /* PRODUCTOS */,
585
- total: params.total,
586
- fecha: params.fecha,
587
- comprador: {
588
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
589
- nroDocumento: "0"
590
- }
591
- });
592
- }
593
- /**
594
- * Verifica el estado de los servidores de ARCA (FEDummy)
595
- * 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')
596
597
  */
597
598
  static async checkStatus(environment = "homologacion") {
598
599
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
@@ -623,164 +624,265 @@ var WsfeService = class _WsfeService {
623
624
  throw new ArcaError("Respuesta FEDummy inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
624
625
  }
625
626
  return {
626
- AppServer: data.AppServer,
627
- DbServer: data.DbServer,
628
- AuthServer: data.AuthServer
627
+ appServer: data.AppServer,
628
+ dbServer: data.DbServer,
629
+ authServer: data.AuthServer
629
630
  };
630
631
  }
631
632
  /**
632
- * Verifica el estado de los servidores de ARCA (FEDummy)
633
- * No requiere autenticación.
633
+ * Verifica el estado de los servidores de ARCA.
634
+ * Versión de instancia — usa el ambiente configurado.
634
635
  */
635
636
  async checkStatus() {
636
637
  return _WsfeService.checkStatus(this.config.environment);
637
638
  }
639
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
640
+ // Emisión de comprobantes
641
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
638
642
  /**
639
- * Emite un Ticket C completo (con detalle de items)
640
- * 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.
641
645
  */
642
- async emitirTicketC(params) {
643
- const total = redondear(calcularTotal(params.items));
644
- const cae = await this.emitirComprobante({
645
- tipo: 83 /* TICKET_C */,
646
- 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 */,
647
667
  total,
648
- fecha: params.fecha,
649
- comprador: {
650
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
651
- nroDocumento: "0"
668
+ date: params.date,
669
+ buyer: {
670
+ docType: 99 /* FINAL_CONSUMER */,
671
+ docNumber: "0"
652
672
  }
653
673
  });
654
- return {
655
- ...cae,
656
- items: params.items
657
- };
674
+ return { ...cae, items: params.items };
658
675
  }
659
676
  /**
660
- * Emite una Factura B (monotributo a responsable inscripto)
661
- * REQUIERE detalle de items con IVA discriminado
677
+ * Emite una Factura C (consumidor final, sin discriminación de IVA).
662
678
  */
663
- async emitirFacturaB(params) {
664
- this.validateItemsWithIVA(params.items);
665
- const incluyeIva = params.incluyeIva || false;
666
- const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);
667
- return this.emitirComprobante({
668
- tipo: 6 /* FACTURA_B */,
669
- concepto: params.concepto || 1 /* PRODUCTOS */,
670
- items: params.items,
671
- comprador: params.comprador,
672
- fecha: params.fecha,
673
- ivaData,
674
- 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
675
691
  });
676
692
  }
677
693
  /**
678
- * Emite una Factura A (RI a RI)
679
- * REQUIERE detalle de items con IVA discriminado
694
+ * Emite una Factura B (con IVA discriminado).
695
+ * REQUIERE `vatRate` en todos los items.
680
696
  */
681
- async emitirFacturaA(params) {
682
- this.validateItemsWithIVA(params.items);
683
- const incluyeIva = params.incluyeIva || false;
684
- const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);
685
- return this.emitirComprobante({
686
- tipo: 1 /* FACTURA_A */,
687
- 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 */,
688
704
  items: params.items,
689
- comprador: params.comprador,
690
- fecha: params.fecha,
691
- ivaData,
692
- incluyeIva
705
+ buyer: params.buyer,
706
+ date: params.date,
707
+ vatData,
708
+ includesVAT
693
709
  });
694
710
  }
695
711
  /**
696
- * 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.
697
714
  */
698
- validateItemsWithIVA(items) {
699
- const sinIva = items.filter(
700
- (item) => item.alicuotaIva === void 0 || item.alicuotaIva === null
701
- );
702
- if (sinIva.length > 0) {
703
- throw new ArcaValidationError(
704
- "Esta operaci\xF3n requiere IVA discriminado en todos los items",
705
- {
706
- itemsSinIva: sinIva.map((i) => i.descripcion),
707
- hint: "Agreg\xE1 alicuotaIva a cada item (21, 10.5, 27, o 0)"
708
- }
709
- );
710
- }
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
+ });
711
728
  }
729
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
730
+ // Consultas
731
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
712
732
  /**
713
- * Calcula IVA agrupado por alícuota
714
- * 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
715
737
  */
716
- calcularIVAPorAlicuota(items, incluyeIva = false) {
717
- const porAlicuota = /* @__PURE__ */ new Map();
718
- items.forEach((item) => {
719
- const alicuota = item.alicuotaIva || 0;
720
- let precioNeto = item.precioUnitario;
721
- if (incluyeIva && alicuota) {
722
- precioNeto = item.precioUnitario / (1 + alicuota / 100);
723
- }
724
- const base = item.cantidad * precioNeto;
725
- const importe = base * alicuota / 100;
726
- const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };
727
- porAlicuota.set(alicuota, {
728
- base: actual.base + base,
729
- importe: actual.importe + importe
730
- });
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
731
767
  });
732
- return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({
733
- alicuota,
734
- baseImponible: redondear(valores.base),
735
- importe: redondear(valores.importe)
736
- }));
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
+ };
737
803
  }
738
804
  /**
739
- * Emite una Factura C (consumidor final)
740
- * Forma simplificada sin especificar comprador
805
+ * Lista los puntos de venta habilitados para el CUIT autenticado (FEParamGetPtosVenta).
741
806
  */
742
- async emitirFacturaC(params) {
743
- const total = redondear(calcularTotal(params.items));
744
- return this.emitirComprobante({
745
- tipo: 11 /* FACTURA_C */,
746
- concepto: params.concepto || 1 /* PRODUCTOS */,
747
- total,
748
- fecha: params.fecha,
749
- comprador: {
750
- tipoDocumento: 99 /* CONSUMIDOR_FINAL */,
751
- 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"
752
828
  },
753
- items: params.items
829
+ body: soapRequest,
830
+ timeout: this.config.timeout
754
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
+ }));
755
854
  }
855
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
856
+ // Métodos internos
857
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
756
858
  /**
757
- * Emite un comprobante (método genérico interno)
859
+ * Método genérico interno para emitir cualquier tipo de comprobante.
758
860
  */
759
- async emitirComprobante(request) {
760
- const nroComprobante = await this.obtenerProximoNumero(request.tipo);
861
+ async issueDocument(request) {
862
+ const invoiceNumber = await this.getNextInvoiceNumber(request.type);
761
863
  let total = request.total || 0;
762
- let subtotal = total;
763
- let iva = 0;
864
+ let net = total;
865
+ let vat = 0;
764
866
  if (request.items && request.items.length > 0) {
765
- const incluyeIva = request.incluyeIva || false;
766
- subtotal = redondear(calcularSubtotal(request.items, incluyeIva));
767
- iva = redondear(calcularIVA(request.items, incluyeIva));
768
- 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));
769
871
  }
770
872
  if (total <= 0) {
771
873
  throw new ArcaValidationError("El monto total debe ser mayor a 0");
772
874
  }
773
- const soapRequest = this.buildCAESolicitarRequest({
774
- tipo: request.tipo,
775
- puntoVenta: this.config.puntoVenta,
776
- nroComprobante,
777
- concepto: request.concepto,
778
- fecha: request.fecha || /* @__PURE__ */ new Date(),
779
- comprador: request.comprador,
780
- subtotal,
781
- 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,
782
884
  total,
783
- ivaData: request.ivaData
885
+ vatData: request.vatData
784
886
  });
785
887
  const endpoint = getWsfeEndpoint(this.config.environment);
786
888
  const response = await callArcaApi(endpoint, {
@@ -801,24 +903,19 @@ var WsfeService = class _WsfeService {
801
903
  }
802
904
  const responseXml = await response.text();
803
905
  const result = await this.parseCAEResponse(responseXml);
804
- const urlQr = generarUrlQR(
805
- result,
806
- this.config.cuit,
807
- total,
808
- request.comprador
809
- );
906
+ const qrUrl = generateQRUrl(result, this.config.cuit, total, request.buyer);
810
907
  return {
811
908
  ...result,
812
909
  items: request.items,
813
- iva: request.ivaData,
814
- urlQr
910
+ vat: request.vatData,
911
+ qrUrl
815
912
  };
816
913
  }
817
914
  /**
818
- * Obtiene el próximo número de comprobante disponible
915
+ * Obtiene el próximo número de comprobante disponible (FECompUltimoAutorizado + 1)
819
916
  */
820
- async obtenerProximoNumero(tipo) {
821
- const soapRequest = this.buildProximoNumeroRequest(tipo);
917
+ async getNextInvoiceNumber(type) {
918
+ const soapRequest = this.buildLastInvoiceRequest(type);
822
919
  const endpoint = getWsfeEndpoint(this.config.environment);
823
920
  const response = await callArcaApi(endpoint, {
824
921
  method: "POST",
@@ -830,7 +927,7 @@ var WsfeService = class _WsfeService {
830
927
  timeout: this.config.timeout
831
928
  });
832
929
  if (!response.ok) {
833
- 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");
834
931
  }
835
932
  const responseXml = await response.text();
836
933
  const result = parseXml(responseXml);
@@ -845,23 +942,87 @@ var WsfeService = class _WsfeService {
845
942
  getArcaHint(code)
846
943
  );
847
944
  }
848
- const nro = data?.CbteNro;
849
- return typeof nro === "number" ? nro + 1 : 1;
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
+ );
963
+ }
964
+ }
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
+ }));
850
989
  }
851
- buildCAESolicitarRequest(params) {
852
- const fechaStr = params.fecha.toISOString().split("T")[0].replace(/-/g, "");
853
- let ivaXml = "";
854
- if (params.ivaData && params.ivaData.length > 0) {
855
- ivaXml = "<ar:Iva>";
856
- params.ivaData.forEach((aliquot) => {
857
- ivaXml += `
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 += `
858
1019
  <ar:AlicIva>
859
- <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>
860
- <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>
861
- <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>
862
1023
  </ar:AlicIva>`;
863
1024
  });
864
- ivaXml += "\n </ar:Iva>";
1025
+ vatXml += "\n </ar:Iva>";
865
1026
  }
866
1027
  return `<?xml version="1.0" encoding="UTF-8"?>
867
1028
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
@@ -877,26 +1038,26 @@ var WsfeService = class _WsfeService {
877
1038
  <ar:FeCAEReq>
878
1039
  <ar:FeCabReq>
879
1040
  <ar:CantReg>1</ar:CantReg>
880
- <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>
881
- <ar:CbteTipo>${params.tipo}</ar:CbteTipo>
1041
+ <ar:PtoVta>${params.pointOfSale}</ar:PtoVta>
1042
+ <ar:CbteTipo>${params.type}</ar:CbteTipo>
882
1043
  </ar:FeCabReq>
883
1044
  <ar:FeDetReq>
884
1045
  <ar:FECAEDetRequest>
885
- <ar:Concepto>${params.concepto}</ar:Concepto>
886
- <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>
887
- <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>
888
- <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>
889
- <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>
890
- <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>
891
1052
  <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>
892
1053
  <ar:ImpTotConc>0.00</ar:ImpTotConc>
893
- <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>
1054
+ <ar:ImpNeto>${params.net.toFixed(2)}</ar:ImpNeto>
894
1055
  <ar:ImpOpEx>0.00</ar:ImpOpEx>
895
- <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>
1056
+ <ar:ImpIVA>${params.vat.toFixed(2)}</ar:ImpIVA>
896
1057
  <ar:ImpTrib>0.00</ar:ImpTrib>
897
1058
  <ar:MonId>PES</ar:MonId>
898
1059
  <ar:MonCotiz>1</ar:MonCotiz>
899
- ${ivaXml}
1060
+ ${vatXml}
900
1061
  </ar:FECAEDetRequest>
901
1062
  </ar:FeDetReq>
902
1063
  </ar:FeCAEReq>
@@ -904,29 +1065,7 @@ var WsfeService = class _WsfeService {
904
1065
  </soapenv:Body>
905
1066
  </soapenv:Envelope>`;
906
1067
  }
907
- /**
908
- * Mapea alícuota % a código ARCA
909
- */
910
- getCodigoAlicuota(porcentaje) {
911
- const mapa = {
912
- 0: 3,
913
- 10.5: 4,
914
- 21: 5,
915
- 27: 6
916
- };
917
- const codigo = mapa[porcentaje];
918
- if (!codigo) {
919
- throw new ArcaValidationError(
920
- `Al\xEDcuota IVA inv\xE1lida: ${porcentaje}%`,
921
- {
922
- alicuotasValidas: [0, 10.5, 21, 27],
923
- hint: "Usar una de las al\xEDcuotas oficiales de Argentina"
924
- }
925
- );
926
- }
927
- return codigo;
928
- }
929
- buildProximoNumeroRequest(tipo) {
1068
+ buildLastInvoiceRequest(type) {
930
1069
  return `<?xml version="1.0" encoding="UTF-8"?>
931
1070
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
932
1071
  xmlns:ar="http://ar.gov.afip.dif.FEV1/">
@@ -938,8 +1077,8 @@ var WsfeService = class _WsfeService {
938
1077
  <ar:Sign>${this.config.ticket.sign}</ar:Sign>
939
1078
  <ar:Cuit>${this.config.cuit}</ar:Cuit>
940
1079
  </ar:Auth>
941
- <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>
942
- <ar:CbteTipo>${tipo}</ar:CbteTipo>
1080
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1081
+ <ar:CbteTipo>${type}</ar:CbteTipo>
943
1082
  </ar:FECompUltimoAutorizado>
944
1083
  </soapenv:Body>
945
1084
  </soapenv:Envelope>`;
@@ -965,20 +1104,20 @@ var WsfeService = class _WsfeService {
965
1104
  if (!det) {
966
1105
  throw new ArcaError("Respuesta WSFE incompleta: falta detalle del comprobante", "PARSE_ERROR");
967
1106
  }
968
- const observaciones = [];
1107
+ const observations = [];
969
1108
  if (det.Observaciones) {
970
1109
  const obsArray = Array.isArray(det.Observaciones.Obs) ? det.Observaciones.Obs : [det.Observaciones.Obs];
971
- obsArray.forEach((o) => observaciones.push(o.Msg));
1110
+ obsArray.forEach((o) => observations.push(o.Msg));
972
1111
  }
973
1112
  return {
974
- tipoComprobante: cab.CbteTipo,
975
- puntoVenta: cab.PtoVta,
976
- nroComprobante: Number(det.CbteDesde),
977
- fecha: det.CbteFch,
1113
+ invoiceType: cab.CbteTipo,
1114
+ pointOfSale: cab.PtoVta,
1115
+ invoiceNumber: Number(det.CbteDesde),
1116
+ date: det.CbteFch,
978
1117
  cae: String(det.CAE),
979
- vencimientoCae: String(det.CAEFchVto),
980
- resultado: det.Resultado,
981
- observaciones: observaciones.length > 0 ? observaciones : void 0
1118
+ caeExpiry: String(det.CAEFchVto),
1119
+ result: det.Resultado,
1120
+ observations: observations.length > 0 ? observations : void 0
982
1121
  };
983
1122
  }
984
1123
  };
@@ -1000,12 +1139,12 @@ var PadronService = class {
1000
1139
  });
1001
1140
  }
1002
1141
  /**
1003
- * Consulta los datos de una persona por CUIT
1004
- *
1005
- * @param idPersona CUIT a consultar
1006
- * @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
1007
1146
  */
1008
- async getPersona(idPersona) {
1147
+ async getTaxpayer(taxId) {
1009
1148
  const ticket = await this.wsaa.login();
1010
1149
  const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1011
1150
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
@@ -1016,7 +1155,7 @@ var PadronService = class {
1016
1155
  <token>${ticket.token}</token>
1017
1156
  <sign>${ticket.sign}</sign>
1018
1157
  <cuitRepresentada>${this.config.cuit}</cuitRepresentada>
1019
- <idPersona>${idPersona}</idPersona>
1158
+ <idPersona>${taxId}</idPersona>
1020
1159
  </a13:getPersona>
1021
1160
  </soapenv:Body>
1022
1161
  </soapenv:Envelope>`;
@@ -1026,14 +1165,13 @@ var PadronService = class {
1026
1165
  headers: {
1027
1166
  "Content-Type": "text/xml; charset=utf-8",
1028
1167
  "SOAPAction": ""
1029
- // A13 no suele requerir SOAPAction específica en el header
1030
1168
  },
1031
1169
  body: soapRequest,
1032
1170
  timeout: this.config.timeout || 15e3
1033
1171
  });
1034
1172
  if (!response.ok) {
1035
1173
  throw new ArcaNetworkError(
1036
- `Error HTTP al comunicarse con Padron A13: ${response.status}`,
1174
+ `Error HTTP al comunicarse con Padr\xF3n A13: ${response.status}`,
1037
1175
  { status: response.status }
1038
1176
  );
1039
1177
  }
@@ -1041,7 +1179,7 @@ var PadronService = class {
1041
1179
  return this.parseResponse(xml);
1042
1180
  }
1043
1181
  /**
1044
- * Parsear la respuesta XML de getPersona
1182
+ * Parsea la respuesta XML de getPersona
1045
1183
  */
1046
1184
  parseResponse(xml) {
1047
1185
  const parser = new XMLParser2({
@@ -1051,7 +1189,7 @@ var PadronService = class {
1051
1189
  const result = parser.parse(xml);
1052
1190
  const body = result.Envelope?.Body;
1053
1191
  if (!body) {
1054
- 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");
1055
1193
  }
1056
1194
  const response = body.getPersonaResponse?.personaReturn;
1057
1195
  if (!response) {
@@ -1068,81 +1206,79 @@ var PadronService = class {
1068
1206
  if (!p) {
1069
1207
  return { error: "CUIT no encontrado" };
1070
1208
  }
1071
- const persona = {
1072
- idPersona: Number(p.idPersona),
1073
- tipoPersona: p.tipoPersona,
1074
- nombre: p.nombre,
1075
- apellido: p.apellido,
1076
- razonSocial: p.razonSocial,
1077
- estadoClave: p.estadoClave,
1078
- domicilio: this.mapDomicilios(p.domicilio),
1079
- actividad: this.mapActividades(p.actividad),
1080
- impuesto: this.mapImpuestos(p.impuesto),
1081
- descripcionActividadPrincipal: p.descripcionActividadPrincipal,
1082
- 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),
1083
1221
  // 30 = IVA
1084
- esMonotributista: this.checkImpuesto(p, 20),
1222
+ isMonotax: this.hasTaxId(p, 20),
1085
1223
  // 20 = Monotributo
1086
- esExento: this.checkImpuesto(p, 32)
1087
- // 32 = Exento
1224
+ isVATExempt: this.hasTaxId(p, 32)
1225
+ // 32 = IVA Exento
1088
1226
  };
1089
- return { persona };
1227
+ return { taxpayer };
1090
1228
  }
1091
- mapDomicilios(d) {
1092
- if (!d) return [];
1093
- const list = this.ensureArray(d);
1094
- return list.map((item) => ({
1095
- direccion: item.direccion,
1096
- localidad: item.localidad,
1097
- codPostal: item.codPostal,
1098
- idProvincia: Number(item.idProvincia),
1099
- descripcionProvincia: item.descripcionProvincia,
1100
- 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
1101
1238
  }));
1102
1239
  }
1103
- mapActividades(a) {
1104
- if (!a) return [];
1105
- const list = this.ensureArray(a);
1106
- return list.map((item) => ({
1107
- idActividad: Number(item.idActividad),
1108
- descripcion: item.descripcion,
1109
- orden: Number(item.orden),
1110
- periodo: Number(item.periodo)
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)
1111
1247
  }));
1112
1248
  }
1113
- mapImpuestos(i) {
1114
- if (!i) return [];
1115
- const list = this.ensureArray(i);
1116
- return list.map((item) => ({
1117
- idImpuesto: Number(item.idImpuesto),
1118
- descripcion: item.descripcion,
1119
- periodo: Number(item.periodo)
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)
1120
1255
  }));
1121
1256
  }
1122
- checkImpuesto(p, id) {
1123
- const impuestos = p.impuesto;
1124
- if (!impuestos) return false;
1125
- const list = this.ensureArray(impuestos);
1126
- return list.some((i) => Number(i.idImpuesto) === id);
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);
1127
1261
  }
1128
1262
  /**
1129
- * Helper para normalizar la respuesta de XML que puede ser objeto único o array
1263
+ * Normaliza un valor que puede ser un objeto único o un array (comportamiento de fast-xml-parser)
1130
1264
  */
1131
- ensureArray(data) {
1265
+ toArray(data) {
1132
1266
  if (data === void 0 || data === null) return [];
1133
- return Array.isArray(data) ? data : [data];
1267
+ if (Array.isArray(data)) return data;
1268
+ return [data];
1134
1269
  }
1135
1270
  };
1136
1271
  export {
1137
1272
  ArcaAuthError,
1138
1273
  ArcaError,
1274
+ ArcaNetworkError,
1139
1275
  ArcaValidationError,
1140
- Concepto,
1276
+ BillingConcept,
1277
+ InvoiceType,
1141
1278
  PadronService,
1142
- TipoComprobante,
1143
- TipoDocumento,
1279
+ TaxIdType,
1144
1280
  WsaaService,
1145
1281
  WsfeService,
1146
- generarUrlQR
1282
+ generateQRUrl
1147
1283
  };
1148
1284
  //# sourceMappingURL=index.js.map