Todo sistema empresarial termina necesitando “subir un Excel”. Y todo importador ingenuo termina fallando con los archivos reales de los usuarios, que nunca se parecen a la plantilla. Estos son los casos que más veo y cómo los resuelvo.
1. El encabezado no está en la primera fila
El usuario agregó un título, un logo o dos filas de notas arriba de la tabla. Tu importador toma “REPORTE MENSUAL” como nombre de columna y todo falla.
Solución: busca la fila de encabezados en las primeras filas, comparándola contra las columnas que esperas:
import * as XLSX from 'xlsx';
const norm = (v) => String(v ?? '').normalize('NFD').replace(/[̀-ͯ]/g, '').trim().toLowerCase();
function encontrarEncabezado(filas, esperadas, maxFilas = 15) {
const set = esperadas.map(norm);
let mejor = { fila: -1, aciertos: 0 };
filas.slice(0, maxFilas).forEach((fila, i) => {
const aciertos = fila.filter((c) => set.includes(norm(c))).length;
if (aciertos > mejor.aciertos) mejor = { fila: i, aciertos };
});
return mejor.aciertos >= Math.ceil(set.length / 2) ? mejor.fila : -1;
}
const wb = XLSX.read(buffer, { type: 'buffer' });
const filas = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1, raw: false, defval: '' });
const h = encontrarEncabezado(filas, ['Código', 'Descripción', 'Cantidad']);
Normalizar (sin acentos, minúsculas, sin espacios) evita que “Descripción” y “DESCRIPCION “ sean columnas distintas.
2. IDs largos convertidos en notación científica
Un código de 16 dígitos aparece como 1.23456789012346E+15. Excel guarda los números con 15 dígitos de precisión: si la celda ya se guardó como número, los últimos dígitos se perdieron y no hay forma de recuperarlos.
- Lee con
raw: falsepara recibir el texto tal como se ve. - Detecta el patrón
E+y rechaza la fila con un mensaje claro (“formatea la columna como texto y vuelve a pegar los datos”). - En tus plantillas, deja esas columnas con formato de texto desde el inicio.
3. CSV que llega “en una sola columna”
Excel en español guarda CSV con punto y coma. Si tu lector espera comas, recibes una sola columna con todo pegado. Detecta el separador contando en la primera línea:
const primera = texto.split(/\r?\n/)[0];
const sep = [';', ',', '\t', '|'].sort((a, b) => primera.split(b).length - primera.split(a).length)[0];
4. Acentos rotos
Descripción en lugar de Descripción: el archivo venía en Windows-1252 y lo leíste como UTF-8. Intenta UTF-8 y, si aparecen caracteres de reemplazo (�), vuelve a decodificar como windows-1252. Quita también el BOM () del inicio.
5. Fechas que son números
Una fecha puede llegar como 45567 (número de serie de Excel), 03/10/2026 (¿marzo u octubre?) o texto libre. Define un formato por columna, conviértelo explícitamente y reporta las filas que no cumplen, en vez de adivinar.
6. Importar sin ver antes
El importador que guarda directo es una bomba. El flujo que uso siempre:
- Subir el archivo.
- Vista previa: filas válidas, filas con error (con el motivo) y qué se va a crear o actualizar.
- Confirmar, guardando en bloque y con bitácora de quién importó qué.
- Opción de importar solo lo faltante, para corregir el archivo y volver a subirlo sin duplicar.
Checklist de un buen importador
- Detecta la fila de encabezados.
- Normaliza nombres de columna.
- Detecta separador y codificación.
- Protege IDs largos y fechas.
- Vista previa con errores por fila.
- Idempotente: volver a subir el mismo archivo no duplica.
- Bitácora de la importación.
Si tu equipo pierde horas corrigiendo importaciones fallidas, esto se puede resolver.