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.js CHANGED
@@ -244,6 +244,18 @@ var TicketManager = class {
244
244
 
245
245
  // src/utils/network.ts
246
246
  import https from "https";
247
+ import tls from "tls";
248
+ function checkArcaServerIdentity(hostname, cert) {
249
+ const error = tls.checkServerIdentity(hostname, cert);
250
+ if (error && hostname.endsWith("arca.gob.ar")) {
251
+ const fallbackHost = hostname.replace("arca.gob.ar", "afip.gov.ar");
252
+ const retryError = tls.checkServerIdentity(fallbackHost, cert);
253
+ if (!retryError) {
254
+ return void 0;
255
+ }
256
+ }
257
+ return error;
258
+ }
247
259
  async function callArcaApi(url, options) {
248
260
  const timeout = options.timeout || 15e3;
249
261
  const isNode = typeof process !== "undefined" && process.versions && process.versions.node;
@@ -259,7 +271,8 @@ async function callArcaApi(url, options) {
259
271
  minVersion: "TLSv1",
260
272
  // @ts-ignore - Propiedad específica para mitigar "dh key too small" en Node 18+
261
273
  minDHSize: 1024,
262
- rejectUnauthorized: true
274
+ rejectUnauthorized: true,
275
+ checkServerIdentity: checkArcaServerIdentity
263
276
  });
264
277
  const reqOptions = {
265
278
  method: options.method,
@@ -459,23 +472,23 @@ var WsaaService = class {
459
472
  };
460
473
 
461
474
  // src/types/wsfe.ts
462
- var InvoiceType = /* @__PURE__ */ ((InvoiceType2) => {
463
- InvoiceType2[InvoiceType2["FACTURA_A"] = 1] = "FACTURA_A";
464
- InvoiceType2[InvoiceType2["NOTA_DEBITO_A"] = 2] = "NOTA_DEBITO_A";
465
- InvoiceType2[InvoiceType2["NOTA_CREDITO_A"] = 3] = "NOTA_CREDITO_A";
466
- InvoiceType2[InvoiceType2["RECIBO_A"] = 4] = "RECIBO_A";
467
- InvoiceType2[InvoiceType2["FACTURA_B"] = 6] = "FACTURA_B";
468
- InvoiceType2[InvoiceType2["NOTA_DEBITO_B"] = 7] = "NOTA_DEBITO_B";
469
- InvoiceType2[InvoiceType2["NOTA_CREDITO_B"] = 8] = "NOTA_CREDITO_B";
470
- InvoiceType2[InvoiceType2["RECIBO_B"] = 9] = "RECIBO_B";
471
- InvoiceType2[InvoiceType2["FACTURA_C"] = 11] = "FACTURA_C";
472
- InvoiceType2[InvoiceType2["NOTA_DEBITO_C"] = 12] = "NOTA_DEBITO_C";
473
- InvoiceType2[InvoiceType2["NOTA_CREDITO_C"] = 13] = "NOTA_CREDITO_C";
474
- InvoiceType2[InvoiceType2["RECIBO_C"] = 15] = "RECIBO_C";
475
- InvoiceType2[InvoiceType2["TICKET_A"] = 81] = "TICKET_A";
476
- InvoiceType2[InvoiceType2["TICKET_B"] = 82] = "TICKET_B";
477
- InvoiceType2[InvoiceType2["TICKET_C"] = 83] = "TICKET_C";
478
- return InvoiceType2;
475
+ var InvoiceType = /* @__PURE__ */ ((InvoiceType3) => {
476
+ InvoiceType3[InvoiceType3["FACTURA_A"] = 1] = "FACTURA_A";
477
+ InvoiceType3[InvoiceType3["NOTA_DEBITO_A"] = 2] = "NOTA_DEBITO_A";
478
+ InvoiceType3[InvoiceType3["NOTA_CREDITO_A"] = 3] = "NOTA_CREDITO_A";
479
+ InvoiceType3[InvoiceType3["RECIBO_A"] = 4] = "RECIBO_A";
480
+ InvoiceType3[InvoiceType3["FACTURA_B"] = 6] = "FACTURA_B";
481
+ InvoiceType3[InvoiceType3["NOTA_DEBITO_B"] = 7] = "NOTA_DEBITO_B";
482
+ InvoiceType3[InvoiceType3["NOTA_CREDITO_B"] = 8] = "NOTA_CREDITO_B";
483
+ InvoiceType3[InvoiceType3["RECIBO_B"] = 9] = "RECIBO_B";
484
+ InvoiceType3[InvoiceType3["FACTURA_C"] = 11] = "FACTURA_C";
485
+ InvoiceType3[InvoiceType3["NOTA_DEBITO_C"] = 12] = "NOTA_DEBITO_C";
486
+ InvoiceType3[InvoiceType3["NOTA_CREDITO_C"] = 13] = "NOTA_CREDITO_C";
487
+ InvoiceType3[InvoiceType3["RECIBO_C"] = 15] = "RECIBO_C";
488
+ InvoiceType3[InvoiceType3["TICKET_A"] = 81] = "TICKET_A";
489
+ InvoiceType3[InvoiceType3["TICKET_B"] = 82] = "TICKET_B";
490
+ InvoiceType3[InvoiceType3["TICKET_C"] = 83] = "TICKET_C";
491
+ return InvoiceType3;
479
492
  })(InvoiceType || {});
480
493
  var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
481
494
  BillingConcept2[BillingConcept2["PRODUCTS"] = 1] = "PRODUCTS";
@@ -483,19 +496,19 @@ var BillingConcept = /* @__PURE__ */ ((BillingConcept2) => {
483
496
  BillingConcept2[BillingConcept2["PRODUCTS_AND_SERVICES"] = 3] = "PRODUCTS_AND_SERVICES";
484
497
  return BillingConcept2;
485
498
  })(BillingConcept || {});
486
- var TaxIdType = /* @__PURE__ */ ((TaxIdType2) => {
487
- TaxIdType2[TaxIdType2["CUIT"] = 80] = "CUIT";
488
- TaxIdType2[TaxIdType2["CUIL"] = 86] = "CUIL";
489
- TaxIdType2[TaxIdType2["CDI"] = 87] = "CDI";
490
- TaxIdType2[TaxIdType2["LE"] = 89] = "LE";
491
- TaxIdType2[TaxIdType2["LC"] = 90] = "LC";
492
- TaxIdType2[TaxIdType2["FOREIGN_ID"] = 91] = "FOREIGN_ID";
493
- TaxIdType2[TaxIdType2["PASSPORT"] = 94] = "PASSPORT";
494
- TaxIdType2[TaxIdType2["BUENOS_AIRES_ID"] = 95] = "BUENOS_AIRES_ID";
495
- TaxIdType2[TaxIdType2["NATIONAL_POLICE_ID"] = 96] = "NATIONAL_POLICE_ID";
496
- TaxIdType2[TaxIdType2["DNI"] = 96] = "DNI";
497
- TaxIdType2[TaxIdType2["FINAL_CONSUMER"] = 99] = "FINAL_CONSUMER";
498
- return TaxIdType2;
499
+ var TaxIdType = /* @__PURE__ */ ((TaxIdType3) => {
500
+ TaxIdType3[TaxIdType3["CUIT"] = 80] = "CUIT";
501
+ TaxIdType3[TaxIdType3["CUIL"] = 86] = "CUIL";
502
+ TaxIdType3[TaxIdType3["CDI"] = 87] = "CDI";
503
+ TaxIdType3[TaxIdType3["LE"] = 89] = "LE";
504
+ TaxIdType3[TaxIdType3["LC"] = 90] = "LC";
505
+ TaxIdType3[TaxIdType3["FOREIGN_ID"] = 91] = "FOREIGN_ID";
506
+ TaxIdType3[TaxIdType3["PASSPORT"] = 94] = "PASSPORT";
507
+ TaxIdType3[TaxIdType3["BUENOS_AIRES_ID"] = 95] = "BUENOS_AIRES_ID";
508
+ TaxIdType3[TaxIdType3["NATIONAL_POLICE_ID"] = 96] = "NATIONAL_POLICE_ID";
509
+ TaxIdType3[TaxIdType3["DNI"] = 96] = "DNI";
510
+ TaxIdType3[TaxIdType3["FINAL_CONSUMER"] = 99] = "FINAL_CONSUMER";
511
+ return TaxIdType3;
499
512
  })(TaxIdType || {});
500
513
  var VatCondition = /* @__PURE__ */ ((VatCondition2) => {
501
514
  VatCondition2[VatCondition2["IVA_RESPONSABLE_INSCRIPTO"] = 1] = "IVA_RESPONSABLE_INSCRIPTO";
@@ -1007,7 +1020,6 @@ var WsfeService = class _WsfeService {
1007
1020
  * Método genérico interno para emitir cualquier tipo de comprobante.
1008
1021
  */
1009
1022
  async issueDocument(request) {
1010
- const invoiceNumber = await this.getNextInvoiceNumber(request.type);
1011
1023
  let total = request.total || 0;
1012
1024
  let net = total;
1013
1025
  let vat = 0;
@@ -1020,6 +1032,8 @@ var WsfeService = class _WsfeService {
1020
1032
  if (total <= 0) {
1021
1033
  throw new ArcaValidationError("El monto total debe ser mayor a 0");
1022
1034
  }
1035
+ this.validateBuyer(request.buyer, total);
1036
+ const invoiceNumber = await this.getNextInvoiceNumber(request.type);
1023
1037
  const soapRequest = this.buildCAERequest({
1024
1038
  type: request.type,
1025
1039
  pointOfSale: this.config.pointOfSale,
@@ -1113,6 +1127,23 @@ var WsfeService = class _WsfeService {
1113
1127
  );
1114
1128
  }
1115
1129
  }
1130
+ /**
1131
+ * Valida obligatoriamente al comprador cuando el monto es mayor o igual a $10.000.000 para consumidor final
1132
+ */
1133
+ validateBuyer(buyer, total) {
1134
+ const isFinalConsumer = !buyer || buyer.docType === 99 /* FINAL_CONSUMER */ || buyer.docNumber === "0" || buyer.docNumber === "";
1135
+ if (total >= 1e7 && isFinalConsumer) {
1136
+ const errorMessage = "Para importes mayores o iguales a $10.000.000 es obligatorio identificar al comprador (RG 5824/2026).";
1137
+ if (process.env.NODE_ENV !== "production") {
1138
+ console.warn(`[arca-sdk WARNING] ${errorMessage}`);
1139
+ }
1140
+ throw new ArcaValidationError(errorMessage, {
1141
+ total,
1142
+ buyer,
1143
+ hint: "Actualiz\xE1 el objeto buyer con un tipo de documento v\xE1lido (DNI, CUIT, CUIL) y su n\xFAmero correspondiente."
1144
+ });
1145
+ }
1146
+ }
1116
1147
  /**
1117
1148
  * Calcula el IVA agrupado por alícuota (requerido por ARCA para Factura A/B)
1118
1149
  */
@@ -1412,8 +1443,10 @@ var PadronService = class {
1412
1443
  mainActivity: p.descripcionActividadPrincipal,
1413
1444
  isVATRegistered: this.hasTaxId(p, 30),
1414
1445
  // 30 = IVA
1415
- isMonotax: this.hasTaxId(p, 20),
1416
- // 20 = Monotributo
1446
+ isMonotax: this.hasTaxId(p, 20) || this.hasTaxId(p, 24) || this.hasTaxId(p, 21),
1447
+ // General o Social/Autónomo
1448
+ isSocialMonotax: this.hasTaxId(p, 24) || this.hasTaxId(p, 21),
1449
+ // 24 = Obra Social / Promovido, 21 = Autónomo
1417
1450
  isVATExempt: this.hasTaxId(p, 32)
1418
1451
  // 32 = IVA Exento
1419
1452
  };
@@ -1461,12 +1494,476 @@ var PadronService = class {
1461
1494
  return [data];
1462
1495
  }
1463
1496
  };
1497
+
1498
+ // src/services/caea.ts
1499
+ var CaeaService = class {
1500
+ config;
1501
+ constructor(config) {
1502
+ this.validateConfig(config);
1503
+ this.config = config;
1504
+ }
1505
+ validateConfig(config) {
1506
+ if (!config.ticket || !config.ticket.token) {
1507
+ throw new ArcaValidationError(
1508
+ "Ticket WSAA requerido. Ejecut\xE1 wsaa.login() primero.",
1509
+ { hint: "El ticket se obtiene del servicio WsaaService" }
1510
+ );
1511
+ }
1512
+ if (!config.pointOfSale || config.pointOfSale < 1 || config.pointOfSale > 9999) {
1513
+ throw new ArcaValidationError(
1514
+ "Punto de venta inv\xE1lido: debe ser un n\xFAmero entre 1 y 9999",
1515
+ { pointOfSale: config.pointOfSale }
1516
+ );
1517
+ }
1518
+ }
1519
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1520
+ // Métodos del Ciclo de Vida de CAEA
1521
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1522
+ /**
1523
+ * Solicita un CAEA (Código de Autorización Electrónico Anticipado) para una quincena específica (FECAEASolicitar).
1524
+ */
1525
+ async solicitCAEA(params) {
1526
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1527
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1528
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1529
+ <soapenv:Header/>
1530
+ <soapenv:Body>
1531
+ <ar:FECAEASolicitar>
1532
+ <ar:Auth>
1533
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1534
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1535
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1536
+ </ar:Auth>
1537
+ <ar:Periodo>${params.period}</ar:Periodo>
1538
+ <ar:Orden>${params.order}</ar:Orden>
1539
+ </ar:FECAEASolicitar>
1540
+ </soapenv:Body>
1541
+ </soapenv:Envelope>`;
1542
+ const endpoint = getWsfeEndpoint(this.config.environment);
1543
+ const response = await callArcaApi(endpoint, {
1544
+ method: "POST",
1545
+ headers: {
1546
+ "Content-Type": "text/xml; charset=utf-8",
1547
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASolicitar"
1548
+ },
1549
+ body: soapRequest,
1550
+ timeout: this.config.timeout
1551
+ });
1552
+ if (!response.ok) {
1553
+ throw new ArcaError(`Error HTTP al solicitar CAEA: ${response.status}`, "HTTP_ERROR");
1554
+ }
1555
+ const responseXml = await response.text();
1556
+ const result = parseXml(responseXml);
1557
+ const data = result?.Envelope?.Body?.FECAEASolicitarResponse?.FECAEASolicitarResult;
1558
+ if (!data) {
1559
+ throw new ArcaError("Respuesta FECAEASolicitar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1560
+ }
1561
+ if (data.Errors) {
1562
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1563
+ const code = error?.Code || "UNKNOWN";
1564
+ throw new ArcaError(
1565
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1566
+ "ARCA_ERROR",
1567
+ data.Errors,
1568
+ getArcaHint(code)
1569
+ );
1570
+ }
1571
+ const res = data.ResultGet;
1572
+ return {
1573
+ caea: String(res.CAEA),
1574
+ period: Number(res.Periodo),
1575
+ order: Number(res.Orden),
1576
+ expiryDate: String(res.FchVto),
1577
+ actualDate: String(res.FchVigDesde),
1578
+ receptionDate: String(res.FchVigHasta),
1579
+ limitDate: String(res.FchTopeInf)
1580
+ };
1581
+ }
1582
+ /**
1583
+ * Consulta un CAEA ya emitido (FECAEAConsultar).
1584
+ */
1585
+ async getCAEA(caea) {
1586
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1587
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1588
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1589
+ <soapenv:Header/>
1590
+ <soapenv:Body>
1591
+ <ar:FECAEAConsultar>
1592
+ <ar:Auth>
1593
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1594
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1595
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1596
+ </ar:Auth>
1597
+ <ar:Caea>${caea}</ar:Caea>
1598
+ </ar:FECAEAConsultar>
1599
+ </soapenv:Body>
1600
+ </soapenv:Envelope>`;
1601
+ const endpoint = getWsfeEndpoint(this.config.environment);
1602
+ const response = await callArcaApi(endpoint, {
1603
+ method: "POST",
1604
+ headers: {
1605
+ "Content-Type": "text/xml; charset=utf-8",
1606
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEAConsultar"
1607
+ },
1608
+ body: soapRequest,
1609
+ timeout: this.config.timeout
1610
+ });
1611
+ if (!response.ok) {
1612
+ throw new ArcaError(`Error HTTP al consultar CAEA: ${response.status}`, "HTTP_ERROR");
1613
+ }
1614
+ const responseXml = await response.text();
1615
+ const result = parseXml(responseXml);
1616
+ const data = result?.Envelope?.Body?.FECAEAConsultarResponse?.FECAEAConsultarResult;
1617
+ if (!data) {
1618
+ throw new ArcaError("Respuesta FECAEAConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1619
+ }
1620
+ if (data.Errors) {
1621
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1622
+ const code = error?.Code || "UNKNOWN";
1623
+ throw new ArcaError(
1624
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1625
+ "ARCA_ERROR",
1626
+ data.Errors,
1627
+ getArcaHint(code)
1628
+ );
1629
+ }
1630
+ const res = data.ResultGet;
1631
+ return {
1632
+ caea: String(res.CAEA),
1633
+ period: Number(res.Periodo),
1634
+ order: Number(res.Orden),
1635
+ expiryDate: String(res.FchVto),
1636
+ actualDate: String(res.FchVigDesde),
1637
+ receptionDate: String(res.FchVigHasta),
1638
+ limitDate: String(res.FchTopeInf)
1639
+ };
1640
+ }
1641
+ /**
1642
+ * Rinde/registra informativamente los comprobantes emitidos en contingencia local bajo un CAEA específico (FECAEARegInformativo).
1643
+ * Soporta el envío en lotes de comprobantes homogéneos del mismo tipo de factura y punto de venta.
1644
+ */
1645
+ async reportCAEAPeriod(params) {
1646
+ if (!params.invoices || params.invoices.length === 0) {
1647
+ throw new ArcaValidationError("Debe proveer al menos un comprobante para realizar la rendici\xF3n del CAEA.");
1648
+ }
1649
+ const firstInvoice = params.invoices[0];
1650
+ const invoiceType = firstInvoice.invoiceType;
1651
+ const countReg = params.invoices.length;
1652
+ const invalidInvoices = params.invoices.filter((inv) => inv.invoiceType !== invoiceType);
1653
+ if (invalidInvoices.length > 0) {
1654
+ throw new ArcaValidationError("Todos los comprobantes en un mismo lote de rendici\xF3n informativa deben poseer el mismo tipo de factura (invoiceType).");
1655
+ }
1656
+ let detXml = "";
1657
+ params.invoices.forEach((inv) => {
1658
+ const date = inv.date || /* @__PURE__ */ new Date();
1659
+ const dateStr = date.toISOString().split("T")[0].replace(/-/g, "");
1660
+ let total = 0;
1661
+ let net = 0;
1662
+ let vat = 0;
1663
+ let vatXml = "";
1664
+ const includesVAT = inv.includesVAT || false;
1665
+ if (inv.items && inv.items.length > 0) {
1666
+ net = round(calculateSubtotal(inv.items, includesVAT));
1667
+ vat = round(calculateVAT(inv.items, includesVAT));
1668
+ total = round(calculateTotal(inv.items, includesVAT));
1669
+ const byRate = /* @__PURE__ */ new Map();
1670
+ inv.items.forEach((item) => {
1671
+ const rate = item.vatRate || 0;
1672
+ let netPrice = item.unitPrice;
1673
+ if (includesVAT && rate) {
1674
+ netPrice = item.unitPrice / (1 + rate / 100);
1675
+ }
1676
+ const base = item.quantity * netPrice;
1677
+ const amount = base * rate / 100;
1678
+ const current = byRate.get(rate) || { base: 0, amount: 0 };
1679
+ byRate.set(rate, {
1680
+ base: current.base + base,
1681
+ amount: current.amount + amount
1682
+ });
1683
+ });
1684
+ const vatEntries = Array.from(byRate.entries()).map(([rate, values]) => ({
1685
+ rate,
1686
+ taxBase: values.base,
1687
+ amount: values.amount
1688
+ }));
1689
+ if (vatEntries.length > 0) {
1690
+ vatXml = "<ar:Iva>";
1691
+ vatEntries.forEach((entry) => {
1692
+ vatXml += `
1693
+ <ar:AlicIva>
1694
+ <ar:Id>${this.getVATCode(entry.rate)}</ar:Id>
1695
+ <ar:BaseImp>${entry.taxBase.toFixed(2)}</ar:BaseImp>
1696
+ <ar:Importe>${entry.amount.toFixed(2)}</ar:Importe>
1697
+ </ar:AlicIva>`;
1698
+ });
1699
+ vatXml += "\n </ar:Iva>";
1700
+ }
1701
+ }
1702
+ if (total <= 0) {
1703
+ throw new ArcaValidationError(`El monto total del comprobante n\xFAmero ${inv.invoiceNumber} debe ser mayor a 0.`);
1704
+ }
1705
+ const condicionIVAReceptorXml = inv.buyer?.vatCondition !== void 0 ? `
1706
+ <ar:CondicionIVAReceptorId>${inv.buyer.vatCondition}</ar:CondicionIVAReceptorId>` : "";
1707
+ let fechasServicioXml = "";
1708
+ if (inv.concept === 2 /* SERVICES */ || inv.concept === 3 /* PRODUCTS_AND_SERVICES */) {
1709
+ const defaultDateStr = date.toISOString().split("T")[0].replace(/-/g, "");
1710
+ fechasServicioXml = `
1711
+ <ar:FchServDesde>${defaultDateStr}</ar:FchServDesde>
1712
+ <ar:FchServHasta>${defaultDateStr}</ar:FchServHasta>
1713
+ <ar:FchVtoPago>${defaultDateStr}</ar:FchVtoPago>`;
1714
+ }
1715
+ let asocXml = "";
1716
+ if (inv.associatedInvoices && inv.associatedInvoices.length > 0) {
1717
+ asocXml = "<ar:CbtesAsoc>";
1718
+ inv.associatedInvoices.forEach((asoc) => {
1719
+ asocXml += `
1720
+ <ar:CbteAsoc>
1721
+ <ar:Tipo>${asoc.type}</ar:Tipo>
1722
+ <ar:PtoVta>${asoc.pointOfSale}</ar:PtoVta>
1723
+ <ar:Nro>${asoc.invoiceNumber}</ar:Nro>
1724
+ ${asoc.cuit ? `<ar:Cuit>${asoc.cuit}</ar:Cuit>` : ""}
1725
+ ${asoc.date ? `<ar:CbteFch>${asoc.date.toISOString().split("T")[0].replace(/-/g, "")}</ar:CbteFch>` : ""}
1726
+ </ar:CbteAsoc>`;
1727
+ });
1728
+ asocXml += "\n </ar:CbtesAsoc>";
1729
+ }
1730
+ let optXml = "";
1731
+ if (inv.optionals && inv.optionals.length > 0) {
1732
+ optXml = "<ar:Opcionales>";
1733
+ inv.optionals.forEach((opt) => {
1734
+ optXml += `
1735
+ <ar:Opcional>
1736
+ <ar:Id>${opt.id}</ar:Id>
1737
+ <ar:Valor>${opt.value}</ar:Valor>
1738
+ </ar:Opcional>`;
1739
+ });
1740
+ optXml += "\n </ar:Opcionales>";
1741
+ }
1742
+ detXml += `
1743
+ <ar:FECAEADetRequest>
1744
+ <ar:Concepto>${inv.concept}</ar:Concepto>
1745
+ <ar:DocTipo>${inv.buyer?.docType || 99}</ar:DocTipo>
1746
+ <ar:DocNro>${inv.buyer?.docNumber || 0}</ar:DocNro>${condicionIVAReceptorXml}
1747
+ <ar:CbteDesde>${inv.invoiceNumber}</ar:CbteDesde>
1748
+ <ar:CbteHasta>${inv.invoiceNumber}</ar:CbteHasta>
1749
+ <ar:CbteFch>${dateStr}</ar:CbteFch>
1750
+ <ar:ImpTotal>${total.toFixed(2)}</ar:ImpTotal>
1751
+ <ar:ImpTotConc>0.00</ar:ImpTotConc>
1752
+ <ar:ImpNeto>${net.toFixed(2)}</ar:ImpNeto>
1753
+ <ar:ImpOpEx>0.00</ar:ImpOpEx>
1754
+ <ar:ImpIVA>${vat.toFixed(2)}</ar:ImpIVA>
1755
+ <ar:ImpTrib>0.00</ar:ImpTrib>
1756
+ <ar:MonId>PES</ar:MonId>
1757
+ <ar:MonCotiz>1</ar:MonCotiz>
1758
+ <ar:CAEA>${params.caea}</ar:CAEA>${fechasServicioXml}
1759
+ ${asocXml}
1760
+ ${vatXml}
1761
+ ${optXml}
1762
+ </ar:FECAEADetRequest>`;
1763
+ });
1764
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1765
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1766
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1767
+ <soapenv:Header/>
1768
+ <soapenv:Body>
1769
+ <ar:FECAEARegInformativo>
1770
+ <ar:Auth>
1771
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1772
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1773
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1774
+ </ar:Auth>
1775
+ <ar:FeCAEARegInfReq>
1776
+ <ar:FeCabReq>
1777
+ <ar:CantReg>${countReg}</ar:CantReg>
1778
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1779
+ <ar:CbteTipo>${invoiceType}</ar:CbteTipo>
1780
+ </ar:FeCabReq>
1781
+ <ar:FeDetReq>${detXml}
1782
+ </ar:FeDetReq>
1783
+ </ar:FeCAEARegInfReq>
1784
+ </ar:FECAEARegInformativo>
1785
+ </soapenv:Body>
1786
+ </soapenv:Envelope>`;
1787
+ const endpoint = getWsfeEndpoint(this.config.environment);
1788
+ const response = await callArcaApi(endpoint, {
1789
+ method: "POST",
1790
+ headers: {
1791
+ "Content-Type": "text/xml; charset=utf-8",
1792
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEARegInformativo"
1793
+ },
1794
+ body: soapRequest,
1795
+ timeout: this.config.timeout
1796
+ });
1797
+ if (!response.ok) {
1798
+ throw new ArcaError(`Error HTTP al rendir CAEA: ${response.status}`, "HTTP_ERROR");
1799
+ }
1800
+ const responseXml = await response.text();
1801
+ const result = parseXml(responseXml);
1802
+ const data = result?.Envelope?.Body?.FECAEARegInformativoResponse?.FECAEARegInformativoResult;
1803
+ if (!data) {
1804
+ throw new ArcaError("Respuesta FECAEARegInformativo inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1805
+ }
1806
+ if (data.Errors) {
1807
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1808
+ const code = error?.Code || "UNKNOWN";
1809
+ throw new ArcaError(
1810
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1811
+ "ARCA_ERROR",
1812
+ data.Errors,
1813
+ getArcaHint(code)
1814
+ );
1815
+ }
1816
+ const cab = data.FeCabResp;
1817
+ const det = Array.isArray(data.FeDetResp.FECAEDetResponse) ? data.FeDetResp.FECAEDetResponse[0] : data.FeDetResp.FECAEDetResponse;
1818
+ const observations = [];
1819
+ if (det?.Observaciones) {
1820
+ const obsArray = Array.isArray(det.Observaciones.Obs) ? det.Observaciones.Obs : [det.Observaciones.Obs];
1821
+ obsArray.forEach((o) => observations.push(o.Msg));
1822
+ }
1823
+ return {
1824
+ caea: String(det.CAEA || params.caea),
1825
+ result: cab.Resultado,
1826
+ pointOfSale: Number(cab.PtoVta),
1827
+ invoiceType: Number(cab.CbteTipo),
1828
+ observations: observations.length > 0 ? observations : void 0
1829
+ };
1830
+ }
1831
+ /**
1832
+ * Informa que un CAEA no tuvo movimientos en la quincena (FECAEASinMovimientoInformar).
1833
+ */
1834
+ async reportCAEANoMovement(params) {
1835
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1836
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1837
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1838
+ <soapenv:Header/>
1839
+ <soapenv:Body>
1840
+ <ar:FECAEASinMovimientoInformar>
1841
+ <ar:Auth>
1842
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1843
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1844
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1845
+ </ar:Auth>
1846
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1847
+ <ar:Caea>${params.caea}</ar:Caea>
1848
+ </ar:FECAEASinMovimientoInformar>
1849
+ </soapenv:Body>
1850
+ </soapenv:Envelope>`;
1851
+ const endpoint = getWsfeEndpoint(this.config.environment);
1852
+ const response = await callArcaApi(endpoint, {
1853
+ method: "POST",
1854
+ headers: {
1855
+ "Content-Type": "text/xml; charset=utf-8",
1856
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASinMovimientoInformar"
1857
+ },
1858
+ body: soapRequest,
1859
+ timeout: this.config.timeout
1860
+ });
1861
+ if (!response.ok) {
1862
+ throw new ArcaError(`Error HTTP al informar CAEA sin movimientos: ${response.status}`, "HTTP_ERROR");
1863
+ }
1864
+ const responseXml = await response.text();
1865
+ const result = parseXml(responseXml);
1866
+ const data = result?.Envelope?.Body?.FECAEASinMovimientoInformarResponse?.FECAEASinMovimientoInformarResult;
1867
+ if (!data) {
1868
+ throw new ArcaError("Respuesta FECAEASinMovimientoInformar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1869
+ }
1870
+ if (data.Errors) {
1871
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1872
+ const code = error?.Code || "UNKNOWN";
1873
+ throw new ArcaError(
1874
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1875
+ "ARCA_ERROR",
1876
+ data.Errors,
1877
+ getArcaHint(code)
1878
+ );
1879
+ }
1880
+ }
1881
+ /**
1882
+ * Consulta si se informó la falta de movimientos de un CAEA (FECAEASinMovimientoConsultar).
1883
+ */
1884
+ async getCAEANoMovement(caea) {
1885
+ const soapRequest = `<?xml version="1.0" encoding="UTF-8"?>
1886
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
1887
+ xmlns:ar="http://ar.gov.afip.dif.FEV1/">
1888
+ <soapenv:Header/>
1889
+ <soapenv:Body>
1890
+ <ar:FECAEASinMovimientoConsultar>
1891
+ <ar:Auth>
1892
+ <ar:Token>${this.config.ticket.token}</ar:Token>
1893
+ <ar:Sign>${this.config.ticket.sign}</ar:Sign>
1894
+ <ar:Cuit>${this.config.cuit}</ar:Cuit>
1895
+ </ar:Auth>
1896
+ <ar:PtoVta>${this.config.pointOfSale}</ar:PtoVta>
1897
+ <ar:Caea>${caea}</ar:Caea>
1898
+ </ar:FECAEASinMovimientoConsultar>
1899
+ </soapenv:Body>
1900
+ </soapenv:Envelope>`;
1901
+ const endpoint = getWsfeEndpoint(this.config.environment);
1902
+ const response = await callArcaApi(endpoint, {
1903
+ method: "POST",
1904
+ headers: {
1905
+ "Content-Type": "text/xml; charset=utf-8",
1906
+ "SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAEASinMovimientoConsultar"
1907
+ },
1908
+ body: soapRequest,
1909
+ timeout: this.config.timeout
1910
+ });
1911
+ if (!response.ok) {
1912
+ throw new ArcaError(`Error HTTP al consultar CAEA sin movimientos: ${response.status}`, "HTTP_ERROR");
1913
+ }
1914
+ const responseXml = await response.text();
1915
+ const result = parseXml(responseXml);
1916
+ const data = result?.Envelope?.Body?.FECAEASinMovimientoConsultarResponse?.FECAEASinMovimientoConsultarResult;
1917
+ if (!data) {
1918
+ throw new ArcaError("Respuesta FECAEASinMovimientoConsultar inv\xE1lida", "PARSE_ERROR", { xml: responseXml });
1919
+ }
1920
+ if (data.Errors) {
1921
+ const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;
1922
+ const code = error?.Code || "UNKNOWN";
1923
+ throw new ArcaError(
1924
+ `Error ARCA: ${error?.Msg || "Error desconocido"}`,
1925
+ "ARCA_ERROR",
1926
+ data.Errors,
1927
+ getArcaHint(code)
1928
+ );
1929
+ }
1930
+ return data.ResultGet?.FECAEASinMov || null;
1931
+ }
1932
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1933
+ // Métodos internos auxiliares
1934
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1935
+ /**
1936
+ * Mapea alícuotas numéricas a los códigos internos de ARCA
1937
+ */
1938
+ getVATCode(rate) {
1939
+ switch (rate) {
1940
+ case 0:
1941
+ return 3;
1942
+ // 0% / Exento / No gravado (código 3 en catálogo ARCA)
1943
+ case 10.5:
1944
+ return 4;
1945
+ case 21:
1946
+ return 5;
1947
+ case 27:
1948
+ return 6;
1949
+ case 5:
1950
+ return 8;
1951
+ case 2.5:
1952
+ return 9;
1953
+ default:
1954
+ throw new ArcaValidationError(`Al\xEDcuota IVA no soportada por ARCA: ${rate}%`, {
1955
+ supportedRates: [0, 10.5, 21, 27, 5, 2.5]
1956
+ });
1957
+ }
1958
+ }
1959
+ };
1464
1960
  export {
1465
1961
  ArcaAuthError,
1466
1962
  ArcaError,
1467
1963
  ArcaNetworkError,
1468
1964
  ArcaValidationError,
1469
1965
  BillingConcept,
1966
+ CaeaService,
1470
1967
  InvoiceType,
1471
1968
  PadronService,
1472
1969
  TaxIdType,