Sunteți pe pagina 1din 43

Construccin de modulo en Odoo 10

Iniciar/detener el servidor Odoo


Odoo utiliza una arquitectura cliente / servidor en el que los clientes son los navegadores web que acceden al
servidor Odoo a travs de RPC.
La lgica de negocio y la extensin se lleva a cabo generalmente en el lado del servidor, aunque apoyan las
funciones del cliente (por ejemplo, una nueva representacin de datos tales como mapas interactivos) puede ser
aadido al cliente.
Con el fin de iniciar el servidor, basta con invocar el comando odoo-bin en la cscara, la adicin de la ruta completa al
archivo si es necesario:
odoo - bin
El servidor es detenido por golpear Ctrl-C dos veces desde el terminal, o matando el proceso OS correspondiente.

Construir un mdulo Odoo


Ambas extensiones de servidor y cliente se empaquetan como mdulos que se cargan opcionalmente en una base
de datos.
Los mdulos Odoo puede agregar nueva lgica de negocio a un sistema, o modificar y ampliar la lgica de negocio
existente: un mdulo puede ser creado para aadir reglas de contabilidad de su pas para el apoyo de la contabilidad
general de Odoo, mientras que el siguiente mdulo aade soporte para la visualizacin en tiempo real de una flota de
autobuses. Todo en Odoo por lo tanto comienza y termina con mdulos.

Composicin de un mdulo
Un mdulo Odoo puede contener una serie de elementos:
Objetos de negocio (Business objects)
Declarado como clases de Python, estos recursos se conservan automticamente por Odoo basado en su
configuracin
Archivos de informacin (Data files)
XML o CSV archivos de metadatos que declaran (vistas o flujos de trabajo), los datos de configuracin (mdulos
de parametrizacin), datos de demostracin y ms
Controladores web (Web controllers)
Manejar las solicitudes de los navegadores Web
Datos de la web esttica (Static web data)
Imgenes, archivos CSS o Javascript empleado por la interfaz web o sitio web

Estructura del mdulo


Cada mdulo es un directorio dentro de un directorio de mdulos. Los directorios mdulo se especifican mediante el uso de la --
addons-path opcin.
Tip
La mayora de las opciones de lnea de comandos tambin se pueden configurar mediante un archivo
de configuracin.
Un mdulo Odoo se declara por su manifiesta (manifest). Consulte la documentacin de manifiesto al respecto.
Un mdulo es tambin un paquete de Python con un archivo __init__.py, que contiene instrucciones de
importacin para diversos archivos de Python en el mdulo.
Por ejemplo, si el mdulo tiene un solo mymodule.py archivo __init__.py puede contener:
from . import mymodule
Odoo proporciona un mecanismo para ayudar a configurar un nuevo mdulo, odoo-bin tiene un
subcomando scaffold para crear un mdulo vaco:

$ odoo-bin scaffold <module name> <where to put it>


El comando crea un subdirectorio para su mdulo, y crea automticamente un montn de archivos estndar para un
mdulo. La mayora de ellos simplemente contener cdigo XML o comentado. El uso de la mayor parte de esos
archivos se explicar a lo largo de este tutorial.

Ejercicio
Creacin de mdulos
Utilice la lnea de comandos anterior para crear un mdulo vaco abierto Academia, e instalarlo en Odoo.

1. Invocar el comando odoo-bin scaffold openacademy addons.


2. Adaptar el archivo de manifiesto a su mdulo.
3. No preocuparse por los dems archivos.

Mapeo objeto-relacional
Un componente clave de Odoo es la capa ORM. Esta capa evita tener que escribir la mayor parte de SQL con la
mano y proporciona servicios de extensibilidad y seguridad ( 2 ).
Los objetos de negocio se declaran como clases de Python que se extienden Model, que los integra en el sistema
automatizado de persistencia.
Los modelos pueden ser configurados mediante el establecimiento de una serie de atributos bajo su definicin. El
atributo ms importante es _name que se requiere y define el nombre para el modelo en el sistema Odoo. He aqu
una definicin mnimamente completa de un modelo:
from odoo import models
class MinimalModel(models.Model):
_name = 'test.model'

Campos del modelo


Los campos que se utilizan para definir lo que el modelo puede almacenar y dnde. Los campos son definidos como
atributos en la clase del modelo:
from odoo import models, fields

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

name = fields.Char()

Los atributos comunes


Al igual que el modelo en s, sus campos se pueden configurar, mediante el paso de configuracin de atributos como
parmetros:
name = field.Char(required=True)
Algunos atributos estn disponibles en todos los campos, estos son los ms comunes:
string( unicode, Por defecto: el nombre del campo)
La etiqueta del campo en la interfaz de usuario (visible por los usuarios).
required( bool, Por defecto: False)
Si el campo True no puede estar vaco, o bien debe tener un valor por defecto o que siempre se le da un
valor al crear un registro.
help( unicode, Por defecto: '')
Formato largo, proporciona una informacin sobre herramientas ayuda a los usuarios en la interfaz de
usuario.
index( bool, Por defecto: False)
Pide que Odoo crear un ndice de base de datos en la columna.

Por defecto en Odoo se escribe default.


Campos simples
Hay dos amplias categoras de campos: campos "simples", que son valores atmicos almacenados directamente en
la mesa del modelo y campos "relacionales" vinculacin de registros (del mismo modelo o de diferentes modelos).
Ejemplo de campos simples son Boolean, Date, Char.

Campos reservados
Odoo crea algunos campos en todos los modelos ( 1 ). Estos campos son gestionados por el sistema y no se deben
escribirse. Pueden ser ledos si til o necesario:
id( Id)
El identificador nico para un registro en su modelo.
create_date( Datetime)
Fecha de creacin del registro.
create_uid( Many2one)
Usuario que cre el registro.
write_date( Datetime)
Fecha de la ltima modificacin del registro.
write_uid( Many2one)
Usuario que modific por ltima vez el registro.

Los campos especiales


Por defecto, Odoo tambin requiere un campo name en todos los modelos para diferentes comportamientos de
visualizacin y bsqueda. El campo que se utiliza para estos fines puede ser anulado por el ajuste _rec_name.

Ejercicio
Definir un modelo
Definir un nuevo modelo de datos curso en el mdulo openacademy. Un curso tiene un ttulo y una
descripcin. Los cursos deben tener un ttulo.
Editar el archivo openacademy/models/models.py para incluir un curso de clases.
openacademy/models.py
from odoo import models, fields, api

class Course(models.Model):
_name = 'openacademy.course'

name = fields.Char(string="Title", required=True)


description = fields.Text()

Archivos de informacin
Odoo es un sistema altamente impulsado por datos. Aunque el comportamiento se personaliza mediante cdigo
Python, una parte del valor de un mdulo se encuentra en los datos que establece al cargarse.
Tip Existen algunos mdulos nicamente para aadir datos en Odoo

Los mdulos de datos se declara a travs de data files, archivos XML con elementos <record>. Cada
elemento <record> crea o actualiza un registro de base de datos.

<odoo>
<data>
<record model="{model name}" id="{record identifier}">
<field name="{a field name}">{a value}</field>
</record>
</data>
</odoo>
model es el nombre del modelo Odoo para el registro.
id es un identificador externo , permite referr al registro (sin necesidad de conocer su identificador en la base
de datos).
<field> elementos tienen un name que es el nombre del campo en el modelo (por ejemplo description). Su
cuerpo es el valor del campo.

Los archivos de informacin tienen que ser declarados en el archivo de manifiesto para ser cargados, pueden ser
declaradas en la lista 'data' (siempre cargado) o en la lista 'demo' (slo se cargan en modo de demostracin).

Ejercicio
Definir los datos de demostracin
Crear datos de demostracin llenando el modelo de Cursos con unos cuantos cursos de demostracin. Editar el
archivo openacademy/demo/demo.xml para incluir algunos datos.
openacademy/demo.xml
<odoo>
<data>
<record model="openacademy.course" id="course0">
<field name="name">Course 0</field>
<field name="description">Course 0's description

Can have multiple lines


</field>
</record>
<record model="openacademy.course" id="course1">
<field name="name">Course 1</field>
<!-- no description for this one -->
</record>
<record model="openacademy.course" id="course2">
<field name="name">Course 2</field>
<field name="description">Course 2's description</field>
</record>
</data>
</odoo>

Acciones y Mens
Acciones y mens son registros regulares en la base de datos, normalmente declarados a travs de archivos de
datos. Las acciones pueden ser activadas de tres maneras:

1. haciendo clic en los elementos de men (vinculado a acciones especficas)


2. haciendo clic en los botones en las vistas (si stos estn conectados a las acciones)
3. como acciones contextuales en objetos

Debido a que los mens son algo complejas, para declarar hay un acceso directo <menuitem> para declarar
una ir.ui.menu y conectarlo a la accin correspondiente con mayor facilidad.
<record model="ir.actions.act_window" id="action_list_ideas">
<field name="name">Ideas</field>
<field name="res_model">idea.idea</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
action="action_list_ideas"/>

Peligro
La accin debe ser declarado antes de su correspondiente men en el archivo XML.
Los archivos de datos se ejecutan secuencialmente, el id de la accin debe estar presente en la base de datos
antes de que se puede crear el men.

Ejercicio
Definir nuevas entradas de men
Definir nuevas entradas de men para acceder a los cursos bajo la entrada del men OpenAcademy. Un usuario
debe ser capaz de:

mostrar una lista de todos los cursos


crear / modificar cursos

1. Crear openacademy/views/openacademy.xml con una accin y los mens que desencadenan la accin
2. Aadirlo a la lista data de openacademy/__manifest__.py

openacademy/__manifest__.py

'data': [
# 'security/ir.model.access.csv',
'templates.xml',
'views/openacademy.xml',
],
# only loaded in demonstration mode
'demo': [

openacademy/views/openacademy.xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- window action -->
<!--
La siguiente etiqueta es una definicin de accin para una "accin de ventana",
Que es una accin que abre una vista o un conjunto de vistas -->
<record model="ir.actions.act_window" id="course_list_action">
<field name="name">Courses</field>
<field name="res_model">openacademy.course</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">Create the first course
</p>
</field>
</record>

<!--
Men de nivel superior: sin padre -->
<menuitem id="main_openacademy_menu" name="Open Academy"/>
<!-- Se necesita un primer nivel en el men del lado izquierdo
Antes de usar la accin = atributo -->
<menuitem id="openacademy_menu" name="Open Academy"
parent="main_openacademy_menu"/>
<!-- El men siguiente debe aparecer * despus de *
Su padre openacademy_menu y * despus de su *
Accin course_list_action -->
<menuitem id="courses_menu" name="Courses" parent="openacademy_menu"
action="course_list_action"/>
<!--
Ubicacin completa de la identificacin:
Action = "openacademy.course_list_action"
No es necesario cuando es el mismo mdulo -->
</data>
</odoo>

Basic views
Las vistas definen la forma en que se muestran los registros de un modelo. Cada tipo de vista representa un modo de
visualizacin (una lista de registros, un grfico de su agregacin, ...). Las vistas pueden ser solicitados ya sea de
forma genrica a travs de su tipo (por ejemplo, una lista de socios ) o especficamente a travs de su ID. Para
solicitudes genricas, se utilizar la vista con el tipo correcto y la prioridad ms baja (lo que la vista de menor
prioridad de cada tipo es la vista por defecto para ese tipo).
View inheritance permite alterar vistas declarados en otra parte (la adicin o eliminacin del contenido).
Generic view declaration
Una vista es declarado como un registro del modelo ir.ui.view. El tipo de vista est implcito en el elemento raz
del campo arch:
<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>
Peligro
El contenido de la vista es XML. El campo arch por lo tanto debe ser declarado como type="xml" para ser
analizado correctamente.

Tree views
Vistas de rbol, tambin llamadas vistas de listas, registros de visualizacin en forma de tabla.
Su elemento raz es <tree>. La forma ms simple de la vista de rbol se limita a enumerar todos los campos a
mostrar en la tabla (cada campo como una columna):
<tree string="Idea list">
<field name="name"/>
<field name="inventor_id"/>
</tree>

Form views
Los formularios se utilizan para crear y editar registros individuales.
Su elemento raz es <form>. Se componen de elementos de alto nivel de la estructura (grupos, cuadernos) y
elementos interactivos (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>

Exercise
Personaliza vista form usando XML
Create your own form view for the Course object. Data displayed should be: the name and the description of the
course.
openacademy/views/openacademy.xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record model="ir.ui.view" id="course_form_view">
<field name="name">course.form</field>
<field name="model">openacademy.course</field>
<field name="arch" type="xml">
<form string="Course Form">
<sheet>
<group>
<field name="name"/>
<field name="description"/>
</group>
</sheet>
</form>
</field>
</record>

<!-- window action -->


<!--
The following tag is an action definition for a "window action",

Ejercicio
NOTEBOOK
En la vista Formulario de curso, coloque el campo de descripcin bajo una pestaa, de manera que sea ms fcil
agregar otras pestaas ms adelante, con informacin adicional.
Modificar el formulario curso de la siguiente manera:
openacademy/views/openacademy.xml
<sheet>
<group>
<field name="name"/>
</group>
<notebook>
<page string="Description">
<field name="description"/>
</page>
<page string="About">
This is an example of notebooks
</page>
</notebook>
</sheet>
</form>
</field>

Las vistas formulario tambin pueden usar plano HTML para los diseos ms flexibles:
<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>

Search views
Las Vistas de bsqueda personalizar el campo de bsqueda asociado a la vista de la lista (y otras vistas
agregadas). Su elemento raz es <search> y que estn compuesto de campos que definen los campos que se
pueden buscar en:
<search>
<field name="name"/>
<field name="inventor_id"/>
</search>

Si no existe Buscar en la vista para el modelo, Odoo genera uno que slo permite la bsqueda en el campo name.

Ejercicio
Buscar cursos
Permitir la bsqueda de cursos en funcin de su ttulo o su descripcin.
openacademy/views/openacademy.xml
</field>
</record>

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


<field name="name">course.search</field>
<field name="model">openacademy.course</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="description"/>
</search>
</field>
</record>

<!-- window action -->


<!--
The following tag is an action definition for a "window action",

Las relaciones entre los modelos


Un registro de un modelo puede estar relacionado con un registro de otro modelo. Por ejemplo, un registro de orden
de venta se relaciona con un registro de cliente que contiene los datos del cliente; Tambin se relaciona con sus
registros de lnea de orden de venta.

Ejercicio
Crear un modelo de sesin
Para el mdulo abierto Academia, consideramos un modelo para sesiones: una sesin es una ocurrencia de un
curso impartido en un momento dado para un pblico determinado.
Crear un modelo para las sesiones. Una sesin tiene un nombre, una fecha de inicio, una duracin y un nmero de
asientos. Aadir una accin y un elemento de men para mostrarlos. Hacer que el nuevo modelo visible a travs de
un elemento de men.

1. Crear la clase de sesin en openacademy/models/models.py.


2. Aadir el acceso al objeto de sesin en openacademy/view/openacademy.xml.

openacademy/models.py
name = fields.Char(string="Title", required=True)
description = fields.Text()

class Session(models.Model):
_name = 'openacademy.session'

name = fields.Char(required=True)
start_date = fields.Date()
duration = fields.Float(digits=(6, 2), help="Duration in days")
seats = fields.Integer(string="Number of seats")

openacademy/views/openacademy.xml
<!-- Full id location:
action="openacademy.course_list_action"
It is not required when it is the same module -->

<!-- session form view -->


<record model="ir.ui.view" id="session_form_view">
<field name="name">session.form</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<form string="Session Form">
<sheet>
<group>
<field name="name"/>
<field name="start_date"/>
<field name="duration"/>
<field name="seats"/>
</group>
</sheet>
</form>
</field>
</record>

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


<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>

<menuitem id="session_menu" name="Sessions"


parent="openacademy_menu"
action="session_list_action"/>
</data>
</odoo>

Nota
digits=(6, 2) especifica la precisin de un nmero float: 6 es el nmero total de dgitos, mientras que 2 es el nmero
de dgitos despus de la coma. Tenga en cuenta que da lugar a los nmeros de un telfono antes de la coma es un
mximo de 4.

Relational fields
Los campos relacionales enlazan registros, ya sea del mismo modelo (jerarquas) o entre diferentes modelos.
Los tipos de campos relacionales son:
Many2one(other_model, ondelete='set null')
Un simple enlace a otro objeto:
print foo.other_id.name

Ver tambin
foreign keys

One2many(other_model, related_field)
Una relacin virtual, inversa de una Many2one. Un One2many se comporta como un contenedor de
registros, el acceso resulta en un conjunto (posiblemente vaco) de registros:
for other in foo.other_ids:
print other.name

Peligro
Debido a que una One2many es una relacin virtual, no debe haber un campo Many2one en el other_model, y su
nombre debe ser related_field

Many2many(other_model)
Bidireccional relacin mltiple, cualquier registro en un lado puede estar relacionado con cualquier nmero de
registros en el otro lado. Se comporta como un recipiente de registros, el acceso tambin resulta en un
conjunto posiblemente vaco de registros:
for other in foo.other_ids:
print other.name

Ejercicio
las relaciones Many2one
El uso de un many2one, modifique las del curso y la Sesin modelos para reflejar su relacin con otros modelos:

Un curso tiene una responsabilidad del usuario; el valor de ese campo es un registro del modelo
integrado res.users.
Una sesin tiene un instructor; el valor de ese campo es un registro del modelo integrado res.partner.
Una sesin se relaciona con un supuesto; el valor de ese campo es un registro del
modelo openacademy.course y no es requerido.
Adaptar los puntos de vista.

1. Aadir los correspondientes campos Many2one de los modelos, y


2. Aadirlos en las vistas.

openacademy/models.py

name = fields.Char(string="Title", required=True)


description = fields.Text()

responsible_id = fields.Many2one('res.users',
ondelete='set null', string="Responsible", index=True)

class Session(models.Model):
_name = 'openacademy.session'

start_date = fields.Date()
duration = fields.Float(digits=(6, 2), help="Duration in days")
seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner', string="Instructor")


course_id = fields.Many2one('openacademy.course',
ondelete='cascade', string="Course", required=True)

openacademy/views/openacademy.xml
<sheet>
<group>
<field name="name"/>
<field name="responsible_id"/>
</group>
<notebook>
<page string="Description">

</field>
</record>

<!-- override the automatically generated list view for courses -->
<record model="ir.ui.view" id="course_tree_view">
<field name="name">course.tree</field>
<field name="model">openacademy.course</field>
<field name="arch" type="xml">
<tree string="Course Tree">
<field name="name"/>
<field name="responsible_id"/>
</tree>
</field>
</record>

<!-- window action -->


<!--
The following tag is an action definition for a "window action",

<form string="Session Form">


<sheet>
<group>
<group string="General">
<field name="course_id"/>
<field name="name"/>
<field name="instructor_id"/>
</group>
<group string="Schedule">
<field name="start_date"/>
<field name="duration"/>
<field name="seats"/>
</group>
</group>
</sheet>
</form>
</field>
</record>

<!-- session tree/list view -->


<record model="ir.ui.view" id="session_tree_view">
<field name="name">session.tree</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<tree string="Session Tree">
<field name="name"/>
<field name="course_id"/>
</tree>
</field>
</record>

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


<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>

Ejercicio
Las relaciones inversas One2Many
Utilizando el inverso one2many campo relacional, modificar los modelos para reflejar la relacin entre los cursos y
sesiones.

1. Modificar la clase Course y


2. Agregar el campo en la vista de formulario de curso.

openacademy/models.py
responsible_id = fields.Many2one('res.users',
ondelete='set null', string="Responsible", index=True)
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")

class Session(models.Model):

openacademy/views/openacademy.xml
<page string="Description">
<field name="description"/>
</page>
<page string="Sessions">
<field name="session_ids">
<tree string="Registered sessions">
<field name="name"/>
<field name="instructor_id"/>
</tree>
</field>
</page>
</notebook>
</sheet>

Ejercicio
Las relaciones mltiples Many2Many
Utilizando el Many2Many campo relacional, modificar la Sesin modelo para relacionar cada sesin a un conjunto
de asistentes. Los asistentes estarn representados por los registros asociados, por lo que se relacionan con el
modelo integrado res.partner. Adaptar las vistas en consecuencia.

1. Modificar la clase Session y


2. agregar el campo en la vista formulario.

openacademy/models.py
instructor_id = fields.Many2one('res.partner', string="Instructor")
course_id = fields.Many2one('openacademy.course',
ondelete='cascade', string="Course", required=True)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/openacademy.xml
<field name="seats"/>
</group>
</group>
<label for="attendee_ids"/>
<field name="attendee_ids"/>
</sheet>
</form>
</field>

Herencia

Modelo de herencia
Odoo proporciona dos mecanismos de herencia para extender un modelo existente de una forma modular.
El primer mecanismo de herencia permite a un mdulo para modificar el comportamiento de un modelo definido en
otro mdulo:

aadir campos a un modelo,


anular la definicin de campos en un modelo,
aadir restricciones a un modelo,
aadir mtodos a un modelo,
reemplazar los mtodos existentes en un modelo.

El segundo mecanismo de herencia (delegacin) permite vincular cada registro de un modelo para un registro en un
modelo padre, y proporciona un acceso transparente a los campos del registro padre.
Ver tambin

_inherit
_inherits

Vista herencia
En lugar de modificar las vistas existentes en su lugar (sobrescribindolas), Odoo proporciona una herencia de vistas
en la que las vistas de "extensin" de los hijos se aplican encima de las vistas de la raz y pueden agregar o eliminar
contenido de sus padres.
Una vista de extensin hace referencia a su matriz usando el campo inherit_id, y en lugar de una sola vista su
campo arch se compone de cualquier nmero de elementos xpath de seleccin y alterar el contenido de su vista
padre:
<!lista de categorias de ideas mejoradas -->
<record id="idea_category_list2" model="ir.ui.view">
<field name="name">id.category.list2</field>
<field name="model">idea.category</field>
<field name="inherit_id" ref="id_category_list"/>
<field name="arch" type="xml">
<!-- Encontrar la descripcin del campo y agregar el campo
Idea_ids despus de ella -->
<xpath expr="//field[@name='description']" position="after">
<field name="idea_ids" string="Number of ideas"/>
</xpath>
</field>
</record>

expr
Un XPath expresin seleccionar un solo elemento en la vista padre. Genera un error si coincide con ningn
elemento o ms de uno
position
Operacin que se aplicar al elemento coincidente:
inside
Aade el cuerpo de xpath en el extremo del elemento emparejado
replace
Sustituye el elemento emparejaron con el cuerpo de xpath, en sustitucin de cualquier $0ocurrencia nodo en
el nuevo cuerpo con el elemento original,
before
Inserta el cuerpo xpath como un hermano antes del elemento emparejado
after
Inserta el cuerpo xpath como un hermano despus del elemento emparejado
attributes
Altera los atributos del elemento emparejado usando elementos especiales attribute en el cuerpo de xpath
Tip
Cuando bsqueda de un solo elemento, el atributo position se puede establecer directamente sobre el elemento
que se encuentran. Ambas herencias a continuacin le dar el mismo resultado.
<xpath expr="//field[@name='description']" position="after">
<field name="idea_ids" />
</xpath>

<field name="description" position="after">


<field name="idea_ids" />
</field>

Ejercicio
Alterar el contenido existente

Utilizando el modelo de herencia, modificar el vigente socio modelo para aadir un campo booleano instructor , y un
campo Many2Many que corresponde a la relacin de sesin - socio
Usar la vista de la herencia, Muestra este campos en la vista formulario de empresa

Nota
Esta es la oportunidad para introducir el modo de desarrollador para inspeccionar la vista, encontrar su ID externo y
el lugar para poner el nuevo campo.

1. Crear un archivo openacademy/models/partner.py e importarlo en __init__.py


2. Crear un archivo openacademy/views/partner.xml y aadirlo a __manifest__.py

openacademy/__init__.py
# -*- coding: utf-8 -*-
from . import controllers
from . import models
from . import partner

openacademy/__manifest__.py
# 'security/ir.model.access.csv',
'templates.xml',
'views/openacademy.xml',
'views/partner.xml',
],
# only loaded in demonstration mode
'demo': [

openacademy/partner.py
# -*- coding: utf-8 -*-
from odoo import fields, models
class Partner(models.Model):
_inherit = 'res.partner'
# Agreggue una columna nueva al modelo res.partner, por defecto los partners no son
# instructores
instructor = fields.Boolean("Instructor", default=False)

session_ids = fields.Many2many('openacademy.session',
string="Attended Sessions", readonly=True)

openacademy/views/partner.xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- Add instructor field to existing view -->
<record model="ir.ui.view" id="partner_instructor_form_view">
<field name="name">partner.instructor</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Sessions">
<group>
<field name="instructor"/>
<field name="session_ids"/>
</group>
</page>
</notebook>
</field>
</record>

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


<field name="name">Contacts</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="configuration_menu" name="Configuration"
parent="main_openacademy_menu"/>
<menuitem id="contact_menu" name="Contacts"
parent="configuration_menu"
action="contact_list_action"/>
</data>
</odoo>

Domains
En Odoo, dominios son valores que codifican las condiciones en registros. Un dominio es una lista de
los criterios utilizados para seleccionar un subconjunto de los registros de un modelo. Cada criterio es
un triplete, con un nombre de campo, un operador y un valor.
Por ejemplo, cuando se utiliza en el producto modelo de dominio de la siguiente selecciona todos los
servicios con un precio unitario mayor a 1000:
[('product_type', '=', 'service'), ('unit_price', '>', 1000)]

Por criterios predeterminados se combinan con un AND implcito. Los operadores lgicos &(Y), |(OR)
y !(NO) se pueden utilizar para combinar explcitamente criterios. Se utilizan en posicin prefijo (el
operador se inserta antes de que sus argumentos en lugar de entre). Por ejemplo para seleccionar los
productos "que son servicios de O tienen un precio unitario que es NO entre 1000 y 2000":
['|',
('product_type', '=', 'service'),
'!', '&',
('unit_price', '>=', 1000),
('unit_price', '<', 2000)]

Un parametro domain puede ser aadido a los campos relacionales para limitar los registros vlidos
para la relacin cuando se trata de seleccionar registros en la interfaz del cliente.
Ejercicio
Dominios en los campos relacionales
Al seleccionar el instructor para una sesin , slo los instructores (socios con instructorconjunto
a True) deben ser visibles.
openacademy/models.py
duration = fields.Float(digits=(6, 2), help="Duration in days")
seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner', string="Instructor",


domain=[('instructor', '=', True)])
course_id = fields.Many2one('openacademy.course',
ondelete='cascade', string="Course", required=True)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

Nota
Un dominio declarado como una lista literal se evala en el lado del servidor y no puede referirse a valores dinmicos en el lado
derecho, un dominio declarado como una cadena es evaluada en el lado del cliente y permite los nombres de campo en el lado
derecho

Ejercicio
Dominios ms complejos
Crear nuevas categoras de socios Maestro / Nivel 1 y Maestros / Nivel 2. El instructor para una sesin puede ser un
instructor o un maestro (de cualquier nivel).

1. Modificar la sesin del dominio del modelo


2. Modificar openacademy/view/partner.xmlpara acceder a categoras de socios :

openacademy/models.py
seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner', string="Instructor",


domain=['|', ('instructor', '=', True),
('category_id.name', 'ilike', "Teacher")])
course_id = fields.Many2one('openacademy.course',
ondelete='cascade', string="Course", required=True)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/partner.xml
<menuitem id="contact_menu" name="Contacts"
parent="configuration_menu"
action="contact_list_action"/>

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


<field name="name">Contact Tags</field>
<field name="res_model">res.partner.category</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="contact_cat_menu" name="Contact Tags"
parent="configuration_menu"
action="contact_cat_list_action"/>

<record model="res.partner.category" id="teacher1">


<field name="name">Teacher / Level 1</field>
</record>
<record model="res.partner.category" id="teacher2">
<field name="name">Teacher / Level 2</field>
</record>
</data>
</odoo>

Los campos calculados y los valores predeterminados


Los campos hasta ahora se han almacenado directamente en y recuperados directamente de la base de datos. Los
campos tambin puedes ser calculadas. En ese caso, el valor del campo no se recupera de la base de datos pero
calcula sobre la marcha llamando a un mtodo del modelo.
Para crear un campo calculado, cree un campo y establezca su atributo compute con el nombre de un mtodo. El
mtodo de clculo debera simplemente establecer el valor del campo para calcular en cada registro self.

Peligro
self es una coleccin
El objeto self es un conjunto de registros, es decir, una coleccin ordenada de registros. Es compatible con las
operaciones estndar de Python en colecciones, como len(self)y iter(self), adems de las operaciones de
configuracin adicionales como recs1 + recs2.
Interactuando sobre registros self uno por uno, donde cada registro es en s mismo es una coleccin de tamao 1.
Se puede acceder a / asignar campos en los registros individuales mediante el uso de la notacin de puntos, al igual
que record.name.
import random
from odoo import models, fields, api

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

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

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

Dependencias
El valor de un campo computado por lo general depende de los valores de otros campos en el registro computado. El
ORM espera que el desarrollador especifique esas dependencias en el mtodo de clculo con el
decorador depends(). Las dependencias dadas son utilizadas por el ORM para disparar el reclculo del campo cada
vez que alguna de sus dependencias se han modificado:

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

Ejercicio
Los campos calculados

Aadir el porcentaje de asientos ocupados a la Sesin modelo


Mostrar que el campo en el rbol y formar puntos de vista
Mostrar el campo como una barra de progreso

1. Aadir un campo calculado a Sesin


2. Mostrar el campo en la sesin de vista:

openacademy/models.py

course_id = fields.Many2one('openacademy.course',
ondelete='cascade', string="Course", required=True)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')

@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:
if not r.seats:
r.taken_seats = 0.0
else:
r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats
openacademy/views/openacademy.xml
<field name="start_date"/>
<field name="duration"/>
<field name="seats"/>
<field name="taken_seats" widget="progressbar"/>
</group>
</group>
<label for="attendee_ids"/>

<tree string="Session Tree">


<field name="name"/>
<field name="course_id"/>
<field name="taken_seats" widget="progressbar"/>
</tree>
</field>
</record>

Valores predeterminados
Cualquier campo puede dar un valor por defecto. En la definicin de campo, aadir la opcin default=X donde X es o
bien un valor Python literal (boolean, integer, float, string), o una funcin de tomar un conjunto de registros y devolver
un valor:
name = fields.Char(default="Unknown")
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)

Nota
El objeto self.env da acceso a los parmetros de solicitud y otras cosas tiles:

self.env.cro self._cr es la base de datos cursor objeto; se utiliza para consultar la base de datos
self.env.uido self._uid es el ID de la base de datos del usuario actual
self.env.user es el registro del usuario actual
self.env.contexto self._context es el diccionario de contexto
self.env.ref(xml_id) devuelve el registro correspondiente a un id XML
self.env[model_name] devuelve una instancia de la modelo dado

Ejercicio
objetos activos - Los valores por defecto
Definir el valor por defecto fecha_inicial como en la actualidad (ver Date).
Aadir un campo active en la sesin de clase, y establecer sesiones como activa por defecto.
openacademy/models.py
_name = 'openacademy.session'

name = fields.Char(required=True)
start_date = fields.Date(default=fields.Date.today)
duration = fields.Float(digits=(6, 2), help="Duration in days")
seats = fields.Integer(string="Number of seats")
active = fields.Boolean(default=True)

instructor_id = fields.Many2one('res.partner', string="Instructor",


domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml
<field name="course_id"/>
<field name="name"/>
<field name="instructor_id"/>
<field name="active"/>
</group>
<group string="Schedule">
<field name="start_date"/>
Nota
Odoo tiene reglas incorporadas en la toma de los campos con an activefield establecido
en Falseinvisible.

onchange
El mecanismo de "onchange" (en cambio) proporciona una forma para que la interfaz cliente actualice un formulario
siempre que el usuario haya rellenado un valor en un campo, sin guardar nada en la base de datos.
Por ejemplo, supongamos que un modelo tiene tres campos amount, unit_pricey price, y desea actualizar el precio
en el formulario cuando se modifica cualquiera de los otros campos. Para lograr esto, definir un mtodo en el que self
representa el registro en la vista de la forma, y se adorna con onchange() especificar en qu campo tiene que ser
activado. Cualquier cambio que realice en self se ver reflejado en el formulario.
<!-- content of form view -->
<field name="amount"/>
<field name="unit_price"/>
<field name="price" readonly="1"/>

# 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",
}
}

Para los campos calculados, el comportamiento valorado onchange es una funcin de como se puede ver jugando
con el formulario Sesin: cambiar el nmero de asientos o participantes, y la barra de progreso de taken_seats se
actualiza automticamente.

Ejercicio
Advertencia
Aadir un onchange explcita para advertir sobre los valores no vlidos, como un nmero negativo de asientos, o
ms participantes que los asientos.
openacademy/models.py
r.taken_seats = 0.0
else:
r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats

@api.onchange('seats', 'attendee_ids')
def _verify_valid_seats(self):
if self.seats < 0:
return {
'warning': {
'title': "Incorrect 'seats' value",
'message': "The number of available seats may not be negative",
},
}
if self.seats < len(self.attendee_ids):
return {
'warning': {
'title': "Too many attendees",
'message': "Increase seats or remove excess attendees",
},
}

Restricciones de un modelo
Odoo ofrece dos formas de configurar invariantes verificadas de forma automtica: Python constraintsy SQL
constraints.
Una restriccin Python se define como un mtodo decorado con constrains(), y se invoca en un conjunto de
registros. El decorador especifica qu campos estn involucrados en la restriccin, por lo que la restriccin se evala
automticamente cuando uno de ellos se modifica. Se espera que el mtodo para producir una excepcin si es
invariante no est satisfecho:
from odoo.exceptions import ValidationError

@api.constrains('age')
def _check_something(self):
for record in self:
if record.age > 20:
raise ValidationError("Your record is too old: %s" % record.age)
# all records passed the test, don't return anything

Ejercicio
Aadir restricciones de Python
Aadir una restriccin que comprueba que el instructor no est presente en los asistentes de su / su propia sesin.
openacademy/models.py
# -*- coding: utf-8 -*-

from odoo import models, fields, api, exceptions

class Course(models.Model):
_name = 'openacademy.course'

'message': "Increase seats or remove excess attendees",


},
}

@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:
if r.instructor_id and r.instructor_id in r.attendee_ids:
raise exceptions.ValidationError("A session's instructor can't be an attendee")

Las limitaciones de SQL se definen a travs del atributo model _sql_constraints. Este ltimo es asignado a una lista
de triples de cuerdas (name, sql_definition, message), donde name es un nombre de restriccin de SQL
vlida, sql_definition es un table_constraint expresin, y message es el mensaje de error.

Ejercicio
Aadir restricciones de SQL
Con la ayuda de la documentacin de PostgreSQL , aadir las siguientes limitaciones:

1. Compruebe que la descripcin del curso y el ttulo del curso son diferentes
2. Hacer el nombre del curso nico

openacademy/models.py
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")

_sql_constraints = [
('name_description_check',
'CHECK(name != description)',
"The title of the course should not be the description"),

('name_unique',
'UNIQUE(name)',
"The course title must be unique"),
]

class Session(models.Model):
_name = 'openacademy.session'
Ejercicio
Ejercicio 6 - Aadir una opcin duplicado
Puesto que hemos aadido una restriccin para el nombre del curso singularidad, no es posible utilizar la funcin de
"duplicar" ms ( Formulario duplicados ).
Vuelva a aplicar su propio mtodo de "copiar" lo que permite duplicar el objeto del curso, cambiar el nombre original
en "Copia de [nombre original]".
openacademy/models.py
session_ids = fields.One2many(
'openacademy.session', 'course_id', string="Sessions")

@api.multi
def copy(self, default=None):
default = dict(default or {})

copied_count = self.search_count(
[('name', '=like', u"Copy of {}%".format(self.name))])
if not copied_count:
new_name = u"Copy of {}".format(self.name)
else:
new_name = u"Copy of {} ({})".format(self.name, copied_count)

default['name'] = new_name
return super(Course, self).copy(default)

_sql_constraints = [
('name_description_check',
'CHECK(name != description)',

Vistas avanzada

Vistas de rbol
Las vistas de rbol pueden tener atributos complementarios para personalizar an ms su comportamiento:

decoration-{$name}
Permite cambiar el estilo del texto de una fila basado en los atributos del registro correspondiente.

Los valores son expresiones de Python. Para cada registro, la expresin se evala con los atributos del registro como
valores de contexto y si es true, el estilo correspondiente se aplica a la fila. Otros valores de contexto son uid(el id del
usuario actual) y current_date(la fecha actual como una cadena de la forma yyyy-MM-dd).

{$name}puede ser bf( font-weight: bold), it ( font-style: italic), o cualquier color de contextual
bootstrap ( danger, info, muted, primary, successo warning).
<tree string="Idea Categories" decoration-info="state=='draft'"
decoration-danger="state=='trashed'">
<field name="name"/>
<field name="state"/>
</tree>

editable
Ya sea "top"o "bottom". Hace que la vista editable rbol en el lugar (en lugar de tener que ir a travs de la
vista de formulario), el valor es la posicin en la que aparecen nuevas filas.

Ejercicio
Colorear lista
Modificar la vista de rbol Sesin de tal manera que las sesiones que duran menos de 5 das son de color azul, y los
que duran ms de 15 das son de color rojo.
Modificar la vista de rbol de sesin:
openacademy/views/openacademy.xml
<field name="name">session.tree</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<tree string="Session Tree" decoration-info="duration&lt;5" decoration-danger="duration&
gt;15">
<field name="name"/>
<field name="course_id"/>
<field name="duration" invisible="1"/>
<field name="taken_seats" widget="progressbar"/>
</tree>
</field>

Calendarios
Muestra los registros como los eventos del calendario. Su elemento raz es <calendar>y sus atributos ms comunes
son:
color
El nombre del campo utilizado para la segmentacin de color . Los colores se distribuyen automticamente a
eventos, pero los eventos en el mismo segmento de color (registros que tienen el mismo valor para
su @colorcampo) se les da el mismo color.
date_start
Campo de registro de la celebracin del inicio de fecha / hora para el evento
date_stop (Opcional)
Campo de registro sujeta el extremo de fecha / hora para el evento
field (para definir la etiqueta para cada evento del calendario)
<calendar string="Ideas" date_start="invent_date" color="inventor_id">
<field name="name"/>
</calendar>

Ejercicio
Vista del calendario
Agregar una vista de calendario para la Sesin modelo que permite al usuario ver los eventos asociados a la
Academia abierto.
1. Aadir un campo end_date calculado a partir de start_date y duration
Tip
La funcin inversa hace que el escribible campo, y permite mover las sesiones (a travs de arrastrar y soltar)
en la vista de calendario

2. Agregar una vista de calendario a la Sesin modelo


3. Y aadir la vista del calendario a la sesin de las acciones del modelo

openacademy/models.py
# -*- coding: utf-8 -*-

from datetime import timedelta


from odoo import models, fields, api, exceptions

class Course(models.Model):

attendee_ids = fields.Many2many('res.partner', string="Attendees")

taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')


end_date = fields.Date(string="End Date", store=True,
compute='_get_end_date', inverse='_set_end_date')

@api.depends('seats', 'attendee_ids')
def _taken_seats(self):

},
}
@api.depends('start_date', 'duration')
def _get_end_date(self):
for r in self:
if not (r.start_date and r.duration):
r.end_date = r.start_date
continue

# aadir duracin a start_date, pero: lunes + 5 das = sbado, por lo que


# Restar un segundo para obtener el viernes en su lugar
start = fields.Datetime.from_string(r.start_date)
duration = timedelta(days=r.duration, seconds=-1)
r.end_date = start + duration

def _set_end_date(self):
for r in self:
if not (r.start_date and r.end_date):
continue

# Calcule la diferencia entre las fechas, pero: Viernes - Lunes = 4 das,


# As que agregue un da para obtener 5 das en su lugar
start_date = fields.Datetime.from_string(r.start_date)
end_date = fields.Datetime.from_string(r.end_date)
r.duration = (end_date - start_date).days + 1

@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:

openacademy/views/openacademy.xml
</field>
</record>

<!-- calendar view -->


<record model="ir.ui.view" id="session_calendar_view">
<field name="name">session.calendar</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<calendar string="Session Calendar" date_start="start_date"
date_stop="end_date"
color="instructor_id">
<field name="name"/>
</calendar>
</field>
</record>

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


<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar</field>
</record>

<menuitem id="session_menu" name="Sessions"

Vistas de bsqueda
Los elementos de la vista de busqueda <field> pueden tener una @filter_domain que anula el dominio
generado para buscar en el campo dado. En el dominio dado, self representa el valor introducido por
el usuario. En el siguiente ejemplo, se utiliza para buscar en ambos campos namey description.
Las Vistas de bsqueda tambin pueden contener elementos <filter> que actan como palancas para
bsquedas predefinidas. Los filtros deben tener uno de los siguientes atributos:
domain

Aadir el dominio dado a la bsqueda actual


context
Aadir un poco de contexto a la bsqueda actual; utilizar la clave group_by de los resultados del
grupo en el nombre de campo dado
<search string="Ideas">
<field name="name"/>
<field name="description" string="Name and description"
filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
<field name="inventor_id"/>
<field name="country_id" widget="selection"/>

<filter name="my_ideas" string="My Ideas"


domain="[('inventor_id', '=', uid)]"/>
<group string="Group By">
<filter name="group_by_inventor" string="Inventor"
context="{'group_by': 'inventor_id'}"/>
</group>
</search>

Para utilizar una vista de bsqueda no predeterminado en una accin, debe estar vinculada con el campo
search_view_id del registro de la accin.
La accin tambin puede establecer valores predeterminados para los campos de bsqueda a travs de sus campos
context: claves de contexto de forma inicializar nombre_campo con el valor proporcionado. Los filtros de bsqueda
deben tener una opcin para tener una forma predeterminada y se comportan como booleanos (slo pueden ser
activadas por defecto).search_default_field_name@name

Ejercicio
Vistas de bsqueda

1. Aadir un botn para filtrar los cursos para los cuales el usuario actual es el responsable en la vista de
bsqueda de cursos. Que sea seleccionada por defecto.
2. Aadir un botn para cursos en grupo por el usuario responsable.

openacademy/views/openacademy.xml
<search>
<field name="name"/>
<field name="description"/>
<filter name="my_courses" string="My Courses"
domain="[('responsible_id', '=', uid)]"/>
<group string="Group By">
<filter name="by_responsible" string="Responsible"
context="{'group_by': 'responsible_id'}"/>
</group>
</search>
</field>
</record>

<field name="res_model">openacademy.course</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context" eval="{'search_default_my_courses': 1}"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Create the first course
</p>

Gantt
Grficos de barras horizontales normalmente se utilizan para mostrar la planificacin y el avance del proyecto, su
elemento es la raz <gantt>.
<gantt string="Ideas"
date_start="invent_date"
date_stop="date_finished"
progress="progress"
default_group_by="inventor_id" />
Ejercicio
Diagramas de Gantt
Aadir un diagrama de Gantt que permite al usuario ver la programacin de sesiones relacionado con el mdulo
abierto Academia. Las sesiones deben ser agrupadas por el instructor.

1. Crear un campo calculado expresando la duracin de la sesin en horas


2. Aadir la definicin de la vista de Gantt, y aadir la vista de Gantt para la sesin de la accin del modelo

openacademy/models.py
end_date = fields.Date(string="End Date", store=True,
compute='_get_end_date', inverse='_set_end_date')

hours = fields.Float(string="Duration in hours",


compute='_get_hours', inverse='_set_hours')

@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:

end_date = fields.Datetime.from_string(r.end_date)
r.duration = (end_date - start_date).days + 1

@api.depends('duration')
def _get_hours(self):
for r in self:
r.hours = r.duration * 24

def _set_hours(self):
for r in self:
r.duration = r.hours / 24

@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:

openacademy/views/openacademy.xml
</field>
</record>

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


<field name="name">session.gantt</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<gantt string="Session Gantt" color="course_id"
date_start="start_date" date_delay="hours"
default_group_by='instructor_id'>
<field name="name"/>
</gantt>
</field>
</record>

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


<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,gantt</field>
</record>

<menuitem id="session_menu" name="Sessions"

Vistas de grficos
Vistas de grficos permiten descripcin y anlisis de los modelos agregados, su elemento es la raz <graph>.

Nota
Vistas de giro (elemento <pivot>) una tabla multidimensional, permite la seleccin de los contribuyentes y
dimensiones para obtener el conjunto de datos agregada a la derecha antes de pasar a una visin ms grfica. La
vista de pivote comparte la misma definicin contenido como vistas de grficos.
Las Vistas de grficos tienen 4 modos de visualizacin, el modo por defecto se selecciona usando el @type atributo.
Bar (por defecto)
un grfico de barras, la primera dimensin se utiliza para definir los grupos en el eje horizontal, otras
dimensiones definen bares agregados dentro de cada grupo.
Por bares predeterminados son de lado a lado, que pueden ser apilados mediante el
uso @stacked="True"de la<graph>
Line
Grfico de lneas de 2 dimensiones
Pie
Pastel de 2 dimensiones
Vistas de grficos contienen <field>con una obligatoria @typeatributo tomar los valores:
row (default)
El campo debe ser agregado por defecto
measure

El campo debe ser agregado en lugar de estar agrupado


Advertencia
Vistas de grficos realizan agregaciones en los valores de base de datos, que no funcionan con campos calculados
no almacenados.

Ejercicio
Vista grfico
Aadir una vista de grfico en el objeto Session que muestra, para cada supuesto, el nmero de
asistentes bajo la forma de un grfico de barras.

1. Aadir el nmero de asistentes como un campo calculado almacenado


2. A continuacin, aadir la vista relevante

openacademy/models.py
hours = fields.Float(string="Duration in hours",
compute='_get_hours', inverse='_set_hours')

attendees_count = fields.Integer(
string="Attendees count", compute='_get_attendees_count', store=True)

@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:

for r in self:
r.duration = r.hours / 24

@api.depends('attendee_ids')
def _get_attendees_count(self):
for r in self:
r.attendees_count = len(r.attendee_ids)

@api.constrains('instructor_id', 'attendee_ids')
def _check_instructor_not_in_attendees(self):
for r in self:

openacademy/views/openacademy.xml
</field>
</record>

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


<field name="name">openacademy.session.graph</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<graph string="Participations by Courses">
<field name="course_id"/>
<field name="attendees_count" type="measure"/>
</graph>
</field>
</record>

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


<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,gantt,graph</field>
</record>

<menuitem id="session_menu" name="Sessions"

Kanban
Se utiliza para organizar las tareas, procesos de produccin, etc. Su elemento raz es <kanban>.
Una vista kanban muestra un conjunto de tarjetas, posiblemente agrupadas en columnas. Cada tarjeta representa un
registro, y cada columna los valores de un campo de agregacin.
Por ejemplo, las tareas del proyecto pueden ser organizados por etapas (cada columna es una etapa), o por el
responsable (cada columna es un usuario), y as sucesivamente.
Las Vistas Kanban definen la estructura de cada tarjeta como una mezcla de elementos de forma (incluyendo HTML
bsico) y Qweb .

Ejercicio
Kanban vista
Aadir una vista que muestra Kanban sesiones agrupadas por supuesto (columnas son, pues, los
cursos).

1. Aadir un entero colorcampo de la Sesin modelo


2. Aadir la vista Kanban y actualizar la accin

openacademy/models.py
duration = fields.Float(digits=(6, 2), help="Duration in days")
seats = fields.Integer(string="Number of seats")
active = fields.Boolean(default=True)
color = fields.Integer()

instructor_id = fields.Many2one('res.partner', string="Instructor",


domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml
</record>

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


<field name="name">openacad.session.kanban</field>
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<kanban default_group_by="course_id">
<field name="color"/>
<templates>
<t t-name="kanban-box">
<div
t-attf-class="oe_kanban_color_{{kanban_getcolor(record.color.raw_val
ue)}}
oe_kanban_global_click_edit oe_semantic_html_override
oe_kanban_card {{record.group_fancy==1 ? 'oe_kanban_ca
rd_fancy' : ''}}">
<div class="oe_dropdown_kanban">
<!-- dropdown menu -->
<div class="oe_dropdown_toggle">
<i class="fa fa-bars fa-lg"/>
<ul class="oe_dropdown_menu">
<li>
<a type="delete">Delete</a>
</li>
<li>
<ul class="oe_kanban_colorpicker"
data-field="color"/>
</li>
</ul>
</div>
<div class="oe_clear"></div>
</div>
<div t-attf-class="oe_kanban_content">
<!-- title -->
Session name:
<field name="name"/>
<br/>
Start date:
<field name="start_date"/>
<br/>
duration:
<field name="duration"/>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>

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


<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,gantt,graph,kanban</field>
</record>

<menuitem id="session_menu" name="Sessions"


parent="openacademy_menu"

Workflows
Los flujos de trabajo son modelos asociados a objetos de negocio que describen su dinmica. Los flujos de trabajo
tambin se utilizan para realizar un seguimiento de los procesos que evolucionan con el tiempo.

Ejercicio
Casi un flujo de trabajo
Aadir un campo state a la Sesin modelo. Se utiliza para definir un flujo de trabajo-ish.
Una sesion puede tener tres estados posibles: Proyecto (por defecto), confirmados y hecho.
En la forma de sesiones, agregar un campo (slo lectura) para visualizar el estado, y los botones para
cambiarlo. Las transiciones vlidas son:

Proyecto -> Confirmado


Confirmado -> Proyecto
Confirmado -> Hecho
Hecho -> Proyecto

1. Aadir un nuevo statecampo


2. Aadir los mtodos de la transicin del estado, los que se puede llamar desde botones de vista para cambiar
el estado del registro
3. Y aadir los botones correspondientes a la vista de formulario de la sesin

openacademy/models.py
attendees_count = fields.Integer(
string="Attendees count", compute='_get_attendees_count', store=True)
state = fields.Selection([
('draft', "Draft"),
('confirmed', "Confirmed"),
('done', "Done"),
], default='draft')

@api.multi
def action_draft(self):
self.state = 'draft'

@api.multi
def action_confirm(self):
self.state = 'confirmed'

@api.multi
def action_done(self):
self.state = 'done'

@api.depends('seats', 'attendee_ids')
def _taken_seats(self):
for r in self:

openacademy/views/openacademy.xml
<field name="model">openacademy.session</field>
<field name="arch" type="xml">
<form string="Session Form">
<header>
<button name="action_draft" type="object"
string="Reset to draft"
states="confirmed,done"/>
<button name="action_confirm" type="object"
string="Confirm" states="draft"
class="oe_highlight"/>
<button name="action_done" type="object"
string="Mark as done" states="confirmed"
class="oe_highlight"/>
<field name="state" widget="statusbar"/>
</header>

<sheet>
<group>
<group string="General">

Los flujos de trabajo pueden estar asociadas con cualquier objeto en Odoo, y son totalmente adaptable. Los flujos de
trabajo se utilizan para estructurar y gestionar los ciclos de vida de los objetos de negocio y documentos, y definir las
transiciones, triggers, etc., con herramientas grficas. Flujos de trabajo, actividades nodos (o acciones) y las
transiciones (condiciones) se declaran como registros XML, como de costumbre. Las fichas que navegan en los flujos
de trabajo se denominan elementos de trabajo.
Advertencia
Un flujo de trabajo asociado a un modelo slo se crea cuando se crean los registros del modelo. Por lo tanto no hay
una instancia de flujo de trabajo asociado con casos de sesin creadas antes de la definicin de flujo de trabajo

Ejercicio
Work Flow
Vuelva a colocar la ad-hoc el flujo de trabajo Sesin utilizando un flujo de trabajo real. Transformar la vista Sesin
por lo que sus botones llamar el flujo de trabajo en lugar de los mtodos del modelo.
openacademy/__manifest__.py
'templates.xml',
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
],
# only loaded in demonstration mode
'demo': [

openacademy/models.py
('draft', "Draft"),
('confirmed', "Confirmed"),
('done', "Done"),
])

@api.multi
def action_draft(self):

openacademy/views/openacademy.xml
<field name="arch" type="xml">
<form string="Session Form">
<header>
<button name="draft" type="workflow"
string="Reset to draft"
states="confirmed,done"/>
<button name="confirm" type="workflow"
string="Confirm" states="draft"
class="oe_highlight"/>
<button name="done" type="workflow"
string="Mark as done" states="confirmed"
class="oe_highlight"/>
<field name="state" widget="statusbar"/>

openacademy/views/session_workflow.xml
<odoo>
<data>
<record model="workflow" id="wkf_session">
<field name="name">OpenAcademy sessions workflow</field>
<field name="osv">openacademy.session</field>
<field name="on_create">True</field>
</record>

<record model="workflow.activity" id="draft">


<field name="name">Draft</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="flow_start" eval="True"/>
<field name="kind">function</field>
<field name="action">action_draft()</field>
</record>
<record model="workflow.activity" id="confirmed">
<field name="name">Confirmed</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="kind">function</field>
<field name="action">action_confirm()</field>
</record>
<record model="workflow.activity" id="done">
<field name="name">Done</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="kind">function</field>
<field name="action">action_done()</field>
</record>

<record model="workflow.transition" id="session_draft_to_confirmed">


<field name="act_from" ref="draft"/>
<field name="act_to" ref="confirmed"/>
<field name="signal">confirm</field>
</record>
<record model="workflow.transition" id="session_confirmed_to_draft">
<field name="act_from" ref="confirmed"/>
<field name="act_to" ref="draft"/>
<field name="signal">draft</field>
</record>
<record model="workflow.transition" id="session_done_to_draft">
<field name="act_from" ref="done"/>
<field name="act_to" ref="draft"/>
<field name="signal">draft</field>
</record>
<record model="workflow.transition" id="session_confirmed_to_done">
<field name="act_from" ref="confirmed"/>
<field name="act_to" ref="done"/>
<field name="signal">done</field>
</record>
</data>
</odoo>
Tip
Con el fin de comprobar si las instancias del flujo de trabajo se crean correctamente junto sesiones, vaya
a instancias Ajustes flujos de trabajo tcnico

Ejercicio
Transiciones automticas
Transforma automticamente las sesiones de borrador a confirmado cuando se reservan ms de la mitad de los
asientos de la sesin.
openacademy/views/session_workflow.xml
<field name="act_to" ref="done"/>
<field name="signal">done</field>
</record>

<record model="workflow.transition" id="session_auto_confirm_half_filled">


<field name="act_from" ref="draft"/>
<field name="act_to" ref="confirmed"/>
<field name="condition">taken_seats &gt; 50</field>
</record>
</data>
</odoo>

Ejercicio
Las acciones del servidor
Reemplazar los mtodos de Python para la sincronizacin de estado de la sesin por las acciones del servidor.
Tanto el flujo de trabajo y las acciones del servidor podran haber sido creados por completo de la interfaz de
usuario.
openacademy/views/session_workflow.xml
<field name="on_create">True</field>
</record>

<record model="ir.actions.server" id="set_session_to_draft">


<field name="name">Set session to Draft</field>
<field name="model_id" ref="model_openacademy_session"/>
<field name="code">
model.search([('id', 'in', context['active_ids'])]).action_draft()
</field>
</record>
<record model="workflow.activity" id="draft">
<field name="name">Draft</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="flow_start" eval="True"/>
<field name="kind">dummy</field>
<field name="action"></field>
<field name="action_id" ref="set_session_to_draft"/>
</record>

<record model="ir.actions.server" id="set_session_to_confirmed">


<field name="name">Set session to Confirmed</field>
<field name="model_id" ref="model_openacademy_session"/>
<field name="code">
model.search([('id', 'in', context['active_ids'])]).action_confirm()
</field>
</record>
<record model="workflow.activity" id="confirmed">
<field name="name">Confirmed</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="kind">dummy</field>
<field name="action"></field>
<field name="action_id" ref="set_session_to_confirmed"/>
</record>

<record model="ir.actions.server" id="set_session_to_done">


<field name="name">Set session to Done</field>
<field name="model_id" ref="model_openacademy_session"/>
<field name="code">
model.search([('id', 'in', context['active_ids'])]).action_done()
</field>
</record>
<record model="workflow.activity" id="done">
<field name="name">Done</field>
<field name="wkf_id" ref="wkf_session"/>
<field name="kind">dummy</field>
<field name="action"></field>
<field name="action_id" ref="set_session_to_done"/>
</record>

<record model="workflow.transition" id="session_draft_to_confirmed">

Security
Los mecanismos de control de acceso deben estar configurados para lograr una poltica de seguridad coherente.

Mecanismos de control de acceso basadas en grupos


Los grupos se crean como registros normales en el modelo res.groups, y el acceso a travs del men otorgado
definiciones de men. Sin embargo, incluso sin un men, objetos todava pueden ser accesibles indirectamente, lo
que los permisos de nivel de objeto real (leer, escribir, crear, desvincular) debe definirse para grupos. Por lo general,
se insertan a travs de archivos CSV dentro de los mdulos. Tambin es posible restringir el acceso a los campos
especficos en una visin o un objeto utilizando grupos del campo de atributo.

Derechos de acceso
Los derechos de acceso se definen como registros del modelo ir.model.access. Cada derecho de acceso est
asociada a un modelo, un grupo (o hay un grupo para el acceso global), y un conjunto de permisos: leer, escribir,
crear, desvincular. Estos derechos de acceso se crean normalmente por un archivo CSV nombre 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

Ejercicio
Aadir el control de acceso a travs de la interfaz de Odoo
Crear un nuevo usuario "John Smith". A continuacin, crear un grupo "OpenAcademy / Sesin Lea" con acceso de
lectura a la Sesin modelo.

1. Crear un nuevo usuario John Smith a travs de Configuracin de usuarios Usuarios


2. Crear un nuevo grupo session_read a travs de la configuracin de usuarios Grupos , debe tener
acceso de lectura en la sesin de modelo
3. Editar John Smith para que sean un miembro de session_read
4. Inicie la sesin como John Smith para comprobar los derechos de acceso son correctos

Ejercicio
Aadir el control de acceso a travs de los archivos de datos en el mdulo
El uso de archivos de datos,

Crear un grupo OpenAcademy / Gestor con pleno acceso a todos los modelos OpenAcademy
Hacer Sesin y Curso ledos por todos los usuarios

1. Crear un nuevo archivo openacademy/security/security.xmlpara mantener el grupo Administrador de


OpenAcademy
2. Editar el archivo openacademy/security/ir.model.access.csvcon los derechos de acceso a los modelos
3. Finalmente actualizar openacademy/__manifest__.pypara aadir los nuevos archivos de datos a ella.

openacademy/__manifest__.py
# always loaded
'data': [
'security/security.xml',
'security/ir.model.access.csv',
'templates.xml',
'views/openacademy.xml',
'views/partner.xml',

openacademy/security/ir.model.access.csv
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1
session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1
course_read_all,course all,model_openacademy_course,,1,0,0,0
session_read_all,session all,model_openacademy_session,,1,0,0,0

openacademy/security/security.xml
<odoo>
<data>
<record id="group_manager" model="res.groups">
<field name="name">OpenAcademy / Manager</field>
</record>
</data>
</odoo>

Record rules
Una regla de registro restringe los derechos de acceso a un subconjunto de registros del modelo dado. Una regla es
un registro de modelo ir.rule, y se asocia a un modelo, un nmero de grupos (campo Many2Many), permisos al que
se aplica la restriccin, y un dominio. El dominio especfica a la que registra los derechos de acceso son limitadas.
He aqu un ejemplo de una regla que impide la eliminacin de los cables que no estn en el estado cancel. Observe
que el valor del campo groups debe seguir la misma convencin como el mtodo write()del 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>

Ejercicio
Regla de registro
Aadir una regla de registro para el modelo curso y el grupo "OpenAcademy /Manager", que restringe write
y unlink accede a la responsable de un curso. Si un curso no tiene ninguna responsabilidad, todos los usuarios del
grupo debe ser capaz de modificarlo.
Crear una nueva regla en openacademy/security/security.xml:
openacademy/security/security.xml
<record id="group_manager" model="res.groups">
<field name="name">OpenAcademy / Manager</field>
</record>

<record id="only_responsible_can_modify" model="ir.rule">


<field name="name">Only Responsible can modify Course</field>
<field name="model_id" ref="model_openacademy_course"/>
<field name="groups" eval="[(4, ref('openacademy.group_manager'))]"/>
<field name="perm_read" eval="0"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="0"/>
<field name="perm_unlink" eval="1"/>
<field name="domain_force">
['|', ('responsible_id','=',False),
('responsible_id','=',user.id)]
</field>
</record>
</data>
</odoo>

Wizards
Los Wizards describen sesiones interactivas con el usuario (o cuadros de dilogo) a travs de formularios
dinmicos. Un asistente es simplemente un modelo que ampla la clase TransientModel en lugar de Model. La
clase TransientModel se extiende Model y reutilizacin de todos sus mecanismos existentes, con las siguientes
particularidades:

Registros del asistente no estn destinados a ser persistentes; que se eliminan automticamente de la base
de datos despus de un cierto tiempo. Es por eso que se llaman transitoria.
Asistente modelos no requieren derechos de acceso explcitos: los usuarios tienen todos los permisos en los
registros del asistente.
Registros asistente puede referirse a los registros regulares o registros del asistente a travs de campos
many2one, pero los registros regulares no pueden referirse a los registros del asistente travs de un campo
many2one.

Queremos crear un asistente que permiten a los usuarios crear asistentes para una sesin particular, o para obtener
una lista de las sesiones a la vez.

Ejercicio
Definir el asistente
Crear un modelo de mago con una relacin many2one con la Sesin de modelo y una relacin Many2Many con
el socio de modelo.
Aadir un nuevo archivo openacademy/wizard.py:
openacademy/__init__.py
from . import controllers
from . import models
from . import partner
from . import wizard

openacademy/wizard.py
# -*- coding: utf-8 -*-

from odoo import models, fields, api

class Wizard(models.TransientModel):
_name = 'openacademy.wizard'

session_id = fields.Many2one('openacademy.session',
string="Session", required=True)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

Lanzamientos de wizards
Los wizards son lanzados por registros ir.actions.act_window, con el campo target se establece en el
valor new. Este ltimo se abre la vista asistente en una ventana emergente. La accin puede ser provocada por un
elemento de men.
Hay otra manera de iniciar el asistente de: utilizar un ir.actions.act_window disco como el anterior, pero con un
campo adicional src_model que especifica en el contexto del modelo de la accin, que est disponible. El asistente
aparecer en las acciones contextuales del modelo, por encima de la vista principal. Debido a algunos ganchos
internos en el ORM, tal accin se declara en XML con la etiqueta act_window.
<act_window id="launch_the_wizard"
name="Launch the Wizard"
src_model="context.model.name"
res_model="wizard.model.name"
view_mode="form"
target="new"
key2="client_action_multi"/>

Para Wizards vistas regulares y sus botones pueden utilizar el atributo special="cancel" para cerrar la ventana del
asistente sin guardar.

Ejercicio
Iniciar el asistente

1. Definir una vista de formulario para el asistente.


2. Aadir la accin para poner en marcha en el marco de la Sesin modelo.
3. Definir un valor predeterminado para el campo de sesin en el asistente; utilizar el parmetro de
contexto self._context para recuperar la sesin actual.

openacademy/wizard.py
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'

def _default_session(self):
return self.env['openacademy.session'].browse(self._context.get('active_id'))

session_id = fields.Many2one('openacademy.session',
string="Session", required=True, default=_default_session)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/openacademy.xml
parent="openacademy_menu"
action="session_list_action"/>

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


<field name="name">wizard.form</field>
<field name="model">openacademy.wizard</field>
<field name="arch" type="xml">
<form string="Add Attendees">
<group>
<field name="session_id"/>
<field name="attendee_ids"/>
</group>
</form>
</field>
</record>

<act_window id="launch_session_wizard"
name="Add Attendees"
src_model="openacademy.session"
res_model="openacademy.wizard"
view_mode="form"
target="new"
key2="client_action_multi"/>
</data>
</odoo>

Ejercicio
Registro asistentes
Aadir botones para el asistente, y aplicar el procedimiento correspondiente para la adicin de los
asistentes a la sesin dada.
openacademy/views/openacademy.xml
<field name="attendee_ids"/>
</group>
<footer>
<button name="subscribe" type="object"
string="Subscribe" class="oe_highlight"/>
or
<button special="cancel" string="Cancel"/>
</footer>
</form>
</field>
</record>

openacademy/wizard.py
session_id = fields.Many2one('openacademy.session',
string="Session", required=True, default=_default_session)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

@api.multi
def subscribe(self):
self.session_id.attendee_ids |= self.attendee_ids
return {}

Ejercicio
Registro asistentes a varias sesiones
Modificar el modelo de asistente para que los asistentes se pueden registrar en varias sesiones.
openacademy/views/openacademy.xml
<form string="Add Attendees">
<group>
<field name="session_ids"/>
<field name="attendee_ids"/>
</group>
<footer>
<button name="subscribe" type="object"

openacademy/wizard.py
class Wizard(models.TransientModel):
_name = 'openacademy.wizard'

def _default_sessions(self):
return self.env['openacademy.session'].browse(self._context.get('active_ids'))

session_ids = fields.Many2many('openacademy.session',
string="Sessions", required=True, default=_default_sessions)
attendee_ids = fields.Many2many('res.partner', string="Attendees")

@api.multi
def subscribe(self):
for session in self.session_ids:
session.attendee_ids |= self.attendee_ids
return {}

Internacionalizacin
Cada mdulo puede proporcionar a sus propias traducciones en el directorio i18n, por tener archivos con el nombre
LANG.po donde idioma es el cdigo de regin para el idioma o el idioma y el pas combinacin cuando difieren (por
ejemplo pt.po o pt_BR.po). Las traducciones sern cargados automticamente por Odoo para todos los idiomas
habilitados. Los desarrolladores utilizan siempre Ingls al crear un mdulo, a continuacin, exportar los trminos del
mdulo utilizando caracterstica de exportacin gettext POT de Odoo ( Ajustes Las traducciones Importar /
Exportar exportacin de traduccin sin especificar un idioma), para crear el archivo POT plantilla de mdulo, y
luego se derivan del traducidos archivos PO. Muchos de IDE tienen plugins o modos de edicin y combinacin de
archivos / PO POT.
Tip
Los archivos generados por objeto porttil Odoo se publican en Transifex , por lo que es fcil de traducir el software.
|- idea/ # The module directory
|- i18n/ # Translation files
| - idea.pot # Translation Template (exported from Odoo)
| - fr.po # French translation
| - pt_BR.po # Brazilian Portuguese translation
| (...)
Tip
Por defecto exportacin POT de Odoo slo extrae las etiquetas dentro de archivos XML o definiciones de campo en
el interior en el cdigo Python, pero cualquier cadena de Python se puede traducir de esta manera por lo rodea con
la funcin odoo._() (por ejemplo _("Label"))

Ejercicio
Traducir un mdulo
Elegir un segundo idioma para la instalacin de Odoo. Traducir el mdulo utilizando las facilidades proporcionadas
por Odoo.

1. Crear un directorio openacademy/i18n/


2. Instalar el idioma que desee ( Administracin Las traducciones cargar una traduccin oficial )
3. Sincronizar los trminos traducibles ( Administracin Traducciones Trminos de Aplicaciones
Sincronizar Traducciones )
4. Crear una plantilla de archivo de traduccin mediante la exportacin ( Administracin Traducciones ->
Importar / Exportar exportacin traduccin ) sin especificar un idioma, salvo en openacademy/i18n/
5. Crear un archivo de traduccin mediante la exportacin ( Las traducciones Importar / Exportar
exportacin Traduccin Administracin ) y la especificacin de un idioma. Guardarlo
en openacademy/i18n/
6. Abra el archivo de traduccin exportado (con un editor de texto bsico o un dedicado PO-editor de archivos
por ejemplo Poedit y traducir los trminos que faltan
7. En models.py, aadir una instruccin de importacin para la funcin odoo._y marcar las cadenas faltantes
como traducible
8. Repita los pasos 3-6

openacademy/models.py
# -*- coding: utf-8 -*-

from datetime import timedelta


from odoo import models, fields, api, exceptions, _

class Course(models.Model):
_name = 'openacademy.course'

default = dict(default or {})

copied_count = self.search_count(
[('name', '=like', _(u"Copy of {}%").format(self.name))])
if not copied_count:
new_name = _(u"Copy of {}").format(self.name)
else:
new_name = _(u"Copy of {} ({})").format(self.name, copied_count)

default['name'] = new_name
return super(Course, self).copy(default)

if self.seats < 0:
return {
'warning': {
'title': _("Incorrect 'seats' value"),
'message': _("The number of available seats may not be negative"),
},
}
if self.seats < len(self.attendee_ids):
return {
'warning': {
'title': _("Too many attendees"),
'message': _("Increase seats or remove excess attendees"),
},
}

def _check_instructor_not_in_attendees(self):
for r in self:
if r.instructor_id and r.instructor_id in r.attendee_ids:
raise exceptions.ValidationError(_("A session's instructor can't be an attendee"))
Reporting (informes)
Los informes impresos
Odoo 8.0 viene con un nuevo motor de informes basado en QWEB , Twitter Bootstrap y wkhtmltopdf .
Un informe es una combinacin de dos elementos:
Una ir.actions.report.xml, por el cual una <report>se proporciona elemento de acceso directo, se establece
varios parmetros bsicos para el informe (tipo predeterminado, si el informe debe ser guardados en la base
de datos tras generacin, ...)
<report
id="account_invoices"
model="account.invoice"
string="Invoices"
report_type="qweb-pdf"
name="account.report_invoice"
file="account.report_invoice"
attachment_use="True"
attachment="(object.state in ('open','paid')) and
('INV'+(object.number or '').replace('/','')+'.pdf')"
/>
Un estndar vista QWEB para el informe real:
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="report.external_layout">
<div class="page">
<h2>Report title</h2>
</div>
</t>
</t>
</t>

el contexto de representacin estndar proporciona un nmero de elementos, el ms


ser importante:

`` docs``
los registros para los que se imprime el informe
`` user``
al usuario imprimir el informe

Dado que los informes son pginas web estndar, estn disponibles a travs de un parmetros de URL y de
salida pueden ser manipuladas a travs de esta URL, por ejemplo, la versin HTML de la factura informe est
disponible a travs de http: // localhost: 8069 / informe / html / account.report_invoice / 1 (si account est
instalada) y la versin PDF a travs de http: // localhost: 8069 / report / pdf / account.report_invoice / 1 .

Peligro
Si parece que su informe de PDF le faltan los estilos (es decir, el texto aparece pero el estilo / diseo es diferente de
la versin html), probablemente su wkhtmltopdf proceso no puede llegar a su servidor web para descargarlos.
Si se revisa los registros del servidor y ver que los estilos CSS no estn siendo descargados al generar un informe
en PDF, con toda seguridad este es el problema.
El wkhtmltopdf proceso va a utilizar el parmetro del sistema web.base.url como la ruta raz de todos los archivos
vinculados, pero este parmetro se actualiza automticamente cada vez que el administrador est conectado. Si el
servidor se encuentra protegido por algn tipo de proxy, que no podan ser alcanzable. Puede solucionar este
problema mediante la adicin de uno de estos parmetros del sistema:

report.url, Que apunta a una URL accesible desde el servidor (probablemente http://localhost:8069o algo
similar). Ser utilizado slo para este propsito en particular.
web.base.url.freeze, Cuando se establece en True, parar las actualizaciones automticas web.base.url.

Ejercicio
Crear un informe para el modelo de sesin
Para cada sesin, que debe mostrar el nombre de la sesin, su inicio y fin, y la lista de asistentes de la sesin.
openacademy/__manifest__.py
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
'reports.xml',
],
# only loaded in demonstration mode
'demo': [

openacademy/reports.xml
<odoo>
<data>
<report
id="report_session"
model="openacademy.session"
string="Session Report"
name="openacademy.report_session_view"
file="openacademy.report_session"
report_type="qweb-pdf" />

<template id="report_session_view">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="report.external_layout">
<div class="page">
<h2 t-field="doc.name"/>
<p>From <span t-field="doc.start_date"/> to <span t-field="doc.end_date"/></p>
<h3>Attendees:</h3>
<ul>
<t t-foreach="doc.attendee_ids" t-as="attendee">
<li><span t-field="attendee.name"/></li>
</t>
</ul>
</div>
</t>
</t>
</t>
</template>
</data>
</odoo>

Dashboards
Ejercicio
Definir un tablero de instrumentos (dashboard)
Definir un cuadro de mando que contiene la vista de grfico que ha creado, la vista del calendario de sesiones y una
vista de la lista de los cursos (conmutable a una vista de formulario). Este panel debe estar disponible a travs de un
elemento de men en el men, y se muestra automticamente en el cliente Web cuando se selecciona el men
principal OpenAcademy.
1. Crear un archivo openacademy/views/session_board.xml. Debe contener la opinin de la tarjeta, las
acciones que se hace referencia en ese punto de vista, una accin para abrir el tablero de instrumentos y
una re-definicin del elemento del men principal para aadir la accin salpicadero
Nota
Estilos disponibles del tablero de instrumentos son 1, 1-1, 1-2, 2-1 y 1-1-1

2. Actualizacin de openacademy/__manifest__.py hace referencia al nuevo archivo de datos.

openacademy/__manifest__.py
'version': '0.1',

# any module necessary for this one to work correctly


'depends': ['base', 'board'],
# always loaded
'data': [

'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
'views/session_board.xml',
'reports.xml',
],
# only loaded in demonstration mode

openacademy/views/session_board.xml
<?xml version="1.0"?>
<odoo>
<data>
<record model="ir.actions.act_window" id="act_session_graph">
<field name="name">Attendees by course</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">graph</field>
<field name="view_id"
ref="openacademy.openacademy_session_graph_view"/>
</record>
<record model="ir.actions.act_window" id="act_session_calendar">
<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">calendar</field>
<field name="view_id" ref="openacademy.session_calendar_view"/>
</record>
<record model="ir.actions.act_window" id="act_course_list">
<field name="name">Courses</field>
<field name="res_model">openacademy.course</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<record model="ir.ui.view" id="board_session_form">
<field name="name">Session Dashboard Form</field>
<field name="model">board.board</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Session Dashboard">
<board style="2-1">
<column>
<action
string="Attendees by course"
name="%(act_session_graph)d"
height="150"
width="510"/>
<action
string="Sessions"
name="%(act_session_calendar)d"/>
</column>
<column>
<action
string="Courses"
name="%(act_course_list)d"/>
</column>
</board>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="open_board_session">
<field name="name">Session Dashboard</field>
<field name="res_model">board.board</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="usage">menu</field>
<field name="view_id" ref="board_session_form"/>
</record>

<menuitem
name="Session Dashboard" parent="base.menu_reporting_dashboard"
action="open_board_session"
sequence="1"
id="menu_board_session" icon="terp-graph"/>
</data>
</odoo>

WebServices
El mdulo de servicios web ofrece una interfaz comn para todos los de servicios web:

XML-RPC
JSON-RPC

Los objetos de negocio tambin se puede acceder a travs del mecanismo de objetos distribuidos. Todos ellos
pueden ser modificados a travs de la interfaz de cliente con vistas contextuales.
Odoo es accesible a travs de interfaces XML-RPC / JSON-RPC, para los que existen bibliotecas en muchos
idiomas.

XML-RPC Library
El siguiente ejemplo es un programa Python que interacta con un servidor Odoo con la biblioteca xmlrpclib:

import xmlrpclib

root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)

uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)


print "Logged in as %s (uid: %d)" % (USER, uid)

# Create a new note


sock = xmlrpclib.ServerProxy(root + 'object')
args = {
'color' : 8,
'memo' : 'This is a note',
'create_uid': uid,
}
note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)

Ejercicio
Aadir un nuevo servicio al cliente
Escribir un programa Python capaz de enviar peticiones XML-RPC a un PC con Odoo (el suyo o de su
instructor). Este programa debe mostrar todas las sesiones, y su correspondiente nmero de asientos. Tambin
debe crear una nueva sesin para uno de los cursos.
import functools
import xmlrpclib
HOST = 'localhost'
PORT = 8069
DB = 'openacademy'
USER = 'admin'
PASS = 'admin'
ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)

# 1. Login
uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
print "Logged in as %s (uid:%d)" % (USER,uid)

call = functools.partial(
xmlrpclib.ServerProxy(ROOT + 'object').execute,
DB, uid, PASS)

# 2. Read the sessions


sessions = call('openacademy.session','search_read', [], ['name','seats'])
for session in sessions:
print "Session %s (%s seats)" % (session['name'], session['seats'])
# 3.create a new session
session_id = call('openacademy.session', 'create', {
'name' : 'My session',
'course_id' : 2,
})

En lugar de utilizar una ID de curso no modificable, el cdigo puede buscar un curso por su nombre:
# 3.create a new session for the "Functional" course
course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
session_id = call('openacademy.session', 'create', {
'name' : 'My session',
'course_id' : course_id,
})

JSON-RPC Library
El siguiente ejemplo es un programa Python que interacta con un servidor Odoo con las bibliotecas estndar de
Python urllib2y json:

import json
import random
import urllib2

def json_rpc(url, method, params):


data = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": random.randint(0, 1000000000),
}
req = urllib2.Request(url=url, data=json.dumps(data), headers={
"Content-Type":"application/json",
})
reply = json.load(urllib2.urlopen(req))
if reply.get("error"):
raise Exception(reply["error"])
return reply["result"]

def call(url, service, method, *args):


return json_rpc(url, "call", {"service": service, "method": method, "args": args})

# log in the given database


url = "http://%s:%s/jsonrpc" % (HOST, PORT)
uid = call(url, "common", "login", DB, USER, PASS)

# create a new note


args = {
'color' : 8,
'memo' : 'This is another note',
'create_uid': uid,
}
note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args)
Aqu est el mismo programa, el uso de la biblioteca jsonrpclib :
import jsonrpclib

# server proxy object


url = "http://%s:%s/jsonrpc" % (HOST, PORT)
server = jsonrpclib.Server(url)

# log in the given database


uid = server.call(service="common", method="login", args=[DB, USER, PASS])

# helper function for invoking model methods


def invoke(model, method, *args):
args = [DB, uid, PASS, model, method] + list(args)
return server.call(service="object", method="execute", args=args)

# create a new note


args = {
'color' : 8,
'memo' : 'This is another note',
'create_uid': uid,
}
note_id = invoke('note.note', 'create', args)

Estos ejemplos pueden ser fcilmente adaptados de XML-RPC a JSON-RPC.


Nota
Hay una serie de APIs de alto nivel en diversos idiomas para acceder a sistemas Odoo sin explcitamente ir a travs
de XML-RPC o JSON-RPC, tales como:

https://github.com/akretion/ooor
https://github.com/syleam/openobject-library
https://github.com/nicolas-van/openerp-client-lib
http://pythonhosted.org/OdooRPC
https://github.com/abhishek-jaiswal/php-openerp-lib

[1] es possible disable the automatic creation of some fields (Desactivar la creacin automtica de algunos
campos)
[2] escribir consultas SQL primas es posible, pero requiere cuidado, ya que no pasa por todos los mecanismos de
autenticacin y seguridad Odoo.

S-ar putea să vă placă și