arca-sdk 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -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,
@@ -291,6 +292,18 @@ var TicketManager = class {
291
292
 
292
293
  // src/utils/network.ts
293
294
  var import_https = __toESM(require("https"), 1);
295
+ var import_tls = __toESM(require("tls"), 1);
296
+ function checkArcaServerIdentity(hostname, cert) {
297
+ const error = import_tls.default.checkServerIdentity(hostname, cert);
298
+ if (error && hostname.endsWith("arca.gob.ar")) {
299
+ const fallbackHost = hostname.replace("arca.gob.ar", "afip.gov.ar");
300
+ const retryError = import_tls.default.checkServerIdentity(fallbackHost, cert);
301
+ if (!retryError) {
302
+ return void 0;
303
+ }
304
+ }
305
+ return error;
306
+ }
294
307
  async function callArcaApi(url, options) {
295
308
  const timeout = options.timeout || 15e3;
296
309
  const isNode = typeof process !== "undefined" && process.versions && process.versions.node;
@@ -306,7 +319,8 @@ async function callArcaApi(url, options) {
306
319
  minVersion: "TLSv1",
307
320
  // @ts-ignore - Propiedad específica para mitigar "dh key too small" en Node 18+
308
321
  minDHSize: 1024,
309
- rejectUnauthorized: true
322
+ rejectUnauthorized: true,
323
+ checkServerIdentity: checkArcaServerIdentity
310
324
  });
311
325
  const reqOptions = {
312
326
  method: options.method,
@@ -506,23 +520,23 @@ var WsaaService = class {
506
520
  };
507
521
 
508
522
  // src/types/wsfe.ts
509
- var InvoiceType = /* @__PURE__ */ ((InvoiceType2) => {
510
- InvoiceType2[InvoiceType2["FACTURA_A"] = 1] = "FACTURA_A";
511
- InvoiceType2[InvoiceType2["NOTA_DEBITO_A"] = 2] = "NOTA_DEBITO_A";
512
- InvoiceType2[InvoiceType2["NOTA_CREDITO_A"] = 3] = "NOTA_CREDITO_A";
513
- InvoiceType2[InvoiceType2["RECIBO_A"] = 4] = "RECIBO_A";
514
- InvoiceType2[InvoiceType2["FACTURA_B"] = 6] = "FACTURA_B";
515
- InvoiceType2[InvoiceType2["NOTA_DEBITO_B"] = 7] = "NOTA_DEBITO_B";
516
- InvoiceType2[InvoiceType2["NOTA_CREDITO_B"] = 8] = "NOTA_CREDITO_B";
517
- InvoiceType2[InvoiceType2["RECIBO_B"] = 9] = "RECIBO_B";
518
- InvoiceType2[InvoiceType2["FACTURA_C"] = 11] = "FACTURA_C";
519
- InvoiceType2[InvoiceType2["NOTA_DEBITO_C"] = 12] = "NOTA_DEBITO_C";
520
- InvoiceType2[InvoiceType2["NOTA_CREDITO_C"] = 13] = "NOTA_CREDITO_C";
521
- InvoiceType2[InvoiceType2["RECIBO_C"] = 15] = "RECIBO_C";
522
- InvoiceType2[InvoiceType2["TICKET_A"] = 81] = "TICKET_A";
523
- InvoiceType2[InvoiceType2["TICKET_B"] = 82] = "TICKET_B";
524
- InvoiceType2[InvoiceType2["TICKET_C"] = 83] = "TICKET_C";
525
- return InvoiceType2;
523
+ var InvoiceType = /* @__PURE__ */ ((InvoiceType3) => {
524
+ InvoiceType3[InvoiceType3["FACTURA_A"] = 1] = "FACTURA_A";
525
+ InvoiceType3[InvoiceType3["NOTA_DEBITO_A"] = 2] = "NOTA_DEBITO_A";
526
+ InvoiceType3[InvoiceType3["NOTA_CREDITO_A"] = 3] = "NOTA_CREDITO_A";
527
+ InvoiceType3[InvoiceType3["RECIBO_A"] = 4] = "RECIBO_A";
528
+ InvoiceType3[InvoiceType3["FACTURA_B"] = 6] = "FACTURA_B";
529
+ InvoiceType3[InvoiceType3["NOTA_DEBITO_B"] = 7] = "NOTA_DEBITO_B";
530
+ InvoiceType3[InvoiceType3["NOTA_CREDITO_B"] = 8] = "NOTA_CREDITO_B";
531
+ InvoiceType3[InvoiceType3["RECIBO_B"] = 9] = "RECIBO_B";
532
+ InvoiceType3[InvoiceType3["FACTURA_C"] = 11] = "FACTURA_C";
533
+ InvoiceType3[InvoiceType3["NOTA_DEBITO_C"] = 12] = "NOTA_DEBITO_C";
534
+ InvoiceType3[InvoiceType3["NOTA_CREDITO_C"] = 13] = "NOTA_CREDITO_C";
535
+ InvoiceType3[InvoiceType3["RECIBO_C"] = 15] = "RECIBO_C";
536
+ InvoiceType3[InvoiceType3["TICKET_A"] = 81] = "TICKET_A";
537
+ InvoiceType3[InvoiceType3["TICKET_B"] = 82] = "TICKET_B";
538
+ InvoiceType3[InvoiceType3["TICKET_C"] = 83] = "TICKET_C";
539
+ return InvoiceType3;
526
540
  })(InvoiceType || {});
527
541
  var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
528
542
  BillingConcept2[BillingConcept2["PRODUCTS"] = 1] = "PRODUCTS";
@@ -530,19 +544,19 @@ var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
530
544
  BillingConcept2[BillingConcept2["PRODUCTS_AND_SERVICES"] = 3] = "PRODUCTS_AND_SERVICES";
531
545
  return BillingConcept2;
532
546
  })(BillingConcept || {});
533
- var TaxIdType = /* @__PURE__ */ ((TaxIdType2) => {
534
- TaxIdType2[TaxIdType2["CUIT"] = 80] = "CUIT";
535
- TaxIdType2[TaxIdType2["CUIL"] = 86] = "CUIL";
536
- TaxIdType2[TaxIdType2["CDI"] = 87] = "CDI";
537
- TaxIdType2[TaxIdType2["LE"] = 89] = "LE";
538
- TaxIdType2[TaxIdType2["LC"] = 90] = "LC";
539
- TaxIdType2[TaxIdType2["FOREIGN_ID"] = 91] = "FOREIGN_ID";
540
- TaxIdType2[TaxIdType2["PASSPORT"] = 94] = "PASSPORT";
541
- TaxIdType2[TaxIdType2["BUENOS_AIRES_ID"] = 95] = "BUENOS_AIRES_ID";
542
- TaxIdType2[TaxIdType2["NATIONAL_POLICE_ID"] = 96] = "NATIONAL_POLICE_ID";
543
- TaxIdType2[TaxIdType2["DNI"] = 96] = "DNI";
544
- TaxIdType2[TaxIdType2["FINAL_CONSUMER"] = 99] = "FINAL_CONSUMER";
545
- return TaxIdType2;
547
+ var TaxIdType = /* @__PURE__ */ ((TaxIdType3) => {
548
+ TaxIdType3[TaxIdType3["CUIT"] = 80] = "CUIT";
549
+ TaxIdType3[TaxIdType3["CUIL"] = 86] = "CUIL";
550
+ TaxIdType3[TaxIdType3["CDI"] = 87] = "CDI";
551
+ TaxIdType3[TaxIdType3["LE"] = 89] = "LE";
552
+ TaxIdType3[TaxIdType3["LC"] = 90] = "LC";
553
+ TaxIdType3[TaxIdType3["FOREIGN_ID"] = 91] = "FOREIGN_ID";
554
+ TaxIdType3[TaxIdType3["PASSPORT"] = 94] = "PASSPORT";
555
+ TaxIdType3[TaxIdType3["BUENOS_AIRES_ID"] = 95] = "BUENOS_AIRES_ID";
556
+ TaxIdType3[TaxIdType3["NATIONAL_POLICE_ID"] = 96] = "NATIONAL_POLICE_ID";
557
+ TaxIdType3[TaxIdType3["DNI"] = 96] = "DNI";
558
+ TaxIdType3[TaxIdType3["FINAL_CONSUMER"] = 99] = "FINAL_CONSUMER";
559
+ return TaxIdType3;
546
560
  })(TaxIdType || {});
547
561
  var VatCondition = /* @__PURE__ */ ((VatCondition2) => {
548
562
  VatCondition2[VatCondition2["IVA_RESPONSABLE_INSCRIPTO"] = 1] = "IVA_RESPONSABLE_INSCRIPTO";
@@ -1054,7 +1068,6 @@ var WsfeService = class _WsfeService {
1054
1068
  * Método genérico interno para emitir cualquier tipo de comprobante.
1055
1069
  */
1056
1070
  async issueDocument(request) {
1057
- const invoiceNumber = await this.getNextInvoiceNumber(request.type);
1058
1071
  let total = request.total || 0;
1059
1072
  let net = total;
1060
1073
  let vat = 0;
@@ -1067,6 +1080,8 @@ var WsfeService = class _WsfeService {
1067
1080
  if (total <= 0) {
1068
1081
  throw new ArcaValidationError("El monto total debe ser mayor a 0");
1069
1082
  }
1083
+ this.validateBuyer(request.buyer, total);
1084
+ const invoiceNumber = await this.getNextInvoiceNumber(request.type);
1070
1085
  const soapRequest = this.buildCAERequest({
1071
1086
  type: request.type,
1072
1087
  pointOfSale: this.config.pointOfSale,
@@ -1160,6 +1175,23 @@ var WsfeService = class _WsfeService {
1160
1175
  );
1161
1176
  }
1162
1177
  }
1178
+ /**
1179
+ * Valida obligatoriamente al comprador cuando el monto es mayor o igual a $10.000.000 para consumidor final
1180
+ */
1181
+ validateBuyer(buyer, total) {
1182
+ const isFinalConsumer = !buyer || buyer.docType === 99 /* FINAL_CONSUMER */ || buyer.docNumber === "0" || buyer.docNumber === "";
1183
+ if (total >= 1e7 && isFinalConsumer) {
1184
+ const errorMessage = "Para importes mayores o iguales a $10.000.000 es obligatorio identificar al comprador (RG 5824/2026).";
1185
+ if (process.env.NODE_ENV !== "production") {
1186
+ console.warn(`[arca-sdk WARNING] ${errorMessage}`);
1187
+ }
1188
+ throw new ArcaValidationError(errorMessage, {
1189
+ total,
1190
+ buyer,
1191
+ hint: "Actualiz\xE1 el objeto buyer con un tipo de documento v\xE1lido (DNI, CUIT, CUIL) y su n\xFAmero correspondiente."
1192
+ });
1193
+ }
1194
+ }
1163
1195
  /**
1164
1196
  * Calcula el IVA agrupado por alícuota (requerido por ARCA para Factura A/B)
1165
1197
  */
@@ -1459,8 +1491,10 @@ var PadronService = class {
1459
1491
  mainActivity: p.descripcionActividadPrincipal,
1460
1492
  isVATRegistered: this.hasTaxId(p, 30),
1461
1493
  // 30 = IVA
1462
- isMonotax: this.hasTaxId(p, 20),
1463
- // 20 = Monotributo
1494
+ isMonotax: this.hasTaxId(p, 20) || this.hasTaxId(p, 24) || this.hasTaxId(p, 21),
1495
+ // General o Social/Autónomo
1496
+ isSocialMonotax: this.hasTaxId(p, 24) || this.hasTaxId(p, 21),
1497
+ // 24 = Obra Social / Promovido, 21 = Autónomo
1464
1498
  isVATExempt: this.hasTaxId(p, 32)
1465
1499
  // 32 = IVA Exento
1466
1500
  };
@@ -1508,6 +1542,469 @@ var PadronService = class {
1508
1542
  return [data];
1509
1543
  }
1510
1544
  };
1545
+
1546
+ // src/services/caea.ts
1547
+ var CaeaService = class {
1548
+ config;
1549
+ constructor(config) {
1550
+ this.validateConfig(config);
1551
+ this.config = config;
1552
+ }
1553
+ validateConfig(config) {
1554
+ if (!config.ticket || !config.ticket.token) {
1555
+ throw new ArcaValidationError(
1556
+ "Ticket WSAA requerido. Ejecut\xE1 wsaa.login() primero.",
1557
+ { hint: "El ticket se obtiene del servicio WsaaService" }
1558
+ );
1559
+ }
1560
+ if (!config.pointOfSale || config.pointOfSale < 1 || config.pointOfSale > 9999) {
1561
+ throw new ArcaValidationError(
1562
+ "Punto de venta inv\xE1lido: debe ser un n\xFAmero entre 1 y 9999",
1563
+ { pointOfSale: config.pointOfSale }
1564
+ );
1565
+ }
1566
+ }
1567
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1568
+ // Métodos del Ciclo de Vida de CAEA
1569
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1570
+ /**
1571
+ * Solicita un CAEA (Código de Autorización Electrónico Anticipado) para una quincena específica (FECAEASolicitar).
1572
+ */
1573
+ async solicitCAEA(params) {
1574
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1575
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1576
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1577
+ <soapenv:Header/>
1578
+ <soapenv:Body>
1579
+ <ar:FECAEASolicitar>
1580
+ <ar:Auth>
1581
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1582
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1583
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1584
+ </ar:Auth>
1585
+ <ar:Periodo>${params.period}</ar:Periodo>
1586
+ <ar:Orden>${params.order}</ar:Orden>
1587
+ </ar:FECAEASolicitar>
1588
+ </soapenv:Body>
1589
+ </soapenv:Envelope>`;
1590
+ const endpoint = getWsfeEndpoint(this.config.environment);
1591
+ const response = await callArcaApi(endpoint, {
1592
+ method: "POST",
1593
+ headers: {
1594
+ "Content-Type": "text/xml; charset=utf-8",
1595
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASolicitar"
1596
+ },
1597
+ body: soapRequest,
1598
+ timeout: this.config.timeout
1599
+ });
1600
+ if (!response.ok) {
1601
+ throw new ArcaError(`Error HTTP al solicitar CAEA: ${response.status}`, "HTTP_ERROR");
1602
+ }
1603
+ const responseXml = await response.text();
1604
+ const result = parseXml(responseXml);
1605
+ const data = result?.Envelope?.Body?.FECAEASolicitarResponse?.FECAEASolicitarResult;
1606
+ if (!data) {
1607
+ throw new ArcaError("Respuesta FECAEASolicitar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1608
+ }
1609
+ if (data.Errors) {
1610
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1611
+ const code = error?.Code || "UNKNOWN";
1612
+ throw new ArcaError(
1613
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1614
+ "ARCA_ERROR",
1615
+ data.Errors,
1616
+ getArcaHint(code)
1617
+ );
1618
+ }
1619
+ const res = data.ResultGet;
1620
+ return {
1621
+ caea: String(res.CAEA),
1622
+ period: Number(res.Periodo),
1623
+ order: Number(res.Orden),
1624
+ expiryDate: String(res.FchVto),
1625
+ actualDate: String(res.FchVigDesde),
1626
+ receptionDate: String(res.FchVigHasta),
1627
+ limitDate: String(res.FchTopeInf)
1628
+ };
1629
+ }
1630
+ /**
1631
+ * Consulta un CAEA ya emitido (FECAEAConsultar).
1632
+ */
1633
+ async getCAEA(caea) {
1634
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1635
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1636
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1637
+ <soapenv:Header/>
1638
+ <soapenv:Body>
1639
+ <ar:FECAEAConsultar>
1640
+ <ar:Auth>
1641
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1642
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1643
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1644
+ </ar:Auth>
1645
+ <ar:Caea>${caea}</ar:Caea>
1646
+ </ar:FECAEAConsultar>
1647
+ </soapenv:Body>
1648
+ </soapenv:Envelope>`;
1649
+ const endpoint = getWsfeEndpoint(this.config.environment);
1650
+ const response = await callArcaApi(endpoint, {
1651
+ method: "POST",
1652
+ headers: {
1653
+ "Content-Type": "text/xml; charset=utf-8",
1654
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEAConsultar"
1655
+ },
1656
+ body: soapRequest,
1657
+ timeout: this.config.timeout
1658
+ });
1659
+ if (!response.ok) {
1660
+ throw new ArcaError(`Error HTTP al consultar CAEA: ${response.status}`, "HTTP_ERROR");
1661
+ }
1662
+ const responseXml = await response.text();
1663
+ const result = parseXml(responseXml);
1664
+ const data = result?.Envelope?.Body?.FECAEAConsultarResponse?.FECAEAConsultarResult;
1665
+ if (!data) {
1666
+ throw new ArcaError("Respuesta FECAEAConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1667
+ }
1668
+ if (data.Errors) {
1669
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1670
+ const code = error?.Code || "UNKNOWN";
1671
+ throw new ArcaError(
1672
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1673
+ "ARCA_ERROR",
1674
+ data.Errors,
1675
+ getArcaHint(code)
1676
+ );
1677
+ }
1678
+ const res = data.ResultGet;
1679
+ return {
1680
+ caea: String(res.CAEA),
1681
+ period: Number(res.Periodo),
1682
+ order: Number(res.Orden),
1683
+ expiryDate: String(res.FchVto),
1684
+ actualDate: String(res.FchVigDesde),
1685
+ receptionDate: String(res.FchVigHasta),
1686
+ limitDate: String(res.FchTopeInf)
1687
+ };
1688
+ }
1689
+ /**
1690
+ * Rinde/registra informativamente los comprobantes emitidos en contingencia local bajo un CAEA específico (FECAEARegInformativo).
1691
+ * Soporta el envío en lotes de comprobantes homogéneos del mismo tipo de factura y punto de venta.
1692
+ */
1693
+ async reportCAEAPeriod(params) {
1694
+ if (!params.invoices || params.invoices.length === 0) {
1695
+ throw new ArcaValidationError("Debe proveer al menos un comprobante para realizar la rendici\xF3n del CAEA.");
1696
+ }
1697
+ const firstInvoice = params.invoices[0];
1698
+ const invoiceType = firstInvoice.invoiceType;
1699
+ const countReg = params.invoices.length;
1700
+ const invalidInvoices = params.invoices.filter((inv) => inv.invoiceType !== invoiceType);
1701
+ if (invalidInvoices.length > 0) {
1702
+ throw new ArcaValidationError("Todos los comprobantes en un mismo lote de rendici\xF3n informativa deben poseer el mismo tipo de factura (invoiceType).");
1703
+ }
1704
+ let detXml = "";
1705
+ params.invoices.forEach((inv) => {
1706
+ const date = inv.date || /* @__PURE__ */ new Date();
1707
+ const dateStr = date.toISOString().split("T")[0].replace(/-/g, "");
1708
+ let total = 0;
1709
+ let net = 0;
1710
+ let vat = 0;
1711
+ let vatXml = "";
1712
+ const includesVAT = inv.includesVAT || false;
1713
+ if (inv.items && inv.items.length > 0) {
1714
+ net = round(calculateSubtotal(inv.items, includesVAT));
1715
+ vat = round(calculateVAT(inv.items, includesVAT));
1716
+ total = round(calculateTotal(inv.items, includesVAT));
1717
+ const byRate = /* @__PURE__ */ new Map();
1718
+ inv.items.forEach((item) => {
1719
+ const rate = item.vatRate || 0;
1720
+ let netPrice = item.unitPrice;
1721
+ if (includesVAT && rate) {
1722
+ netPrice = item.unitPrice / (1 + rate / 100);
1723
+ }
1724
+ const base = item.quantity * netPrice;
1725
+ const amount = base * rate / 100;
1726
+ const current = byRate.get(rate) || { base: 0, amount: 0 };
1727
+ byRate.set(rate, {
1728
+ base: current.base + base,
1729
+ amount: current.amount + amount
1730
+ });
1731
+ });
1732
+ const vatEntries = Array.from(byRate.entries()).map(([rate, values]) => ({
1733
+ rate,
1734
+ taxBase: values.base,
1735
+ amount: values.amount
1736
+ }));
1737
+ if (vatEntries.length > 0) {
1738
+ vatXml = "<ar:Iva>";
1739
+ vatEntries.forEach((entry) => {
1740
+ vatXml += `
1741
+ <ar:AlicIva>
1742
+ <ar:Id>${this.getVATCode(entry.rate)}</ar:Id>
1743
+ <ar:BaseImp>${entry.taxBase.toFixed(2)}</ar:BaseImp>
1744
+ <ar:Importe>${entry.amount.toFixed(2)}</ar:Importe>
1745
+ </ar:AlicIva>`;
1746
+ });
1747
+ vatXml += "\n </ar:Iva>";
1748
+ }
1749
+ }
1750
+ if (total <= 0) {
1751
+ throw new ArcaValidationError(`El monto total del comprobante n\xFAmero ${inv.invoiceNumber} debe ser mayor a 0.`);
1752
+ }
1753
+ const condicionIVAReceptorXml = inv.buyer?.vatCondition !== void 0 ? `
1754
+ <ar:CondicionIVAReceptorId>${inv.buyer.vatCondition}</ar:CondicionIVAReceptorId>` : "";
1755
+ let fechasServicioXml = "";
1756
+ if (inv.concept === 2 /* SERVICES */ || inv.concept === 3 /* PRODUCTS_AND_SERVICES */) {
1757
+ const defaultDateStr = date.toISOString().split("T")[0].replace(/-/g, "");
1758
+ fechasServicioXml = `
1759
+ <ar:FchServDesde>${defaultDateStr}</ar:FchServDesde>
1760
+ <ar:FchServHasta>${defaultDateStr}</ar:FchServHasta>
1761
+ <ar:FchVtoPago>${defaultDateStr}</ar:FchVtoPago>`;
1762
+ }
1763
+ let asocXml = "";
1764
+ if (inv.associatedInvoices && inv.associatedInvoices.length > 0) {
1765
+ asocXml = "<ar:CbtesAsoc>";
1766
+ inv.associatedInvoices.forEach((asoc) => {
1767
+ asocXml += `
1768
+ <ar:CbteAsoc>
1769
+ <ar:Tipo>${asoc.type}</ar:Tipo>
1770
+ <ar:PtoVta>${asoc.pointOfSale}</ar:PtoVta>
1771
+ <ar:Nro>${asoc.invoiceNumber}</ar:Nro>
1772
+ ${asoc.cuit ? `<ar:Cuit>${asoc.cuit}</ar:Cuit>` : ""}
1773
+ ${asoc.date ? `<ar:CbteFch>${asoc.date.toISOString().split("T")[0].replace(/-/g, "")}</ar:CbteFch>` : ""}
1774
+ </ar:CbteAsoc>`;
1775
+ });
1776
+ asocXml += "\n </ar:CbtesAsoc>";
1777
+ }
1778
+ let optXml = "";
1779
+ if (inv.optionals && inv.optionals.length > 0) {
1780
+ optXml = "<ar:Opcionales>";
1781
+ inv.optionals.forEach((opt) => {
1782
+ optXml += `
1783
+ <ar:Opcional>
1784
+ <ar:Id>${opt.id}</ar:Id>
1785
+ <ar:Valor>${opt.value}</ar:Valor>
1786
+ </ar:Opcional>`;
1787
+ });
1788
+ optXml += "\n </ar:Opcionales>";
1789
+ }
1790
+ detXml += `
1791
+ <ar:FECAEADetRequest>
1792
+ <ar:Concepto>${inv.concept}</ar:Concepto>
1793
+ <ar:DocTipo>${inv.buyer?.docType || 99}</ar:DocTipo>
1794
+ <ar:DocNro>${inv.buyer?.docNumber || 0}</ar:DocNro>${condicionIVAReceptorXml}
1795
+ <ar:CbteDesde>${inv.invoiceNumber}</ar:CbteDesde>
1796
+ <ar:CbteHasta>${inv.invoiceNumber}</ar:CbteHasta>
1797
+ <ar:CbteFch>${dateStr}</ar:CbteFch>
1798
+ <ar:ImpTotal>${total.toFixed(2)}</ar:ImpTotal>
1799
+ <ar:ImpTotConc>0.00</ar:ImpTotConc>
1800
+ <ar:ImpNeto>${net.toFixed(2)}</ar:ImpNeto>
1801
+ <ar:ImpOpEx>0.00</ar:ImpOpEx>
1802
+ <ar:ImpIVA>${vat.toFixed(2)}</ar:ImpIVA>
1803
+ <ar:ImpTrib>0.00</ar:ImpTrib>
1804
+ <ar:MonId>PES</ar:MonId>
1805
+ <ar:MonCotiz>1</ar:MonCotiz>
1806
+ <ar:CAEA>${params.caea}</ar:CAEA>${fechasServicioXml}
1807
+ ${asocXml}
1808
+ ${vatXml}
1809
+ ${optXml}
1810
+ </ar:FECAEADetRequest>`;
1811
+ });
1812
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1813
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1814
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1815
+ <soapenv:Header/>
1816
+ <soapenv:Body>
1817
+ <ar:FECAEARegInformativo>
1818
+ <ar:Auth>
1819
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1820
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1821
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1822
+ </ar:Auth>
1823
+ <ar:FeCAEARegInfReq>
1824
+ <ar:FeCabReq>
1825
+ <ar:CantReg>${countReg}</ar:CantReg>
1826
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1827
+ <ar:CbteTipo>${invoiceType}</ar:CbteTipo>
1828
+ </ar:FeCabReq>
1829
+ <ar:FeDetReq>${detXml}
1830
+ </ar:FeDetReq>
1831
+ </ar:FeCAEARegInfReq>
1832
+ </ar:FECAEARegInformativo>
1833
+ </soapenv:Body>
1834
+ </soapenv:Envelope>`;
1835
+ const endpoint = getWsfeEndpoint(this.config.environment);
1836
+ const response = await callArcaApi(endpoint, {
1837
+ method: "POST",
1838
+ headers: {
1839
+ "Content-Type": "text/xml; charset=utf-8",
1840
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEARegInformativo"
1841
+ },
1842
+ body: soapRequest,
1843
+ timeout: this.config.timeout
1844
+ });
1845
+ if (!response.ok) {
1846
+ throw new ArcaError(`Error HTTP al rendir CAEA: ${response.status}`, "HTTP_ERROR");
1847
+ }
1848
+ const responseXml = await response.text();
1849
+ const result = parseXml(responseXml);
1850
+ const data = result?.Envelope?.Body?.FECAEARegInformativoResponse?.FECAEARegInformativoResult;
1851
+ if (!data) {
1852
+ throw new ArcaError("Respuesta FECAEARegInformativo inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1853
+ }
1854
+ if (data.Errors) {
1855
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1856
+ const code = error?.Code || "UNKNOWN";
1857
+ throw new ArcaError(
1858
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1859
+ "ARCA_ERROR",
1860
+ data.Errors,
1861
+ getArcaHint(code)
1862
+ );
1863
+ }
1864
+ const cab = data.FeCabResp;
1865
+ const det = Array.isArray(data.FeDetResp.FECAEDetResponse) ? data.FeDetResp.FECAEDetResponse[0] : data.FeDetResp.FECAEDetResponse;
1866
+ const observations = [];
1867
+ if (det?.Observaciones) {
1868
+ const obsArray = Array.isArray(det.Observaciones.Obs) ? det.Observaciones.Obs : [det.Observaciones.Obs];
1869
+ obsArray.forEach((o) => observations.push(o.Msg));
1870
+ }
1871
+ return {
1872
+ caea: String(det.CAEA || params.caea),
1873
+ result: cab.Resultado,
1874
+ pointOfSale: Number(cab.PtoVta),
1875
+ invoiceType: Number(cab.CbteTipo),
1876
+ observations: observations.length > 0 ? observations : void 0
1877
+ };
1878
+ }
1879
+ /**
1880
+ * Informa que un CAEA no tuvo movimientos en la quincena (FECAEASinMovimientoInformar).
1881
+ */
1882
+ async reportCAEANoMovement(params) {
1883
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1884
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1885
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1886
+ <soapenv:Header/>
1887
+ <soapenv:Body>
1888
+ <ar:FECAEASinMovimientoInformar>
1889
+ <ar:Auth>
1890
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1891
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1892
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1893
+ </ar:Auth>
1894
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1895
+ <ar:Caea>${params.caea}</ar:Caea>
1896
+ </ar:FECAEASinMovimientoInformar>
1897
+ </soapenv:Body>
1898
+ </soapenv:Envelope>`;
1899
+ const endpoint = getWsfeEndpoint(this.config.environment);
1900
+ const response = await callArcaApi(endpoint, {
1901
+ method: "POST",
1902
+ headers: {
1903
+ "Content-Type": "text/xml; charset=utf-8",
1904
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASinMovimientoInformar"
1905
+ },
1906
+ body: soapRequest,
1907
+ timeout: this.config.timeout
1908
+ });
1909
+ if (!response.ok) {
1910
+ throw new ArcaError(`Error HTTP al informar CAEA sin movimientos: ${response.status}`, "HTTP_ERROR");
1911
+ }
1912
+ const responseXml = await response.text();
1913
+ const result = parseXml(responseXml);
1914
+ const data = result?.Envelope?.Body?.FECAEASinMovimientoInformarResponse?.FECAEASinMovimientoInformarResult;
1915
+ if (!data) {
1916
+ throw new ArcaError("Respuesta FECAEASinMovimientoInformar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1917
+ }
1918
+ if (data.Errors) {
1919
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1920
+ const code = error?.Code || "UNKNOWN";
1921
+ throw new ArcaError(
1922
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1923
+ "ARCA_ERROR",
1924
+ data.Errors,
1925
+ getArcaHint(code)
1926
+ );
1927
+ }
1928
+ }
1929
+ /**
1930
+ * Consulta si se informó la falta de movimientos de un CAEA (FECAEASinMovimientoConsultar).
1931
+ */
1932
+ async getCAEANoMovement(caea) {
1933
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1934
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1935
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1936
+ <soapenv:Header/>
1937
+ <soapenv:Body>
1938
+ <ar:FECAEASinMovimientoConsultar>
1939
+ <ar:Auth>
1940
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1941
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1942
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1943
+ </ar:Auth>
1944
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1945
+ <ar:Caea>${caea}</ar:Caea>
1946
+ </ar:FECAEASinMovimientoConsultar>
1947
+ </soapenv:Body>
1948
+ </soapenv:Envelope>`;
1949
+ const endpoint = getWsfeEndpoint(this.config.environment);
1950
+ const response = await callArcaApi(endpoint, {
1951
+ method: "POST",
1952
+ headers: {
1953
+ "Content-Type": "text/xml; charset=utf-8",
1954
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASinMovimientoConsultar"
1955
+ },
1956
+ body: soapRequest,
1957
+ timeout: this.config.timeout
1958
+ });
1959
+ if (!response.ok) {
1960
+ throw new ArcaError(`Error HTTP al consultar CAEA sin movimientos: ${response.status}`, "HTTP_ERROR");
1961
+ }
1962
+ const responseXml = await response.text();
1963
+ const result = parseXml(responseXml);
1964
+ const data = result?.Envelope?.Body?.FECAEASinMovimientoConsultarResponse?.FECAEASinMovimientoConsultarResult;
1965
+ if (!data) {
1966
+ throw new ArcaError("Respuesta FECAEASinMovimientoConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1967
+ }
1968
+ if (data.Errors) {
1969
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1970
+ const code = error?.Code || "UNKNOWN";
1971
+ throw new ArcaError(
1972
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1973
+ "ARCA_ERROR",
1974
+ data.Errors,
1975
+ getArcaHint(code)
1976
+ );
1977
+ }
1978
+ return data.ResultGet?.FECAEASinMov || null;
1979
+ }
1980
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1981
+ // Métodos internos auxiliares
1982
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1983
+ /**
1984
+ * Mapea alícuotas numéricas a los códigos internos de ARCA
1985
+ */
1986
+ getVATCode(rate) {
1987
+ switch (rate) {
1988
+ case 0:
1989
+ return 3;
1990
+ // 0% / Exento / No gravado (código 3 en catálogo ARCA)
1991
+ case 10.5:
1992
+ return 4;
1993
+ case 21:
1994
+ return 5;
1995
+ case 27:
1996
+ return 6;
1997
+ case 5:
1998
+ return 8;
1999
+ case 2.5:
2000
+ return 9;
2001
+ default:
2002
+ throw new ArcaValidationError(`Al\xEDcuota IVA no soportada por ARCA: ${rate}%`, {
2003
+ supportedRates: [0, 10.5, 21, 27, 5, 2.5]
2004
+ });
2005
+ }
2006
+ }
2007
+ };
1511
2008
  // Annotate the CommonJS export names for ESM import in node:
1512
2009
  0 && (module.exports = {
1513
2010
  ArcaAuthError,
@@ -1515,6 +2012,7 @@ var PadronService = class {
1515
2012
  ArcaNetworkError,
1516
2013
  ArcaValidationError,
1517
2014
  BillingConcept,
2015
+ CaeaService,
1518
2016
  InvoiceType,
1519
2017
  PadronService,
1520
2018
  TaxIdType,