Por qué tu módulo se rompe en cada actualización de Odoo
La causa casi nunca es Odoo. Es la forma en que se escribió el módulo. Tres patrones que garantizan que tu desarrollo sobreviva el siguiente upgrade.
Cada vez que sale una versión nueva de Odoo se repite la misma escena: alguien actualiza en staging, la mitad de los módulos a medida truena, y el proveedor que los escribió cotiza otra vez casi lo mismo que costó construirlos.
Después de siete años arreglando ese desastre —a veces el nuestro— podemos decirlo sin rodeos: el problema casi nunca es Odoo. Es que el módulo se escribió apostando a que el core nunca cambiaría.
1. Nunca modifiques el core
El pecado original. Alguien necesita cambiar el comportamiento de
stock.picking, abre el archivo de Odoo, edita el método y se va a dormir
tranquilo. Funciona perfecto — hasta que git pull de la siguiente versión
sobrescribe el archivo y el cambio desaparece sin dejar rastro.
Odoo tiene un sistema de herencia precisamente para esto:
from odoo import models, api
class StockPicking(models.Model):
_inherit = 'stock.picking'
def action_confirm(self):
# super() primero: el comportamiento estándar sigue vivo
result = super().action_confirm()
self._millora_validar_tarimas()
return result
La diferencia práctica: cuando Odoo 19 cambie action_confirm, tu código sigue
llamando a super() y hereda la mejora en lugar de pelearse con ella.
2. Trata los campos calculados como contratos, no como atajos
Un compute sin depends correcto es una bomba de tiempo. Funciona en tu
prueba porque el registro se recalcula por casualidad, y falla en producción
cuando el ORM decide que no hace falta recalcular.
cantidad_disponible = fields.Float(
compute='_compute_cantidad_disponible',
store=True, # si lo vas a filtrar o agrupar, tiene que estar almacenado
)
@api.depends('move_ids.state', 'move_ids.product_uom_qty')
def _compute_cantidad_disponible(self):
for picking in self:
picking.cantidad_disponible = sum(
move.product_uom_qty
for move in picking.move_ids
if move.state != 'cancel'
)
Regla que aplicamos sin excepción: si el campo se usa en un filtro, un
group by o un reporte, va almacenado y con depends explícito. Si no,
no pasa la revisión de código.
3. Las vistas se extienden con xpath, no se reemplazan
Reemplazar una vista completa es la versión gráfica de modificar el core. Funciona, y te deja fuera de todas las mejoras de UI que venga después.
<record id="view_picking_form_millora" model="ir.ui.view">
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="tarima_id"/>
</xpath>
</field>
</record>
Si el xpath deja de encontrar su ancla en una versión nueva, Odoo falla al
instalar con un mensaje claro. Eso es una ventaja: te enteras en staging,
en dos minutos, en lugar de descubrirlo tres semanas después porque un usuario
reportó que "ya no aparece el campo".
Lo que esto significa a la hora de contratar
Cuando evalúes a un proveedor de desarrollo Odoo, pregunta tres cosas:
- ¿Modificas archivos del core en algún caso? (La respuesta correcta es no.)
- ¿Cómo pruebas el módulo antes de entregarlo? (Debe existir un staging con los datos del cliente.)
- ¿Qué pasa cuando salga la siguiente versión de Odoo? (Debe haber una respuesta concreta, no "lo vemos cuando llegue".)
Si las tres respuestas son vagas, el precio que te están cotizando no es el precio real: es el primer pago de varios.
En nuestro catálogo cada desarrollo se prueba contra las versiones que decimos soportar, de Odoo 16 a 19. Y si necesitas algo que no está ahí, puedes contratar horas sin pasar por tres juntas de descubrimiento.

