IAtechXSoftware y automatización

Tutoriales Técnicos con Código e Implementación

Guías paso a paso con código fuente para implementar automatizaciones reales en tu empresa.

Intermedio45 min de implementación

Automatizar la Extracción de Facturas en PDF con Make y un Modelo de Visión

Del correo a la hoja de cálculo, con validación antes de guardar: aritmética, dígito de control del NIF y detección de duplicados. Lo que falle va a revisión humana.

1Entender qué vas a procesar realmente

Antes de conectar nada, reúne treinta facturas reales que hayas recibido el último mes y míralas. Es el paso que casi todo el mundo se salta, y el que decide si esto funciona. Fíjate en: cuántos proveedores distintos hay, si cada uno usa su propia plantilla, si hay facturas escaneadas en lugar de PDF nativo, y si alguna tiene varias páginas o varias monedas. Si son cinco proveedores con plantillas estables, esto va bien. Si son cuarenta, la mitad en papel escaneado torcido, cuenta con bastante más trabajo — y quizá con procesar solo los cinco mayores, que suelen ser el 80% del volumen.

2Recibir las facturas en un solo sitio

Crea una dirección dedicada — por ejemplo `facturas@` — y pide a los proveedores que envíen ahí. Un filtro que reenvíe a esa dirección resuelve los que sigan enviando a tu correo personal. En Make, el disparador es el módulo de correo observando ese buzón, o Google Drive observando una carpeta. Prefiere el correo: elimina el paso manual de guardar el adjunto. Guarda el fichero original antes de procesarlo. Lo necesitarás cuando algo salga mal, y la normativa de archivo en España y Portugal exige conservar el documento, no solo los datos extraídos.

3Extraer los campos con un modelo de visión

Aquí es donde un modelo de lenguaje con visión hace lo que las reglas no hacen: lee plantillas que nunca ha visto. Pero solo funciona si le pides un formato rígido. Dos cuidados que evitan la mayoría de los problemas. Primero, pide JSON con un esquema fijo y di explícitamente qué hacer cuando un campo no existe — sin eso, el modelo inventa. Segundo, pon la temperatura a cero: quieres la misma respuesta para la misma factura, no creatividad. El prompt de abajo es deliberadamente restrictivo. La tentación es pedir "extrae los datos de la factura"; el resultado es inconsistente entre ejecuciones.

O `null` explícito é o que separa um campo em falta de um valor inventado.
{
  "model": "gpt-4o",
  "temperature": 0,
  "response_format": { "type": "json_object" },
  "messages": [
    {
      "role": "system",
      "content": "Extrais dados de faturas. Devolves APENAS JSON com este esquema exacto. Quando um campo não existir no documento, devolve null — nunca inventes nem estimes. Valores numéricos sem símbolo de moeda, ponto como separador decimal. Datas em ISO 8601."
    },
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "Esquema: {numero_fatura, data_emissao, data_vencimento, fornecedor_nome, fornecedor_nif, base_tributavel, taxa_iva, valor_iva, total, moeda}" },
        { "type": "image_url", "image_url": { "url": "data:application/pdf;base64,{{1.data}}" } }
      ]
    }
  ]
}

4Validar antes de guardar

Nunca guardes lo que devuelve el modelo sin verificar. Tres validaciones cogen casi todo. **¿Cuadra la aritmética?** Base imponible más IVA tiene que dar el total. Si no cuadra, algún campo se leyó mal. **¿El NIF es válido?** Tanto el NIF español como el portugués tienen dígito de control calculable. Comprobarlo detecta errores de lectura en segundos. **¿La factura ya se procesó?** El número más el NIF del proveedor forman una clave única. Sin esta comprobación, un reenvío del proveedor duplica el asiento. Lo que falle la validación no se descarta: va a una hoja aparte, para revisión humana. Automatizar no es decidir sin supervisión.

O que falha vai para revisão humana, não para o lixo nem para a contabilidade.
// Módulo de código no fluxo, entre a extracção e a gravação.
const f = JSON.parse(input.jsonDoModelo);

const erros = [];

// 1. A aritmética tem de fechar, com tolerância para arredondamento.
const somado = Number(f.base_tributavel) + Number(f.valor_iva);
if (Math.abs(somado - Number(f.total)) > 0.02) {
  erros.push(`total nao bate: ${somado.toFixed(2)} vs ${f.total}`);
}

// 2. Dígito de controlo do NIF espanhol (8 dígitos + letra).
function nifEspanholValido(nif) {
  const m = /^(\d{8})([A-Z])$/.exec((nif || '').toUpperCase().trim());
  if (!m) return false;
  return 'TRWAGMYFPDXBNJZSQVHLCKE'[Number(m[1]) % 23] === m[2];
}

// 3. Campos que nunca podem vir nulos.
for (const campo of ['numero_fatura', 'data_emissao', 'total']) {
  if (f[campo] == null) erros.push(`campo em falta: ${campo}`);
}

return { valida: erros.length === 0, erros, dados: f };

5Guardar, archivar y avisar

El destino depende de lo que ya uses. Una hoja de cálculo basta para empezar y es más fácil de auditar de lo que parece; un sistema de facturación evita el asiento manual después. Sea cual sea, guarda también el enlace al fichero original y la fecha en que se procesó. Cuando alguien pregunte de dónde salió un importe dentro de seis meses, eso es lo que responde. Y pon notificación de fallo desde el primer día. Un flujo que falla en silencio es peor que no tener flujo: sigues creyendo que las facturas se procesan cuando han dejado de procesarse.

6Medir si mereció la pena

Al cabo de un mes, compara con lo que mediste antes de empezar: cuántas facturas entraron, cuántas pasaron sin intervención, cuántas fueron a revisión, y cuánto tardó la revisión. Un flujo que procesa el 70% sin intervención ya ahorra tiempo real. Uno que manda la mitad a revisión está cambiando el trabajo de sitio, no reduciéndolo — y en ese caso el problema casi siempre está en los proveedores con plantillas inestables, que conviene tratar aparte. El coste del modelo por factura procesada es pequeño comparado con el tiempo humano, pero confírmalo con tu factura del proveedor, no con estimaciones.