Saltar al contenido
NUEVOGuía práctica con 5 agentes Python para Odoo, verificada contra Odoo 20 →
Focuz/academy

casos practicos

Odoo Community y Enterprise alineados: nuestra imagen Docker propia

Un ImportError en ai_website nos mostró que mezclábamos un Enterprise más nuevo con un Community más viejo. Así construimos una imagen Docker propia y un script que los mantiene alineados.

Focuz Academy · 8 de octubre de 2026 · 7 min

El 7 de octubre instalábamos los módulos de IA de Odoo 20 Enterprise en nuestra instancia de demostración y uno de ellos, ai_website, se detuvo con este error:

ImportError: cannot import name 'search_icons' from 'odoo.addons.html_editor.tools'

Un módulo Enterprise importaba una función de Community que nuestro Odoo no tenía. No era un bug de Odoo: estábamos mezclando un Enterprise más nuevo con un Community más viejo. En este post contamos cómo lo diagnosticamos y cómo construimos el flujo que hoy mantiene alineados a erp.focuz.io y a nuestra demo. Todo lo que mostramos lo ejecutamos y comprobamos en nuestros servidores.

Por qué Community y Enterprise se desalinean

Odoo Enterprise no es un programa aparte: es un conjunto de addons que se instalan encima de Odoo Community y que dependen de él. Su código está en el repositorio oficial odoo/enterprise de GitHub, que es privado: solo pueden clonarlo las cuentas a las que Odoo da acceso. Lo clonamos con un token de solo lectura.

Nuestro error fue combinar dos fuentes que avanzan a ritmos distintos:

  • Community salía de la imagen oficial odoo:20.0 de Docker Hub. Esa imagen se construye con el Dockerfile oficial de odoo/docker, que fija el paquete diario de Odoo en dos líneas: ARG ODOO_RELEASE=20260926 y ARG ODOO_SHA=7cb4a582…. El último cambio a ese Dockerfile es del 26 de septiembre de 2026.
  • Enterprise salía de la rama 20.0 de odoo/enterprise: el commit que teníamos era del 2 de octubre a las 11:59 UTC.

La función que faltaba entró a Community en el commit 2bf0b2ad de odoo/odoo ("search the icons in the user's language"), integrado a la rama 20.0 el 2 de octubre a las 00:29 UTC. Ese commit agrega def search_icons(env, needle='') a addons/html_editor/tools.py. Nuestro Enterprise, de unas horas después, ya la usaba; nuestro Community del 26 de septiembre todavía no la tenía.

Trampa: Docker Hub mostraba el tag 20.0 actualizado el 2 de octubre. Pero el Odoo que va dentro lo decide el ARG ODOO_RELEASE del Dockerfile, que seguía en el paquete del 26 de septiembre. La fecha del tag no es la versión de Odoo.

La regla

Lo ideal es que Community y Enterprise sean del mismo momento. Como eso rara vez se puede exacto, adoptamos esta regla:

Enterprise puede ser un poco anterior a Community, nunca posterior.

Enterprise depende de Community y Community nunca depende de Enterprise. Si Enterprise es más nuevo, puede llamar a funciones que tu Community aún no tiene, como nos pasó.

Paso 1: una imagen propia con el Dockerfile oficial

Para elegir la fecha de Community necesitamos construir nuestra propia imagen. No escribimos un Dockerfile desde cero: copiamos el oficial de odoo/docker sin cambiar nada más que esos dos argumentos. Así heredamos todo lo que mantiene Odoo S.A. (dependencias, wkhtmltopdf, entrypoint) y nuestra diferencia con el original cabe en dos líneas.

Los paquetes diarios se publican en nightly.odoo.com. Junto a cada uno, Odoo publica un archivo .changes con sus checksums oficiales. De ahí sacamos el SHA1:

DATE=20261004
curl -fsS "https://nightly.odoo.com/20.0/nightly/deb/odoo_20.0.${DATE}_amd64.changes" \
  | awk '/Checksums-Sha1/{f=1;next} f&&/_all\.deb/{print $1; exit}'
# a3d823f4a4700180b86bf566a2dc2f2fff7e1cc9

Y estas son las únicas dos líneas que cambian en el Dockerfile oficial:

ARG ODOO_RELEASE=20261004
ARG ODOO_SHA=a3d823f4a4700180b86bf566a2dc2f2fff7e1cc9

El propio Dockerfile verifica el paquete con echo "${ODOO_SHA} odoo.deb" | sha1sum -c -: si el archivo descargado no coincide con el checksum oficial, el build falla. Construimos y etiquetamos con la fecha del paquete, para que el nombre de la imagen diga qué Odoo lleva dentro:

docker build -t focuz/odoo:20.0-20261004 .

Paso 2: elegir el commit de Enterprise que corresponde

Necesitamos saber cuándo se empaquetó ese Community y quedarnos con el último commit de Enterprise anterior a ese momento.

Nuestra primera versión tomaba la hora que muestra el índice de nightly: "04-Oct-2026 08:24". Al preparar este post comprobamos que esa hora no está en UTC: la cabecera Last-Modified del mismo archivo dice 06:24:47 GMT. El índice está en hora de Bélgica (UTC+2 en verano). Nuestro script se daba dos horas de más. No afectó la combinación que teníamos en producción, cuyo commit de Enterprise es de horas antes, pero lo corregimos.

La fuente correcta está en el propio paquete: el campo Date: del .changes, con su zona horaria explícita.

Date: Sun, 04 Oct 2026 06:16:33 +0000

A esa hora le restamos un margen prudente de dos horas, porque el código fuente se toma antes de empaquetar. Ese margen es una decisión nuestra, no un dato de Odoo. Con el corte calculado, git encuentra el commit:

NIGHTLY=https://nightly.odoo.com/20.0/nightly/deb
BUILT=$(curl -fsS "$NIGHTLY/odoo_20.0.${DATE}_amd64.changes" | sed -n 's/^Date: //p' | head -1)
CUTOFF=$(date -u -d "$BUILT - 2 hours" +%Y-%m-%dT%H:%M:%SZ)   # 2026-10-04T04:16:33Z

git -C enterprise fetch origin 20.0   # origin = https://github.com/odoo/enterprise.git
git -C enterprise rev-list -1 --before="$CUTOFF" FETCH_HEAD

git rev-list -1 --before devuelve el commit más reciente anterior a esa fecha. En nuestro caso: el del 3 de octubre a las 23:45 UTC, anterior al paquete de Community. Regla cumplida.

Dos detalles que nos costaron un error cada uno:

  • Token sin rastro. Pasamos el token con git -c http.extraHeader="Authorization: Basic …" fetch, para que no quede guardado en .git/config.
  • Clones superficiales. Si clonaste Enterprise con --depth, git fetch --shallow-since=<fecha> falló con fatal: error processing shallow info: 4 cuando esa fecha dejaba fuera el commit en el que estaba el clon. Lo resolvimos usando la fecha más antigua entre "corte menos 4 días" y "commit actual menos 1 día".

Paso 3: un script con modo plan

Juntamos todo en update-odoo.sh. Sin argumentos extra solo muestra el plan; con --apply lo ejecuta. Esta es la salida real del plan para el paquete del 4 de octubre:

Community : odoo_20.0.20261004 empaquetado Sun, 04 Oct 2026 06:16:33 +0000 (sha1 a3d823f4…); corte Enterprise: 2026-10-04T04:16:33Z
Enterprise: 2e8534e5 2026-10-03T23:45:33+00:00 [I18N] *: fetch latest Weblate translations
Modo plan. Agrega --apply para ejecutar.

Con --apply, el script:

  1. Respalda la base de datos (pg_dump -Fc), el filestore, el docker-compose.yml y el commit actual de Enterprise.
  2. Construye focuz/odoo:20.0-<fecha> con el checksum oficial.
  3. Mueve Enterprise al commit calculado y cambia la imagen en el docker-compose.yml.
  4. Actualiza todos los módulos con odoo -d <base> -u all --stop-after-init --no-http y vuelve a levantar Odoo.

Paso 4: verificar, primero en la demo

Nunca aplicamos un cambio así directo en producción. En la demo comprobamos:

  • -u all sin errores en sus 347 módulos, y ai_website instalado.
  • Ningún módulo en estado intermedio: SELECT count(*) FROM ir_module_module WHERE state LIKE 'to %'; devuelve 0.
  • La versión que reporta el servidor (POST /web/webclient/version_info): 20.0+e-20261004. La fecha confirma que corre nuestra imagen.
  • El log sin errores y nuestro agente conectado al servidor MCP de Odoo 20 respondiendo igual que antes.

Solo con la demo verificada lo aplicamos en erp.focuz.io: 68 módulos, 0 errores, y una prueba real de que los formularios del sitio siguen creando oportunidades en el CRM.

Lo que aprendimos

  1. La fecha del tag de Docker no es la versión de Odoo. Revisa el ARG ODOO_RELEASE del Dockerfile o lo que reporta version_info.
  2. Etiqueta tu imagen con la fecha del paquete (20.0-20261004), no con latest ni con 20.0 a secas.
  3. Usa lo que declara el propio paquete (checksum y Date: del .changes), no lo que muestra una página web.
  4. Enterprise nunca más nuevo que Community, y con la menor distancia posible.
  5. Demo primero, con respaldo, y un modo plan para ver qué va a pasar antes de que pase.

Si vas a migrar módulos propios a esta versión, sigue con Odoo 20 sin ir.rule: migra tus módulos con upgrade_code.

Ejercicio

  1. En tu instalación, consulta /web/webclient/version_info y anota la fecha del paquete de Community.
  2. En tu carpeta de Enterprise, ejecuta git log -1 --format=%cI. Si esa fecha es posterior al paquete, tienes la misma bomba de tiempo que tuvimos nosotros.
  3. Elige el paquete de hoy en nightly.odoo.com, saca su SHA1 y su Date: del .changes, calcula el commit de Enterprise que le corresponde y construye la imagen en un entorno de prueba.

Fuentes: Dockerfile oficial de Odoo 20.0 (odoo/docker), paquetes diarios de Odoo 20.0, commit 2bf0b2ad de odoo/odoo, imagen oficial de Odoo en Docker Hub, documentación de git rev-list.

Guía gratuita: 5 agentes Python para Odoo

Casos de Junior a Senior, con código verificado contra Odoo 20.

Descargar →

¿Empiezas desde cero? Lee qué es el agent loop y sigue las lecciones gratuitas.

Sigue leyendo