Migrar de EuroWin o Sage a Odoo: guía de migración para distribución

Caso real Cymit Química: estrategia, mapeo de datos, ETL, cutover y go-live al migrar de EuroWin y Access a Odoo en una empresa de distribución con más de dos millones de referencias.

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:

  1. Fase 0 (preparación): configuración de Odoo, maestros (clientes, proveedores, productos), configuración de contabilidad. Operativa en paralelo.
  2. Fase 1 (ventas y compras): pedidos de venta y compra en Odoo; facturación sigue en el sistema antiguo temporalmente o en paralelo.
  3. Fase 2 (almacén): gestión de stock y albaranes en Odoo. El inventario inicial se toma como punto de referencia.
  4. Fase 3 (contabilidad): cierre contable en el sistema antiguo y apertura de saldos en Odoo. Facturación completa en Odoo.
  5. 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 / SageCampo Odoo (res.partner)Notas
Código clienterefConservar como referencia interna
Nombre fiscalnameNormalizar mayúsculas/minúsculas
CIF/NIFvatAñadir prefijo ES si falta
Direcciónstreet, city, zip, state_id, country_idNormalizar usando catálogo de municipios INE
Teléfonophone / mobileVerificar formato E.164
EmailemailValidar sintaxis; descartar los inválidos
Tarifa / descuentoproperty_product_pricelistCrear lista de precios equivalente en Odoo
Forma de pagoproperty_payment_term_idCrear condiciones de pago equivalentes
Cuenta contableproperty_account_receivable_idMapear 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:

  1. 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.
  2. 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:

  1. Comunicar a todo el equipo que se activa el rollback.
  2. Desactivar el acceso de usuarios a Odoo (para que no generen datos que luego son inconsistentes).
  3. Reactivar EuroWin/Sage con los datos del backup del viernes.
  4. Documentar todos los movimientos que se hicieron en Odoo antes del rollback para reproducirlos en el sistema antiguo.
  5. 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

RiesgoProbabilidadImpactoMitigación
Datos de origen con más duplicados y errores de los esperadosAltaAlto (retrasa el proyecto)Auditoría de datos en las primeras semanas, no en la última
Rechazo del equipo al cambioMediaAlto (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 corteBajaCríticoBackups verificados antes del cutover, plan de rollback probado
Integración con otras herramientas que falla (tienda online, TPV, logística)MediaAltoInventariar todas las integraciones en la fase de diagnóstico, no al final
El proyecto se alarga indefinidamente (scope creep)AltaMedio (coste)Definir alcance cerrado para la migración base; mejoras en fases posteriores
Paralización por no poder acceder al históricoMediaBajo-MedioMantener 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.

¿Estás valorando migrar de EuroWin, Sage u otro ERP legacy a Odoo?

Solicitar auditoría de migración gratuita

Integrar IA en Odoo: lead scoring y modelos predictivos con Python
Guía técnica completa para conectar modelos de machine learning a Odoo CRM: desde el diseño del pipeline de datos hasta el scoring en tiempo real de oportunidades comerciales