Skip to content

🧾 Generador de PDF ​

El paquete @arcasdk/pdf permite generar comprobantes electrónicos en formato PDF con el diseño oficial de ARCA (ex AFIP). Soporta Facturas, Notas de Crédito, Notas de Débito y comprobantes MiPyME en letras A, B, C, E y M.

Paquete independiente

@arcasdk/pdf es un paquete independiente de @arcasdk/core. Podés usarlo sin el SDK core construyendo los datos manualmente, o combinarlo con @arcasdk/core para generar PDFs a partir de comprobantes ya emitidos.


Vista previa ​

🧾 Factura A · datos de prueba
Vista previa de Factura A generada con @arcasdk/pdf

Factura A con 3 ítems, desglose de IVA, tributos y código QR. Todos los datos son ficticios.

Comprobante Factura A 00001-00000487
Emisor Estudio Contable de Prueba
Receptor Farmacia de Prueba S.A.
Importe total $ 711.275,00
Ver PDF embebido

Los datos de este ejemplo (ficticios) son los mismos que podés usar en tu código:

ts
const data: InvoiceData = {
  emisor: {
    razonSocial: "ESTUDIO CONTABLE DE PRUEBA",
    domicilioComercial: "Mitre 456 Piso 2 Of. C - C1036AAR - CABA",
    condicionIva: "IVA Responsable Inscripto",
    cuit: "30702345678",
    iibb: "903-223344-5",
    fechaInicioActividades: "20110301",
    contacto: "contacto@estudio-prueba.com.ar",
  },
  receptor: {
    razonSocial: "FARMACIA DE PRUEBA S.A.",
    domicilio: "Bv. España 2300 - S2000DSR - Rosario - Santa Fe",
    condicionIva: "IVA Responsable Inscripto",
    documentoTipo: "CUIT",
    documentoNro: "30901876543",
  },
  cbteTipo: 1,
  cbteLetra: "A",
  puntoVenta: 1,
  cbteDesde: 487,
  cbteHasta: 487,
  cbteFecha: "20260501",
  concepto: 2,
  moneda: "PES",
  condicionVenta: "Cuenta Corriente - 30 días",
  fechaServicioDesde: "20260401",
  fechaServicioHasta: "20260430",
  fechaVtoPago: "20260531",
  items: [
    {
      codigo: "HON-001",
      descripcion: "Honorarios profesionales - Liquidación de sueldos abril 2026",
      cantidad: 1,
      unidadMedida: "unidades",
      precioUnitario: 320000,
      subtotal: 320000,
      alicuotaIva: 21,
    },
    {
      codigo: "HON-002",
      descripcion: "Asesoramiento impositivo mensual",
      cantidad: 1,
      unidadMedida: "unidades",
      precioUnitario: 180000,
      bonificacion: 10,
      subtotal: 162000,
      alicuotaIva: 21,
    },
    {
      codigo: "HON-003",
      descripcion: "Certificación de balances",
      cantidad: 1,
      unidadMedida: "unidades",
      precioUnitario: 95000,
      subtotal: 95000,
      alicuotaIva: 10.5,
    },
  ],
  importeNetoGravado: 577000,
  importeIva: 111195,
  iva: [
    { id: 5, descripcion: "21%", baseImponible: 482000, importe: 101220 },
    { id: 4, descripcion: "10.5%", baseImponible: 95000, importe: 9975 },
  ],
  tributos: [
    {
      id: 99,
      descripcion: "Percepción IIBB CABA",
      baseImponible: 577000,
      alicuota: 3,
      importe: 17310,
    },
    {
      id: 6,
      descripcion: "Percepción IVA",
      baseImponible: 577000,
      alicuota: 1,
      importe: 5770,
    },
  ],
  importeTributos: 23080,
  importeTotal: 711275,
  cae: "72643218900002",
  caeFechaVencimiento: "20260511",
};

const generator = new InvoicePdfGenerator();
const pdfBuffer = await generator.generate(data);

Para regenerar la vista previa de la documentación después de cambiar el template:

bash
npm run generate:docs-demo --workspace=@arcasdk/pdf

Instalación ​

bash
npm i @arcasdk/pdf
bash
yarn add @arcasdk/pdf
bash
pnpm add @arcasdk/pdf

Requisito

Este paquete usa Puppeteer para renderizar el HTML a PDF. Puppeteer descarga Chromium automáticamente al instalar. En entornos serverless o CI, podés necesitar configurar Puppeteer para usar un binario externo.


Uso básico ​

ts
import { InvoicePdfGenerator, InvoiceData } from "@arcasdk/pdf";
import { writeFileSync } from "fs";

const data: InvoiceData = {
  emisor: {
    razonSocial: "Empresa de Prueba S.A.",
    domicilioComercial: "Av. Corrientes 1234, CABA",
    condicionIva: "IVA Responsable Inscripto",
    cuit: "20111111117",
    iibb: "12-3456789-0",
    fechaInicioActividades: "20180301",
  },
  receptor: {
    razonSocial: "Cliente de Prueba S.R.L.",
    domicilio: "Calle Falsa 456, Rosario",
    condicionIva: "IVA Responsable Inscripto",
    documentoTipo: "CUIT",
    documentoNro: "30111111118",
  },
  cbteTipo: 1,
  cbteLetra: "A",
  puntoVenta: 3,
  cbteDesde: 152,
  cbteHasta: 152,
  cbteFecha: "20240515",
  concepto: 1,
  moneda: "PES",
  condicionVenta: "Contado",
  items: [
    {
      descripcion: "Servicio de consultoría",
      cantidad: 10,
      unidadMedida: "horas",
      precioUnitario: 5000,
      subtotal: 50000,
      alicuotaIva: 21,
    },
  ],
  importeNetoGravado: 50000,
  importeIva: 10500,
  iva: [{ id: 5, descripcion: "21%", baseImponible: 50000, importe: 10500 }],
  importeTotal: 60500,
  cae: "74512345678901",
  caeFechaVencimiento: "20240525",
};

const generator = new InvoicePdfGenerator();
const pdfBuffer = await generator.generate(data);
writeFileSync("factura.pdf", pdfBuffer);

Opciones de configuración ​

Se pasan al constructor de InvoicePdfGenerator:

ts
const generator = new InvoicePdfGenerator({
  pageSize: "A4",
  margin: 10,
  includeQr: true,
  footerText: "Documento no válido como factura",
  logo: "data:image/png;base64,...",
  logoWidth: 80,
});
OpciónTipoDefaultDescripción
pageSize"A4" | "LETTER" | "LEGAL""A4"Tamaño de página
marginnumber10Márgenes en mm
includeQrbooleantrueIncluir código QR de verificación ARCA
copyCopyType"ORIGINAL"Banner de copia (ORIGINAL, DUPLICADO, etc.)
copiesCopyType[]—Generar múltiples copias en un solo PDF
logostring—Logo del emisor como data URL (data:image/png;base64,...)
logoWidthnumber60Ancho del logo en px
footerTextstring—Texto adicional en el pie de cada página
templatestring | ((props: InvoiceTemplateProps) => string)—Template personalizado (Handlebars string o función)

CopyType ​

ts
type CopyType = "ORIGINAL" | "DUPLICADO" | "TRIPLICADO" | "CUADRUPLICADO";

Múltiples copias ​

Para generar ORIGINAL + DUPLICADO + TRIPLICADO en un solo PDF:

ts
const generator = new InvoicePdfGenerator({
  copies: ["ORIGINAL", "DUPLICADO", "TRIPLICADO"],
});

const pdfBuffer = await generator.generate(data);

Stream ​

Para obtener un Readable stream en lugar de un Buffer:

ts
const generator = new InvoicePdfGenerator();
const stream = await generator.generateStream(data);

// Ejemplo con Express
res.setHeader("Content-Type", "application/pdf");
stream.pipe(res);

Estructura de datos ​

InvoiceData ​

CampoTipoRequeridoDescripción
emisorEmisorData✅Datos del emisor
receptorReceptorData✅Datos del receptor
cbteTiponumber✅Tipo de comprobante (1, 6, 11, 19, etc.)
cbteLetrastring✅Letra del comprobante (A, B, C, E, M)
puntoVentanumber✅Punto de venta
cbteDesdenumber✅Número de comprobante
cbteHastanumber✅Número de comprobante hasta
cbteFechastring✅Fecha (YYYYMMDD)
conceptonumber✅1=Productos, 2=Servicios, 3=Ambos
itemsInvoiceItem[]✅Items de la factura
importeNetoGravadonumber✅Importe neto gravado
importeIvanumber✅Importe total de IVA
importeTotalnumber✅Importe total
caestring✅CAE
caeFechaVencimientostring✅Vencimiento del CAE (YYYYMMDD)
monedastring—Moneda ("PES", "DOL")
cotizacionnumber—Cotización de la moneda
condicionVentastring—Condición de venta
fechaServicioDesdestring—Período desde (conceptos 2 y 3)
fechaServicioHastastring—Período hasta (conceptos 2 y 3)
fechaVtoPagostring—Vencimiento de pago
importeNetoNoGravadonumber—Importe no gravado
importeExentonumber—Importe exento
ivaIvaDetail[]—Desglose de IVA por alícuota
tributosTributoDetail[]—Otros tributos
importeTributosnumber—Importe total de tributos
cbtesAsociadosComprobanteAsociado[]—Comprobantes asociados (NC/ND)
observacionesstring—Observaciones
cbteDescripcionstring—Descripción personalizada del comprobante
importeTotalPesosnumber—Total en ARS pre-calculado (moneda extranjera)
divisastring—Divisa (Factura E)
destinoComprobantestring—País destino (Factura E)
formaPagostring—Forma de pago (Factura E)
incotermsstring—Incoterms (Factura E)
fechaPagostring—Fecha de pago YYYYMMDD (Factura E)
condicionIvaExportacionstring—Leyenda IVA exportación (Factura E)

EmisorData ​

CampoTipoRequeridoDescripción
razonSocialstring✅Razón social
domicilioComercialstring✅Domicilio comercial
condicionIvastring✅Condición frente al IVA
cuitstring✅CUIT (11 dígitos sin guiones)
iibbstring✅Ingresos Brutos
fechaInicioActividadesstring✅Fecha inicio actividades (YYYYMMDD)
contactostring—Contacto (email, teléfono, web)

ReceptorData ​

CampoTipoRequeridoDescripción
razonSocialstring✅Razón social
condicionIvastring✅Condición frente al IVA
documentoTipostring✅Tipo de documento ("CUIT", "DNI")
documentoNrostring✅Número de documento
domiciliostring—Domicilio
cuitPaisstring—CUIT país destino (Factura E)
idImpositivostring—ID impositivo exterior (Factura E)

InvoiceItem ​

CampoTipoRequeridoDescripción
descripcionstring✅Descripción del producto/servicio
cantidadnumber✅Cantidad
unidadMedidastring✅Unidad de medida
precioUnitarionumber✅Precio unitario
subtotalnumber✅Subtotal del item
codigostring—Código del producto
bonificacionnumber—Porcentaje de bonificación
importeBonificacionnumber—Importe bonificación pre-calculado (si no se provee, se calcula)
alicuotaIvanumber—Alícuota de IVA (solo Factura A)

IvaDetail ​

CampoTipoRequeridoDescripción
idnumber✅ID de la alícuota
descripcionstring✅Descripción ("21%", "10.5%", etc.)
baseImponiblenumber✅Base imponible
importenumber✅Importe del IVA

TributoDetail ​

CampoTipoRequeridoDescripción
idnumber✅ID del tributo
descripcionstring✅Descripción del tributo
baseImponiblenumber✅Base imponible
alicuotanumber✅Alícuota en porcentaje
importenumber✅Importe del tributo

ComprobanteAsociado ​

CampoTipoRequeridoDescripción
tiponumber✅Tipo de comprobante
puntoVentanumber✅Punto de venta
numeronumber✅Número de comprobante
cuitstring—CUIT del emisor original
fechastring—Fecha del comprobante

Diferencias por letra ​

Factura A ​

  • 7 columnas: Cantidad, Producto/Servicio, Precio unit, %Bonif., $ Bonif., %IVA, Subtotal s/IVA
  • Tabla de Otros Tributos con 5 filas fijas
  • Desglose de IVA por alícuota (27%, 21%, 10.5%, 5%, 2.5%, 0%)
  • Leyenda "Son PESOS ARGENTINOS..."

Factura B ​

  • 6 columnas (sin %IVA)
  • Tabla de Otros Tributos con 5 filas fijas
  • Subtotal + Importe Otros Tributos + Importe Total
  • Leyenda "Son PESOS ARGENTINOS..."

Factura C ​

  • 6 columnas (sin %IVA)
  • Sin tabla de tributos
  • Importe Neto Gravado + Importe Total
  • Leyenda "Son PESOS ARGENTINOS..."

Factura E (Exportación) ​

  • 5 columnas: Ítem, Descripción, Cantidad, Precio Unit. (USD), Total por ítem (USD)
  • Sección receptor con "Señor(es)", CUIT País, ID Impositivo, Divisa, Destino del Comprobante
  • Tabla de Forma de Pago / Fecha de Pago / Incoterms
  • Tipo de Cambio + Divisa en el footer
  • Importe Total en moneda extranjera

Factura M ​

  • Mismo layout que Factura B (6 columnas, sin desglose IVA)

Cálculos automáticos ​

El template muestra los valores que le pasás, sin recalcular totales. Hay dos excepciones con override opcional:

CálculoSe calcula comoOverride
Bonificación ($) por itemprecioUnitario × cantidad × (bonificacion / 100)item.importeBonificacion
Total en ARS (moneda ext.)importeTotal × cotizacionimporteTotalPesos

Si proveés el campo de override, se usa directamente sin calcular.


Ejemplo Factura B ​

ts
const facturaB: InvoiceData = {
  emisor: {
    razonSocial: "Emisor de Prueba",
    domicilioComercial: "Av. Belgrano 789, CABA",
    condicionIva: "IVA Responsable Inscripto",
    cuit: "20111111117",
    iibb: "901-234567-0",
    fechaInicioActividades: "20150601",
  },
  receptor: {
    razonSocial: "Receptor de Prueba",
    condicionIva: "Consumidor Final",
    documentoTipo: "DNI",
    documentoNro: "31456789",
  },
  cbteTipo: 6,
  cbteLetra: "B",
  puntoVenta: 1,
  cbteDesde: 500,
  cbteHasta: 500,
  cbteFecha: "20240610",
  concepto: 2,
  fechaServicioDesde: "20240501",
  fechaServicioHasta: "20240531",
  fechaVtoPago: "20240625",
  items: [
    {
      descripcion: "Liquidación de sueldos - Mayo 2024",
      cantidad: 1,
      unidadMedida: "servicio",
      precioUnitario: 25000,
      subtotal: 25000,
    },
  ],
  importeNetoGravado: 25000,
  importeIva: 0,
  importeTotal: 25000,
  cae: "74512345678902",
  caeFechaVencimiento: "20240620",
};

Ejemplo Factura E (Exportación) ​

ts
const facturaE: InvoiceData = {
  emisor: {
    razonSocial: "Exportadora de Prueba S.A.",
    domicilioComercial: "Puerto Madero 100, CABA",
    condicionIva: "IVA Responsable Inscripto",
    cuit: "30111111118",
    iibb: "12-3456789-0",
    fechaInicioActividades: "20200101",
  },
  receptor: {
    razonSocial: "Empresa Exterior de Prueba",
    domicilio: "1234 Market St, San Francisco, CA, USA",
    condicionIva: "Cliente del Exterior",
    documentoTipo: "CUIT",
    documentoNro: "55000000000",
    cuitPais: "55000002126 (ESTADOS UNIDOS - Persona Jurídica)",
    idImpositivo: "261907945",
  },
  cbteTipo: 19,
  cbteLetra: "E",
  puntoVenta: 5,
  cbteDesde: 10,
  cbteHasta: 10,
  cbteFecha: "20240701",
  concepto: 1,
  moneda: "DOL",
  cotizacion: 1391.5,
  condicionIvaExportacion: "IVA EXENTO OPERACIÓN DE EXPORTACIÓN",
  divisa: "USD - Dólar Estadounidense",
  destinoComprobante: "ESTADOS UNIDOS",
  formaPago: "Transferencia SWIFT - Moneda Extranjera",
  fechaPago: "20240715",
  incoterms: "FOB",
  items: [
    {
      descripcion: "Consulting services",
      cantidad: 40,
      unidadMedida: "horas",
      precioUnitario: 150,
      subtotal: 6000,
    },
  ],
  importeNetoGravado: 6000,
  importeIva: 0,
  importeTotal: 6000,
  cae: "74512345678903",
  caeFechaVencimiento: "20240711",
};

Template personalizado ​

Template Handlebars (string) ​

ts
const miTemplate = `
<html>
<body>
  <h1>{{data.emisor.razonSocial}}</h1>
  <p>{{descTipo data.cbteTipo}} {{data.cbteLetra}}</p>
  <p>Nro: {{voucherNumber data.puntoVenta data.cbteDesde}}</p>
  <p>Fecha: {{formatDate data.cbteFecha}}</p>
  <p>Total: {{formatCurrency data.importeTotal data.moneda}}</p>
  {{#if qrDataUrl}}<img src="{{qrDataUrl}}" width="100" />{{/if}}
</body>
</html>
`;

const generator = new InvoicePdfGenerator({ template: miTemplate });

Template función ​

ts
function MiTemplate({ data, options, qrDataUrl }: InvoiceTemplateProps): string {
  return `
    <html>
      <body>
        <h1>${data.emisor.razonSocial}</h1>
        <p>Total: $${data.importeTotal}</p>
      </body>
    </html>
  `;
}

const generator = new InvoicePdfGenerator({ template: MiTemplate });

Helpers Handlebars disponibles ​

HelperDescripciónEjemplo
formatDateYYYYMMDD → DD/MM/YYYY{{formatDate data.cbteFecha}}
formatCuitCUIT con guiones{{formatCuit data.emisor.cuit}}
formatCurrencyImporte con símbolo de moneda{{formatCurrency data.importeTotal data.moneda}}
formatUnitPricePrecio unitario formateado{{formatUnitPrice item.precioUnitario}}
formatCotizacionCotización con 3 decimales{{formatCotizacion data.cotizacion}}
voucherNumberNro comprobante (00003-00000152){{voucherNumber data.puntoVenta data.cbteDesde}}
descTipoDescripción del tipo de comprobante{{descTipo data.cbteTipo}}
currencyNameNombre completo de la moneda{{currencyName data.moneda}}
currencyCodeCódigo ISO de la moneda (ARS, USD){{currencyCode data.moneda}}
currencyShortNameNombre corto de la moneda{{currencyShortName data.moneda}}
numberToWordsImporte en letras (español){{numberToWords data.importeTotal}}
docNumberFormatea doc (CUIT con guiones si 11 dígitos){{docNumber data.receptor.documentoNro}}
padStartRellena con ceros{{padStart data.puntoVenta 5}}
bonAmountCalcula bonificación (con override){{bonAmount precio cant bonif importeBonif}}
ivaImporteBusca importe de IVA por descripción{{ivaImporte data.iva "21%"}}
tributoImporteBusca importe de tributo por descripción{{tributoImporte data.tributos "..."}}
multiplyMultiplicación{{multiply data.importeTotal data.cotizacion}}
eq, neqIgualdad / desigualdad{{#if (eq data.cbteLetra "A")}}
gt, gteMayor / mayor o igual{{#if (gt data.importeTotal 1000)}}
or, and, notOperadores lógicos{{#or condA condB}}...{{/or}}
defaultValor por defecto si falsy{{default data.moneda "PES"}}
isNullComprueba si es null/undefined{{#if (isNull val)}}
lenLargo de un array{{len data.tributos}}

Monedas soportadas ​

CódigoMonedaSímbolo
PESPesos Argentinos (ARS)$
DOLDólares Estadounidenses (USD)US$

Cuando la moneda no es PES y se provee cotizacion, el PDF muestra una línea de conversión a pesos argentinos al pie del comprobante.

Made by Rodrigo Alcorta with ❤️