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
- Leer el texto de cada página.
- Encontrar en ese texto algo que identifique al destinatario: RFC, número de proveedor, folio.
- Agrupar páginas consecutivas del mismo destinatario (un comprobante puede ocupar dos hojas).
- Crear un PDF por grupo con un nombre claro.
- 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.