Estudio de Odoo — Módulos y Programas

Odoo es una suite ERP open source (Python + PostgreSQL) extensible por módulos custom. Este estudio integra toda la documentación del OKF: qué es, cómo generar módulos (scaffold, manifest, modelo, vista, seguridad) y cómo generar programas que usan Odoo (API externa XML-RPC/JSON-RPC y Odoo MCP), con sus scripts minimalistas.

Resumen con para qué y cómo, listo para pegar en tu OKF/Obsidian (el agente lo implementa sin gastar tokens).
🤖 Skill de implementación: odoo-minimal (odoo-bin · scaffold · ORM · XML-RPC/JSON-RPC · MCP)

1 · Odoo — Fundamentos y arquitectura

La base: ERP open source + arquitectura + entorno ERNE (odoo_demo, COP, PUC).

🏗️ Arquitectura

# Cliente web (Owl JS) → XML-RPC/JSON-RPC/HTTP → Servidor Odoo 18 (Python) → PostgreSQL
odoo-bin -d odoo_demo --addons-path=addons,custom_addons
# odoo/  framework (ORM, vistas, RPC) · addons/  ~640 módulos de negocio · custom_addons/  módulos propios

🚀 Arranque del entorno (ERNE)

# Clonar Odoo 18 + venv + dependencias (una sola vez)
py -3.12 -m venv C:\Users\TUF\odoo\odoo18\.venv
C:\Users\TUF\odoo\odoo18\.venv\Scripts\pip install -r C:\Users\TUF\odoo\odoo18\requirements.txt
# Servidor con la base demo
cd C:\Users\TUF\odoo\odoo18
.venv\Scripts\odoo-bin -d odoo_demo --addons-path=addons,custom_addons
💡 Contexto OKF: base odoo_demo, empresa ERNE (res.company id=1), Colombia 🇨🇴 · COP · plan contable PUC. ⚠️ Informes financieros (Balance/P&L) son exclusivos de Enterprise; Community no los tiene.

2 · Odoo — Generación de módulos

Crear funcionalidad propia sin tocar el core: scaffold → manifest → modelo → vista → seguridad → instalar.

📁 Scaffold del módulo

cd custom_addons
../../odoo-bin scaffold mi_modulo
# Genera: __init__.py · __manifest__.py · models/ · views/ · security/

📦 __manifest__.py

import... # no importa nada: es un dict
{
    'name': 'Mi Módulo',
    'version': '18.0.1.0.0',
    'depends': ['base'],
    'data': ['security/ir.model.access.csv', 'views/mi_modulo_views.xml'],
    'installable': True,
    'application': True,
}

🧠 Modelo (models/mi_modulo.py)

from odoo import fields, models

class MiModelo(models.Model):
    _name = 'mi.modelo'
    _description = 'Mi Modelo'
    name = fields.Char(string='Nombre', required=True)
    descripcion = fields.Text(string='Descripción')
    activo = fields.Boolean(string='Activo', default=True)

🔐 Seguridad (security/ir.model.access.csv)

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mi_modelo,mi.modelo,model_mi_modelo,base.group_user,1,1,1,1

🖥️ Vista (views/mi_modulo_views.xml) — tree + form + acción + menú

<odoo>
  <record id="view_mi_modelo_tree" model="ir.ui.view">
    <field name="model">mi.modelo</field>
    <field name="arch" type="xml">
      <tree string="Mi Modelo"><field name="name"/><field name="activo"/></tree>
    </field>
  </record>
  <record id="view_mi_modelo_form" model="ir.ui.view">
    <field name="model">mi.modelo</field>
    <field name="arch" type="xml">
      <form string="Mi Modelo"><sheet><group>
        <field name="name"/><field name="descripcion"/><field name="activo"/>
      </group></sheet></form>
    </field>
  </record>
  <record id="action_mi_modelo" model="ir.actions.act_window">
    <field name="name">Mi Modelo</field>
    <field name="res_model">mi.modelo</field>
    <field name="view_mode">tree,form</field>
  </record>
  <menuitem id="menu_mi_modulo_root" name="Mi Módulo"/>
  <menuitem id="menu_mi_modelo" parent="menu_mi_modulo_root" action="action_mi_modelo"/>
</odoo>

⚙️ Instalar / actualizar

# -i instala · -u actualiza
../../odoo-bin -d odoo_demo -i mi_modulo --addons-path=addons,custom_addons
../../odoo-bin -d odoo_demo -u mi_modulo --addons-path=addons,custom_addons
💡 Ejemplo real del OKF: el módulo iot_sensor (modelos iot.sensor + iot.sensor.reading, menú "IoT", esqueleto iot_handlers/). Los módulos custom viven en custom_addons sin tocar el core.

3 · Odoo — Cómo generar programas usando Odoo

Desde fuera de la UI: API externa XML-RPC/JSON-RPC y servidor MCP (agente IA).

🐍 Python XML-RPC — autenticar + CRUD

import xmlrpc.client

url, db, username, password = 'http://localhost:8069', 'odoo_demo', 'admin', 'admin'

common = xmlrpc.client.ServerProxy(f'{url}/xmlrpc/2/common')
uid = common.authenticate(db, username, password, {})
models = xmlrpc.client.ServerProxy(f'{url}/xmlrpc/2/object')

pid = models.execute_kw(db, uid, password, 'res.partner', 'create',
                     [{'name': 'Cliente Demo'}])
ids = models.execute_kw(db, uid, password, 'res.partner', 'search',
                     [[['is_company', '=', True]]])
recs = models.execute_kw(db, uid, password, 'res.partner', 'read',
                     [ids[:5]], {'fields': ['name', 'country_id']})
models.execute_kw(db, uid, password, 'res.partner', 'write',
                     [[pid], {'name': 'Nuevo Nombre'}])

🕸️ JSON-RPC (el mismo execute_kw en JSON)

{"jsonrpc": "2.0", "method": "call",
 "params": {"service": "object", "method": "execute_kw",
   "args": [db, uid, password, "res.partner", "create", [{}]]}}
⚠️ Regla crítica: NO insertar directo en PostgreSQL. Odoo maneja la lógica (defaults, computed, validaciones, seguridad) en el ORM; escribir directo rompe la integridad. Todo pasa por execute_kw.

🤖 Odoo MCP — odoo-mcp-local (16 tools)

# Agente IA → MCP (stdio) → odoo_mcp/server.py (FastMCP) → xmlrpc.client → Odoo → PostgreSQL
# Datos:   list_models · get_model_fields · search_records · get_record
#          create_record · update_record · delete_record · call_method
# Módulos: list_modules · get_module_info · install_module · upgrade_module · uninstall_module
# Config:  get_config_param · set_config_param · list_config_params
💡 Decisiones: SDK mcp 1.x (FastMCP), canal xmlrpc.client (stdlib, sin módulo extra), conexión perezosa (arranca aunque Odoo esté apagado). Registrado en .vscode/mcp.json como odoo-local.

4 · Grafo animado — del módulo al programa

El token 🟠 recorre: generar el módulo, definirlo, verlo, instalarlo y usarlo por API/MCP. Clic reinicia.

Iniciando…

5 · Referencia y regla de librerías

Comando / conceptoQué hace
odoo-bin scaffold mi_moduloGenera el esqueleto del módulo
__manifest__.pyMetadatos + depends + data a cargar
models.Model + _nameBase del ORM (modelo con puntos)
ir.ui.view · ir.actions.act_windowVistas tree/form + acción + menú
ir.model.access.csvPermisos ACL del modelo
-i / -uInstalar / actualizar módulo
/xmlrpc/2/commonEndpoint sin auth (authenticate)
/xmlrpc/2/object + execute_kwCRUD del ORM por RPC
search_read · search_countAtajos buscar+leer / contar
ir.model · ir.model.fieldsInspección y modelos dinámicos (x_)
📦 Regla de librerías (local primero): Odoo usa xmlrpc.client (stdlib, sin dependencia) para la API; el MCP usa el SDK mcp 1.x (fijado mcp>=1.9,<2.0). Docs: https://www.odoo.com/documentation/18.0/.