OpenERP: Desarrollo de un módulo

Ya subelo, acaso tiene que ser perfeto (subido y publucado por Anaa sin permiso de xmeele)

Bien como en el resumen del año 2013 prometí escribir un artículo que hable de como crear un módulo y que sea superior al que ya puse pues venga aquí está, no voy a hablar ahora de cosas básicas como los archivos __init__.py o __openerp__.py pues ya lo hice en el post anterior, sino que voy a ir al grano a los widgets, a los diferentes controles que se pueden usar como pestañas, check’s, vistas kanban, definiciones para las busquedas y agrupaciones, realizar totales o promedios de datos pero voy a resumir lo básico en algunos párrafos.

Resumen de lo básico:

OpenERP (OE en adelante) está basado en un framework llamado OpenObject y este implementa un ORM que es un manejador de objetos de datos, este se encarga de traer los datos y convertirlos en objetos así veamos esta tabla “productos” con campos: producto_id, nombre, unidad_id, costo, cantidad y la tabla “unidades” con campos: unidad_id, nombre, categoriaunidad_id. Como pueden ver la tabla productos tiene un campo que es un FK (unidad_id) hacia una tabla unidades que hace una relacion de muchos a uno, osea varios productos podrián apuntar a una sola unidad, si lo trataramos desde sql pues tendríamos que trabajar con joins y demas, y no podriamos acceder a los campos de las unidades como en este caso el campo “categoria_unidad_id” que es un fk a una tabla de categorías de unidades, pues si quisieramos hacerlo la consulta sería cada vez más y más complicada, pero si usamos el ORM de OE podríamos acceder a todos los objetos ya que si quisiera saber la categoría de la unidad de un producto haría esto: producto.uidda_id.categoriaunidad_id.nombre y eso me daría el nombre de la categoría de la unidad sin tener que hacer consultas a la base de daos el ORM se encarga de eso.

Cosas que se tienen que tener en cuenta por estandar de OE son la siguientes:

Nombres de las clases: por recomendación de OE (no necesariaente tiene que ser así) se deben de nombrar las clases de está manera: nombremodulo_nombreclase, así si tenemos un módulo va a hacer un kardex lo lógico es que está tenga por nombre “kardex” y etonces tendríamos una clase con su mismo nombre “kardex”, el nombre de nuestra clase debe de ser: kardex_kardex, ahora el atributo _name del módulo tambien tiene que ser similar solo quitando el subguión de está manera “kardex.kardex”

El campo que devuelve por defecto: OE tiene determinado por defecto que se devuelva un campo llamado “name”, si este no existe pues entonces retorna el id, así si quisieramos hacer un campo muchos a uno y representarlo este en un combobox si no tenemos en nuestra clase relacionada un campo llamado “name” pues vamos a ver el ID en el combobox claro esto se puede solucionar usando el atributo _recname=’nombredelcampopordefecto’ pero por estandar es mejor siempre definir un campo llamado “name” en todas las clases y nos olvidamos de definir ese valor.

No usar nunca mayúsculas, carácteres que no sean del ingles en la definición de los campos por que simplemnete no compila y lo de las mayúsculas bueno eso es por estandar de desarrollo, recuerden que el nombre de las columnas de las clases son los nombres de los campos de las tablas y no se deben de usar mayúsculas por estadar.

Al definir los nombres de los archivos se debe de seguir este estandar: <nombreclase/modulo>.py para los archivos de codigo y <nombreclase/modulo>_view.xml para las definiciones de las interfaces, asi si tubieras nuestro módulo de control de notas y queremos definir por separado cada clase en un archivo podriamos tener alumnos.py y alumnos_view.xml.

OE está diseñado para ser extendido mediante módulos y estos se componen de dos tipos de archivos: los archivos que contienen la definición de las clases (python) y los que definen la interface (XML), bien en los primeros escribimos las definciones de las clases con sus métodos y propiedades (las propiedades de una clase siempres erán los campos de las tablas) y los métodos bueno son eso acciones que modifican las propiedades.

La estructura de las clases (sin herencia) de OE es la siguiente:

classs nombre_de_clase(osv.osv):
_name=”nombre.de.clase”
_columns={
‘nombrecampo1’:fields.<tipodecampo>(valores correspondientes),
‘nombrecampo2:fields.<tipodecampo>(valores correspondientes),
‘nombrecampo3:fields.<tipodecampo>(valores correspondientes),
}
nombre_de_clase()

Por otro lado tenemos los archivos XML que definen la interface lo que en OE se denominan “vistas”, estas vistas pueden ser de varios tipos: tree=listado, form=formularios, kanban=vista de icónos, calendar=vista de calendario o agenda, gant=vista tipo diagrama de Gant y graph= vista en forma de gráfico estadístico. Al lado de estas definciones tambien tenemos otras como la definción de las búsquedas (search) en donde se definen los campos por lo que se puede buscar y/o agrupar los datos en las vistas tree y kanban, las actividades (action) que determina que tipos de vistas tiene una clase, el orden eque se deben mostrar y los menús que usan los actions para enlazar estas opciones al menu de OE, asi en lo referido a la interface tenemos:

Menu
Action
Search
Vistas (tree,form,etc.)

Todo eso se tiene que definir para poder mostrar un módulo en la interface, pero no es muy complicado y tampoco resulta que se tenga que escribir una cantidad de líneas de código infinitas para lograrlo el framework implementa maneras simples de hacerlo.

Exsiten elementos como los widget’s que no son otra que controles ya predefinidos para las vistas, así tenemos controles para los datos: selection= combobox (para relaciones muchos a uno o campos de tipo selección simple), one2many=cuadricula de detalles, este tipo de widget permite mostrar una cuadrícula que enlaza los valores de un formulario padre-hijo, asi si vemos la factura y el detalle de la misma pues ese detalle ya viene programado en este widget, solo se tiene que definir en el XML los campos del objeto detalle. Tambien existe un control al que llaman “notebook” que es un control tipo Tab osea se pueden poner muchas tab’s dentro de el a las que llaman “page”, bueno eso son los widget`s (controles) mas usados pero existen varios más como el termometro o el tipo money.

En cuanto a los campos aparte de los tipos de campo normales (integer, float, char, boolean) existen campos especiales que son: one2many campo que determina relaciones uno a muchos, many2one que determina realción de muchos a uno, many2many que determina una relación de muchos a muchos, campo selection que es una lista con valores fijos por ejemplo si se quisiera tener un combo con valores fijos y los campos tipo función que ejecutan métodos de la clase para obtener sus valores.

En lo referido a las clases estas tienen elementos que son obligatorios como el nombre _name=’nombredeclase’ que debería ser el mismo que el nombre de la definicón de la clase pero con puntos en lugar de los subguiones, _colunms={<lista de columnas>} y los que no son de obligatoriedad pero son importantes como:_defauls={‘nombrecampo’:valorpordefecto,}, _sql_constraints una lista con los campos que no deben de tener valores repetidos, _order campo(s) por los cuales se ordenrán los dato en las vistas tree y kanban.

Cosas de Python:

Todos los elementos que engloban código (clases, funciones, métodos, estructuras de control) deben de terminar en dos puntos “:” y las lineas de código que son englobadas deben de tener un indentado que puede ser desde un espacio, cuatro, dos o un tab asi definiriamos un IF:

n=1
if n==31:
n=n+1
n=n+100

Lo que he colocado enlas líneas superiores indica que si n es igual a 31 entonces sumarle 1 a n y luego ya fuera del “if” sumarle 100 por lo que al final de este código tendremos que n=101 por que n comienza en 1 y no es igual a 31 pero lo que hace que no se ejecute el n=n+1 es que esta línea está indentada dentro del “if”, ahora no se te ocurra usar tab’s y mezclarlos con espacios para indentar por que simplemente el compilador te va tirar una excepción, si usas tab’s usalo siempre, si usas espacio pues lo mismo, no los mezcles.

Las listas y los diccionarios son muy usados al deesarrollar en python y con OpenObjects no es diferente una lista es en realidad un array asi podemos definir una lista de esta manera milista = [1,2,3,4,5,6,] eso es una lista y un diccionario es lo mas parecido a los array con nombre de PHP asi definimos un diccionario midiccionario = {‘nombre’:’Juan’, ‘edad’:25,’salario’:2500,} si siempre terminan en una coma al final el porqué no lo sé pero asi lo definen siempre (recuerden que no se nada de python) ahora como acceder a estos elementos pues en phyton el for en realidad es un foreach y se usa de esta manera

c=0
for n in milista:
c=c+n

Con esa sentencia estoy accediendo a los elementos de una lista.

Cosas previas a crear módulos:

La codificación del fichero: siempre y por siempre la primera línea de tus archivos .py tiene que ser así # -*- coding: utf-8 -*-
La clase padre de todos los objetos del ORM: from osv import osv, fields ese es el import más importante si quieres manejar clases de OpenERP.
Los ficheros de vistas XML siempre tienen estos tag’s:
<?xml version=”1.0″ encoding=”utf-8″?>
<openerp>
<data>
tu codigo xml
</data>
</openerp>

Los archivos de configuración de los módulos son dos __init__.py y __openerp__.py, en el primero tenemos que colocar los imports de los archivos de código que usas en tu módulo, asi si defino tres archivos .py donde están las definiciones de mis clases como clase1.py, clase2.py y clase3.py y una carpeta interna llamada clases2, mi archivo __init__.py debe de quedar como esto:
import clase1
import clase2
import clase3
import clases2

En la carpeta clases2 debe de existir otro archivo __init__.py que tenga los imports para los archivos .py que se quieran incluir al compilar ya que el compilador de phyton busca siempre en todas las carpetas un archivo llamado __init__.py para saber que archivos va a tener que compilar.

En lo referido al archivo __openerp__.py este es algo más complicado, este puede ser un ejemplo de este archivo:

{
‘name’: ‘Control de notas’,
‘version’: ‘0.1’,
‘description’: “””Ejemplo de como desarollar modulos con openerp”””,
‘author’: ‘Jorge Prado’,
‘depends’: [],
‘data’: [
“ctrl_notas.xml”,
],
‘installable’: True,
‘active’: False,
}

Sí, es un diccionario en el que los cuatro primeros elementos se describen por si mismos (name, version,desciption, author), ahora bien el elemento depends es una lista que indica las dependencia de tu módulo y se especificaría de esta manera: [‘sale’,’mrp’,’mrp_operation’,] como pueden ver son los nombres de los módulos y en este caso estaría diciendo que mi módulo depende de los módulos ventas (sale), producción (mrp) y operaciones de producción (mrp_operations), la etiqueta “data” es tambien una lista que incluye los archivos de tus vistas (modelo vista controlador, las vistas son definiciones de las interfaces), datos y similares asi si tengo varios archivos de vistas pues los colocas de esta manera [‘ventas_view.xml’,’produccion_view.xml’,’clases2/clasebasica_view.xml’], si ven en este listado he incluido uno llamado ‘clases2/casebasica_view.xml’ que indica que en la carpeta clases2 existe un archivo llamado “clasebasica_view.xml” ya que a diferenica de los archivos .py que tienen los imports en el archivo __init__.py de cada carpeta los xml’s no así que tiene que ser indicados en el archivo __openerp__.py, existen otras cosas como licencia,demo, etc., pero no son determinantes el elemento instalable=True es importante para que puedas instalar tu módulo y la etiqueta auto, si está definida en True pues se auto instala el módulo.

Las clases:

Nunca pierdan de vista la idea de que esto es bases de datos, las clases que definan son tablas y las propiedades de las mismas son campos de las tablas.

La definción de una clase está conformada por:
Nombre de clase y nombre de tabla: class <nombredeclase>(osv.osv), siempre usen subgiones ej.: ctrlnotas_alumnos (obligatorio)
Nombre de la clase para el ORM: _name=”nombreclase”, usen el mismo nombre de la clase pero los subguiones se reemplazan por puntos (.) ej.: ctrlnotas.alumnos (obligatorio)
Descipción de clase: __description=”texto con la descripción”
Columnas: _columns={diccionario de columnas} (obligatorio)

Recuenden los estandares de OE, las clases son nombradas <nombremódulo_nombreclase> y los nombres de las clases para el ORM <nombremódulo.nombreclase>, no usen caractéres con tildes, son variables y usenlas así, todo en minúsculas siempre.

Las columnas o campos de la tabla:

En OE existen campos básicos como el de cualquier tabla (enteros, decimales, caracteres, lógicos y texto) estos se definen deforma sencilla: ‘nombrecampo’:fields.<tipodedato>(‘etiquetadecampo’,<atributo>=valor), bien los tipos de datos son:

Enteros: fields.integer(‘<etiqueta>’)
Decimales: fields.float(‘<etiqueta>’,digits=(12,2))
Caracteres: fields.char(‘<etiqueta>’,size=<entero con el ancho del campo>)
Lógicos: fields.boolean(‘<etiqueta>’)
Texto: fields.text(‘<etiqueta>’)

Esos son los campos a los que yo llamo campos comunes pues son fáciles de entender desde la visión de una tabla, pero OE implementa otros tipos de campos que he catalogado en dos grupos campos relacionales y campos de interface.

Campos relacionales: Estos campos son como su nombre lo indica campos que haces las relaciones entre las tablas y son:
Muchos a uno o uno a uno: en las versiones anteriores de OE (la 5 si no me equivoco) existia un campo llamado one2one, pero ha sido eliminado y ahora solo existe el many2one para suplir ambos tipos de ralaciones uno a muchos y uno a uno, y su definción sería:
‘<nombrecampo>’:fields.many2one(‘nombreclase’,’etiqueta’,<atributos>)
Ej.: alumno_id’:fields.many2one(‘ctrlnotas.alumno’,’Examen de alumno’),

Uno a muchos: one2many(‘nombreclase’,’nombrecampoenlatablarelacionada’,’etiqueta’,<atributos>), este campo que indica una relación de uno a muchos necesita tener en la tabla relacionada un campo de muchos a uno así podemos ver un ejemplo:
Clase ctrlnotas_examen: ‘alumnos_ids’:fields.one2many(‘ctrlnotas.alumno’,’examen_id’,’Alumnos a examinar’),
Clase ctrlnotas_alumno: ‘examen_id’:field.many2one(‘crtlnotas.examen’,’Examen de alumno’),

Por estandar de OE cuando definen un campo que hace referencia a ID’s de otras clases usen <nombreclase>_id

Campos muchos a muchos, la relación mostrada anteriormente entre alumnos y examen funciona cuando solo se toma un examen por alumno pero que pasa si un alumno tiene muchos examenes entonces la relación es de muchos a muchos OE lo implementa y se hace de esta manera:
‘nombrecampo’: fields.many2many(‘nombreclaserelacionada’, ‘nombrerelación_rel’, ‘nombrecampoid1’, ‘nombrecampoid2’, ‘etiqueta’,<atributos>),
Lo que hace OE es que crea una tabla con el nombre colocado en ‘nombrerelación_rel’ formada por dos campos que hacen referencia a los id’s de cada una de las tablas, asi siguiendo con el ejemplo defino esta relación de muchos a muchos:
Clase crtlnotas_examen: ‘alumno_ids’:fields.many2many(‘ctrlnotas.alumno’,’ctrlnotas_alumnoexamen_rel’,’examen_id’,’alumno_id’, ‘Alumnos y examenes’), si vamos a las tablas verán una tabla llamada “ctrlnotas_alumnoexamen_rel” y que tiene como campos: ‘examen_id’,’alumno_id’, en donde se colocan los id’s de cada tabla, para hacer la relación muchos a muchos.´

Todas la tablas relacionales de muchos a muchos deben de terminar en la palabra _rel es un estandar no una obligación, pero es mejor seguir los estandares de OE.

Campor relativos:

‘currency_id’: fields.related(‘pricelist_id’, ‘currency_id’, type=”many2one”, relation=”res.currency”, string=”Currency”, readonly=True, required=True),

Atributos comunes: estos se aplican a todos los tipos de campos
Requerido: required en True si es exigido para la creación del registro por defecto False (equivalente a no null en la base de datos).
Solo lectura: readonly True si es de solo lectura por defecto False. Se puede usar filtros basados en campos para usar este atributo basado en campos de la clase así si tengo un campo “estado”, puedo decirle que es de solo lectura cuando el estado sea ‘done’ de esta manera: readonly=[(‘estado’,’=’,’done’)] eso hace que cuando el campo estado tenga el valor ‘done’ el campo referenciado será de solo lectura en cualquier otro caso podrá ser escrito.
Solo disponible para ciertos grupos de usuarios: groups='<nombregrupo>’, pueden poner más de un grupo separado por comas.

Existen otros elementos de las clases que si no pierden de vista la idea de están definiendo las tablas de una base de datos se les harán muy sencillo de comprender:

Valores por defecto de los campos: _defaults={‘nombrecampo1′:valor1,’nombrecampo2’:valor2,’nombrecampo7:valor7,} estos valores por defecto pueden ser valores fijos o funciones definidas en la clase.
Orden por defecto: _order=”campoorden1,campoorden2″
Campo retornado por defecto: _recname=’nombrecampo’, por defecto OE retorna el campo llamado ‘name’, si no lo encuentra o encuentra esta etiqueta retorna este campo, si no especifica campo “name” y tampoco esta etiqueta retorna el ID del registro.
Indices únicos: _sql_constraints=[(‘nombreindice’,’unique(nombrecampo)’, ‘Mensaje en caso de error)]

Bien se que no es todo y tampoco me ufano de conocer todo el framework pero al menos es todo lo que conosco (hasta hoy)

 

 

 

Bueno ya tenemos suficiente de teoría, vamos con lo divertido, voy a retomar el módulo de control de notas del post anterior pero vamos a completarlo de modo que podamos usar todos o casi todos los elementos descritos anteriormente tanto en la interface como en la lógica de los módulos, pero ahora aplicando los estandares de OE.

Acerca de

Antes que nada voy a dejar en claro algo, en este blog escribo como se me place, asi que no busquen errores de ortografia o de redaccion que los van a encontrar a montones y tampoco me critiquen o me digan nada sobre ellos pues no tengo intencion alguna de cambiarlos, lo que escribo lo dejo asi y no lo corrijo,claro esta a menos que sean lineas de codigo. Jorge Prado Anci, profesional en desarrollo de aplicaciones, en especial las dirigidas a bases de datos. He trabajado con VFP en casi todas sus versiones, se algo de Java (que no me gusta, es eso solo no me gusta, es bueno pero no me gusta), lo suficiente de PHP como para tener mi propio CMS(es que sigue sin gustarme por la capacidad de desorden que te permite este “lenguaje”), VB lo conoci y lo deteste tanto que lo olvide al punto de no querer adoptar ni por obligacion a su reemplazante VB .NET (por lo mismo de Java) y si C# este si me encanta y aun que conozco bastante bien el lenguaje, la verdad es que me falta mucho del Framework (del 100% estare en un 65%). Soy un apasionado por el orden (en los proyectos de desarrollo), de la programacion en capas (de MVC conozco pero no he aplicado mucho), los estandares y las condenadas pruebas unitaria. Venga creo que ya con esto fue suficiente.

Publicado en OpenERP
9 comments on “OpenERP: Desarrollo de un módulo
  1. Hola compañero, me puedes ayudar con algo, necesito añadir columnas a la orden de pedido, quedo atento.

    • xmeele dice:

      class purchase_order(osv.osv):
      _name=’purchase.order’
      _inherit=’purchase.order’
      _order=’name desc’
      _columns={
      ‘stock_act’:fields.float(‘Stock actual’,readonly=True),
      ‘rotation’:fields.float(‘Rotación’, help=””” Promedio trimestral de envíos ultimo trimestre”””,readonly=True),
      ‘last_cost’:fields.float(‘Última compra’,readonly=True),
      }
      purchase_order()

  2. Luis Felipe dice:

    Hola muy buenos tus tutoriales, tengo una duda que se aleja un poco de este tema pero quizás me puedas ayudar, tengo un módulo que extiende de res.partner y todo me va ok, pero quisiera que cuando los usuarios entren a la vista de cliente, la vista que vieran por defecto sea la de Vista de tree y no la vista Kanban, que podría hacer para lograrlo?? Gracias.

    • xmeele dice:

      Edita el action la verdad yo no he podido hacerlo desde programación je leido que se debe copiar el action y reemplazarlo pero no me gusta tocar el los módulos originales así que lo hago desde la interface cambio a modo desarrollador en la interface (En al about de open ERP alli sale cambiar a modo desarrollador) y luego ya en la vista kamban que quieres modificar seleccino en el combo editar acción lueego vaz aver la lista de vistas que tienen en el caso de clientes va a salir “kanban, form,tree” o algo parecido la primera es la vista que se muestra por defecto, si la dejas así “tree,form,kanban” va a mostrar primero el tree.

      • Luis Felipe dice:

        Gracias xmeele funcionó a la primera ahora me urge pero luego intentare hacerlo por código, otra duda a ver si me puedes dar una idea de como implementarlo o quizás ya exista algún modulo y desconozco, es que necesito realizar un proceso que me permita prestar productos, o sea, seria prestarlos a ciertos clientes por un plazo x y ellos pagan por dicho prestamos (hasta aki seria como una venta) la diferencia que le veo a una venta es que dichos productos permanecen inventariados en la empresa, pero no me decido como implementarlo, tienes alguna idea??? Gracias por tu blog y por responder tan rápido, saludos.

  3. xmeele dice:

    Esté módulo debe de ayudarte en lo que quieres https://www.openerp.com/apps/7.0/sale_rental/, no lo he probado y una guía para encontrar módulos para openerp pon lo siguiente en google “openerp apps (caracterisca que buscas en ingles)” así en tu caso yo puse “openerp apps rental” sin las comillas claro.

    • Luis Felipe dice:

      Gracias xmeele, justo lo que necesitaba me ahorraste un buen de trabajo, y gracias por la guía para encontrar módulos es de mucha ayuda, saludos y sigue publicando que soy un asiduo lector de tu blog, me ha ayudado mucho para iniciarme en el mundo de openerp.

  4. jessica herrera dice:

    Hola xmeele queria consultarte si sabes de algun desarrollo en el cual si elijo un producto desde la orden de pedido me muestre la lista de subproducto asociados a ese producto.
    saludos y muchas gracias

  5. hernandez dice:

    Hola que tal alguien sabe si es posible a un campo many2one agregarle una vista kanban?

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Escribe tu dirección de correo electrónico para suscribirte a este blog, y recibir notificaciones de nuevos mensajes por correo.

Únete a otros 441 seguidores

Blog Stats
  • 304,137 hits
A %d blogueros les gusta esto: