Un "modelo financiero", en un archivo Excel (.xlsx) de 80 Megabytes inflado a base de macros en VBA, miles de BUSCARV cruzados y pestañas ocultas, se acaba corrompiendo. A Excel le pasa como a los políticos, se corromper por el éxito.

Llevo años diciendo que usar la hoja de cálculo de Microsoft como base de datos o motor de reglas de negocio es una bomba de relojería, pero en el mundo corporativo, la gente se aferra a Excel como si fuera una religión. Ayer decidí explicar que esto tiene que acabar: vamos a automatizar esto como informáticos de verdad.

El infierno de las macros y el BUSCARV

El problema de Excel no es la herramienta en sí (es fantástica para hacer prototipos y matemáticas rápidas), sino el ciclo de vida del software. Un Excel gigante no tiene control de versiones (¿quién no ha visto archivos llamados Reporte_Final_V3_definitivo_ESTE.xlsx?), no separa los datos de la presentación, y sus fórmulas son cajas negras inescrutables que no se pueden testear.

Me senté con los que entienden la Excel, reconstruimos la lógica de lo que hacía su famoso fichero a partir de una copia de seguridad antigua, y lo pasé a código.

Python al rescate

La lógica de negocio era sorprendentemente sencilla: extraer datos de tres tablas distintas, cruzarlos y aplicar un cálculo de comisiones. En lugar de hacer que Excel sufra cruzando 500.000 filas en memoria, delegué el trabajo pesado a nuestro Data Warehouse y escribí un script en Python para generar el reporte de forma automática.

Usando la librería estándar csv (y la maravillosa conexión pyodbc para SQL Server), el proceso es transparente, rápido e infalible:

import pyodbc
import csv

# Conexión directa a nuestro SQL Server
conn = pyodbc.connect('DRIVER={SQL Server};SERVER=servidor_dw;DATABASE=Analitica;UID=uid;PWD=secreto')
cursor = conn.cursor()

# El motor de base de datos hace el trabajo pesado
query = """
    SELECT 
        c.Nombre, 
        SUM(v.Importe) as TotalVentas,
        SUM(v.Importe) * 0.05 as ComisionCalculada
    FROM Fact_Ventas v
    JOIN Dim_Comerciales c ON v.IdComercial = c.IdComercial
    WHERE v.Fecha >= '2012-01-01'
    GROUP BY c.Nombre
"""

cursor.execute(query)

# Volcamos directamente a un CSV limpio que no se corrompe
with open('reporte_comisiones_generado.csv', 'wb') as f:
    writer = csv.writer(f)
    writer.writerow([column[0] for column in cursor.description]) # Cabeceras
    for row in cursor.fetchall():
        writer.writerow(row)

print "Reporte generado en 0.5 segundos. Adiós VBA."

Reflexión: La eterna batalla

Sé que esta es una batalla perdida a medias. Los usuarios de negocio necesitan la flexibilidad de toquetear las celdas, y nosotros los informáticos queremos rigidez y control. Herramientas visuales como QlikView o Tableau están ayudando a crear un puente, pero el instinto de "descargar a Excel" está grabado a fuego en el ADN corporativo. Supongo que seguiremos rescatando .xlsx corruptos hasta el fin de los tiempos, pero al menos es necesario poner límites.