arca-sdk 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +538 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -2
- package/dist/index.d.ts +125 -2
- package/dist/index.js +537 -38
- package/dist/index.js.map +1 -1
- package/package.json +59 -59
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
ArcaNetworkError: () => ArcaNetworkError,
|
|
36
36
|
ArcaValidationError: () => ArcaValidationError,
|
|
37
37
|
BillingConcept: () => BillingConcept,
|
|
38
|
+
CaeaService: () => CaeaService,
|
|
38
39
|
InvoiceType: () => InvoiceType,
|
|
39
40
|
PadronService: () => PadronService,
|
|
40
41
|
TaxIdType: () => TaxIdType,
|
|
@@ -385,6 +386,7 @@ async function callArcaApi(url, options) {
|
|
|
385
386
|
}
|
|
386
387
|
|
|
387
388
|
// src/auth/wsaa.ts
|
|
389
|
+
var import_fast_xml_parser2 = require("fast-xml-parser");
|
|
388
390
|
var WsaaService = class {
|
|
389
391
|
config;
|
|
390
392
|
ticketManager;
|
|
@@ -486,13 +488,27 @@ var WsaaService = class {
|
|
|
486
488
|
body: this.buildSoapRequest(cms),
|
|
487
489
|
timeout: this.config.timeout
|
|
488
490
|
});
|
|
491
|
+
const responseText = await response.text();
|
|
489
492
|
if (!response.ok) {
|
|
493
|
+
let errorMessage = `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`;
|
|
494
|
+
try {
|
|
495
|
+
const parser = new import_fast_xml_parser2.XMLParser({
|
|
496
|
+
ignoreAttributes: false,
|
|
497
|
+
removeNSPrefix: true
|
|
498
|
+
});
|
|
499
|
+
const result = parser.parse(responseText);
|
|
500
|
+
const fault = result.Envelope?.Body?.Fault;
|
|
501
|
+
if (fault && fault.faultstring) {
|
|
502
|
+
errorMessage = `Error AFIP WSAA: ${fault.faultstring}`;
|
|
503
|
+
}
|
|
504
|
+
} catch (e) {
|
|
505
|
+
}
|
|
490
506
|
throw new ArcaAuthError(
|
|
491
|
-
|
|
492
|
-
{ status: response.status, statusText: response.statusText }
|
|
507
|
+
errorMessage,
|
|
508
|
+
{ status: response.status, statusText: response.statusText, body: responseText }
|
|
493
509
|
);
|
|
494
510
|
}
|
|
495
|
-
const responseXml =
|
|
511
|
+
const responseXml = responseText;
|
|
496
512
|
return parseWsaaResponse(responseXml);
|
|
497
513
|
}
|
|
498
514
|
/**
|
|
@@ -519,23 +535,23 @@ var WsaaService = class {
|
|
|
519
535
|
};
|
|
520
536
|
|
|
521
537
|
// src/types/wsfe.ts
|
|
522
|
-
var InvoiceType = /* @__PURE__ */ ((
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
return
|
|
538
|
+
var InvoiceType = /* @__PURE__ */ ((InvoiceType3) => {
|
|
539
|
+
InvoiceType3[InvoiceType3["FACTURA_A"] = 1] = "FACTURA_A";
|
|
540
|
+
InvoiceType3[InvoiceType3["NOTA_DEBITO_A"] = 2] = "NOTA_DEBITO_A";
|
|
541
|
+
InvoiceType3[InvoiceType3["NOTA_CREDITO_A"] = 3] = "NOTA_CREDITO_A";
|
|
542
|
+
InvoiceType3[InvoiceType3["RECIBO_A"] = 4] = "RECIBO_A";
|
|
543
|
+
InvoiceType3[InvoiceType3["FACTURA_B"] = 6] = "FACTURA_B";
|
|
544
|
+
InvoiceType3[InvoiceType3["NOTA_DEBITO_B"] = 7] = "NOTA_DEBITO_B";
|
|
545
|
+
InvoiceType3[InvoiceType3["NOTA_CREDITO_B"] = 8] = "NOTA_CREDITO_B";
|
|
546
|
+
InvoiceType3[InvoiceType3["RECIBO_B"] = 9] = "RECIBO_B";
|
|
547
|
+
InvoiceType3[InvoiceType3["FACTURA_C"] = 11] = "FACTURA_C";
|
|
548
|
+
InvoiceType3[InvoiceType3["NOTA_DEBITO_C"] = 12] = "NOTA_DEBITO_C";
|
|
549
|
+
InvoiceType3[InvoiceType3["NOTA_CREDITO_C"] = 13] = "NOTA_CREDITO_C";
|
|
550
|
+
InvoiceType3[InvoiceType3["RECIBO_C"] = 15] = "RECIBO_C";
|
|
551
|
+
InvoiceType3[InvoiceType3["TICKET_A"] = 81] = "TICKET_A";
|
|
552
|
+
InvoiceType3[InvoiceType3["TICKET_B"] = 82] = "TICKET_B";
|
|
553
|
+
InvoiceType3[InvoiceType3["TICKET_C"] = 83] = "TICKET_C";
|
|
554
|
+
return InvoiceType3;
|
|
539
555
|
})(InvoiceType || {});
|
|
540
556
|
var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
|
|
541
557
|
BillingConcept2[BillingConcept2["PRODUCTS"] = 1] = "PRODUCTS";
|
|
@@ -543,19 +559,19 @@ var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
|
|
|
543
559
|
BillingConcept2[BillingConcept2["PRODUCTS_AND_SERVICES"] = 3] = "PRODUCTS_AND_SERVICES";
|
|
544
560
|
return BillingConcept2;
|
|
545
561
|
})(BillingConcept || {});
|
|
546
|
-
var TaxIdType = /* @__PURE__ */ ((
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
return
|
|
562
|
+
var TaxIdType = /* @__PURE__ */ ((TaxIdType3) => {
|
|
563
|
+
TaxIdType3[TaxIdType3["CUIT"] = 80] = "CUIT";
|
|
564
|
+
TaxIdType3[TaxIdType3["CUIL"] = 86] = "CUIL";
|
|
565
|
+
TaxIdType3[TaxIdType3["CDI"] = 87] = "CDI";
|
|
566
|
+
TaxIdType3[TaxIdType3["LE"] = 89] = "LE";
|
|
567
|
+
TaxIdType3[TaxIdType3["LC"] = 90] = "LC";
|
|
568
|
+
TaxIdType3[TaxIdType3["FOREIGN_ID"] = 91] = "FOREIGN_ID";
|
|
569
|
+
TaxIdType3[TaxIdType3["PASSPORT"] = 94] = "PASSPORT";
|
|
570
|
+
TaxIdType3[TaxIdType3["BUENOS_AIRES_ID"] = 95] = "BUENOS_AIRES_ID";
|
|
571
|
+
TaxIdType3[TaxIdType3["NATIONAL_POLICE_ID"] = 96] = "NATIONAL_POLICE_ID";
|
|
572
|
+
TaxIdType3[TaxIdType3["DNI"] = 96] = "DNI";
|
|
573
|
+
TaxIdType3[TaxIdType3["FINAL_CONSUMER"] = 99] = "FINAL_CONSUMER";
|
|
574
|
+
return TaxIdType3;
|
|
559
575
|
})(TaxIdType || {});
|
|
560
576
|
var VatCondition = /* @__PURE__ */ ((VatCondition2) => {
|
|
561
577
|
VatCondition2[VatCondition2["IVA_RESPONSABLE_INSCRIPTO"] = 1] = "IVA_RESPONSABLE_INSCRIPTO";
|
|
@@ -1067,7 +1083,6 @@ var WsfeService = class _WsfeService {
|
|
|
1067
1083
|
* Método genérico interno para emitir cualquier tipo de comprobante.
|
|
1068
1084
|
*/
|
|
1069
1085
|
async issueDocument(request) {
|
|
1070
|
-
const invoiceNumber = await this.getNextInvoiceNumber(request.type);
|
|
1071
1086
|
let total = request.total || 0;
|
|
1072
1087
|
let net = total;
|
|
1073
1088
|
let vat = 0;
|
|
@@ -1080,6 +1095,8 @@ var WsfeService = class _WsfeService {
|
|
|
1080
1095
|
if (total <= 0) {
|
|
1081
1096
|
throw new ArcaValidationError("El monto total debe ser mayor a 0");
|
|
1082
1097
|
}
|
|
1098
|
+
this.validateBuyer(request.buyer, total);
|
|
1099
|
+
const invoiceNumber = await this.getNextInvoiceNumber(request.type);
|
|
1083
1100
|
const soapRequest = this.buildCAERequest({
|
|
1084
1101
|
type: request.type,
|
|
1085
1102
|
pointOfSale: this.config.pointOfSale,
|
|
@@ -1173,6 +1190,23 @@ var WsfeService = class _WsfeService {
|
|
|
1173
1190
|
);
|
|
1174
1191
|
}
|
|
1175
1192
|
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Valida obligatoriamente al comprador cuando el monto es mayor o igual a $10.000.000 para consumidor final
|
|
1195
|
+
*/
|
|
1196
|
+
validateBuyer(buyer, total) {
|
|
1197
|
+
const isFinalConsumer = !buyer || buyer.docType === 99 /* FINAL_CONSUMER */ || buyer.docNumber === "0" || buyer.docNumber === "";
|
|
1198
|
+
if (total >= 1e7 && isFinalConsumer) {
|
|
1199
|
+
const errorMessage = "Para importes mayores o iguales a $10.000.000 es obligatorio identificar al comprador (RG 5824/2026).";
|
|
1200
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1201
|
+
console.warn(`[arca-sdk WARNING] ${errorMessage}`);
|
|
1202
|
+
}
|
|
1203
|
+
throw new ArcaValidationError(errorMessage, {
|
|
1204
|
+
total,
|
|
1205
|
+
buyer,
|
|
1206
|
+
hint: "Actualiz\xE1 el objeto buyer con un tipo de documento v\xE1lido (DNI, CUIT, CUIL) y su n\xFAmero correspondiente."
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1176
1210
|
/**
|
|
1177
1211
|
* Calcula el IVA agrupado por alícuota (requerido por ARCA para Factura A/B)
|
|
1178
1212
|
*/
|
|
@@ -1376,7 +1410,7 @@ var WsfeService = class _WsfeService {
|
|
|
1376
1410
|
};
|
|
1377
1411
|
|
|
1378
1412
|
// src/services/padron.ts
|
|
1379
|
-
var
|
|
1413
|
+
var import_fast_xml_parser3 = require("fast-xml-parser");
|
|
1380
1414
|
var PadronService = class {
|
|
1381
1415
|
wsaa;
|
|
1382
1416
|
config;
|
|
@@ -1435,7 +1469,7 @@ var PadronService = class {
|
|
|
1435
1469
|
* Parsea la respuesta XML de getPersona
|
|
1436
1470
|
*/
|
|
1437
1471
|
parseResponse(xml) {
|
|
1438
|
-
const parser = new
|
|
1472
|
+
const parser = new import_fast_xml_parser3.XMLParser({
|
|
1439
1473
|
ignoreAttributes: false,
|
|
1440
1474
|
removeNSPrefix: true
|
|
1441
1475
|
});
|
|
@@ -1472,8 +1506,10 @@ var PadronService = class {
|
|
|
1472
1506
|
mainActivity: p.descripcionActividadPrincipal,
|
|
1473
1507
|
isVATRegistered: this.hasTaxId(p, 30),
|
|
1474
1508
|
// 30 = IVA
|
|
1475
|
-
isMonotax: this.hasTaxId(p, 20),
|
|
1476
|
-
//
|
|
1509
|
+
isMonotax: this.hasTaxId(p, 20) || this.hasTaxId(p, 24) || this.hasTaxId(p, 21),
|
|
1510
|
+
// General o Social/Autónomo
|
|
1511
|
+
isSocialMonotax: this.hasTaxId(p, 24) || this.hasTaxId(p, 21),
|
|
1512
|
+
// 24 = Obra Social / Promovido, 21 = Autónomo
|
|
1477
1513
|
isVATExempt: this.hasTaxId(p, 32)
|
|
1478
1514
|
// 32 = IVA Exento
|
|
1479
1515
|
};
|
|
@@ -1521,6 +1557,469 @@ var PadronService = class {
|
|
|
1521
1557
|
return [data];
|
|
1522
1558
|
}
|
|
1523
1559
|
};
|
|
1560
|
+
|
|
1561
|
+
// src/services/caea.ts
|
|
1562
|
+
var CaeaService = class {
|
|
1563
|
+
config;
|
|
1564
|
+
constructor(config) {
|
|
1565
|
+
this.validateConfig(config);
|
|
1566
|
+
this.config = config;
|
|
1567
|
+
}
|
|
1568
|
+
validateConfig(config) {
|
|
1569
|
+
if (!config.ticket || !config.ticket.token) {
|
|
1570
|
+
throw new ArcaValidationError(
|
|
1571
|
+
"Ticket WSAA requerido. Ejecut\xE1 wsaa.login() primero.",
|
|
1572
|
+
{ hint: "El ticket se obtiene del servicio WsaaService" }
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
if (!config.pointOfSale || config.pointOfSale < 1 || config.pointOfSale > 9999) {
|
|
1576
|
+
throw new ArcaValidationError(
|
|
1577
|
+
"Punto de venta inv\xE1lido: debe ser un n\xFAmero entre 1 y 9999",
|
|
1578
|
+
{ pointOfSale: config.pointOfSale }
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1583
|
+
// Métodos del Ciclo de Vida de CAEA
|
|
1584
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1585
|
+
/**
|
|
1586
|
+
* Solicita un CAEA (Código de Autorización Electrónico Anticipado) para una quincena específica (FECAEASolicitar).
|
|
1587
|
+
*/
|
|
1588
|
+
async solicitCAEA(params) {
|
|
1589
|
+
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1590
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
1591
|
+
xmlns:ar="http://ar.gov.afip.dif.FEV1/">
|
|
1592
|
+
<soapenv:Header/>
|
|
1593
|
+
<soapenv:Body>
|
|
1594
|
+
<ar:FECAEASolicitar>
|
|
1595
|
+
<ar:Auth>
|
|
1596
|
+
<ar:Token>${this.config.ticket.token}</ar:Token>
|
|
1597
|
+
<ar:Sign>${this.config.ticket.sign}</ar:Sign>
|
|
1598
|
+
<ar:Cuit>${this.config.cuit}</ar:Cuit>
|
|
1599
|
+
</ar:Auth>
|
|
1600
|
+
<ar:Periodo>${params.period}</ar:Periodo>
|
|
1601
|
+
<ar:Orden>${params.order}</ar:Orden>
|
|
1602
|
+
</ar:FECAEASolicitar>
|
|
1603
|
+
</soapenv:Body>
|
|
1604
|
+
</soapenv:Envelope>`;
|
|
1605
|
+
const endpoint = getWsfeEndpoint(this.config.environment);
|
|
1606
|
+
const response = await callArcaApi(endpoint, {
|
|
1607
|
+
method: "POST",
|
|
1608
|
+
headers: {
|
|
1609
|
+
"Content-Type": "text/xml; charset=utf-8",
|
|
1610
|
+
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASolicitar"
|
|
1611
|
+
},
|
|
1612
|
+
body: soapRequest,
|
|
1613
|
+
timeout: this.config.timeout
|
|
1614
|
+
});
|
|
1615
|
+
if (!response.ok) {
|
|
1616
|
+
throw new ArcaError(`Error HTTP al solicitar CAEA: ${response.status}`, "HTTP_ERROR");
|
|
1617
|
+
}
|
|
1618
|
+
const responseXml = await response.text();
|
|
1619
|
+
const result = parseXml(responseXml);
|
|
1620
|
+
const data = result?.Envelope?.Body?.FECAEASolicitarResponse?.FECAEASolicitarResult;
|
|
1621
|
+
if (!data) {
|
|
1622
|
+
throw new ArcaError("Respuesta FECAEASolicitar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
|
|
1623
|
+
}
|
|
1624
|
+
if (data.Errors) {
|
|
1625
|
+
const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
|
|
1626
|
+
const code = error?.Code || "UNKNOWN";
|
|
1627
|
+
throw new ArcaError(
|
|
1628
|
+
`Error ARCA: ${error?.Msg || "Error desconocido"}`,
|
|
1629
|
+
"ARCA_ERROR",
|
|
1630
|
+
data.Errors,
|
|
1631
|
+
getArcaHint(code)
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const res = data.ResultGet;
|
|
1635
|
+
return {
|
|
1636
|
+
caea: String(res.CAEA),
|
|
1637
|
+
period: Number(res.Periodo),
|
|
1638
|
+
order: Number(res.Orden),
|
|
1639
|
+
expiryDate: String(res.FchVto),
|
|
1640
|
+
actualDate: String(res.FchVigDesde),
|
|
1641
|
+
receptionDate: String(res.FchVigHasta),
|
|
1642
|
+
limitDate: String(res.FchTopeInf)
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Consulta un CAEA ya emitido (FECAEAConsultar).
|
|
1647
|
+
*/
|
|
1648
|
+
async getCAEA(caea) {
|
|
1649
|
+
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1650
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
1651
|
+
xmlns:ar="http://ar.gov.afip.dif.FEV1/">
|
|
1652
|
+
<soapenv:Header/>
|
|
1653
|
+
<soapenv:Body>
|
|
1654
|
+
<ar:FECAEAConsultar>
|
|
1655
|
+
<ar:Auth>
|
|
1656
|
+
<ar:Token>${this.config.ticket.token}</ar:Token>
|
|
1657
|
+
<ar:Sign>${this.config.ticket.sign}</ar:Sign>
|
|
1658
|
+
<ar:Cuit>${this.config.cuit}</ar:Cuit>
|
|
1659
|
+
</ar:Auth>
|
|
1660
|
+
<ar:Caea>${caea}</ar:Caea>
|
|
1661
|
+
</ar:FECAEAConsultar>
|
|
1662
|
+
</soapenv:Body>
|
|
1663
|
+
</soapenv:Envelope>`;
|
|
1664
|
+
const endpoint = getWsfeEndpoint(this.config.environment);
|
|
1665
|
+
const response = await callArcaApi(endpoint, {
|
|
1666
|
+
method: "POST",
|
|
1667
|
+
headers: {
|
|
1668
|
+
"Content-Type": "text/xml; charset=utf-8",
|
|
1669
|
+
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEAConsultar"
|
|
1670
|
+
},
|
|
1671
|
+
body: soapRequest,
|
|
1672
|
+
timeout: this.config.timeout
|
|
1673
|
+
});
|
|
1674
|
+
if (!response.ok) {
|
|
1675
|
+
throw new ArcaError(`Error HTTP al consultar CAEA: ${response.status}`, "HTTP_ERROR");
|
|
1676
|
+
}
|
|
1677
|
+
const responseXml = await response.text();
|
|
1678
|
+
const result = parseXml(responseXml);
|
|
1679
|
+
const data = result?.Envelope?.Body?.FECAEAConsultarResponse?.FECAEAConsultarResult;
|
|
1680
|
+
if (!data) {
|
|
1681
|
+
throw new ArcaError("Respuesta FECAEAConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
|
|
1682
|
+
}
|
|
1683
|
+
if (data.Errors) {
|
|
1684
|
+
const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
|
|
1685
|
+
const code = error?.Code || "UNKNOWN";
|
|
1686
|
+
throw new ArcaError(
|
|
1687
|
+
`Error ARCA: ${error?.Msg || "Error desconocido"}`,
|
|
1688
|
+
"ARCA_ERROR",
|
|
1689
|
+
data.Errors,
|
|
1690
|
+
getArcaHint(code)
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
const res = data.ResultGet;
|
|
1694
|
+
return {
|
|
1695
|
+
caea: String(res.CAEA),
|
|
1696
|
+
period: Number(res.Periodo),
|
|
1697
|
+
order: Number(res.Orden),
|
|
1698
|
+
expiryDate: String(res.FchVto),
|
|
1699
|
+
actualDate: String(res.FchVigDesde),
|
|
1700
|
+
receptionDate: String(res.FchVigHasta),
|
|
1701
|
+
limitDate: String(res.FchTopeInf)
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Rinde/registra informativamente los comprobantes emitidos en contingencia local bajo un CAEA específico (FECAEARegInformativo).
|
|
1706
|
+
* Soporta el envío en lotes de comprobantes homogéneos del mismo tipo de factura y punto de venta.
|
|
1707
|
+
*/
|
|
1708
|
+
async reportCAEAPeriod(params) {
|
|
1709
|
+
if (!params.invoices || params.invoices.length === 0) {
|
|
1710
|
+
throw new ArcaValidationError("Debe proveer al menos un comprobante para realizar la rendici\xF3n del CAEA.");
|
|
1711
|
+
}
|
|
1712
|
+
const firstInvoice = params.invoices[0];
|
|
1713
|
+
const invoiceType = firstInvoice.invoiceType;
|
|
1714
|
+
const countReg = params.invoices.length;
|
|
1715
|
+
const invalidInvoices = params.invoices.filter((inv) => inv.invoiceType !== invoiceType);
|
|
1716
|
+
if (invalidInvoices.length > 0) {
|
|
1717
|
+
throw new ArcaValidationError("Todos los comprobantes en un mismo lote de rendici\xF3n informativa deben poseer el mismo tipo de factura (invoiceType).");
|
|
1718
|
+
}
|
|
1719
|
+
let detXml = "";
|
|
1720
|
+
params.invoices.forEach((inv) => {
|
|
1721
|
+
const date = inv.date || /* @__PURE__ */ new Date();
|
|
1722
|
+
const dateStr = date.toISOString().split("T")[0].replace(/-/g, "");
|
|
1723
|
+
let total = 0;
|
|
1724
|
+
let net = 0;
|
|
1725
|
+
let vat = 0;
|
|
1726
|
+
let vatXml = "";
|
|
1727
|
+
const includesVAT = inv.includesVAT || false;
|
|
1728
|
+
if (inv.items && inv.items.length > 0) {
|
|
1729
|
+
net = round(calculateSubtotal(inv.items, includesVAT));
|
|
1730
|
+
vat = round(calculateVAT(inv.items, includesVAT));
|
|
1731
|
+
total = round(calculateTotal(inv.items, includesVAT));
|
|
1732
|
+
const byRate = /* @__PURE__ */ new Map();
|
|
1733
|
+
inv.items.forEach((item) => {
|
|
1734
|
+
const rate = item.vatRate || 0;
|
|
1735
|
+
let netPrice = item.unitPrice;
|
|
1736
|
+
if (includesVAT && rate) {
|
|
1737
|
+
netPrice = item.unitPrice / (1 + rate / 100);
|
|
1738
|
+
}
|
|
1739
|
+
const base = item.quantity * netPrice;
|
|
1740
|
+
const amount = base * rate / 100;
|
|
1741
|
+
const current = byRate.get(rate) || { base: 0, amount: 0 };
|
|
1742
|
+
byRate.set(rate, {
|
|
1743
|
+
base: current.base + base,
|
|
1744
|
+
amount: current.amount + amount
|
|
1745
|
+
});
|
|
1746
|
+
});
|
|
1747
|
+
const vatEntries = Array.from(byRate.entries()).map(([rate, values]) => ({
|
|
1748
|
+
rate,
|
|
1749
|
+
taxBase: values.base,
|
|
1750
|
+
amount: values.amount
|
|
1751
|
+
}));
|
|
1752
|
+
if (vatEntries.length > 0) {
|
|
1753
|
+
vatXml = "<ar:Iva>";
|
|
1754
|
+
vatEntries.forEach((entry) => {
|
|
1755
|
+
vatXml += `
|
|
1756
|
+
<ar:AlicIva>
|
|
1757
|
+
<ar:Id>${this.getVATCode(entry.rate)}</ar:Id>
|
|
1758
|
+
<ar:BaseImp>${entry.taxBase.toFixed(2)}</ar:BaseImp>
|
|
1759
|
+
<ar:Importe>${entry.amount.toFixed(2)}</ar:Importe>
|
|
1760
|
+
</ar:AlicIva>`;
|
|
1761
|
+
});
|
|
1762
|
+
vatXml += "\n </ar:Iva>";
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
if (total <= 0) {
|
|
1766
|
+
throw new ArcaValidationError(`El monto total del comprobante n\xFAmero ${inv.invoiceNumber} debe ser mayor a 0.`);
|
|
1767
|
+
}
|
|
1768
|
+
const condicionIVAReceptorXml = inv.buyer?.vatCondition !== void 0 ? `
|
|
1769
|
+
<ar:CondicionIVAReceptorId>${inv.buyer.vatCondition}</ar:CondicionIVAReceptorId>` : "";
|
|
1770
|
+
let fechasServicioXml = "";
|
|
1771
|
+
if (inv.concept === 2 /* SERVICES */ || inv.concept === 3 /* PRODUCTS_AND_SERVICES */) {
|
|
1772
|
+
const defaultDateStr = date.toISOString().split("T")[0].replace(/-/g, "");
|
|
1773
|
+
fechasServicioXml = `
|
|
1774
|
+
<ar:FchServDesde>${defaultDateStr}</ar:FchServDesde>
|
|
1775
|
+
<ar:FchServHasta>${defaultDateStr}</ar:FchServHasta>
|
|
1776
|
+
<ar:FchVtoPago>${defaultDateStr}</ar:FchVtoPago>`;
|
|
1777
|
+
}
|
|
1778
|
+
let asocXml = "";
|
|
1779
|
+
if (inv.associatedInvoices && inv.associatedInvoices.length > 0) {
|
|
1780
|
+
asocXml = "<ar:CbtesAsoc>";
|
|
1781
|
+
inv.associatedInvoices.forEach((asoc) => {
|
|
1782
|
+
asocXml += `
|
|
1783
|
+
<ar:CbteAsoc>
|
|
1784
|
+
<ar:Tipo>${asoc.type}</ar:Tipo>
|
|
1785
|
+
<ar:PtoVta>${asoc.pointOfSale}</ar:PtoVta>
|
|
1786
|
+
<ar:Nro>${asoc.invoiceNumber}</ar:Nro>
|
|
1787
|
+
${asoc.cuit ? `<ar:Cuit>${asoc.cuit}</ar:Cuit>` : ""}
|
|
1788
|
+
${asoc.date ? `<ar:CbteFch>${asoc.date.toISOString().split("T")[0].replace(/-/g, "")}</ar:CbteFch>` : ""}
|
|
1789
|
+
</ar:CbteAsoc>`;
|
|
1790
|
+
});
|
|
1791
|
+
asocXml += "\n </ar:CbtesAsoc>";
|
|
1792
|
+
}
|
|
1793
|
+
let optXml = "";
|
|
1794
|
+
if (inv.optionals && inv.optionals.length > 0) {
|
|
1795
|
+
optXml = "<ar:Opcionales>";
|
|
1796
|
+
inv.optionals.forEach((opt) => {
|
|
1797
|
+
optXml += `
|
|
1798
|
+
<ar:Opcional>
|
|
1799
|
+
<ar:Id>${opt.id}</ar:Id>
|
|
1800
|
+
<ar:Valor>${opt.value}</ar:Valor>
|
|
1801
|
+
</ar:Opcional>`;
|
|
1802
|
+
});
|
|
1803
|
+
optXml += "\n </ar:Opcionales>";
|
|
1804
|
+
}
|
|
1805
|
+
detXml += `
|
|
1806
|
+
<ar:FECAEADetRequest>
|
|
1807
|
+
<ar:Concepto>${inv.concept}</ar:Concepto>
|
|
1808
|
+
<ar:DocTipo>${inv.buyer?.docType || 99}</ar:DocTipo>
|
|
1809
|
+
<ar:DocNro>${inv.buyer?.docNumber || 0}</ar:DocNro>${condicionIVAReceptorXml}
|
|
1810
|
+
<ar:CbteDesde>${inv.invoiceNumber}</ar:CbteDesde>
|
|
1811
|
+
<ar:CbteHasta>${inv.invoiceNumber}</ar:CbteHasta>
|
|
1812
|
+
<ar:CbteFch>${dateStr}</ar:CbteFch>
|
|
1813
|
+
<ar:ImpTotal>${total.toFixed(2)}</ar:ImpTotal>
|
|
1814
|
+
<ar:ImpTotConc>0.00</ar:ImpTotConc>
|
|
1815
|
+
<ar:ImpNeto>${net.toFixed(2)}</ar:ImpNeto>
|
|
1816
|
+
<ar:ImpOpEx>0.00</ar:ImpOpEx>
|
|
1817
|
+
<ar:ImpIVA>${vat.toFixed(2)}</ar:ImpIVA>
|
|
1818
|
+
<ar:ImpTrib>0.00</ar:ImpTrib>
|
|
1819
|
+
<ar:MonId>PES</ar:MonId>
|
|
1820
|
+
<ar:MonCotiz>1</ar:MonCotiz>
|
|
1821
|
+
<ar:CAEA>${params.caea}</ar:CAEA>${fechasServicioXml}
|
|
1822
|
+
${asocXml}
|
|
1823
|
+
${vatXml}
|
|
1824
|
+
${optXml}
|
|
1825
|
+
</ar:FECAEADetRequest>`;
|
|
1826
|
+
});
|
|
1827
|
+
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1828
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
1829
|
+
xmlns:ar="http://ar.gov.afip.dif.FEV1/">
|
|
1830
|
+
<soapenv:Header/>
|
|
1831
|
+
<soapenv:Body>
|
|
1832
|
+
<ar:FECAEARegInformativo>
|
|
1833
|
+
<ar:Auth>
|
|
1834
|
+
<ar:Token>${this.config.ticket.token}</ar:Token>
|
|
1835
|
+
<ar:Sign>${this.config.ticket.sign}</ar:Sign>
|
|
1836
|
+
<ar:Cuit>${this.config.cuit}</ar:Cuit>
|
|
1837
|
+
</ar:Auth>
|
|
1838
|
+
<ar:FeCAEARegInfReq>
|
|
1839
|
+
<ar:FeCabReq>
|
|
1840
|
+
<ar:CantReg>${countReg}</ar:CantReg>
|
|
1841
|
+
<ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
|
|
1842
|
+
<ar:CbteTipo>${invoiceType}</ar:CbteTipo>
|
|
1843
|
+
</ar:FeCabReq>
|
|
1844
|
+
<ar:FeDetReq>${detXml}
|
|
1845
|
+
</ar:FeDetReq>
|
|
1846
|
+
</ar:FeCAEARegInfReq>
|
|
1847
|
+
</ar:FECAEARegInformativo>
|
|
1848
|
+
</soapenv:Body>
|
|
1849
|
+
</soapenv:Envelope>`;
|
|
1850
|
+
const endpoint = getWsfeEndpoint(this.config.environment);
|
|
1851
|
+
const response = await callArcaApi(endpoint, {
|
|
1852
|
+
method: "POST",
|
|
1853
|
+
headers: {
|
|
1854
|
+
"Content-Type": "text/xml; charset=utf-8",
|
|
1855
|
+
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEARegInformativo"
|
|
1856
|
+
},
|
|
1857
|
+
body: soapRequest,
|
|
1858
|
+
timeout: this.config.timeout
|
|
1859
|
+
});
|
|
1860
|
+
if (!response.ok) {
|
|
1861
|
+
throw new ArcaError(`Error HTTP al rendir CAEA: ${response.status}`, "HTTP_ERROR");
|
|
1862
|
+
}
|
|
1863
|
+
const responseXml = await response.text();
|
|
1864
|
+
const result = parseXml(responseXml);
|
|
1865
|
+
const data = result?.Envelope?.Body?.FECAEARegInformativoResponse?.FECAEARegInformativoResult;
|
|
1866
|
+
if (!data) {
|
|
1867
|
+
throw new ArcaError("Respuesta FECAEARegInformativo inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
|
|
1868
|
+
}
|
|
1869
|
+
if (data.Errors) {
|
|
1870
|
+
const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
|
|
1871
|
+
const code = error?.Code || "UNKNOWN";
|
|
1872
|
+
throw new ArcaError(
|
|
1873
|
+
`Error ARCA: ${error?.Msg || "Error desconocido"}`,
|
|
1874
|
+
"ARCA_ERROR",
|
|
1875
|
+
data.Errors,
|
|
1876
|
+
getArcaHint(code)
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
const cab = data.FeCabResp;
|
|
1880
|
+
const det = Array.isArray(data.FeDetResp.FECAEDetResponse) ? data.FeDetResp.FECAEDetResponse[0] : data.FeDetResp.FECAEDetResponse;
|
|
1881
|
+
const observations = [];
|
|
1882
|
+
if (det?.Observaciones) {
|
|
1883
|
+
const obsArray = Array.isArray(det.Observaciones.Obs) ? det.Observaciones.Obs : [det.Observaciones.Obs];
|
|
1884
|
+
obsArray.forEach((o) => observations.push(o.Msg));
|
|
1885
|
+
}
|
|
1886
|
+
return {
|
|
1887
|
+
caea: String(det.CAEA || params.caea),
|
|
1888
|
+
result: cab.Resultado,
|
|
1889
|
+
pointOfSale: Number(cab.PtoVta),
|
|
1890
|
+
invoiceType: Number(cab.CbteTipo),
|
|
1891
|
+
observations: observations.length > 0 ? observations : void 0
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Informa que un CAEA no tuvo movimientos en la quincena (FECAEASinMovimientoInformar).
|
|
1896
|
+
*/
|
|
1897
|
+
async reportCAEANoMovement(params) {
|
|
1898
|
+
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1899
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
1900
|
+
xmlns:ar="http://ar.gov.afip.dif.FEV1/">
|
|
1901
|
+
<soapenv:Header/>
|
|
1902
|
+
<soapenv:Body>
|
|
1903
|
+
<ar:FECAEASinMovimientoInformar>
|
|
1904
|
+
<ar:Auth>
|
|
1905
|
+
<ar:Token>${this.config.ticket.token}</ar:Token>
|
|
1906
|
+
<ar:Sign>${this.config.ticket.sign}</ar:Sign>
|
|
1907
|
+
<ar:Cuit>${this.config.cuit}</ar:Cuit>
|
|
1908
|
+
</ar:Auth>
|
|
1909
|
+
<ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
|
|
1910
|
+
<ar:Caea>${params.caea}</ar:Caea>
|
|
1911
|
+
</ar:FECAEASinMovimientoInformar>
|
|
1912
|
+
</soapenv:Body>
|
|
1913
|
+
</soapenv:Envelope>`;
|
|
1914
|
+
const endpoint = getWsfeEndpoint(this.config.environment);
|
|
1915
|
+
const response = await callArcaApi(endpoint, {
|
|
1916
|
+
method: "POST",
|
|
1917
|
+
headers: {
|
|
1918
|
+
"Content-Type": "text/xml; charset=utf-8",
|
|
1919
|
+
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASinMovimientoInformar"
|
|
1920
|
+
},
|
|
1921
|
+
body: soapRequest,
|
|
1922
|
+
timeout: this.config.timeout
|
|
1923
|
+
});
|
|
1924
|
+
if (!response.ok) {
|
|
1925
|
+
throw new ArcaError(`Error HTTP al informar CAEA sin movimientos: ${response.status}`, "HTTP_ERROR");
|
|
1926
|
+
}
|
|
1927
|
+
const responseXml = await response.text();
|
|
1928
|
+
const result = parseXml(responseXml);
|
|
1929
|
+
const data = result?.Envelope?.Body?.FECAEASinMovimientoInformarResponse?.FECAEASinMovimientoInformarResult;
|
|
1930
|
+
if (!data) {
|
|
1931
|
+
throw new ArcaError("Respuesta FECAEASinMovimientoInformar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
|
|
1932
|
+
}
|
|
1933
|
+
if (data.Errors) {
|
|
1934
|
+
const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
|
|
1935
|
+
const code = error?.Code || "UNKNOWN";
|
|
1936
|
+
throw new ArcaError(
|
|
1937
|
+
`Error ARCA: ${error?.Msg || "Error desconocido"}`,
|
|
1938
|
+
"ARCA_ERROR",
|
|
1939
|
+
data.Errors,
|
|
1940
|
+
getArcaHint(code)
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Consulta si se informó la falta de movimientos de un CAEA (FECAEASinMovimientoConsultar).
|
|
1946
|
+
*/
|
|
1947
|
+
async getCAEANoMovement(caea) {
|
|
1948
|
+
const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1949
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
1950
|
+
xmlns:ar="http://ar.gov.afip.dif.FEV1/">
|
|
1951
|
+
<soapenv:Header/>
|
|
1952
|
+
<soapenv:Body>
|
|
1953
|
+
<ar:FECAEASinMovimientoConsultar>
|
|
1954
|
+
<ar:Auth>
|
|
1955
|
+
<ar:Token>${this.config.ticket.token}</ar:Token>
|
|
1956
|
+
<ar:Sign>${this.config.ticket.sign}</ar:Sign>
|
|
1957
|
+
<ar:Cuit>${this.config.cuit}</ar:Cuit>
|
|
1958
|
+
</ar:Auth>
|
|
1959
|
+
<ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
|
|
1960
|
+
<ar:Caea>${caea}</ar:Caea>
|
|
1961
|
+
</ar:FECAEASinMovimientoConsultar>
|
|
1962
|
+
</soapenv:Body>
|
|
1963
|
+
</soapenv:Envelope>`;
|
|
1964
|
+
const endpoint = getWsfeEndpoint(this.config.environment);
|
|
1965
|
+
const response = await callArcaApi(endpoint, {
|
|
1966
|
+
method: "POST",
|
|
1967
|
+
headers: {
|
|
1968
|
+
"Content-Type": "text/xml; charset=utf-8",
|
|
1969
|
+
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASinMovimientoConsultar"
|
|
1970
|
+
},
|
|
1971
|
+
body: soapRequest,
|
|
1972
|
+
timeout: this.config.timeout
|
|
1973
|
+
});
|
|
1974
|
+
if (!response.ok) {
|
|
1975
|
+
throw new ArcaError(`Error HTTP al consultar CAEA sin movimientos: ${response.status}`, "HTTP_ERROR");
|
|
1976
|
+
}
|
|
1977
|
+
const responseXml = await response.text();
|
|
1978
|
+
const result = parseXml(responseXml);
|
|
1979
|
+
const data = result?.Envelope?.Body?.FECAEASinMovimientoConsultarResponse?.FECAEASinMovimientoConsultarResult;
|
|
1980
|
+
if (!data) {
|
|
1981
|
+
throw new ArcaError("Respuesta FECAEASinMovimientoConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
|
|
1982
|
+
}
|
|
1983
|
+
if (data.Errors) {
|
|
1984
|
+
const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
|
|
1985
|
+
const code = error?.Code || "UNKNOWN";
|
|
1986
|
+
throw new ArcaError(
|
|
1987
|
+
`Error ARCA: ${error?.Msg || "Error desconocido"}`,
|
|
1988
|
+
"ARCA_ERROR",
|
|
1989
|
+
data.Errors,
|
|
1990
|
+
getArcaHint(code)
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
return data.ResultGet?.FECAEASinMov || null;
|
|
1994
|
+
}
|
|
1995
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1996
|
+
// Métodos internos auxiliares
|
|
1997
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1998
|
+
/**
|
|
1999
|
+
* Mapea alícuotas numéricas a los códigos internos de ARCA
|
|
2000
|
+
*/
|
|
2001
|
+
getVATCode(rate) {
|
|
2002
|
+
switch (rate) {
|
|
2003
|
+
case 0:
|
|
2004
|
+
return 3;
|
|
2005
|
+
// 0% / Exento / No gravado (código 3 en catálogo ARCA)
|
|
2006
|
+
case 10.5:
|
|
2007
|
+
return 4;
|
|
2008
|
+
case 21:
|
|
2009
|
+
return 5;
|
|
2010
|
+
case 27:
|
|
2011
|
+
return 6;
|
|
2012
|
+
case 5:
|
|
2013
|
+
return 8;
|
|
2014
|
+
case 2.5:
|
|
2015
|
+
return 9;
|
|
2016
|
+
default:
|
|
2017
|
+
throw new ArcaValidationError(`Al\xEDcuota IVA no soportada por ARCA: ${rate}%`, {
|
|
2018
|
+
supportedRates: [0, 10.5, 21, 27, 5, 2.5]
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
1524
2023
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1525
2024
|
0 && (module.exports = {
|
|
1526
2025
|
ArcaAuthError,
|
|
@@ -1528,6 +2027,7 @@ var PadronService = class {
|
|
|
1528
2027
|
ArcaNetworkError,
|
|
1529
2028
|
ArcaValidationError,
|
|
1530
2029
|
BillingConcept,
|
|
2030
|
+
CaeaService,
|
|
1531
2031
|
InvoiceType,
|
|
1532
2032
|
PadronService,
|
|
1533
2033
|
TaxIdType,
|