Saltar a contenido

133. Odoo desarrollo Módulo Odoo

133.1 Introducción

------ REVISAR----- fichero odoo.conf y odoo-bin -u modulo

Arquitectura de tres capas: presentación, lógica de negocio y datos:

ar

133.2 Preparar e instalar entorno de desarrollo

Preparar la instalacion de desarrollo Instalar odoo

Siempre se debe utilizar un entorno ddistinto al de producción.

133.2.1 Instalamos odoo desde fuente

Creamos el directorio ./src que usaremos para todo el proceso.

Creamos un entorno virtual Python
Creamos y activamos un entorno virtual

python -m venv ./envodoo
source ./envodoo/bin/activate
Clonamos el repositorio de Odoo

Ver documento de odoo para Linux

Tendremos instalados git y
Python 3.7 o posterior Clonamos Odoo:

 git clone https://github.com/odoo/odoo.git -b 15.0 --depth=1 

Normalmente para cambiar de rama se utiliza:

git  git switch 15.0
No vamos a modificar ningún fichero de Odoo.

PostgresSQL En el entorno de desarrollo instalamos el resto de los componentes de odoo como postgresql

sudo apt install postgresql postgresql-client
Por defecto, el único usuario es postgres pero Odoo no permite conectarse como postgres, así que necesita crear un nuevo usuario de PostgreSQL:

Instalamos postgres:

 sudo apt install postgresql postgresql-client
Con el usuario postgres creamos un nuevo usuario en la BD

$ sudo -u postgres createuser -s $USER
$ createdb $USER
Dependencias -paquetes

Para el desarrollo son necesarios algunos paquetes Linux:

sudo apt install python3-dev libxml2-dev libxslt1-dev libldap2-dev libsasl2-dev \
    libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev libfreetype6-dev \
    liblcms2-dev libwebp-dev libharfbuzz-dev libfribidi-dev libxcb1-dev libpq-dev
dependencias python En el directorio de instalación odoo se encuentra el fichero requirements.txt con los paquetes Python dependientes.
Con el entorno python activado se instalan en envodoo\lib

$ pip3 install setuptools wheel
$ pip3 install -r requirements.txt

Sia instalación falla en gevent-21.8.0 y greelet (PENDIENTE DE CORREGIR) Comentamos las líneas:

#gevent==1.5.0 ; python_version == '3.7'
#gevent==20.9.0 ; python_version >= '3.8'
#gevent==21.8.0 ; python_version > '3.9'  # (Jammy)
#greenlet==0.4.15 ; python_version == '3.7'
#greenlet==0.4.17 ; python_version > '3.7'
#greenlet==1.1.2 ; python_version  > '3.9'  # (Jammy)

Que nos permite continuar el desarrollo del módulo ----- Explorar soluciones ----

Truco

From there, you can create and manage new users.
The user account you use to log into Odoo’s web interface differs from the --db_user CLI argument.
Ejecutar odoo Estas son las configuraciones necesarias más comunes:

Usuario y contraseña de PostgreSQL. Odoo no tiene predeterminados fuera de los predeterminados psycopg2: conecta con un socket UNIX en el puerto 5432 con el usuario actual y sin contraseña. Rutas de complementos personalizadas más allá de lo predeterminado para cargar los módulos. Una forma típica de ejecutar el servidor sería desde el directorio de instalación

python3 odoo-bin --addons-path=addons -d mydb -p 8070
-p 8070 será el puerto utilizado por el servidor web Odoo.

Si aparece un error que mydb no está inicializada se fuerza usando -i base ( literal como aparece aquí )

Algunos argumentos de la línea de comandos:

-d<database>
Base de datos que se va a usar
--addons-path<directories>
Lista de directorios separados por coma (`,` ) donde se encuentran los módulos 

--limit-time-cpu<limit>
Prevent the worker from using more than <limit> CPU seconds for each request.
--limit-time-real<limit>
Prevent the worker from taking longer than <limit> seconds to process a request.

para conectar http://localhost:8070 como administrador principal usar:
admin Para el correo y la clave.

133.3 Configuration file

Most of the command-line options can also be specified via a configuration file. Most of the time, they use similar names with the prefix - removed and other - are replaced by _ e.g. --db-template becomes db_template.

Some conversions don’t match the pattern:

--db-filter becomes dbfilter --no-http corresponds to the http_enable boolean logging presets (all options starting with --log- except for --log-handler and --log-db) just add content to log_handler, use that directly in the configuration file --smtp is stored as smtp_server --database is stored as db_name --i18n-import and --i18n-export aren’t available at all from configuration files The default configuration file is $HOME/.odoorc which can be overridden using --config. Specifying --save will save the current configuration state back to that file. The configuration items relative to the command-line are to be specified in the section [options].

Here is a sample file:

[options]
db_user=odoo
dbfilter=odoo

133.4 Activar modo desarrollador

El modo desarrollador permite usar herramientas avanzadas. Es necesario para el resto del tutorial tener activado el modo desarrollador.

133.5 HERRAMIENTAS

Con git tenemos el comando para cambiar a la rama 15.0:

 git switch 15.0

133.6 Crear un módulo odoo

Un nuevo módulo servirá para extender la funcionalidad o modificar la existente.
La lógica de negocio y las extensiones se desarrollan en el lado serividor con facilidades para el lado cliente.
Una aplicación en Odoo es un conjunto de módulos que realizan una funcionalidad completa.

PASOS para crear un módudlo Odoo:
1. Crea una carpeta con el nombre del módulo en el directorio addons en la instalación de Odoo ( ./src/custom-addons)
2. Crea un archivo llamado __manifest__.py en la carpeta del módulo que acabas de crear. Este archivo es esencial ya que es el que indica a Odoo que existe un módulo en esa carpeta.
3. Crea un archivo llamado models.py en la carpeta del módulo. Este archivo es donde se definirán los modelos (tablas) que utilizará el módulo.
4. Crea un archivo llamado views.xml en la carpeta del módulo. Este archivo es donde se definirán las vistas (interfaz de usuario) que utilizará el módulo.
5. Crea un archivo llamado security.xml en la carpeta del módulo. Este archivo es donde se definirán los permisos (acceso) que utilizará el módulo.
6. Crea un archivo llamado __init__.py en la carpeta del módulo. Este archivo es esencial ya que es el que indica a Python que la carpeta es un paquete.
7. Crea un archivo llamado __init__.py en la carpeta models. Este archivo es esencial ya que es el que indica a Python que la carpeta es un paquete. Finalmente, actualiza la lista de módulos en Odoo y busca tu módulo para instalarlo.

Un poco más adelante se muestra la estructura de directorios y ficheros. Esta estructura se puede crear usando el comando odoo-bin scaffold que veremos en el ejemplo de la academia.

133.7 Componentes del módulo

  • Objetos de negocio, modelos
  • Ficheros de datos: XML o CSV para declarar metadata:
  • views y reports
  • Datos de configuración y reglas de seguridad
  • datos de demostración
  • mas...
  • Web controllers : gestionan peticiones desde los navegadores web

133.8 Creación y estructura del módulo

Cada 'módulo Odoo' es un directorio. Se especifican con la opción --addons-path cuando ejecutamos el servidor con odoo-bin.

Un módulo odoo debe declararse en el manifiesto.

Un módulo odoo es un paquete python que incluye __init__.py .
Odoo-bin dispone del siguiente comando para crear la estructura de directorio:

 odoo-bin scaffold <module name> <where to put it>
Los módulos deben crearse en el entorno de dearrollo e instalarse en producción en el directorio /opt/odoo15/custom-addons

La estructura de directorios de un módulo 'my_module' sería:

odoo-bin scaffold "Open Academy" ./
tree
── open_academy
    ├── controllers
       ├── controllers.py
       └── __init__.py
    ├── demo
       └── demo.xml
    ├── __init__.py
    ├── __manifest__.py
    ├── models
       ├── __init__.py
       └── models.py
    ├── security
       └── ir.model.access.csv
    └── views
        ├── templates.xml
        └── views.xml

133.9 Fichero de manifiesto (manifest.py)

El manifiesto es un diccionarios con metadata para Odoo:

{
    'name': "A Module",
    'version': '1.0',
    'depends': ['base'],
    'author': "Author Name",
    'category': 'Category',
    'description': """
    Description text
    """,
    # data files always loaded at installation
    'data': [
        'views/mymodule_view.xml',
    ],
    # data files containing optionally loaded demonstration data
    'demo': [
        'demo/demo_data.xml',
    ],
}

Se puede consultar la referencia y las entradas que admite el diccionario.

category
Aparece en la categoría en el apartado de app de Odoo. Se pueden usar las categorías definidas o crear nuevas. Se utiliz la barra / para crear categorías de forma jerarquica

etc.

133.10 Modelo. Object-Relational Mapping (ORM)

ORM permite tratar con objetos Python en lugar de sql.
Un objeto de negocio se declara en Python extendiento la clase Model que se encarga de la persistencia.

Los modelos se configuran declarando atributos en la nueva clase. El principal atributo es _name que define el nombre del modelo en Odoo. Por ejemplo:

from odoo import models
class MinimalModel(models.Model):
    _name = 'test.model'
Se debe considerar esta clase como el "manejador" de una tabla de la base de datos. La clase lee,escribe, borra etc los registros de la tabla.
En las variables de clase del modelo incluimos campos (field) que sólo pueden ser los definidos en el ORM de Odoo.

133.10.1 Campos del modelo (fields)

Los campos definen que guarda el modelo y donde (similar a los campos en una tabla sql).

from odoo import models, fields

class LessMinimalModel(models.Model):
    _name = 'test.model2'

    nombre = fields.Char()
En este campo nombre es un campo de tipo Char y se corresponde a una columna de la tabla en la BD.
Otro campos definidos:

133.10.1.1 Atributos de los campos

Se pueden pasar atributos en los campos, por ejemplo para indicar que el campo es obligatorio:

name = fields.Char(required=True)
otros privativos.

Algunos son comunes a todos los campos, como :
* string * required * help * index Una lista completa en esta documentacion

Otros solamente se utilizan con un campo.

133.10.1.2 Campos simples

Hay dos categorías de campos 'simples' valores atomicos que se guardan directamente en las tablas y 'relacionales' que enlazan registros.

Simples como:
* Char * Float * Integer

133.10.1.3 Campos avanzados

Como
* Binary * Html * Image * Monetary * Selection * Text

133.10.1.3.1 Campos de Date(time)
133.10.1.3.2 Relacionales
  • Many2One
  • One2Many
  • Many2many

Command Y campos pseudorelacionales:
* Reference * Many2oneReference

Campos de cálculo:
*

133.10.1.4 Campos automáticos.

Odoo crea unos pocos campos que se gestionan solamente por el sistema y no pueen escribirse.
* id : identificador único. Si se intenta insertar con mismo id levanta una excepción * create_date(Date) * create_uid(Many2one) usuario que crea el registro * write_date(Datetime) * write_uid(Many2one) usuario que escribe el registro.

133.10.1.5 Campos reservados

  • name
  • active
  • state
  • parent_id
  • parent_path
  • company_id

La referencia ORM API contiene información detallada de campos y atributos.

133.10.2 ficheros de datos (DATA FILES)

133.11 Declaraciones de vistas genéricos.

133.12 BASIC VIEWS

Las vistas definen como se ven los registros del modelo. Cada tipo de vista representa un modo de visualización (listas, graficos, etc) .....

<record model="ir.ui.view" id="view_id">
    <field name="name">view.name</field>
    <field name="model">object_name</field>
    <field name="priority" eval="16"/>
    <field name="arch" type="xml">
        <!-- view content: <form>, <tree>, <graph>, ... -->
    </field>
</record>

133.12.1 Tree views

También llamado list views, muestran elementos tabulados
El elemento raíz es <tree>

<tree string="Idea list">
    <field name="name"/>
    <field name="inventor_id"/>
</tree>

133.12.2 Form views

Sirven para crear y modificar registros.
El elemento raíz is <form>. Se componen de elementos de estructura como groups y notebooks y elementos interactivos como botones y campos

<form string="Idea form">
    <group colspan="4">
        <group colspan="2" col="2">
            <separator string="General stuff" colspan="2"/>
            <field name="name"/>
            <field name="inventor_id"/>
        </group>

        <group colspan="2" col="2">
            <separator string="Dates" colspan="2"/>
            <field name="active"/>
            <field name="invent_date" readonly="1"/>
        </group>

        <notebook colspan="4">
            <page string="Description">
                <field name="description" nolabel="1"/>
            </page>
        </notebook>

        <field name="state"/>
    </group>
</form>

Las vistan form pueden crearse también usando html :

<form string="Idea Form">
    <header>
        <button string="Confirm" type="object" name="action_confirm"
                states="draft" class="oe_highlight" />
        <button string="Mark as done" type="object" name="action_done"
                states="confirmed" class="oe_highlight"/>
        <button string="Reset to draft" type="object" name="action_draft"
                states="confirmed,done" />
        <field name="state" widget="statusbar"/>
    </header>
    <sheet>
        <div class="oe_title">
            <label for="name" class="oe_edit_only" string="Idea Name" />
            <h1><field name="name" /></h1>
        </div>
        <separator string="General" colspan="2" />
        <group colspan="2" col="2">
            <field name="description" placeholder="Idea description..." />
        </group>
    </sheet>
</form>

133.12.3 Search views

Las vistas se declaran como un registro del modelo ir.ui.view .....

133.13 Relaciones entre modelos

Un registro de un modelo puede relacionarse con un registro de otro. Por ejemplo, el registro orden de ventas se relaciona con el del cliente.

133.13.1 Relational fields

Los campos relación enlazan registros, bien del mismo modelo (herarquicos) o entre modelos
Tipos:

Many2one(other_model, ondelete='set null') enlace simple a otro modelo

One2many(other_model, related_field)
....
Many2many(other_model)
....

133.14 Herencia

133.14.1 Herencia de modelos

133.14.2 Herencia de Vistas

133.14.2.1 Dominios

133.15 Computed fields y valores por defecto

En este caso los campos no se obtienen directamente de la BD si no que son calculados.
Los campos calculados son campos con el atributo compute y valor igual al métodos de la clase que hece el cálculo y asigna valor al campo, ejemplo:

computed fields
import random
from odoo import models, fields, api

class ComputedModel(models.Model):
    _name = 'test.computed'

    name = fields.Char(compute='_compute_name')

    def _compute_name(self):
        for record in self:
            record.name = str(random.randint(1, 1e6))

133.15.1 Dependencias

Los campos calculados suelen depender de otros campos del mismo registro. El modeo ORM espera que se especifiquen estas dependencias en le método que realiza el cálculo. Esto se consigue usando el decorador depends . Y es usamo por el ORM para recalcular el campo cuando alguna dependencia cambia. En el siguiente ejemplo,name depende de value :

from odoo import models, fields, api

class ComputedModel(models.Model):
    _name = 'test.computed'

    name = fields.Char(compute='_compute_name')
    value = fields.Integer()

    @api.depends('value')
    def _compute_name(self):
        for record in self:
            record.name = "Record with value %s" % record.value

133.15.2 Valores por defecto

Se puede dar un valor por defecto a cualquier campo en la definición de campo con default=xxx

name = fields.Char(default="Unknown")
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
NOTA:

The object self.env gives access to request parameters and other useful things:

self.env.cr or self._cr is the database cursor object; it is used for querying the database
self.env.uid or self._uid is the current user’s database id
self.env.user is the current user’s record
self.env.context or self._context is the context dictionary
self.env.ref(xml_id) returns the record corresponding to an XML id
self.env[model_name] returns an instance of the given model

133.16 Onchange

El mecanismo «onchange» permite al intefaz cliente actualizar una forma cuando el usuario rellena un campo, sin guardar nada en la BD Por ejemplo, suponer un modelo con tres campos:

<!-- content of form view -->
<field name="amount"/>
<field name="unit_price"/>
<field name="price" readonly="1"/>
Y queremos actualizar el precio si cambia alguno de los otros campos. Esto se consigue usando y teniendo en cuenta: * Se define un métodos donde self representa el registro en el formulario y * Usando el decorador onchange() en el métodos donde se especifica en que campo se recacula. Cualquier campo en selfse refleja en el formularios

# onchange handler
@api.onchange('amount', 'unit_price')
def _onchange_price(self):
    # set auto-changing field
    self.price = self.amount * self.unit_price
    # Can optionally return a warning and domains
    return {
        'warning': {
            'title': "Something bad happened",
            'message': "It was very bad indeed",
        }
    }

133.17 Model constraints

133.18 Seguridad

Los requisitos mínimos de seguridad para usar un módulo aqui .

Los grupos se crean como registros del modelo res.group y dando acceso a los menús via definición de menús. Sin menús también es posible acceder a los modelos indirectamente y los permisos reales de los objetos (CRUD) deben ser definidos para los grupos. Normalmente con ficheros CSV.

133.18.1 Access rights

Se definen como registros del modelo ir.model.access Cada derecho de acceso se asocia a un modelo, grupo ( o ninguno) y un conjunto de permisos CRU. Normalmente en ficheros CSV nombrados después de su modelo ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0

133.18.2 Record rules

Record rules restringen el acceso a un subconjunto de registos de un modelo. Una regla es un registro del modelo ir.rule y está asociado a un modelo, un numero de grupos (campo many2many ), permisos a los que se aplica la restricción y un dominio.

El siguiente ejemplo evita el borrado de "leads" que no estén en el estado "cancel".
Observar que el valor del grupo sigue la misma forma que el método write() del modelo ORM.

<record id="delete_cancelled_only" model="ir.rule">
    <field name="name">Only cancelled leads may be deleted</field>
    <field name="model_id" ref="crm.model_crm_lead"/>
    <field name="groups" eval="[(4, ref('sales_team.group_sale_manager'))]"/>
    <field name="perm_read" eval="0"/>
    <field name="perm_write" eval="0"/>
    <field name="perm_create" eval="0"/>
    <field name="perm_unlink" eval="1" />
    <field name="domain_force">[('state','=','cancel')]</field>
</record>

133.19 Vistas avanzadas

Para la vista continuar aqui

133.20 Wizards (Asistentes)

Los asistentes simplifican y automatizan ciertas tares en Odoo.

133.21 Internacionalización

133.22 Reporting (Informes)

133.22.1 Informes impresos

133.22.2 Dashboard (Tablero de instrumentos)

133.23 CLI Command Line Interface

https://www.odoo.com/documentation/15.0/es/developer/cli.html#command-line-interface-cli

133.24 Enlaces

133.24.1 Ejemplos de módulos para v15.0

Interesante Openacademy y demo

Modulo con video sobre hospital