47526968 creacion del primer modulo

34
Creación de un módulo OpenERP

Upload: brujitaest-gatita

Post on 14-Apr-2015

91 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 47526968 Creacion Del Primer Modulo

Creación de un móduloOpenERP

Page 2: 47526968 Creacion Del Primer Modulo

Índice de contenidos1. Introducción......................................................................................................................................32. Modelo, Vista, Controlador (MVC)..................................................................................................4

2.1. Modelo Vista Controlador en OpenERP...................................................................................53. Los módulos.....................................................................................................................................6

3.1. Módulo ejemplo academy.........................................................................................................64. Descripción de módulo: Fichero __openerp__.py............................................................................8

4.1. Módulo ejemplo academy.........................................................................................................85. Programación del modelo en Python..............................................................................................10

5.1. Definición de las tablas...........................................................................................................105.1.1. Definición de las columnas..............................................................................................10

a) Campos simples................................................................................................................10b) Campos relacionales.........................................................................................................11c) Campos funcionales.........................................................................................................12

5.1.2. Módulo ejemplo academy................................................................................................126. Programación de los menús, acciones y vistas: Archivos XML.....................................................15

6.1. Los menús................................................................................................................................166.1.1. Módulo ejemplo academy................................................................................................17

6.2. Las acciones............................................................................................................................186.2.1. Acciones window (apertura de vistas).............................................................................186.2.2. El dominio de la acción. Condiciones.............................................................................216.2.3. Módulo ejemplo academy...............................................................................................22

6.3. Las vistas.................................................................................................................................236.3.1. Vistas de formulario........................................................................................................25

a) Módulo ejemplo academy................................................................................................286.3.2. Vistas de lista o árbol......................................................................................................31

a) Módulo ejemplo academy.................................................................................................317. Anexo 1...........................................................................................................................................34

Page 3: 47526968 Creacion Del Primer Modulo

1.  Introducción

OpenERP es un programa Cliente/Servidor  basado en Python para  la  planificación de recursos empresariales   (ERP).   Consta   de   un   cliente  OpenERP­Client  y   un   servidor  OpenERP­Server mientras   que   la   persistencia   de   datos   está   proporcionada   por   la   base   de   datos   relacional PostgreSQL.

Figura 1.1: Estructura de OpenERP 

OpenERP utiliza el protocolo XML­RPC o NET­RPC para la comunicación entre cliente y servidor También dispone de un cliente web para generar la interfaz gráfica del usuario como una página web y así poder usar OpenERP desde un navegador web (figura 1.1).

Una vez instalado, OpenERP tiene una estructura modular permitiendo añadir módulos según vaya siendo necesario.

OpenERP   funciona   sobre   un   entorno   de   desarrollo   o   framework   llamado   OpenObject.   Este framework permite el desarrollo rápido de aplicaciones (RAD ­ Rapid Application development)

OpenObject es un entorno de desarrollo rápido de aplicaciones de gestión orientado a objetos en Python usando base de datos PostgreSQL. Sus componentes son:

● ORM (Object Relational Mapping): Mapeo de bases de datos relacionales a objetos Python

● Arquitectura MVC (Modelo Vista Controlador)

● Sistema de flujos de trabajo o workflows

● Diseñador de informes o reports (OpenOffice, JasperReports, ...)

● Herramientas BI (Business Intelligence) y OLAP cube engine

● Clientes Gtk, Koo (KDE) y Web

Page 4: 47526968 Creacion Del Primer Modulo

2.  Modelo, Vista, Controlador (MVC)

OpenERP utiliza el patrón de arquitectura de software Modelo Vista Controlador (figura 2.1).

Una definición de este patrón podría ser la que ofrece Wikipedia: Modelo Vista Controlador (MVC) es un patrón de arquitectura de software que separa en tres componentes distintos:

● Los datos de la aplicación (modelo)

● La interfaz de usuario (vista)

● La lógica de control (controlador)

El patrón MVC se ve frecuentemente en aplicaciones web donde:

● La vista es la página HTML y el código que provee de datos dinámicos a la página.

● El modelo es el Sistema de Gestión de Base de Datos y la lógica de negocio.

● El controlador es el responsable de recibir los eventos de entrada desde la vista.

En aplicaciones complejas que presentan multitud de datos para el usuario, a menudo es deseable separar los datos (modelo) y la interfaz de usuario (vista), de manera que los cambios en la interfaz de usuario no afectan a la manipulación de datos, y que los datos pueden ser reorganizados sin cambiar   la   interfaz de usuario.  El  modelo­vista­controlador   resuelve este  problema mediante  la disociación del acceso a datos y la lógica de negocio de la presentación de los datos y la interacción del usuario, mediante la introducción de un componente intermedio: el controlador.

Figura 2.1: Modelo Vista Controlador.

Por ejemplo, en el diagrama anterior, las flechas continuas que van desde el controlador a la vista y al modelo significan que el controlador tiene acceso completo tanto a la vista como al modelo. Las flechas discontinuas que van desde la vista hacia  el controlador  significan que la vista tiene un acceso limitado al controlador.

Las razones de este diseño son las siguientes:

Page 5: 47526968 Creacion Del Primer Modulo

• Acceso de modelo a vista: El modelo envía la notificación a la vista cuando sus datos han sido modificados, con el fin de que la vista pueda actualizar su contenido. El modelo no necesita conocer el funcionamiento interno de la vista para realizar esta operación. Sin embargo, la vista necesita acceso a la partes internas del controlador.

• Acceso de  vista a  controlador: Las dependencias que la vista tiene sobre el controlador deben ser mínimas ya que el controlador pueda ser sustituido en cualquier momento.

2.1.  Modelo Vista Controlador en OpenERP

En OpenERP, podemos aplicar este modelo­vista­controlador de la siguiente manera:

• Modelo (model): Los objetos de OpenERP con sus columnas que normalmente se guardan en las tablas de PostgresSQL con sus campos. Permite la creación/actualización automática de las tablas y acceder a las tablas sin usar SQL.

• Vista (view): Listas, formularios, calendarios, gráficos, ... definidas en archivos XML. En estos archivos también se definen menús, acciones, informes, asistentes, ...

• Controlador  (controller):  Métodos Python definidos dentro de los objetos de OpenERP que proporcionan la lógica: Validación de datos, cálculos, ...

Page 6: 47526968 Creacion Del Primer Modulo

3.  Los módulos

El uso de los módulos es  la  manera de ampliar  la  funcionalidad OpenERP. Una instalación de OpenERP se compone de un núcleo y de varios  módulos  dependiendo de  las  necesidades,  por ejemplo:

• base: El módulo básico compuesto por los objetos básicos como compañías, monedas, usuarios, empresas, direcciones, ... (res.company, res.currency, res.user, res.partner, res.partner.address, ...). Este módulo se instala siempre. 

• account: Gestión contable / financiera / analítica.• crm: Gestión de la relación con los clientes/proveedores. • product: Gestión de productos, tarifas.• sale: Gestión de ventas.• purchase: Gestión de compras.• stock: Gestión de logística, almacenes.• mrp: Fabricación y planificación de recursos. 

Los nuevos módulos pueden programarse fácilmente y requieren un poco de práctica en XML y Python.

Todos   los   módulos   están   ubicados   en   la   carpeta  bin/addons  del   directorio   de   instalación  del servidor OpenERP.

Los pasos básicos para generar un módulo son:

• Crear un subdirectorio dentro de la carpeta bin/addons del directorio de instalación del servidor OpenERP. 

• Crear un archivo de inicio del módulo de Python: __init__.py (que importa los otros archivos .py que tenga el módulo).

• Crear un archivo con la descripción del módulo de OpenERP: __openerp__.py  (Nota: Hasta la versión 5.0 se denominaba __terp__.py).

• Crear los archivos Python que contendrán los objetos (clases con atributos y métodos). Aquí  es donde se define el modelo y el controlador. 

• Crear los archivos XML para la definición de datos (vistas, acciones, menús, datos de ejemplo, ...). Estos archivos deben ser indicados dentro del archivo __openerp__.py.

• Opcionalmente,   crear   informes/listados,   asistentes   y   flujos   de   trabajo.   Pueden   estar   en subcarpetas (report, wizard, ...)

• Opcionalmente, aunque recomendable, poner las traducciones en una carpeta llamada i18n  i las reglas de seguridad en una carpeta llamada security dentro del mismo módulo.

En todos los ficheros acostumbra a aparecer una cabecera que es un comentario Python referido a la licencia  del  módulo  y   el   copyright  del   autor/es,   esto   se  puede  copiar   de  cualquier   fichero  de cualquier módulo y adaptarlo a nuestras necesidades.

3.1.  Módulo ejemplo academy

La carpeta de nuestro módulo academy tendrá una estructura similar a esta:

• academy– __openerp__.py– __init__.py

Page 7: 47526968 Creacion Del Primer Modulo

– academy.py– academy_view.xml– security

∙ ir.model.access.csv– i18n

∙ academy.pot∙ es.po∙ ca.po

Si dentro de nuestro módulo academy creamos un archivo "academy.py" que contiene la definición de nuestros objetos, tenemos que incluir la siguiente línea en el archivo __init__.py:

import academy

Page 8: 47526968 Creacion Del Primer Modulo

4.  Descripción de módulo: Fichero __openerp__.py

Cualquier módulo que creemos debe contener un un archivo __openerp__.py (hasta la versión 5.0 se denominaba __tinyerp__.py) ubicado en la raíz de nuestro módulo . Este archivo, que debe tener formato de un diccionario de Python, es responsable de determinar, entre otras cosas:

• El nombre, descripción, autores, licencia, versión, categoría, ... del módulo.• Los archivos XML que serán analizados durante el arranque del servidor OpenERP. • Las dependencias con otros módulos.

Este archivo debe contener los siguientes valores dentro de un diccionario Python  (estructura de datos cerrada con llaves { }):

• name: Nombre del módulo.• version: Versión del módulo• description: Descripción del módulo.• author: Autor del módulo• website: Sitio web del modulo• license: Licencia del módulo (por defecto GPL­3)• category:  Categoría  a   la  que  pertenece  el  módulo.  Texto   separado  por  barras   /   con   la   ruta 

jerárquica de las categorías. • depends: Lista de módulos de los que depende este módulo. Muchas veces se incluye el módulo 

base en las dependencias ya que algunos datos de este módulo son necesarios para las vistas, informes, ...

• init_xml: Lista de archivos XML que se cargaran al iniciar el servidor OpenERP con la opción "­init=modulo". Las rutas de los archivos debe ser relativas al directorio donde está el módulo.

• update_xml:  Lista  de archivos XML que se cargaran al   iniciar  el  servidor  OpenERP con la opción "­update=modulo". Las rutas de los archivos debe ser relativas al directorio donde está el módulo.

• demo_xml: Lista de archivos XML que se cargaran si se ha instalado el servidor OpenERP con datos de demostración o ejemplo. Las rutas de los archivos debe ser relativas al directorio donde está el módulo.

• installable: Acepta valores True o False y determina si el módulo es instalable o no.• active: acepta valores True o False y determina los si el módulo será instalado cuando se crea la 

base de datos (por defecto False)

4.1.  Módulo ejemplo academy

Este es un ejemplo del archivo __openerp__.py para el módulo academy:

{ "name" : "academy", "version" : "0.1", "author" : "zikzakmeida", "website" : "http://zikzakmedia.com", "category" : "Generic Modules", "description": """Academy module description""", "depends" : ['base'], "init_xml" : [ ], "demo_xml" : [ ], "update_xml" : ['academy_view.xml'], "installable": True

Page 9: 47526968 Creacion Del Primer Modulo

}

Los archivos que se deben incluir en el parámetro init_xml son los que se refieren a las definiciones del flujo de trabajo, para cargar datos en la instalación del módulo.

Los archivos que se  incluyen en  update_xml  conciernen a vistas,  acciones,  menús,  informes y asistentes.

Los archivos que se incluyen en demo_xml son para cargar los datos de demostración.

Observar que:

• Todos los datos del archivo  __openerp__.py  están dentro de un diccionario Python dentro de llaves { }.

• Los archivos XML de los parámetros  init_xml,  update_xml  o  demo_xml  están dentro de una lista de Python dentro de corchetes [].

Page 10: 47526968 Creacion Del Primer Modulo

5.  Programación del modelo en Python

Después de describir el módulo mediante el fichero __openerp__.py vamos a definir los modelos u objetos en OpenERP, los cuales crearán de forma automática las tablas y campos en la base de datos PostgreSQL.

5.1.  Definición de las tablas

En OpenERP, las tablas de PostgreSQL se crean mediante una serie de atributos predefinidos  en clases escritas en codigo Python heredadas de la clase osv.osv.  Aunque estrictamente hablando sean clases de Python, se les acostumbra a llamar objectos de OpenObject. Hay dos atributos obligatorios (que siempre se deben definir) y el resto son opcionales. Los obligatorio son:

• _name: Contiene el nombre de la tabla (normalmente incluye al principio el nombre del módulo separado   por   un   punto).   Este   nombre   se   usará   por   defecto   como   nombre   de  la   tabla   de PostgreSQL, cambiando los puntos por guiones bajos.

• _columns: Contiene un diccionario Python con la declaración de las columnas de la tabla.

Entre los atributos opcionales podemos destacar:

• _defaults: Sirve para determinar valores por defecto en determinadas columnas.• _inherit: Sirve para heredar de otra clase existente en OpenERP.• _constraints: Se utiliza para definir restricciones a nivel de código Python en la gestión de las 

tablas a través de OpenERP.• _slq_constraints:   Se   utiliza   para   definir   restricciones   a   nivel   de   código   SQL   en   las   tablas 

gestionadas por PostgreSQL.• _table: Nombre de la tabla SQL si queremos que sea distinto de _name (se sustituye los puntos 

por guiones bajos).

5.1.1.  Definición de las columnas

Dentro del atributo  _columns  de la clase que define la tabla, deben definirse las columnas de la misma. Para ello, el componente de OpenObject, Object Service (OSV), proporciona una serie de campos (fields) predefinidos.

La sintaxis general para definir una columna es la siguiente:

'nombre_campo':fields.tipo_de_campo('String',['parametro_opcional',...])

Donde tipo_de_campo es el tipo de campo de la tabla relacional PostgreSQL. Existen los siguientes:

a)  Campos simples• boolean: Sólo puede tomar los valores verdadero y falso (True o False).• integer: Número entero.• date: Fecha.• datetime: Fecha y hora.• time: Hora.• char: Cadena de caracteres.• text: Texto.• float: Número real.• selection: Toma uno de una serie de valores predefinidos.

Page 11: 47526968 Creacion Del Primer Modulo

• reference: Campo con una relación dinámica hacia otro objeto de OpenERP seleccionable dentro de una lista de posibles objetos (se puede editar desde el menú  Administración/Objetos de bajo  nivel/Solicitudes/Enlaces acceptados en solicitudes).

• binary: Campo para almacenar un archivo o contenido binario.

Todos estos campos tienen una serie de atributos comunes que se pasan como parámetros de la función y entre los que cabe destacar:

'string',['parametro_opcional',...]

• string: Es una cadena de texto que pone nombre a la etiqueta del campo. Este atributo es el único obligatorio.

• required: Si el campo es obligatorio, True. Por defecto es False.• readonly: Si el campo es de sólo lectura, True. Por defecto es False.• select: Si se quiere que de dicho campo se genere un índice (de PostgreSQL) para optimizar las 

búsquedas de las vistas, poner a  1  (búsqueda simple). Para búsqueda avanzada poner a  2. Por defecto es 0.

• help: Mensaje de ayuda (el que aparece en el interrogante verde al lado de la etiqueta del campo).

Además, algunos tipos de campo tienen atributos específicos:

• translate: que es True si el valor del campo puede ser traducido por los usuarios. Disponible para los campos char y text.

• size: que es el máximo número de caracteres para los campos de tipo char.• digits: proporciona la precisión y la escala que se el desea asignar mediante una tupla en los 

campos de tipo float.• values: campo que permite seleccionar entre un conjunto de valores predefinidos en los campos 

de tipo selection. Este atributo se coloca antes que el atributo string, y se define mediante una lista de valores o una función que devuelve una lista de valores. La lista debe estar compuesta por tuplas que contienen una clave y su etiqueta.

[('key1','Label1'),('key2','Labe2'),...],'String',...

b)  Campos relacionales

Según la wikipedia, las relaciones “permiten establecer interconexiones (relaciones) entre los datos (que están guardados en tablas),  y a  través de dichas conexiones relacionar los datos de ambas tablas”. Se pueden dar tres tipos distintos de relaciones entre los objetos de OpenERP que se pueden plasmar mediante tres tipos diferentes de campos:

• many2one: Un campo many2one expresa una relación hacia un objeto padre (de muchos a uno), usando una llave foránea (foreing key).

'other.object.name', 'Field Name', ...

• one2many: Un campo one2many expresa una relación de un objeto hacia muchos objetos. Es la relación contraria y/o complementaria a many2one.

'other.object.name', 'field_id', 'Field Name', ...

• many2many:  Un campo many2many representa una relación de muchos a muchos entre dos objetos.

'other.object.name','relationship_table','field_id','other_object_field_id',...

Donde:

• other.object.name: Es el nombre del objeto de destino (con el que guarda relación). Común a todos los tipos. Obligatorio.

Page 12: 47526968 Creacion Del Primer Modulo

• Field Name: es el nombre del campo. Común y obligatorio en todos los tipos de campos.• field_id: es el campo identificador de la relación de la propia tabla. Común a los tipos one2many 

y many2many. Obligatorio.• other_object_field_id: es el campo identificador de la relación de la otra tabla. Solo para el tipo 

many2many. Obligatorio.• relationship_table: es la tabla que establece la relación en las relaciones de tipo  many2many. 

Obligatorio.• optionalParameters: para el tipo many2one. Pueden ser:

– ondelete: Indica qué debería ocurrir cuando el recurso al que este campo señala es eliminado.Valor predefinido: cascade, elimina los recursos dependientesValor por defecto: null, no elimina los recursos sino que los establece a un valor nulo.

– required: True por defecto.– readonly: True por defecto.– select: True ­ (crea un índice en el campo de la clave foránea)

• optionalParameters: para el tipo one2many. Pueden ser:– invisible: True/False– readonly: True/False

Por convenio los nombres de los campos many2one  terminan en _id y los nombre de los campos one2many y many2many terminan en _ids. Así el programador, con sólo verlos, sabe a que tipo de campo se refiere.c)  Campos funcionales• function: Los campos funcionales simulan campos reales, pero se calculan mediante una función 

Python en  lugar  de  almacenarse  en  las  base  de datos  PosgreSQL.  En casos  especiales,  para aumentar la velocidad de consulta de OpenERP y facilitar las búsquedas, los campos funcionales también pueden ser guardados en la base de datos aunque siempre son calculados/actualizados por una o varias funciones y no por los usuarios.

• related: Permite visualizar un campo de un objeto relacionado en el propio objeto. Equivalente a navegar por campos encadenados. Por ejemplo, el campo Ciudad de las Empresas es un campo relacionado pues en realidad se usa el campo Ciudad de las Direcciones/Contactos de la empresa. 

• property:   Campos   dinámicos   con   derechos   de   acceso   específicos.   Por   ejemplo,   los   campos Cuenta a cobrar y Cuenta a pagar de la ficha de empresa. Según la compañía a la que pertenezca el usuario, el valor de estos campos varía. Los valores de los campos de tipo property se pueden consultar en el menú Administración/Configuración/Propiedades.

5.1.2.  Módulo ejemplo academy.

Para  crear   el  objeto  classroom  del  módulo  academy.py  que  permita  gestionar   las   aulas  de   la academia se introduce el siguiente código:

class academy_classroom(osv.osv): """Academy Classrooms""" _name = 'academy.classroom' _columns = { 'name': fields.char('Classroom',size=35, help='Name of the classroom'), 'address_ids': fields.many2one('res.partner.address','Address', help='Address of the classroom'), 'capacity': fields.integer('Capacity', help='Capacity of the classroom'), 'state': fields.selection([('usable','Usable'),('in works','In Works')],'State', help='State of the classroom'), 'active': fields.boolean('Active'),

Page 13: 47526968 Creacion Del Primer Modulo

} _defaults = { 'state': lambda *a: 'usable', 'active': lambda *a: 1, } academy_classroom()

Con este código se creará una tabla de PostgreSQL que utilizará el nombre academy_classroom, con los siguientes campos:

• name: Un campo que contiene una cadena de caracteres con el nombre de la aula.• address_id: Un enlace con la clase res.partner.address en una relación varios a uno (many2one). 

Permite guardar la dirección física donde se ubica la aula.• capacity: Un campo que contiene un número entero con la capacidad de la aula.• state: Un campo de selección con el estado en que se encuentra la aula.• active: Un campo booleano. Determina si el campo es visible “True” o no “False”.

Algunos campos necesitan que se modifiquen sus valores por defecto.  Esto se consigue con el atributo _defaults. En este ejemplo, se puede comprobar que los valores por defecto de los campos state y active, se han establecido a los valores por defecto usable y 1 (True) respectivamente.

Los modelos/tablas course y course_category para gestionar cursos y categorías de cursos se crean de forma análoga. Modelo curso:

class academy_course(osv.osv): """Courses taught by academy""" _name = 'academy.course' _columns = { 'name': fields.char('Course',size=32, help='Name of the course'), 'category_id': fields.many2one('academy.course.category','Category', help='Category of the course'), 'classroom_id': fields.many2one('academy.classroom','Classroom', help='Class in which the course is taught'), 'dificulty': fields.selection([('low','Low'),('medium','Medium'),('high','High')],'Dificulty', help='Dificulty of the course'), 'hours': fields.float('Studies Hours', help='Hours of the course'), 'state': fields.selection([('draft','Draft'),('testing','Testing'),('stable','Stable')],'State'), 'description': fields.text('Description', help='Description of the course'), } _defaults = { 'state': lambda *a: 'draft', 'dificulty': lambda *a: 'low', } academy_course()

Observar que en este diseño del módulo  academy se ha supuesto que un curso se imparte en una aula y pertenece a una categoría.

Modelo categoría de cursos:

class academy_course_category(osv.osv): """Courses Categories""" _name = 'academy.course.category' _columns = { 'name': fields.char('Category',size=16, help='Name of the category'), 'parent_id': fields.many2one('academy.course.category','Parent Category', help='Name of the parent category'),

Page 14: 47526968 Creacion Del Primer Modulo

} academy_course_category()

Observar que el campo parent_id es un campo many2one que se apunta a si mismo y permite crear una   estructura   jerárquica   de   categorías,   donde   cada   categoría   apunta   a   su   padre   excepto   las categorías principales.

Page 15: 47526968 Creacion Del Primer Modulo

6.  Programación de los menús, acciones y vistas: Archivos XML

Dado que todos los datos del programa están almacenados en objetos, ¿cómo se muestran dichos objetos al usuario? Primero de todo, nótese que cada tipo de recurso utiliza su propia interfaz. Por ejemplo, el formulario para modificar los datos de una empresa no es el mismo que el utilizado para modificar una factura. En consecuencia, debe tenerse en cuenta que las interfaces son dinámicas. Esto significa que no se describen de forma estática por un código, sino que son construidas de forma dinámica por la descripción XML de la pantalla del cliente. En adelante, se llamará a estas pantallas  de  descripción vistas   (listas,   formularios,  calendarios,  gráficos,   ...).  Una característica notable de dichas vistas es que pueden editarse en cualquier momento (incluso durante la ejecución del programa) desde el menú  Administración / Personalización / Interfaz de usuario / Vistas. Una vez modificada la vista, simplemente se necesita cerrar la pestaña correspondiente a dicha vista y reabrirla para que aparezcan los cambios.

Por lo tanto, una vez creado el primer objeto para el módulo, lo siguiente que se debe hacer es crear el fichero encargado de crear los menús, acciones y vistas para poder interactuar con el objeto. La estructura general del dicho fichero es la siguiente:

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

<record model="object_model_name" id="object_xml_id"> <field name="field1">value1</field> <field name="field2">value2</field>

</record>

<record model="object_model_name2" id="object_xml_id2"> <field name="field1" ref="module.object_xml_id"/> <field name="field2" eval="ref('module.object_xml_id')"/>

</record>

</data> </openerp>

Las vistas describen como se muestra cada objeto (tipo de recurso). Precisando más, para cada objeto, se pueden definir una o varias vistas que describen qué campos deben mostrarse, y cómo debe hacerse.

Hay varios tipos de vistas:

• Formulario• Lista• Árbol• Gráfico• Calendario• Diagramas de Gantt• Búsqueda

Para crear las vistas del módulo se debe crear/editar el fichero nombre_modulo_view.xml. En el presente ejemplo, sería:

academy_view.xml

Pero para acceder a dichas vistas, deben definirse previamente los menús, así como las acciones 

Page 16: 47526968 Creacion Del Primer Modulo

asociadas a los mismos.

6.1.  Los menús.

La figura 6.1 muestra un ejemplo de los menús del módulo empresa, que se instala por defecto en todos los servidores OpenERP.

Figura 6.1: Ejemplo de menús.

Esta es la plantilla para crear un menú:

<menuitem id="menuitem_id" name="Position/Of/The/Menu/Item/In/The/Tree" action="action_id" icon="NAME_FROM_LIST"

parent="menuitem_id" groups="groupname" sequence="<integer>"/>

Donde:

• menuitem_id: Especifica el identificador del menú  en la tabla interna de OpenERP donde se guardan los menús. Este identificador debe ser único. Este campo es obligatorio.

• name: Define el nombre del menú en la jerarquía de menús. Este campo es opcional, si no se indica, el menú tomará el nombre de la acción asociada.

• action: Especifica el identificador de la acción que debe haber sido definida en la tabla de acción (ir.actions.act_window). Nótese que este campo no es obligatorio: se puede definir elementos de menú sin asociarles acción alguna. 

• icon: Especifica qué icono se mostrará para el menú. El icono por defecto es el de una carpeta (STOCK_OPEN).   En   el   apéndice   1   se   muestran   los   iconos   disponibles.   Esto   es   útil   para personalizar  iconos de menú  que deben actuar  como árboles de directorios (por ejemplo,   los iconos “Proyectos”, “Recursos Humanos”, ... se han definido de esta forma).

• parent: Especifica el identificador del menú padre del que depende.

Page 17: 47526968 Creacion Del Primer Modulo

• groups: Especifica qué grupo de usuarios puede ver el menú (por ejemplo: groups=”admin”). Si se   desea   introducir   más   de   un   grupo,   se   deben   separar   mediante   comas   “,”   (por   ejemplo: groups=”admin,user”).

• sequence: es un entero que se usa para ordenar el menú dentro del árbol de menús. Cuanto más grande sea el número de sequence, más abajo aparecerá en el árbol de menús. Este argumento no es obligatorio: si no se especifica  sequence, el menú toma el valor por defecto:  10. Los menús que tengan el mismo valor de sequence se ordenan por orden de creación.

6.1.1.  Módulo ejemplo academy

Estas podrían ser las líneas (dentro del fichero academy_view.xml) que definiesen los menús del módulo academy.py:

<menuitem name="Academy" id="menu_academy"/><menuitem name="Courses" id="menu_academy_course" action="action_academy_course" parent="menu_academy"/><menuitem name="Classroom" id="menu_academy_classroom" action="action_academy_classroom" parent="menu_academy"/><menuitem name="Courses Categories" id="menu_academy_course_category" action="action_academy_course_category" parent="menu_academy"/>

Como se puede ver, hay un menú principal (no tiene padre) denominado Academy que se muestra en la barra de menús de OpenERP y tres menús que dependen del primero:  Courses,  Classroom  y Courses Categories (figura 6.2) que ejecutarán 3 acciones distintas.

Figura 6.2: Menú Academy

Page 18: 47526968 Creacion Del Primer Modulo

6.2.  Las acciones

Las acciones definen el comportamiento del sistema en respuesta a los eventos generado por el usuario. Hay diferentes tipos de acciones, las más importantes:

• window: Abre una vista en una nueva ventana/pestaña.• report: Imprime un informe.• wizard: Ejecuta un asistente para realizar un determinado trabajo o proceso.

Las acciones se utilizan para capturar los siguientes eventos en el cliente:

• Los dobles clics del ratón del usuario sobre un menú (normalmente se ejecutan acciones del tipo window, aunque también pueden ser report o wizard).

• Los clics de ratón del usuario sobre el icono “imprimir” (se ejecuta una acción report) o “acción” (se ejecuta una acción wizard).

• El clic en algún botón de una vista que esté asociado a una acción.

Las acciones window son las más habituales.

6.2.1.  Acciones window (apertura de vistas)

Las acciones window (denominadas internamente ir.actions.act_window o ir.acciones.acc_ventena) permiten abrir diferentes tipos y modos de vistas (en el apartado “6.3. Las vistas”, se describirán las diferencias entre tipos y modos de vistas). La tabla 1 muestra qué modos de vista pueden abrir cada tipo de vista.

Tipos de vista Modos de vista permitidos

● Tree (Árbol) ● tree (árbol)● form (formulario)

● Form (Formulario)

● tree (lista)● form (formulario)● graph (gráfico)● gantt (diagrama de gantt)● calendar (calendario)● search (búsqueda)● inheritance (herencia)

Tabla1: Tipos de vista y modos de vista permitidos

Para ver ejemplos de acciones asociadas a menús, se recomienda seleccionar un menú concreto (por ejemplo el menú Empresas / Empresas / Empresas por categorías), y cambiar el modo de vista (en este caso a modo de formulario) pulsando Ctrl + L. El resultado es el mostrado en la figura 6.3.

Page 19: 47526968 Creacion Del Primer Modulo

Figura 6.3: Vista en modo formulario del menú “Empresas por categorías”.

Observamos   que   este   menú   denominado  Empresas   por   categoría  tiene   como   padre   el   menú Empresas, como icono el  STOCK_INDENT y no se ha asociado ningún grupo de usuarios. En el campo  Acción  este menú está relacionado con una acción  ir.acciones.acc_ventana. Haciendo clic sobre el icono de la carpeta del campo Acción, se accede a la ventana que describe dicha acción (figura 6.4).

Figura 6.4: Acción asociada al menú “Empresas por categorías” que abre una vista de tipo árbol.

Como puede comprobarse, el tipo de vista para la acción “Empresas por categorías” es “Árbol”, y permite los modos de vista “tree” y “form”, es decir las vistas árbol y formulario.

En cambio, la acción “Proveedores” (figura 6.5) asociada al menú Empresas/Empresas/Proveedores, abre una vista de tipo “Formulario” y permite los modos de vista “tree” y “form”, lo que indica que  

Page 20: 47526968 Creacion Del Primer Modulo

por defecto abre la vista en modo lista y se puede cambiar más tarde a formulario. Observar como esta  acción  tienen como valor de dominio  [('supplier','=',1)]  que es  la  condición para hacer  el filtrado de las empresas que sean proveedores (lista de Python con condiciones).

Figura 6.5: Acción asociada al menú “Proveedores” que abre una vista de tipo formulario.

La acción asociada al menú  Empresas/Empresas/Nueva empresa  (figura  6.6) también es de tipo formulario pero a diferencia de la anterior, abre por defecto la vista en modo formulario y permite más tarde cambiar a la vista en modo lista. Por este motivo al hacer clic este menú nos aparecerá el formulario de empresas en blanco.

Figura 6.6: Acción asociada al menú “Nueva empresa”.

Este es un ejemplo genérico de una acción de ventana guardado en el fichero XML:

<record model="ir.actions.act_window" id="action_id_1"> <field name="name">action.name</field>

Page 21: 47526968 Creacion Del Primer Modulo

<field name="view_id" ref="view_id_1"/> <field name="domain">["list of 3-tuples (max 250 characters)"]</field> <field name="context">{"context dictionary (max 250 characters)"}</field> <field name="res_model">Open.object</field> <field name="view_type">form|tree</field> <field name="view_mode">form,tree|tree,form|form|tree</field> <field name="usage">menu</field> <field name="target">new</field></record>

Donde:

• model: Es el nombre del objeto que creamos. En esta caso siempre será “ir.actions.act_window”.• id: Es el identificador de la acción. Debe ser único.• name: Es el nombre de la acción (obligatorio).• res_model: Es el nombre del objeto sobre el que opera la acción.• view_type: Se debe establecer a “form” para que la acción abra una vista de tipo formulario, y a 

“tree” cuando la acción deba abrir una vista de tipo árbol.• view_mode: Sólo se tiene en cuenta si “view_type” se ha establecido a “form”; en cualquier otro 

caso, se ignora. Lista de vistas a usar (tree, form, calendar, graph, ...). Las cuatro posibilidades más habituales son:– form,tree: La vista se muestra primero en modo formulario, pero se puede mostrar en modo 

lista clicando el botón Lista.– tree,form:   La   vista   se   muestra   primero   en   modo   lista,   pero   se   puede   mostrar   en   modo 

formulario clicando el botón Formulario.– form: La vista se muestra como un formulario y no hay forma de cambiar al modo lista.– tree: La vista se muestra como una lista y no hay forma de cambiar al modo formulario.

• view_id: Es el nombre de la vista a mostrar cuando se activa la acción. Si este campo no se definiese, se usaría el tipo de vista (lista o formulario) asociado al objeto res_model con el campo de mayor prioridad (si dos vistas tuviesen la misma prioridad, se utilizaría el primer tipo de vista definido).

• domain: Es una lista Python de condiciones utilizada para refinar los resultados de una selección, y en consecuencia, para mostrar menos registros en la vista. Las condiciones de la lista se enlazan con una cláusula AND y son tuplas Python con 3 valores ('campo','condición','valor'). Un registro de la tabla sólo se mostrará en la vista si satisface todas las condiciones.

• context: Es el diccionario contextual que se utilizará en la vista que se abra cuando se active la acción. Los diccionarios contextuales se declaran como un diccionario Python. Estos diccionarios contienen información de contexto como por ejemplo el idioma, moneda, tarifa, almacén, ... a usar.

6.2.2.  El dominio de la acción. Condiciones.

Este parámetro permite regular qué recursos serán visibles en la vista seleccionada (condición). Por ejemplo, en el caso de facturas, se puede definir una acción que abra una vista que muestre sólo facturas borrador.

Los dominios se escriben en Python: Lista de tuplas, cada una de las cuales tiene tres elementos:

• El campo en el que se debe realizar la condición.• El operador utilizado para la condición (<, >, =, like).• El valor de la condición. 

Page 22: 47526968 Creacion Del Primer Modulo

Por ejemplo, si se desea obtener solo facturas en estado “Borrador”, se usaría el siguiente dominio: [('state','=','draft')].

6.2.3.  Módulo ejemplo academy

El siguiente código muestra las acciones del módulo academy.

<record model="ir.actions.act_window" id="action_academy_course"> <field name="name">Courses</field> <field name="res_model">academy.course</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record>

<record model="ir.actions.act_window" id="action_academy_classroom"> <field name="name">Classroom</field> <field name="res_model">academy.classroom</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record>

<record model="ir.actions.act_window" id="action_academy_course_category"> <field name="name">Courses Categories</field> <field name="res_model">academy.course.category</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record>

Como se puede comprobar, todas las acciones se almacenan en el modelo “ir.actions.act_window”, cada una tiene un identificador  id único, cada acción opera (res_model) sobre un modelo distinto del módulo academy. Las 3 acciones son de tipo form y permiten los modos de vista tree,form (lista y   formulario).  No  tienen definido  ningún dominio   (por   lo  que se verán  todos  los   registros)  ni tampoco se ha forzado a usar ninguna vista en concreto (se usarán las vista por defecto que veremos en el siguiente apartado).

Page 23: 47526968 Creacion Del Primer Modulo

6.3.  Las vistas.

Existen dos tipos de vistas distintos:  tipo  Árbol  y  tipo  Formulario.  El tipo de vista  Formulario permite a su vez una serie de modos: lista, formulario, calendario, gráfico, ... En la figura 6.7 puede verse como, desde el menú Empresas/Empresas, puede accederse directamente a distintos tipos de vistas.

Figura 6.7: Menús cuyas acciones abren distintos tipos de vistas.

Desde el menú del recuadro azul (“Empresas por categorías”) se accedería a una vista de tipo árbol con una organización interna jerárquica (figura 6.8).

Page 24: 47526968 Creacion Del Primer Modulo

Figura 6.8: Vista de tipo árbol.

Desde el menú del recuadro rojo (“Proveedores”) se accedería a una vista de tipo lista (figura 6.9).

Figura 6.9: Vista en modo lista.

Finalmente, desde el menú del recuadro verde (“Nueva empresa”), se accedería directamente a una vista de formulario (figura 6.10).

Page 25: 47526968 Creacion Del Primer Modulo

Figura 6.10: Vista en modo formulario.

Esta es la declaración genérica del registro XML de una vista:

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

<!-- view content: <form>, <tree>, <graph>, ... --> </field>

</record>

Donde:

• id: Es el identificador único de la vista.• name: Nombre de la vista.• model:  Modelo u objeto sobre el  que se define   la  vista   (el  mismo que el   res_model  de   las 

acciones).• type: Tipo de vista: form, tree, graph, calendar, search, gantt.• priority: Prioridad de la vista, cuanto más pequeño, más prioridad (por defecto 16).• arch: Arquitectura o estructura de la vista. Mediante etiquetas XML se define la composición de 

la vista (etiquetas, campos, pestañas, separadores, ...).

6.3.1.  Vistas de formulario

La disposición de los campos en una vista de formulario siempre sigue el mismo principio: Los campos se distribuyen en la pantalla siguiendo las siguientes reglas:

• Por defecto, cada campo es precedido por una etiqueta con su nombre.• Los campos se colocan en la pantalla de izquierda a derecha y de arriba a abajo, de acuerdo al 

orden con el que están declarados en la vista.

Page 26: 47526968 Creacion Del Primer Modulo

• Cada pantalla se divide en cuatro columnas,  cada columna es capaz de contener,  o bien una etiqueta o bien un campo de edición. Puesto que todos los campos de edición son precedidos (por defecto) con una etiqueta con su nombre, habrá dos campos (y sus respectivas etiquetas) en cada línea de la pantalla. En la figura 6.11, las zonas verdes y rojas, ilustran las cuatro columnas.

Figura 6.11: Colocación por defecto de los campo y sus etiquetas en cuatro columnas.

Pero las vistas también soportan opciones de colocación avanzadas. Un campo puede mostrarse en varias columnas con el atributo colspan. En la figura 6.12, se resalta en rojo un único campo con su correspondiente etiqueta, que ocupa las cuatro columnas de la pantalla.

Page 27: 47526968 Creacion Del Primer Modulo

Figura 6.12: Una campo ocupa varias columnas.

También se puede efectuar la operación contraria: tomar un grupo de columnas y dividirlas en las columnas que se deseen con la etiqueta group y los atributos colspan y col.

Finalmente, hay una forma de distribuir los campos de un objeto en diferentes pestañas (zona azul de la figura 6.13) con las etiquetas notebook y page.

Page 28: 47526968 Creacion Del Primer Modulo

Figura 6.13: Otra forma de mostrar los campos mediante pestañas.

a)  Módulo ejemplo academy

Éste es el código para la vista de formulario classroom:

<record model="ir.ui.view" id="view_academy_classroom_form"> <field name="name">academy.classroom.form</field> <field name="model">academy.classroom</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="academy.classroom"> <field name="name" select="1"/> <field name="address_ids" select="2"/> <field name="capacity" /> <field name="state" /> <field name="active" /> </form> </field> </record>

Donde los siguientes atributos son comunes a todos los elementos:

• string: Es la etiqueta del elemento.• nolabel: Sirve para ocultar la etiqueta del campo (valor 1).• colspan: Sirve para indicar el número de columnas que debe abarcar el campo.• rowspan: Sirve para indicar el número de filas que debe abarcar el campo.• col: Número de columnas que este elemento debe asignar a sus elementos hijos.• invisible: Si su valor se establece a 1, sirve para ocultar completamente este elemento.• eval: Evalúa este código Python como un contenedor de elemento (por defecto, el contenedor es 

una cadena)• attrs:  Asignación Python  que define   las   condiciones  dinámicas  de  estos   atributos:  readonly, 

invisible, required basados en tuplas de búsqueda u otros campos de valor.

Page 29: 47526968 Creacion Del Primer Modulo

Los elementos que se pueden añadir en una vista pueden ser:

field: Campos con su correspondiente widget según el tipo de campo. Por ejemplo un campo de tipo fecha tiene un widget con un calendario para poder seleccionar la fecha con comodidad (figura 6.14). 

Figura 6.14: 

Los atributos específicos del campo field son:

– string: Etiqueta del campo, también para búsquedas (reemplaza el nombre del campo).– select: 1 para mostrar el campo de búsqueda normal, 2 sólo para búsqueda avanzada.– nolabel: 1 para ocultar la etiqueta del campo.– required: Reemplaza al atributo “required” definido en el modelo.– readonly: Reemplaza al atributo “readonly” definido en el modelo.– password: True para ocultar los caracteres escritos en este campo.– context: Código Python para declarar un diccionario contextual.– domain:  Código  Python  para  declarar   una   lista   de   tuplas   con   condiciones  para   restringir 

valores (usado en campos relacionales).– on_change: Método Python que se ejecuta cuando se cambia el valor del campo.– groups: Lista de grupos de usuarios separados por comas (id) autorizados a ver dicho campo.– widget:   Selecciona   un   widget   alternativo   al   proporcionado   de   forma   automática 

(one2many_list,   many2many,   url,   image,   float_time,   reference,   text_wiki,   text_html, progressbar). En las figuras 6.14 a 6.20, pueden verse algunos ejemplos de widgets.

Figura 6.15:  Widget booleano

 

Figura 6.16: Widget selection

Figura 6.17: Widget number

Page 30: 47526968 Creacion Del Primer Modulo

Figura 6.18: Widget one2many

Figura 6.19: Widget many2many

Figura 6.20: Widget url

properties: Widget dinámico que muestra todas las propiedades disponibles.

button:   Widget   en   forma   de   botón   sobre   el   que   se   puede   hacer   clic   que   está   asociado   a determinadas acciones. Atributos específicos:

– type: Tipo de botón. Puede ser “workflow” (flujo de trabajo ­por defecto­), “object” o “action”.∙ workflow: Mediante este tipo se puede cambiar el estado del registro pasando por una serie 

de estados predefinidos.∙ object:∙ action: Tiene asociada una acción que se ejecuta cuando se hace clic sobre el botón. 

– name:   Señal   de   flujo   de   trabajo,   nombre   de   función   (sin   paréntesis)   o   acción   a   llamar (dependiendo del tipo).

– confirm: Texto de mensaje de confirmación cuando se hace clic.– states: Lista de estados separados por comas, en los que dicho botón se muestra.– icon: Icono opcional (se pueden usar todos los iconos de GTK STOCK, por ejemplo gtk­ok).

separator: Línea de separación horizontal para estructurar vistas, con etiqueta opcional.

newline: Añade espacio en blanco para completar la línea actual de la vista y saltar a la siguiente.

label: Título de texto libre o leyenda en el formulario.

group: Utilizado para organizar campos en grupos con etiquetas opcionales (añade marco).

notebook, page: Los elementos notebook son contenedores de pestaña para los elementos page que definen cada una de las pestañas. Atributos:

• name: Etiqueta para la pestaña.• position: posición de la pestaña en el notebook: inside (dentro), top (arriba), bottom (abajo), left 

(izquierda), right (derecha).

Este es el código para la vista de formulario de course:

Page 31: 47526968 Creacion Del Primer Modulo

<record model="ir.ui.view" id="view_academy_course_form"> <field name="name">academy.course.form</field> <field name="model">academy.course</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="academy.course"> <field name="name" select="1"/> <field name="category_id" select="2"/> <field name="classroom_id" /> <field name="dificulty" /> <field name="hours" /> <field name="state" /> <field name="description" colspan="4" /> </form> </field> </record>

Este es le código para la vista de tipo formulario de course_category:

<record model="ir.ui.view" id="view_academy_course_category_form"> <field name="name">academy.course.category.form</field> <field name="model">academy.course.category</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="academy.course.category"> <field name="name" select="1"/> <field name="parent_id" select="2"/> </form> </field> </record>

Como puede comprobarse,  ambos campos aparecerán en  las  opciones  de búsqueda.  El  primero (name) en la búsqueda normal, y el segundo (parent_id) en la búsqueda avanzada.

6.3.2.  Vistas de lista o árbol

Estas vistas se utilizan cuando se trabaja en modo lista (con objeto de visualizar varios recursos a la vez) y en la pantalla de búsqueda. Son más simples que las vistas de formulario y tienen menos opciones. Incluyen elementos field, se crean con el tipo tree, y tienen un elemento padre <tree> en lugar de <form> usado anteriormente.

Entre los atributos que se le pueden añadir caben destacar los siguientes:

• colors: Listas de colores mapeados a Python.• editable: top o botton para permitir editar el lugar en el que colocarlo.• toolbar: Establecer a True para mostrar el nivel superior de la jerarquía de objetos como una 

barra de herramientas lateral (por ejemplo, los menús).

Los elementos permitidos son: field, group, separator, tree, button, filter y newline.

a)  Módulo ejemplo academy

Este es el código para la vista de tipo lista de classroom:

<record model="ir.ui.view" id="view_academy_classroom_tree"> <field name="name">academy.classroom.tree</field> <field name="model">academy.classroom</field> <field name="type">tree</field> <field name="arch" type="xml"> <tree string="academy.classroom">

Page 32: 47526968 Creacion Del Primer Modulo

<field name="name"/> <field name="address_ids"/> <field name="capacity"/> <field name="state"/> <field name="active"/> </tree> </field> </record>

Como puede comprobarse, se mostrarán cinco campos.

Este es el código de la vista de tipo lista de curso:

<record model="ir.ui.view" id="view_academy_course_tree"> <field name="name">academy.course.tree</field> <field name="model">academy.course</field> <field name="type">tree</field> <field name="arch" type="xml"> <tree string="academy.course"> <field name="name"/> <field name="category_id"/> <field name="classroom_id"/> <field name="dificulty"/> <field name="hours"/> <field name="state"/> <field name="description"/> </tree> </field>

</record>

En este caso puede comprobarse como se hace referencia a los objetos  category  y  classroom  a través de los campos category_id y classroom_id.

El código de la vista de tipo lista de course_category es el siguiente:

<record model="ir.ui.view" id="view_academy_course_category_tree"> <field name="name">academy.course.category.tree</field> <field name="model">academy.course.category</field> <field name="type">tree</field> <field name="arch" type="xml"> <tree string="academy.course.category"> <field name="name"/> <field name="parent_id"/> </tree> </field> </record>

En este  caso  puede observarse  como el  objeto hace   referencia  a  sí  mismo a  través  del  campo parent_id.

La figura 6.21 muestra el resultado del código anterior.

Page 33: 47526968 Creacion Del Primer Modulo

Figura 6.21: Ejemplo de Vista de lista o árbol.

Page 34: 47526968 Creacion Del Primer Modulo

7.  Anexo 1.

Lista de iconos disponibles para el parámetro icon de las vistas XML.

STOCK_ABOUT,   STOCK_ADD,   STOCK_APPLY,   STOCK_BOLD,   STOCK_CANCEL, STOCK_CDROM,   STOCK_CLEAR,   STOCK_CLOSE,   STOCK_COLOR_PICKER, STOCK_CONNECT,   STOCK_CONVERT,   STOCK_COPY,   STOCK_CUT,   STOCK_DELETE, STOCK_DIALOG_AUTHENTICATION, STOCK_DIALOG_ERROR, STOCK_DIALOG_INFO, STOCK_DIALOG_QUESTION,   STOCK_DIALOG_WARNING,   STOCK_DIRECTORY, STOCK_DISCONNECT,   STOCK_DND,   STOCK_DND_MULTIPLE,   STOCK_EDIT, STOCK_EXECUTE,   STOCK_FILE,   STOCK_FIND,   STOCK_FIND_AND_REPLACE, STOCK_FLOPPY,   STOCK_GOTO_BOTTOM,   STOCK_GOTO_FIRST,   STOCK_GOTO_LAST, STOCK_GOTO_TOP,   STOCK_GO_BACK,   STOCK_GO_DOWN,   STOCK_GO_FORWARD, STOCK_GO_UP,   STOCK_HARDDISK,   STOCK_HELP,   STOCK_HOME,   STOCK_INDENT, STOCK_INDEX,   STOCK_ITALIC,   STOCK_JUMP_TO,   STOCK_JUSTIFY_CENTER, STOCK_JUSTIFY_FILL,   STOCK_JUSTIFY_LEFT,   STOCK_JUSTIFY_RIGHT, STOCK_MEDIA_FORWARD,   STOCK_MEDIA_NEXT,   STOCK_MEDIA_PAUSE, STOCK_MEDIA_PLAY,   STOCK_MEDIA_PREVIOUS,   STOCK_MEDIA_RECORD, STOCK_MEDIA_REWIND,   STOCK_MEDIA_STOP,   STOCK_MISSING_IMAGE, STOCK_NETWORK,   STOCK_NEW,   STOCK_NO,   STOCK_OK,   STOCK_OPEN, STOCK_PASTE,   STOCK_PREFERENCES,   STOCK_PRINT,   STOCK_PRINT_PREVIEW, STOCK_PROPERTIES, STOCK_QUIT,STOCK_REDO, STOCK_REFRESH, STOCK_REMOVE, STOCK_REVERT_TO_SAVED,   STOCK_SAVE,   STOCK_SAVE_AS, STOCK_SELECT_COLOR,   STOCK_SELECT_FONT,   STOCK_SORT_ASCENDING, STOCK_SORT_DESCENDING,   STOCK_SPELL_CHECK,   STOCK_STOP, STOCK_STRIKETHROUGH,   STOCK_UNDELETE,   STOCK_UNDERLINE,   STOCK_UNDO, STOCK_UNINDENT,   STOCK_YES,   STOCK_ZOOM_100,   STOCK_ZOOM_FIT, STOCK_ZOOM_IN, STOCK_ZOOM_OUT, terp­account, terp­crm, terp­mrp, terp­product, terp­purchase,   terp­sale,   terp­tools,   terp­administration,   terp­hr,   terp­partner,   terp­project,   terp­report, terp­stock