Por qué migrar de EuroWin o Sage a Odoo en distribución
EuroWin y Sage son dos de los ERP más implantados en PYMEs españolas de los sectores de distribución, comercio y fabricación ligera. Son herramientas que resolvieron muy bien los problemas de los años noventa y dos mil: contabilidad, facturación, pedidos de compra y venta, gestión de almacén. Pero en 2026 han envejecido de una forma que no tiene solución de parche: su arquitectura impide la integración real con plataformas de comercio electrónico, la automatización de catálogos, la visibilidad en tiempo real del negocio y la escalabilidad que exigen las operativas modernas de distribución.
La migración que describo en esta guía no es teórica. Lideré personalmente la migración de Cymit Química desde EuroWin + Microsoft Access + CRM Visual hacia Odoo cuando era CEO de la empresa. El resultado fue un crecimiento de facturación de 2 M€ a 4 M€ y, finalmente, la adquisición de la empresa por el Grupo PALEX. Lo que sigue es la metodología real que usé, con los problemas concretos que aparecieron y cómo los resolvimos.
Diagnóstico previo: por qué muchas migraciones fracasan
El fracaso más frecuente en migraciones de ERP no es técnico: es de planificación. Los proyectos que he visto fallar comparten patrones comunes:
- Subestimar la calidad de los datos de origen: EuroWin y Sage almacenan años de datos introducidos manualmente, con inconsistencias, duplicados, clientes mal clasificados y productos con referencias rotas. Limpiar esos datos lleva más tiempo que la propia migración técnica.
- Intentar migrar todo de golpe sin plan de rollback: el «big bang» sin red de seguridad es la receta del desastre en empresas con operativa diaria que no puede parar.
- No involucrar al equipo operativo desde el inicio: los usuarios finales conocen las particularidades del negocio que ningún consultor externo va a descubrir leyendo la documentación. Su input en el mapeo de datos es imprescindible.
- Migrar datos históricos que nadie va a usar: migrar 10 años de histórico de movimientos de almacén tiene un coste alto y un valor cuestionable. Definir con criterio de negocio qué histórico es necesario ahorra semanas de trabajo.
Estrategia de migración: big bang vs por fases
Big bang
La migración en big bang consiste en migrar todos los datos y hacer el corte a Odoo en una única fecha. El sistema antiguo se congela (no se admiten más operaciones), se ejecuta la migración completa y se activa Odoo como único sistema operativo.
Ventajas: no hay período de doble operación; la complejidad de mantener dos sistemas sincronizados desaparece el día del corte. Es más sencilla de gestionar operativamente una vez ejecutada.
Riesgos: si algo falla el día del corte (datos corruptos, integración que no funciona, usuarios que no saben operar el nuevo sistema), toda la operativa se detiene. Requiere un plan de rollback claro y testado.
Cuándo es adecuada: empresas de tamaño pequeño o mediano (hasta ~30 usuarios) con catálogos de productos relativamente acotados, donde el riesgo operativo de una parada corta es asumible.
Migración por fases
La migración por fases activa módulos de Odoo de forma incremental, manteniendo el sistema antiguo para las áreas no migradas hasta que cada fase está consolidada.
Un orden típico para una empresa de distribución:
- Fase 0 (preparación): configuración de Odoo, maestros (clientes, proveedores, productos), configuración de contabilidad. Operativa en paralelo.
- Fase 1 (ventas y compras): pedidos de venta y compra en Odoo; facturación sigue en el sistema antiguo temporalmente o en paralelo.
- Fase 2 (almacén): gestión de stock y albaranes en Odoo. El inventario inicial se toma como punto de referencia.
- Fase 3 (contabilidad): cierre contable en el sistema antiguo y apertura de saldos en Odoo. Facturación completa en Odoo.
- Fase 4 (cierre del sistema antiguo): histórico migrado o archivado en consulta, sistema antiguo desactivado.
Cuándo es adecuada: empresas de mayor tamaño, operativas complejas con muchas integraciones, o cuando el equipo necesita tiempo de aprendizaje sin la presión de que el negocio dependa ya de Odoo.
La decisión en Cymit
En Cymit Química optamos por una aproximación híbrida: fases para la operativa principal (ventas, compras, almacén) pero con fecha de corte definida y firme para la contabilidad. La razón fue que teníamos tres sistemas desconectados (EuroWin, Access, CRM Visual) y no podía alargarse indefinidamente un período de doble operación en tres frentes simultáneos. Definir fases pero con deadlines concretos fue lo que permitió completar la migración sin que se conviertiera en un proyecto sin fin.
Mapeo de datos: de EuroWin y Sage a Odoo
Clientes y contactos
EuroWin almacena clientes en tablas propias con campos específicos del sistema que no tienen correspondencia directa en Odoo. Los problemas más frecuentes:
- Duplicados: un mismo cliente puede tener dos fichas (una creada por el equipo de ventas, otra por el de facturación) con ligeras variaciones en el nombre o en el CIF. La deduplicación es manual o semiautomática y requiere decisión humana.
- Clientes con múltiples direcciones: en EuroWin la dirección es un campo del cliente; en Odoo el modelo permite múltiples contactos (matriz/child) con direcciones distintas. Hay que decidir cómo se mapea esa estructura.
- Clasificaciones y tarifas: las tarifas por cliente (descuentos, precios especiales) deben migrarse a las listas de precios de Odoo, que tienen una estructura más potente pero también más compleja.
Mapeo orientativo de campos de cliente:
| Campo EuroWin / Sage | Campo Odoo (res.partner) | Notas |
|---|---|---|
| Código cliente | ref | Conservar como referencia interna |
| Nombre fiscal | name | Normalizar mayúsculas/minúsculas |
| CIF/NIF | vat | Añadir prefijo ES si falta |
| Dirección | street, city, zip, state_id, country_id | Normalizar usando catálogo de municipios INE |
| Teléfono | phone / mobile | Verificar formato E.164 |
| Validar sintaxis; descartar los inválidos | ||
| Tarifa / descuento | property_product_pricelist | Crear lista de precios equivalente en Odoo |
| Forma de pago | property_payment_term_id | Crear condiciones de pago equivalentes |
| Cuenta contable | property_account_receivable_id | Mapear al plan de cuentas de Odoo |
Productos y catálogo
El catálogo de productos es habitualmente el activo más complejo de migrar en empresas de distribución. En Cymit, con más de dos millones de referencias, fue el trabajo de mayor volumen del proyecto. Los puntos críticos:
- Referencia interna vs referencia de proveedor: EuroWin suele almacenar la referencia del proveedor como referencia principal. Odoo separa la referencia interna (
default_code) de las referencias de proveedor (product.supplierinfo). Hay que decidir qué se convierte en qué. - Categorías y estructura de catálogo: la jerarquía de categorías de EuroWin raramente encaja directamente con la que necesitas en Odoo. Es una oportunidad para rediseñar la taxonomía del catálogo.
- Unidades de medida: Odoo soporta múltiples UoM con conversión; EuroWin y Sage tienen soporte variable. Los productos vendidos en cajas pero almacenados en unidades requieren configuración específica.
- Precios y tarifas: los precios de venta y coste en EuroWin deben mapearse a las pricelists de Odoo, que funcionan de forma distinta (reglas basadas en categorías, cantidades, fechas).
Stock inicial
El inventario inicial es el punto de partida del almacén en Odoo. Las opciones son:
- Tomar el stock actual del ERP antiguo como inventario inicial: se migra la cantidad en stock de cada producto a Odoo mediante un ajuste de inventario. Es el método más rápido pero asume que el stock del sistema antiguo es correcto.
- Hacer un recuento físico antes del corte: más laborioso pero elimina las discrepancias acumuladas. Recomendado si hay motivos para dudar de la fiabilidad del stock en EuroWin o Sage.
En Cymit hicimos un recuento selectivo: productos de alto valor o alta rotación se recontaron físicamente; el resto se tomó del sistema con un factor de corrección estimado. Las diferencias se regularizaron durante el primer mes de operación en Odoo.
Contabilidad e histórico
La migración contable es la más delicada y la que más criterio de negocio requiere. Las decisiones clave son:
- Fecha de apertura: el saldo inicial en Odoo debe cuadrar con el balance de cierre del sistema antiguo. Habitualmente se migra al cierre de un ejercicio fiscal para simplificar la conciliación.
- Histórico de facturas: las facturas emitidas antes de la fecha de corte no se migran como documentos Odoo (es muy costoso y poco útil). Se mantienen en el sistema antiguo en modo consulta. Solo se migran los saldos pendientes de cobro/pago (cartera de efectos) como apuntes de apertura.
- Plan de cuentas: el plan de cuentas de EuroWin o Sage debe mapearse al plan contable español en Odoo. Odoo incluye la localización española (l10n_es) con el PGC completo; el trabajo consiste en mapear las cuentas del sistema antiguo a las equivalentes del PGC.
ETL: extracción, transformación y carga
Extracción desde EuroWin
EuroWin no tiene una API moderna. Las vías de extracción son:
- Exportación a Excel/CSV desde la propia aplicación: la forma más sencilla para volúmenes pequeños. Limitada en flexibilidad y sin acceso a todas las tablas.
- Acceso directo a la base de datos de EuroWin: EuroWin almacena sus datos en una base de datos SQL (SQL Server o similar, según la versión). Con acceso de solo lectura a esa BD, se pueden extraer todos los datos con SQL. Esta es la vía recomendada para migraciones completas.
Ejemplo de extracción de clientes desde la BD de EuroWin (estructura aproximada, varía por versión):
-- Extraer clientes de EuroWin (SQL Server)
SELECT
c.CodCliente AS codigo_externo,
c.RazonSocial AS nombre,
c.CIF AS cif,
c.Direccion AS calle,
c.CodPostal AS cp,
c.Poblacion AS ciudad,
c.Telefono AS telefono,
c.Email AS email,
c.CodFormaPago AS forma_pago_codigo,
c.CodTarifa AS tarifa_codigo,
c.Observaciones AS notas_internas
FROM
Clientes c
WHERE
c.Activo = 1
ORDER BY
c.CodCliente;
Limpieza y deduplicación
El paso más lento y más crítico del proceso ETL es la limpieza de datos. Un script de Python para las tareas más comunes:
import pandas as pd
import re
# Cargar el CSV exportado de EuroWin
df = pd.read_csv('clientes_eurowin.csv', encoding='latin-1', sep=';')
# 1. Normalizar CIF: añadir prefijo ES si falta
def normalizar_cif(cif):
if pd.isna(cif) or str(cif).strip() == '':
return False
cif = str(cif).strip().upper().replace(' ', '').replace('-', '')
if not cif.startswith('ES'):
cif = 'ES' + cif
return cif
df['vat'] = df['cif'].apply(normalizar_cif)
# 2. Normalizar nombre: Title Case, quitar espacios dobles
df['name'] = df['nombre'].str.strip().str.title()
df['name'] = df['name'].str.replace(r'\s+', ' ', regex=True)
# 3. Validar emails
EMAIL_REGEX = re.compile(r'^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$')
df['email_valido'] = df['email'].apply(
lambda x: str(x).strip().lower() if pd.notna(x) and EMAIL_REGEX.match(str(x).strip()) else False
)
# 4. Detectar duplicados por CIF (el identificador más fiable)
duplicados_cif = df[df.duplicated(subset=['vat'], keep=False) & (df['vat'] != False)]
print(f'Clientes con CIF duplicado: {len(duplicados_cif)}')
duplicados_cif.to_csv('revisar_duplicados_cif.csv', index=False)
# 5. Detectar duplicados por nombre (fuzzy: nombres muy similares)
# Requiere: pip install rapidfuzz
from rapidfuzz import fuzz
nombres = df['name'].dropna().unique().tolist()
posibles_duplicados = []
for i, n1 in enumerate(nombres):
for n2 in nombres[i+1:]:
if fuzz.ratio(n1, n2) > 85:
posibles_duplicados.append({'nombre_1': n1, 'nombre_2': n2, 'similitud': fuzz.ratio(n1, n2)})
pd.DataFrame(posibles_duplicados).to_csv('revisar_duplicados_nombre.csv', index=False)
print(f'Pares de nombres con similitud >85%: {len(posibles_duplicados)}')
print('\nResumen:')
print(f' Total clientes exportados: {len(df)}')
print(f' Clientes con CIF válido: {(df['vat'] != False).sum()}')
print(f' Clientes con email válido: {(df['email_valido'] != False).sum()}')
Carga en Odoo vía API XML-RPC
Odoo dispone de una API XML-RPC completa que permite crear y actualizar registros de forma programática. Es la vía recomendada para la carga de datos de migración (en lugar de importaciones CSV manuales, que son propensas a errores y no trazables):
import xmlrpc.client
import pandas as pd
# Conexión a Odoo
ODOO_URL = 'https://odoo.tudominio.com'
DB = 'odoo_produccion'
USER = 'admin@tudominio.com'
PASSWORD = 'la_password_del_admin'
common = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/common')
uid = common.authenticate(DB, USER, PASSWORD, {})
models = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/object')
def crear_cliente_odoo(row, pricelist_map, payment_term_map):
'''Crea un cliente en Odoo desde una fila del DataFrame limpio.'''
vals = {
'name': row['name'],
'customer_rank': 1,
'ref': str(row['codigo_externo']),
'street': row.get('calle', ''),
'zip': str(row.get('cp', '')),
'city': row.get('ciudad', ''),
'country_id': 69, # España — ID verificar en tu instancia
'lang': 'es_ES',
}
if row['vat']:
vals['vat'] = row['vat']
if row['email_valido']:
vals['email'] = row['email_valido']
if row.get('telefono'):
vals['phone'] = str(row['telefono'])
# Mapear tarifa y forma de pago usando los diccionarios de mapeo
tarifa_codigo = row.get('tarifa_codigo')
if tarifa_codigo and tarifa_codigo in pricelist_map:
vals['property_product_pricelist'] = pricelist_map[tarifa_codigo]
pago_codigo = row.get('forma_pago_codigo')
if pago_codigo and pago_codigo in payment_term_map:
vals['property_payment_term_id'] = payment_term_map[pago_codigo]
partner_id = models.execute_kw(
DB, uid, PASSWORD,
'res.partner', 'create', [vals]
)
return partner_id
# Ejecutar la carga con manejo de errores
df_limpio = pd.read_csv('clientes_listos_para_odoo.csv')
errores = []
ok = 0
for idx, row in df_limpio.iterrows():
try:
pid = crear_cliente_odoo(row, pricelist_map={}, payment_term_map={})
ok += 1
if ok % 100 == 0:
print(f'Cargados {ok} clientes...')
except Exception as e:
errores.append({'fila': idx, 'codigo': row['codigo_externo'], 'error': str(e)})
print(f'\nCarga completada: {ok} clientes OK, {len(errores)} errores.')
pd.DataFrame(errores).to_csv('errores_carga_clientes.csv', index=False)
Validación y reconciliación
Cargar los datos no es el final. La validación es el paso que determina si la migración es realmente correcta o si hay problemas que solo aparecerán semanas después en producción.
Las verificaciones mínimas tras la carga:
- Conteo de registros: el número de clientes, productos y proveedores en Odoo debe coincidir con el del sistema de origen, menos los registros que se eliminaron durante la limpieza. Documentar la diferencia y justificarla.
- Reconciliación de saldos contables: el saldo total de cuentas por cobrar en Odoo debe cuadrar con el del sistema antiguo a la fecha de corte. Cualquier diferencia debe tener una explicación (apunte manual, diferencia de redondeo, etc.).
- Verificación de inventario: el valor total del inventario en Odoo debe coincidir con el del sistema antiguo. Muestreo de productos de alto valor para verificar individualmente.
- Prueba de ciclo completo: crear un pedido de prueba en Odoo de principio a fin (pedido → albarán → factura → cobro) con datos reales para verificar que el flujo funciona correctamente antes del go-live.
- Validación con usuarios clave: que los responsables de ventas, almacén y contabilidad revisen sus datos en Odoo antes del corte. Ellos detectarán problemas que ningún script automatizado encontrará.
Plan de corte (cutover)
El cutover es el momento de máximo riesgo y máxima planificación. Un buen plan de corte tiene minuto a minuto lo que va a ocurrir, quién lo ejecuta y qué comprobación confirma que ese paso está hecho correctamente.
Semana previa al corte
- Congelar cambios en el sistema antiguo (no se dan de alta nuevos clientes ni productos en EuroWin/Sage).
- Ejecutar la migración completa en entorno de preproducción para verificar que los scripts funcionan sin errores.
- Formacipon final de usuarios clave (responsables de cada área).
- Preparar el plan de rollback: qué hacer si el lunes por la mañana Odoo no funciona correctamente.
El fin de semana del corte
# Plan de corte — ejemplo Cymit Química (simplificado)
Viernes 18:00 — Cierre operativo en EuroWin
- Verificar que no hay pedidos pendientes de procesar
- Hacer dump final de EuroWin (backup completo)
- Tomar inventario de stock final
Viernes 20:00 — Extracción de datos finales
- Extraer clientes, productos, stock, saldos contables
- Ejecutar scripts de limpieza y transformación
- Generar ficheros de carga para Odoo
Sábado 08:00 — Carga en Odoo producción
- Cargar maestros (clientes, proveedores, productos)
- Ajuste de inventario inicial
- Apertura de saldos contables
- Verificar conteos y reconciliaciones
Sábado 14:00 — Validación con usuarios clave
- Responsable de ventas verifica sus clientes y tarifas
- Responsable de almacén verifica stock
- Contable verifica saldos de apertura
Sábado 18:00 — Decisión GO / NO-GO
- Si todo cuadra: GO (Odoo en producción el lunes)
- Si hay problemas críticos: NO-GO (EuroWin sigue activo, analizar)
Domingo — Buffer para correcciones menores
- Resolver incidencias detectadas el sábado
- Preparar soporte reforzado para el lunes
Lunes 08:00 — Go-live
- Odoo es el único sistema operativo
- Soporte técnico disponible todo el día
Plan de rollback
El plan de rollback debe estar documentado y ser ejecutable sin depender del consultor de migración. Si el lunes por la mañana hay un problema crítico en Odoo que impide la operativa, los pasos deben ser claros:
- Comunicar a todo el equipo que se activa el rollback.
- Desactivar el acceso de usuarios a Odoo (para que no generen datos que luego son inconsistentes).
- Reactivar EuroWin/Sage con los datos del backup del viernes.
- Documentar todos los movimientos que se hicieron en Odoo antes del rollback para reproducirlos en el sistema antiguo.
- Análisis de causa raíz del problema y plan de corrección antes del siguiente intento de cutover.
Formación y gestión del cambio
En Cymit, el mayor obstáculo no fue técnico: fue la resistencia al cambio de un equipo que llevaba años trabajando con EuroWin y conocía sus atajos y sus limitaciones. La formación no puede ser un PDF y una sesión de dos horas.
Lo que funcionó:
- Formación en roles, no en funcionalidades: en lugar de enseñar «esto es Odoo», enseñar «así procesas un pedido de venta en tu nuevo sistema». Cada rol tiene un flujo específico que hay que entrenar hasta que sea automático.
- Entorno de formación con datos reales anonimizados: los usuarios aprenden mejor cuando ven sus propios clientes y productos, aunque estén anonimizados. Un entorno de formación genérico no prepara para la realidad.
- Usuarios campeón por área: identificar a una persona de cada área (ventas, almacén, contabilidad) que se forme más en profundidad y sea el primer punto de contacto para sus compañeros. Esto reduce la dependencia del soporte externo drásticamente.
- Soporte reforzado los primeros 30 días: el período inmediatamente posterior al go-live es donde se concentran el 80% de las dudas. Tener soporte disponible y rápido durante ese mes marca la diferencia entre una migración que «fue bien» y una que «fue un desastre».
Riesgos principales y cómo mitigarlos
| Riesgo | Probabilidad | Impacto | Mitigación |
|---|---|---|---|
| Datos de origen con más duplicados y errores de los esperados | Alta | Alto (retrasa el proyecto) | Auditoría de datos en las primeras semanas, no en la última |
| Rechazo del equipo al cambio | Media | Alto (uso incorrecto del sistema) | Involucrar al equipo desde la fase de diseño, no solo en la formación |
| Pérdida de datos en el corte | Baja | Crítico | Backups verificados antes del cutover, plan de rollback probado |
| Integración con otras herramientas que falla (tienda online, TPV, logística) | Media | Alto | Inventariar todas las integraciones en la fase de diagnóstico, no al final |
| El proyecto se alarga indefinidamente (scope creep) | Alta | Medio (coste) | Definir alcance cerrado para la migración base; mejoras en fases posteriores |
| Paralización por no poder acceder al histórico | Media | Bajo-Medio | Mantener EuroWin/Sage en modo consulta durante 12 meses tras el corte |
Lecciones aprendidas en Cymit
Cinco años después del proceso, y habiendo visto otras migraciones desde entonces, las lecciones que llevaría a cualquier proyecto similar son:
- La calidad de los datos de origen determina el 70% del éxito del proyecto. Invierte tiempo en auditarlos al principio, no al final. Cada problema de calidad de datos que descubres en producción cuesta diez veces más que haberlo detectado antes.
- No migres lo que no uses. El histórico de facturas de hace 8 años que nadie consulta puede quedarse en el sistema antiguo en modo archivo. Migrar por completismo alarga el proyecto sin aportar valor real.
- Define el criterio de éxito antes de empezar. «La migración está bien» es ambiguo. «El saldo de cuentas por cobrar cuadra con el balance anterior con una tolerancia de 1€, los conteos de clientes y productos están dentro del 2% de variación justificada y se ha procesado con éxito un ciclo completo de venta-almacén-facturación» es un criterio verificable.
- El día del go-live no es el final: es el principio. Los primeros 90 días son el período de mayor riesgo y mayor necesidad de soporte. Planifica recursos para ese período antes de empezar el proyecto.
- La plataforma que construyes debe poder ser adquirida. En Cymit, la arquitectura limpia en Odoo fue parte del argumento de valoración en el proceso de M&A con PALEX. Si en tu horizonte existe la posibilidad de una transacción corporativa, la tecnología es parte del precio.