Ir al contenido

Migración de datos a Odoo: CSV, external IDs y load()

Importar sin duplicar, sin perder relaciones y pudiendo repetir la carga las veces que haga falta
17 de octubre de 2026 por
Migración de datos a Odoo: CSV, external IDs y load()
ATEMI, Aitor Atencia

Odoo · Datos · Importación

Traer los datos de un ERP antiguo a Odoo nunca sale bien a la primera. Siempre aparece un cliente con el NIF mal escrito, un producto con una categoría que no existe o un pedido que apunta a un cliente borrado. La pregunta no es si habrá que repetir la carga, sino si repetirla duplicará todo. Con external IDs no lo hace.

Logo Odoo
Cada fila con su identificador del sistema de origen: la segunda carga actualiza en vez de duplicar.

La clave

La columna id hace la carga idempotente

Si el CSV que importas tiene una columna id, Odoo la trata como un external ID (el mismo mecanismo que los xml_id de los módulos). La primera vez crea el registro y guarda el identificador; las siguientes, encuentra el registro por ese identificador y lo actualiza. Usa el código del sistema antiguo con un prefijo propio y podrás relanzar la importación tantas veces como necesites.

id,name,vat,country_id/id,category_id/id
legacy.partner_1001,Ferretería Sol S.L.,ESB12345678,base.es,legacy.cat_retail
legacy.partner_1002,Talleres Martínez,ESB87654321,base.es,legacy.cat_workshop

Las relaciones usan el sufijo /id para referenciar otros registros por su external ID: base.es es España en cualquier base de datos, y legacy.cat_retail es la categoría que importaste antes. Así no dependes de nombres (que pueden repetirse) ni de IDs numéricos (que cambian entre entornos).

Orden

De los catálogos a los documentos

Un registro solo puede referenciar a otro que ya exista. El orden de carga sigue las dependencias: primero lo que no apunta a nada, al final los documentos con líneas.

  1. Catálogos: categorías de cliente, unidades de medida, impuestos que falten.
  2. Contactos: primero las empresas, después las personas que dependen de ellas (parent_id/id).
  3. Productos y sus variantes.
  4. Documentos: pedidos, y después sus líneas (order_id/id).

Por código

load(): el importador sin la interfaz

El asistente de importación de Odoo usa por debajo el método load(), que puedes llamar desde un script de odoo-bin shell o por RPC. Acepta la lista de columnas y las filas, aplica las mismas conversiones que la interfaz y no lanza excepciones: devuelve los mensajes de error. Si hay uno solo, revierte el lote entero y ids vale False.

import csv

with open("/data/partners.csv", newline="", encoding="utf-8") as fh:
    reader = csv.reader(fh)
    fields = next(reader)
    rows = list(reader)

BATCH = 500
for start in range(0, len(rows), BATCH):
    result = env["res.partner"].load(fields, rows[start:start + BATCH])
    errors = [m for m in result["messages"] if m["type"] == "error"]
    if errors:
        for m in errors:
            print(start + m["rows"]["from"], m["message"])
        raise SystemExit("Lote con errores: corrige el CSV y relanza")
    env.cr.commit()   # cada lote correcto queda guardado

Como la carga es idempotente gracias a la columna id, si el lote 7 falla puedes corregir el CSV y relanzar el script completo: los seis primeros lotes se actualizarán sin duplicarse.

Historial

Lo que no se puede importar tal cual

Algunas cosas del sistema antiguo no tienen hueco directo. create_date y write_date los pone siempre el ORM, así que la fecha original de un pedido va en su campo de negocio (date_order), no en la fecha de creación. Y los documentos contables con efectos fiscales (facturas ya declaradas) normalmente no se reimportan como facturas nuevas: se cargan como saldos de apertura y el detalle histórico se conserva como adjunto o en el sistema antiguo.

HerramientaÚsala paraNo es para
Asistente de importaciónCargas puntuales con revisión visual y botón de pruebaDecenas de miles de filas o cargas repetidas
load() en un scriptMigraciones grandes, por lotes, repetiblesDatos que necesitan lógica compleja por fila
XML/CSV en un móduloDatos de configuración que forman parte del productoDatos de negocio del cliente
base_import_moduleInstalar módulos de solo datos (sin Python) desde un zipMigrar datos de negocio

Resumen

Da a cada fila un external ID con prefijo propio y la carga se puede repetir sin duplicar. Referencia las relaciones con /id, carga en orden de dependencias y, para volúmenes grandes, usa load() por lotes con commit tras cada lote correcto. Las fechas originales van en campos de negocio, no en create_date. Más sobre external IDs en secuencias, referencias XML y env.ref().

en Odoo
Quitar modelos y campos en Odoo sin romper la actualización
Tablas huérfanas, registros noupdate que no se borran y defaults que reescriben el pasado