Saltar al contenido
Hugo Méndez
  • PDF
  • Automatización
  • Node.js

Separar un PDF con cientos de comprobantes de pago y enviarlos a cada proveedor

Cómo automatizar el clásico: el banco o el ERP te da un solo PDF con todos los comprobantes. Extraer el texto por página, identificar a quién pertenece cada hoja, separarlo y enviarlo, con Node.js.

Hugo Méndez29 de septiembre de 20262 min de lectura

Tesorería genera los pagos del día y el sistema entrega un solo PDF con 300 comprobantes. Alguien lo parte a mano, renombra cada archivo y lo manda por correo a cada proveedor. Es una tarea perfecta para automatizar.

La idea

  1. Leer el texto de cada página.
  2. Encontrar en ese texto algo que identifique al destinatario: RFC, número de proveedor, folio.
  3. Agrupar páginas consecutivas del mismo destinatario (un comprobante puede ocupar dos hojas).
  4. Crear un PDF por grupo con un nombre claro.
  5. Enviarlo o dejarlo en la carpeta de cada proveedor.

Leer el texto por página

Con pdfjs-dist (el motor de PDF de Firefox) puedes extraer el texto de cada página en Node.js:

import { getDocument } from 'pdfjs-dist/legacy/build/pdf.mjs';

async function textoPorPagina(bytes) {
  const pdf = await getDocument({ data: new Uint8Array(bytes) }).promise;
  const paginas = [];
  for (let i = 1; i <= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const { items } = await page.getTextContent();
    paginas.push(items.map((it) => it.str).join(' '));
  }
  return paginas;
}

Identificar al destinatario

Un RFC mexicano tiene un patrón reconocible (3 o 4 letras, 6 dígitos de fecha y 3 caracteres de homoclave):

const RFC = /\b([A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3})\b/;

function agrupar(paginas, rfcPropio) {
  const grupos = [];
  paginas.forEach((texto, i) => {
    const rfcs = [...texto.matchAll(new RegExp(RFC, 'g'))].map((m) => m[1]).filter((r) => r !== rfcPropio);
    const clave = rfcs[0];
    const ultimo = grupos.at(-1);
    if (!clave && ultimo) ultimo.paginas.push(i);            // hoja de continuación
    else if (ultimo && ultimo.clave === clave) ultimo.paginas.push(i);
    else grupos.push({ clave: clave ?? 'SIN-IDENTIFICAR', paginas: [i] });
  });
  return grupos;
}

Excluye tu propio RFC (aparece en todos los comprobantes) y trata las páginas sin identificador como continuación de la anterior.

Crear un PDF por proveedor

pdf-lib copia páginas de un documento a otro sin perder calidad:

import { PDFDocument } from 'pdf-lib';

async function partir(bytes, grupos) {
  const origen = await PDFDocument.load(bytes);
  const salida = [];
  for (const g of grupos) {
    const doc = await PDFDocument.create();
    const paginas = await doc.copyPages(origen, g.paginas);
    paginas.forEach((p) => doc.addPage(p));
    salida.push({ nombre: `comprobante-${g.clave}.pdf`, bytes: await doc.save() });
  }
  return salida;
}

Lo que hace que sea confiable

  • Vista previa antes de enviar: una tabla con proveedor, páginas y a qué correo se va. Nadie quiere mandar el comprobante equivocado.
  • Casos sin identificar separados para revisión manual, nunca enviados “al azar”.
  • Catálogo de correos por proveedor tomado del ERP o de un catálogo propio.
  • Bitácora: qué se envió, a quién y cuándo.
  • PDFs escaneados: si el PDF es una imagen, no tiene texto. Ahí necesitas OCR (por ejemplo Tesseract) o pedir al sistema de origen un PDF con texto.

Lo que tomaba una mañana entera se vuelve un clic y una revisión de dos minutos. ¿Tienes un proceso así? Cuéntame.

Volver al blog

Servicios relacionados

¿Tienes un proceso que ya no aguanta más Excel?

Cuéntame qué necesitas. En menos de 24 horas hábiles te respondo con una idea clara de cómo resolverlo y cuánto cuesta.