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/CHANGELOG.md +142 -0
- package/README.md +125 -48
- package/dist/index.cjs +489 -352
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -179
- package/dist/index.d.ts +271 -179
- package/dist/index.js +484 -348
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
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
|
-
|
|
37
|
+
BillingConcept: () => BillingConcept,
|
|
38
|
+
InvoiceType: () => InvoiceType,
|
|
37
39
|
PadronService: () => PadronService,
|
|
38
|
-
|
|
39
|
-
TipoDocumento: () => TipoDocumento,
|
|
40
|
+
TaxIdType: () => TaxIdType,
|
|
40
41
|
WsaaService: () => WsaaService,
|
|
41
42
|
WsfeService: () => WsfeService,
|
|
42
|
-
|
|
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
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return
|
|
498
|
-
})(
|
|
499
|
-
var
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
return
|
|
504
|
-
})(
|
|
505
|
-
var
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return
|
|
518
|
-
})(
|
|
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
|
|
522
|
+
function calculateSubtotal(items, includesVAT = false) {
|
|
522
523
|
return items.reduce((sum, item) => {
|
|
523
|
-
let
|
|
524
|
-
if (
|
|
525
|
-
|
|
524
|
+
let netPrice = item.unitPrice;
|
|
525
|
+
if (includesVAT && item.vatRate) {
|
|
526
|
+
netPrice = item.unitPrice / (1 + item.vatRate / 100);
|
|
526
527
|
}
|
|
527
|
-
return sum + item.
|
|
528
|
+
return sum + item.quantity * netPrice;
|
|
528
529
|
}, 0);
|
|
529
530
|
}
|
|
530
|
-
function
|
|
531
|
+
function calculateVAT(items, includesVAT = false) {
|
|
531
532
|
return items.reduce((sum, item) => {
|
|
532
|
-
const
|
|
533
|
-
let
|
|
534
|
-
if (
|
|
535
|
-
|
|
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
|
|
538
|
-
return sum +
|
|
538
|
+
const netSubtotal = item.quantity * netPrice;
|
|
539
|
+
return sum + netSubtotal * rate / 100;
|
|
539
540
|
}, 0);
|
|
540
541
|
}
|
|
541
|
-
function
|
|
542
|
-
if (
|
|
543
|
-
return items.reduce((sum, item) => sum + item.
|
|
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 =
|
|
546
|
-
const
|
|
547
|
-
return subtotal +
|
|
546
|
+
const subtotal = calculateSubtotal(items, false);
|
|
547
|
+
const vat = calculateVAT(items, false);
|
|
548
|
+
return subtotal + vat;
|
|
548
549
|
}
|
|
549
|
-
function
|
|
550
|
-
return Math.round(
|
|
550
|
+
function round(value) {
|
|
551
|
+
return Math.round(value * 100) / 100;
|
|
551
552
|
}
|
|
552
553
|
|
|
553
554
|
// src/utils/qr.ts
|
|
554
|
-
function
|
|
555
|
-
const
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
const
|
|
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:
|
|
564
|
-
cuit: Number(
|
|
565
|
-
ptoVta: Number(caeResponse.
|
|
566
|
-
tipoCmp: Number(caeResponse.
|
|
567
|
-
nroCmp: Number(caeResponse.
|
|
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 (
|
|
573
|
-
|
|
574
|
-
|
|
573
|
+
if (docType !== 99 /* FINAL_CONSUMER */ || Number(docNumber) > 0) {
|
|
574
|
+
qrData.tipoDocRec = Number(docType);
|
|
575
|
+
qrData.nroDocRec = Number(docNumber);
|
|
575
576
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const jsonString = JSON.stringify(
|
|
579
|
-
|
|
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
|
-
//
|
|
586
|
-
501: "
|
|
587
|
-
502: "
|
|
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
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|
|
625
|
+
{ hint: "El ticket se obtiene del servicio WsaaService" }
|
|
613
626
|
);
|
|
614
627
|
}
|
|
615
|
-
if (!config.
|
|
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
|
-
{
|
|
631
|
+
{ pointOfSale: config.pointOfSale }
|
|
619
632
|
);
|
|
620
633
|
}
|
|
621
634
|
}
|
|
635
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
636
|
+
// Estado de los servidores
|
|
637
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
622
638
|
/**
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
|
|
626
|
-
|
|
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
|
-
|
|
672
|
-
|
|
673
|
-
|
|
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
|
|
678
|
-
*
|
|
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
|
|
685
|
-
*
|
|
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
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
|
706
|
-
* REQUIERE detalle de items con IVA discriminado
|
|
723
|
+
* Emite una Factura C (consumidor final, sin discriminación de IVA).
|
|
707
724
|
*/
|
|
708
|
-
async
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
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
|
|
724
|
-
* REQUIERE
|
|
740
|
+
* Emite una Factura B (con IVA discriminado).
|
|
741
|
+
* REQUIERE `vatRate` en todos los items.
|
|
725
742
|
*/
|
|
726
|
-
async
|
|
727
|
-
this.
|
|
728
|
-
const
|
|
729
|
-
const
|
|
730
|
-
return this.
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
751
|
+
buyer: params.buyer,
|
|
752
|
+
date: params.date,
|
|
753
|
+
vatData,
|
|
754
|
+
includesVAT
|
|
738
755
|
});
|
|
739
756
|
}
|
|
740
757
|
/**
|
|
741
|
-
*
|
|
758
|
+
* Emite una Factura A (Responsable Inscripto a Responsable Inscripto, con IVA discriminado).
|
|
759
|
+
* REQUIERE `vatRate` en todos los items.
|
|
742
760
|
*/
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
);
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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
|
-
*
|
|
759
|
-
*
|
|
779
|
+
* Consulta un comprobante ya emitido (FECompConsultar).
|
|
780
|
+
*
|
|
781
|
+
* @param type Tipo de comprobante
|
|
782
|
+
* @param invoiceNumber Número de comprobante
|
|
760
783
|
*/
|
|
761
|
-
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
-
*
|
|
785
|
-
* Forma simplificada sin especificar comprador
|
|
851
|
+
* Lista los puntos de venta habilitados para el CUIT autenticado (FEParamGetPtosVenta).
|
|
786
852
|
*/
|
|
787
|
-
async
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
905
|
+
* Método genérico interno para emitir cualquier tipo de comprobante.
|
|
803
906
|
*/
|
|
804
|
-
async
|
|
805
|
-
const
|
|
907
|
+
async issueDocument(request) {
|
|
908
|
+
const invoiceNumber = await this.getNextInvoiceNumber(request.type);
|
|
806
909
|
let total = request.total || 0;
|
|
807
|
-
let
|
|
808
|
-
let
|
|
910
|
+
let net = total;
|
|
911
|
+
let vat = 0;
|
|
809
912
|
if (request.items && request.items.length > 0) {
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
total =
|
|
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.
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
859
|
-
|
|
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
|
|
866
|
-
const soapRequest = this.
|
|
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
|
|
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
|
|
894
|
-
return typeof
|
|
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
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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.
|
|
905
|
-
<ar:BaseImp>${
|
|
906
|
-
<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
|
-
|
|
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.
|
|
926
|
-
<ar:CbteTipo>${params.
|
|
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.
|
|
931
|
-
<ar:DocTipo>${params.
|
|
932
|
-
<ar:DocNro>${params.
|
|
933
|
-
<ar:CbteDesde>${params.
|
|
934
|
-
<ar:CbteHasta>${params.
|
|
935
|
-
<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.
|
|
1100
|
+
<ar:ImpNeto>${params.net.toFixed(2)}</ar:ImpNeto>
|
|
939
1101
|
<ar:ImpOpEx>0.00</ar:ImpOpEx>
|
|
940
|
-
<ar:ImpIVA>${params.
|
|
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
|
-
${
|
|
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.
|
|
987
|
-
<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
|
|
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) =>
|
|
1156
|
+
obsArray.forEach((o) => observations.push(o.Msg));
|
|
1017
1157
|
}
|
|
1018
1158
|
return {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1159
|
+
invoiceType: cab.CbteTipo,
|
|
1160
|
+
pointOfSale: cab.PtoVta,
|
|
1161
|
+
invoiceNumber: Number(det.CbteDesde),
|
|
1162
|
+
date: det.CbteFch,
|
|
1023
1163
|
cae: String(det.CAE),
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
|
1049
|
-
*
|
|
1050
|
-
* @param
|
|
1051
|
-
* @returns Datos
|
|
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
|
|
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>${
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
-
|
|
1268
|
+
isMonotax: this.hasTaxId(p, 20),
|
|
1130
1269
|
// 20 = Monotributo
|
|
1131
|
-
|
|
1132
|
-
// 32 = Exento
|
|
1270
|
+
isVATExempt: this.hasTaxId(p, 32)
|
|
1271
|
+
// 32 = IVA Exento
|
|
1133
1272
|
};
|
|
1134
|
-
return {
|
|
1273
|
+
return { taxpayer };
|
|
1135
1274
|
}
|
|
1136
|
-
|
|
1137
|
-
if (!
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
-
|
|
1149
|
-
if (!
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
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
|
-
|
|
1159
|
-
if (!
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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
|
-
|
|
1168
|
-
const
|
|
1169
|
-
if (!
|
|
1170
|
-
|
|
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
|
-
*
|
|
1309
|
+
* Normaliza un valor que puede ser un objeto único o un array (comportamiento de fast-xml-parser)
|
|
1175
1310
|
*/
|
|
1176
|
-
|
|
1311
|
+
toArray(data) {
|
|
1177
1312
|
if (data === void 0 || data === null) return [];
|
|
1178
|
-
|
|
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
|
-
|
|
1323
|
+
BillingConcept,
|
|
1324
|
+
InvoiceType,
|
|
1187
1325
|
PadronService,
|
|
1188
|
-
|
|
1189
|
-
TipoDocumento,
|
|
1326
|
+
TaxIdType,
|
|
1190
1327
|
WsaaService,
|
|
1191
1328
|
WsfeService,
|
|
1192
|
-
|
|
1329
|
+
generateQRUrl
|
|
1193
1330
|
});
|
|
1194
1331
|
//# sourceMappingURL=index.cjs.map
|