Cómo crear vistas en Odoo (Parte 1)
Vistas Tree, Form, Search
24 enero, 2021 por
Cómo crear vistas en Odoo (Parte 1)
Yen Martínez
| 2 Comentarios

Comencemos con un pequeño modelo de ejemplo, el cual vamos a visualizar en diferentes variantes de formato (Vistas). 

Paso 1
Teniendo en cuenta la estructura sugerida para una app en Odoo, dentro de la carpeta models, crea un fichero book.py para definir la clase TBook:

from odoo import fields, models, api

# Heredamos de models.Model para crear clases persistentes (tema para otra ocasión).

class TBook(models.Model): 

   _name = 'tutorial.book'

   _description = 'Book Description'

   _inherit = ['mail.thread', 'mail.activity.mixin']


   name = fields.Char('Name')

   is_classic = fields.Boolean('Is classic?', default=False)

   description = fields.Text('Description')

   publish_date = fields.Date('Publish date')

   pages_count = fields.Integer('Pages Count', default=1)

   state = fields.Selection([('available', 'Available'), ('borrowed', 'Borrowed')], 'State', default='available')

   author = fields.Char('Author(s) name')




Asegúrate de incluir un fichero __init__.py dentro de la carpeta models, haciendo referencia a book.py.


from . import book.py


Paso 2
En la carpeta views, crea un fichero book.xml. Comenzaremos por la vista form


<?xml version="1.0" encoding="utf-8"?>

<odoo>

       <record id="book_form_view" model="ir.ui.view">

           <field name="name">tutorial.book.form</field>

           <field name="model">tutorial.book</field>

           <field name="arch" type="xml">

               <form string="Books">

                   <header>

                       <button name="borrow" type="object" string="Borrow book" class="btn btn-primary" states="available"/>

                       <button name="return_book" type="object" string="Return book" class="btn btn-primary" states="borrowed"/>

                       <field name="state" widget="statusbar"/>

                   </header>

                   <sheet>

                       <div class="oe_title">

                           <h1>

                               <field name="name" placeholder="e.g. Utopia" required="1"/>

                           </h1>

                       </div>

                       <group colspan="4">

                           <group col="2">

                               <field name="publish_date"/>

                               <field name="author" required="1"/>

                           </group>

                           <group col="2">

                               <field name="pages_count"/>

                               <field name="is_classic"/>

                           </group>

                       </group>

                       <notebook>

                           <page name="description" string="Description">

                               <group colspan="4">

                                   <field name="description" nolabel="1"/>

                               </group>

                           </page>

                       </notebook>

                   </sheet>

                   <div class="oe_chatter">

                       <field name="message_follower_ids" widget="mail_followers"

                              help="Follow this book to automatically track its state."

                              groups="base.group_user"/>

                       <field name="message_ids" widget="mail_thread"/>

                   </div>

               </form>

           </field>

       </record>

</odoo>

Puedes ver en la siguiente imagen a qué sección corresponde cada parte del código anterior separado por colores.




A tener en cuenta:


  • Dentro de la vista form, encontramos 3 secciones que corresponden a 3 partes diferentes de la vista ‘dibujada’. Las 3 son opcionales, aunque si prescindimos de las 3 el resultado puede afectar negativamente la experiencia de usuario, al prescindir también de las características que se explican a continuación.

  • La etiqueta <header> corresponde al encabezado del formulario. Generalmente es donde se incluye la barra de estados de nuestro modelo (si incluye estados) y los botones de cambio de estado. En este ejemplo se han incluido 2 botones para cambiar de un estado a otro, cuya visibilidad está condicionada, a su vez, por el estado del registro.

  • La etiqueta <sheet> dibuja un contorno alrededor del formulario, delimitando así el área donde se muestra la información del registro. Para ordenar la información en esta sección, se utiliza la etiqueta <group></group>. En Odoo esta etiqueta dispone la información que se ubique dentro (de forma predeterminada), en forma de un bloque de 2 columnas: una para el nombre del campo y otra para el valor del mismo, en ese orden. Generalmente un campo mostrado fuera de esta etiqueta, se mostrará sin nombre o descripción, solo el componente para registrar el dato. La amplitud horizontal de esta etiqueta se regula a través del atributo colspan. Por ejemplo:

                     <group colspan="4">

                           <group col="2">

                               <field name="publish_date"/>

                               <field name="author" required="1"/>

                           </group>

                           <group col="2">

                               <field name="pages_count"/>

                               <field name="is_classic"/>

                           </group>

                       </group>

Esta sección del código anterior, mostrará un grid de 4 columnas, donde los 2 primeros campos se mostrarán en las 2 columnas de la izquierda y los otros 2 en las 2 columnas de la derecha. Siguiendo este funcionamiento, cuando queremos mostrar un campo que incluye mucha información como los de texto extendido, o un listado de conceptos relacionados (campos One2many o Many2many), entonces tal vez sea conveniente mostrar el campo en toda la amplitud del formulario.

                      <group colspan="4">

                           <field name="one2many_field_example"/>

                      </group>


  • Para evitar que tu formulario sea demasiado extenso hacia abajo, y garantizar que el usuario no tenga que usar varias veces el scroll vertical para tener acceso a toda la información del formulario, secciona la información que quieres mostrar y utiliza la etiqueta <notebook></notebook> para separar los datos en pestañas o Tabs. De esta forma puedes intentar tener toda tu información en el área visible de la pantalla.

                       <notebook>

                           <page name="description" string="Description">

                               <group colspan="4">

                                   <field name="description" nolabel="1"/>

                               </group>

                           </page>

                       </notebook>

 

  

  • Para mostrar campos requeridos/obligatorios usa  required="1":
    <field name="author" required="1"/>

Como tip adicional, generalmente especifico mis campos obligatorios desde la vista y no desde el modelo. De esa forma evitas posibles problemas al poner datos en esta tabla de forma automática (saltándose la vista). Con la excepción de los casos donde, intencionalmente, quieras que no te permita poner un registro en base de datos sin ese campo, por supuesto.

  • La última sección de este formulario es equivalente a un footer. Al usar la clase oe_chatter, estamos incluyendo el sistema de mensajería de Odoo, que permitirá poner mensajes a un registro o comunicarnos con otros empleados que podrían estar involucrados en la gestión del mismo, o incluso adicionar seguidores a un registro que luego pueden ser notificados si este se modifica. 


                    <div class="oe_chatter">

                       <field name="message_follower_ids" widget="mail_followers"

                              help="Follow this book to track its state."

                              groups="base.group_user"/>

                       <field name="message_ids" widget="mail_thread"/>

                   </div>


  • Para mostrar el chatter a un formulario en Odoo debes asegurarte de heredar de mail.thread en la declaración de tu modelo:

_inherit = ['mail.thread']


 


Para organizar y diseñar mejor los elementos de la vista puedes auxiliarte de widgets, acá te dejo un enlace para que veas algunos en acción WIDGETS EN ODOO



A continuación ‘dibujamos’ nuestra vista tree para el modelo ‘tutorial.book’, en este caso lo incluiremos luego del <record> que contiene la vista form que hicimos anteriormente

<record id="book_tree_view" model="ir.ui.view">

   <field name="name">tutorial.book.tree</field>

   <field name="model">tutorial.book</field>

   <field name="arch" type="xml">

       <tree string="Tutorial Book"  

decoration-muted="state=='borrowed'" decoration-info="state=='available'">

           <field name="name"/>

           <field name="publish_date"/>

           <field name="author"/>

           <field name="pages_count"/>

           <field name="is_classic"/>

           <field name="state"/>

       </tree>

   </field>

</record>



Dentro de la etiqueta <tree></tree>, vamos listando los campos (que se mostrarán en forma de columnas) para diseñar nuestro listado. En ocasiones es muy útil que nuestro listado nos ayude a ‘hacer muy obvia’ la información que nos está mostrando. Para ello podemos hacer uso de alguna escala de colores asignada según algún criterio. Por ejemplo, en este ejemplo que les muestro, he asignado el color Azul a los libros Disponibles, y el color Gris a los libros Prestados. Es importante que el campo que se use como condicional se incluya entre los campos listados dentro de la etiqueta tree, si no es un campo que quieres mostrar, inclúyelo pero con invisible=’1’, de esta forma lograrás ambos objetivos. 

Para ver con más detalle cómo aplicar colores a las líneas de la vista tree, visita el siguiente enlace Cómo aplicar colores en la vista tree de odoo

Si tenemos una vista tree o de Listado, lo más seguro es que eventualmente necesitemos aplicar búsquedas o agrupadores sobre dicho listado. Por ello vamos a generar una vista search o de Búsqueda.

A continuación de nuestro <record> para la vista tree en el archivo book.xml, vamos a escribir el siguiente código:


<record id="book_search_view" model="ir.ui.view">

   <field name="name">tutorial.book.search</field>

   <field name="model">tutorial.book</field>

   <field name="arch" type="xml">

       <search string="Tutorial Book Search">

           <field name="name" string="Book name"/>

           <field name="author" string="Book author name"/>

           <field name="publish_date" string="Book publish date"/>

           <filter domain="[('state','=','borrowed')]" string="Borrowed"  name="borrowed"/>

           <filter domain="[('state','=','available')]" string="Available" name="available"/>

           <group expand="1" string="Group By">

               <filter string="Classics" name="is_classic" domain="[]"

                       context="{'group_by':'is_classic'}"/>

               <filter string="Publish date" name="publish_date" domain="[]"

                       context="{'group_by':'publish_date'}"/>

               <filter string="State" name="state" domain="[]"

                       context="{'group_by':'state'}"/>

           </group>

       </search>

   </field>

</record>


Esta vista nos permite generar filtros y agrupadores de una vez. Dentro de las etiquetas <search></search>, aquellas que son <field name="x_field_name"/> me van a permitir digitar un criterio de búsqueda, y por cada caracter que digite actualizará el resultado. Por eso en esta sección sugiero incluir campos como nombres, descripciones o algún tipo de texto dentro del formulario. También permite un tipo de búsqueda más ‘atómica’, donde el usuario solo presiona un enlace y de una vez aparecen los registros que cumplan con esa condición, por ejemplo:


<filter domain="[('state','=','borrowed')]" string="Borrowed"  name="borrowed"/>


Al presionar sobre el filtro Borrowed, automáticamente se mostrarán sólo aquellos libros que están en estado Borrowed, pues el filtro se aplica sobre el valor del campo state.

Y por último tenemos los agrupadores, que se mostrarán en un menú diferente de los filtros.


           <group expand="1" string="Group By">

               <filter string="Classics" name="is_classic" domain="[]"

                       context="{'group_by':'is_classic'}"/>

    </group


Acá debemos usar campos que sabemos nos aportarán información valiosa. Por ejemplo, categorías, estados, etc. Para que de forma rápida el sistema pueda mostrarme cuántos registros pertenecen a una misma categoría o tienen el mismo estado. Pero no nos arrojaría mucha información incluir un agrupador que use un campo único, como una secuencia, o tal vez un nombre o identificador (por razones obvias).





Paso 3
Definiremos entonces el action y el menú. Vamos a utilizar un action para especificar cuáles de esas vistas se quiere mostrar. Es posible que yo quiera mostrar los registros de mi modelo desde lugares diferentes, desde un menú quiero que se muestren ciertas vistas y desde otro quiero mostrar vistas diferentes o en un orden diferente. Desde el action podemos controlar esta situación.


Actions

<record id="book_act_window" model="ir.actions.act_window">

   <field name="name">Books</field>

   <field name="type">ir.actions.act_window</field>

   <field name="res_model">tutorial.book</field>

   <field name="view_type">form</field>

   <field name="view_mode">tree,form</field>

   <field name="help" type="html">

       <p class="oe_view_nocontent_create">

           There is no book added yet. Click here to add a new Book.

       </p>

   </field>

</record>


En este segmento de código: <field name="view_mode"></field> podemos especificar qué vistas mostrar y en qué orden. En este caso solo tenemos vista form y tree, pero si necesitamos vistas kanban, tree, form y calendar se escrbiría así:
<field name="view_mode"> kanban,tree,form,calendar</field>. 
Para controlar qué mensaje quiero que se muestre al usuario cuando la búsqueda no arroje resultados (o cuando aún no se ha registrado información), se utiliza el atributo help:
<field name="help" type="html">

       <p class="oe_view_nocontent_create">

           There is no book added yet. Click here to add a new Book.

       </p>

    </field>

El caso de la vista form se mostrará al presionar un registro y la vista search será visible en la esquina superior derecha de la pantalla.

Menuitems

Creamos nuestra entrada de menú, para que al presionarla, el sistema nos muestre estas vistas que acabamos de crear.  


<menuitem name="Library" id="library_root_menu" sequence="1" web_icon="tutorial_create_views,/static/description/icon.jpg"/>


Creamos un menú global que nos permita el acceso a una nueva app o funcionalidad (Que se muestre en el menú general). Si estoy usando un tema donde visualice los íconos de cada app, este ícono se debe especificar en el atributo web_icon. A este tipo de menú, como práctica personal me gusta ponerle la palabra root en el nombre para localizarlo con facilidad en tiempos de soporte y/o estrés ;) 


<menuitem name="Books" id="books_menu" parent="library_root_menu" action="book_act_window"/>


Creamos entonces un submenú que debe tener como parent, el menú externo anterior para que se muestre ‘dentro’ del mismo.Si quiero que estas vistas se incluyan dentro de otro módulo, pues debo especificar el id del menú correspondiente del otro módulo en el atributo parent. En este punto también ponemos la referencia al action que hemos creado para ‘llamar’ a nuestras vistas.


Paso 4
Ahora debemos conceder los permisos apropiados a los usuarios. Creamos un archivo ir.model.access.csv, con la siguiente estructura.

 

"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"

"access_book","access_books","model_tutorial_book","base.group_user",1,1,1,1


En este caso hemos concedido full access al usuario group_user para acceder al modelo tutorial.book. 


Paso 5
Ordenamos nuestro __manifest__.py

{

   'name': "Custom Library App",

   'summary': """

      Books description

       """,

   'description': """

      Books description

   """,

   'author': "Konodoo",

   'website': "https://www.konodoo.com",

   'version': "12.0.1.0.0",

   'depends': ['mail'],

   'data': ['security/ir.model.access.csv',

            'views/book.xml'],

   'license': "AGPL-3",

   'installable': True,

   'application': True,

}

 

Para ver con más detalles las características del fichero __manifest__.py, visita el siguiente enlace Estructura del __manifest__.py en Odoo

Solo te falta poner tu módulo (carpeta) en el addons_path de tu proyecto. Recuerda actualizar la lista de aplicaciones para que aparezca el nuevo módulo y puedas instalarlo.

(Continuará….)


Cómo crear vistas en Odoo (Parte 1)
Yen Martínez
24 enero, 2021
Share this post
Archivar
Registrarse to leave a comment