Tryton Apps (1)

Utilizaremos el motor de TrytonERP para el desarrollo de aplicaciones a medida, utilizando el sistema de módulos del mismo, esto es gracias a que trytond viene solo con los módulos esenciales para el funcionamiento del mismo.

Este tutorial se realiza en base a tutorial de introducción al CakePHP.

Requisitos

Antes de empezar recomendamos crear un directorio para el proceso de desarrollo, todas las instrucciones a continuación son relativas al directorio.

requerimientos para el core trytond, para mas detalles ver Instalación.

tryton-dev# pip install wrapt werkzeug lxml relatorio python-dateutil polib python-sql

además sera necesario un cliente tryton ver Descargas.

Instalacion

Para desarrollar aplicaciones utilizando el core TrytonERP debemos clonar el servidor trytond:

tryton-dev$ hg clone -b 4.4 https://hg.tryton.org/trytond/

ingresamos al directorio y ejecutamos el servidor para chequear que este correctamente funcionando y tenemos todas las dependencias requeridas:

tryton-dev$ ./trytond/bin/trytond --version

lo primero que debemos crear es el archivo de configuración para el servidor, en esta etapa de desarrollo vamos a utilizar sqlite, creamos el archivo trytond.ini:

Documentacion Configuración

[database]
uri = sqlite:///
#indicar la ruta donde se crearan las base de datos SQLite
path = /opt/tryton-dev

creamos e inicializamos la base de datos SQLite.

tryton-dev$ touch /otp/tryton-dev/bookmarks.sqlite
#inicializa base de datos
tryton-dev$ ./trytond/bin/trytond-admin -c trytond.ini -d bookmarks --all -l es
#opcional, cambiar contraseña de usuario admin
tryton-dev$ ./trytond/bin/trytond-admin -c trytond.ini -d bookmarks -p

Tutorial

Para este tutorial vamos a crear el modulo bookmarks, lo primero es crear el directorio tryton-dev/trytond/trytond/modules/bookmarks que contiene los archivos del modulo.

estructura del directorio:

bookmarks/
bookmarks/__init__.py
bookmarks/tryton.cfg
bookmarks/models.py
bookmarks/views.xml
bookmarks/security.xml
bookmarks/view/tag_list.xml
bookmarks/view/tag_form.xml
bookmarks/view/bookmark_list.xml
bookmarks/view/bookmark_form.xml

Empezamos con los archivos esenciales para mas detalle.

tryton.cfg

[tryton]
version=4.4.0
depends=
        ir
        res
xml:
        views.xml
        security.xml

__init__.py

from trytond.pool import Pool

def register():
    True

views.xml

<?xml version="1.0"?>
<tryton>
    <data>
    </data>
</tryton>
#refrescamos la lista de modulos
tryton-dev$ ./trytond/bin/trytond-admin -c trytond.ini -d bookmarks --all -l es

a este punto podriamos visualizar el modulo como activo, antes debemos iniciar el servidor y conectar con algunos de los clientes.

vamos a realizar el modelado, para esto tomaremos la estructura de datos de ejemplo de CakePHP/Bookmarks y construimos los modelos acorde este

ejemplo bookmarsk.sql

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE bookmarks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(50),
    description TEXT,
    url TEXT,
    created DATETIME,
    modified DATETIME,
    FOREIGN KEY user_key (user_id) REFERENCES users(id)
);

CREATE TABLE tags (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    created DATETIME,
    modified DATETIME,
    UNIQUE KEY (title)
);

CREATE TABLE bookmarks_tags (
    bookmark_id INT NOT NULL,
    tag_id INT NOT NULL,
    PRIMARY KEY (bookmark_id, tag_id),
    FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
    FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);

la tabla users no es necesaria ya que utilizariamos user.py del framework de tryton.

A continuación el modelado de las tablas anteriormente propuestas:

models.py

from trytond.model import ModelSQL, ModelView, fields
from trytond.transaction import Transaction

class Bookmark(ModelSQL, ModelView):
    """
	sdfsafdsa
	"""
    
    #identificar unico del modelo, es necesario para referencias posteriores
	#ademas crea la tabla bookmarks_bookmarks
    __name__ = "bookmarks.bookmarks"
    
    #es necesario cuando no existe atributo *name*
    _rec_name = 'title'
    
    #definicion de atributos del modelo
	#definicion de atributos http://doc.tryton.org/4.4/trytond/doc/ref/models/fields.html?highlight=selection#field-types
    user = fields.Many2One('res.user', 'User')
    title = fields.Char("Title")
    description = fields.Text("Description")
    url = fields.Char("URL")
    tags = fields.Many2Many("bookmarks.bookmarks_tags", "bookmark_id", "tag_id", "Tags")
    
    #modificamos la busqueda de registro
    #:clause: - es una clausula con tuple(name, condition, value),
    #reconstruimos la consulta para permitir encontrar valores por *title* o *description*
    #para documentacion de como escribir las clausulas [mirar](http://devops.neurotec.co/trytonerp/sql_clause)
    @classmclassmethodethod
    def search_rec_name(cls, name, clause):
        return ['OR',
                ('title', ) + tuple(clause[1:]),
                ('description', ) + tuple(clause[1:])
            ]
            
   # se registra con el usuario actualmente registrado
   @classmethod
   def default_user(cls):
        current_user = Transaction().user
        return current_user
    
class Tag(ModelSQL, ModelView):
    "Tag"
    __name__ = "bookmarks.tags"
    _rec_name = 'title'
    
    title = fields.Char("Title")

class BookmarkTag(ModelSQL):
    "BookmarkTag"
    __name__ = "bookmarks.bookmarks_tags"

    bookmark_id = fields.Many2One("bookmarks.bookmarks", "Bookmark")
    tag_id = fields.Many2One("bookmarks.tags", "Tag")

y registramos en el Pool los modelos, editando init.py:

__init__.py

from trytond.pool import Pool

from .models import *

def register():
    #registramos los modelos al framework
    Pool.register(
        Bookmark,
        Tag,
        BookmarkTag,
        module="bookmarks", type_="model")
		
	return True

ahora vamos a crear las vistas para los respectivos modelos, la creacion de las vistas se realiza insertando registros utilizando la etiqueta de los respectivos modelos:

  • ir.ui.view
  • ir.action.act_window
  • ir.action.act_window.view

views.xml

<?xml version="1.0"?>
<tryton>
  <data>

    <!-- relacionamos el tipo de vista con el modelo-->
    <!-- asociamos el modelo con las vistas -->
    <record model="ir.ui.view" id="bookmarks_view_tree">
      <field name="model">bookmarks.bookmarks</field>
      <field name="type">tree</field>
      <!--se espera archivo view/bookmarks_list.xml -->
      <field name="name">bookmarks_list</field>
    </record>

    <record model="ir.ui.view" id="bookmarks_view_form">
      <field name="model">bookmarks.bookmarks</field>
      <field name="type">form</field>
      <!-- se espera archivo view/bookmarks_form.xml -->
      <field name="name">bookmarks_form</field>
    </record>

    <record model="ir.ui.view" id="tags_view_tree">
      <field name="model">bookmarks.tags</field>
      <field name="type">tree</field>
      <!--ejemplo de embeder vista-->
      <!-- indica del modelo que columnas se van a mostrar -->
      <field name="arch" type="xml">
        <![CDATA[
        <tree>
          <field name="title"/>
        </tree>
        ]]>
      </field>
    </record>
    
    <!--relacionamos act_window con el modelo para ser visualizadas-->
    <record model="ir.action.act_window" id="act_bookmarks_form">
      <field name="name">Bookmarks</field>
      <field name="res_model">bookmarks.bookmarks</field>
      <!-- se muestran solo los del usuario logeado/activo -->
      <field name="domain"
             eval="[('user', '=', Eval('_user'))]"
             pyson="1"
             />
    </record>
    
    <record model="ir.action.act_window.view" id="act_bookmarks_form_view1">
      <field name="sequence" eval="1"/>
      <field name="view" ref="bookmarks_view_tree"/>
      <field name="act_window" ref="act_bookmarks_form"/>
    </record>

    <record model="ir.action.act_window.view" id="act_bookmarks_form_view2">
      <field name="sequence" eval="2"/>
      <field name="view" ref="bookmarks_view_form"/>
      <field name="act_window" ref="act_bookmarks_form"/>
    </record>

    <!-- creamos menu -->
    <menuitem name="Bookmarks" sequence="1" id="bookmark"/>
    <!-- submenu y enlance al act_window para visualizar -->
    <menuitem name="Bookmarks" id="bookmarks" parent="bookmark" action="act_bookmarks_form"/>

    <!--solo creando el act_window podemos tener acceso al modelo-->
    <record model="ir.action.act_window" id="act_tags_form">
      <field name="name">Tags</field>
      <field name="res_model">bookmarks.tags</field>
    </record>

    <!--
        los iconos puede ser encontrados en
        https://github.com/tryton/trytond/blob/da650fabd2fddacfa6baf90b137fd72384be2c13/trytond/ir/ui/ui.xml-->
    <menuitem name="Tags" id="bookmarks_tags" icon="tryton-spreadsheet" parent="bookmark" action="act_tags_form"/>
  </data>
</tryton>

view/bookmarks_list.xml

<?xml version="1.0"?>
<tree>
  <field name="user"/>
  <field name="title"/>
  <field name="description"/>
  <field name="url"/>
</tree>

view/bookmarks_form.xml

<?xml version="1.0"?>
<form>
  <group col="2">
    <group colspan="2" >
      <label name="user"/>
      <field name="user"/>
    </group>
    <group colspan="2">
      <label name="title"/>
      <field name="title"/>
    </group>
    <group colspan="2">
      <label name="description"/>
      <field name="description"/>
    </group>
    <label name="url"/>
    <field name="url"/>
    <label name="tags"/>
    <field name="tags"/>
  </group>
</form>

Grupos y Permisos

security.xml:

<?xml version="1.0"?>
<tryton>
  <data>
    <!-- creacion del grupo -->
    <record model="res.group" id="group_bookmarks">
      <field name="name">Bookmarks</field>
    </record>

    <!-- por defecto se pueden visualizar todos los registros -->
    <record model="ir.model.access" id="access_bookmarks_all">
      <field name="model" search="[('model', '=', 'bookmarks.bookmarks')]"/>
      <field name="perm_read" eval="True"/>
      <field name="perm_write" eval="False"/>
      <field name="perm_create" eval="False"/>
      <field name="perm_delete" eval="False"/>
    </record>

    <!-- para crear, leer, escribir y eliminar se debe estar en el grupo *group_bookmarks* -->
    <record model="ir.model.access" id="access_bookmarks">
      <field name="model" search="[('model', '=', 'bookmarks.bookmarks')]"/>
      <field name="group" ref="group_bookmarks"/>
      <field name="perm_read" eval="True"/>
      <field name="perm_write" eval="True"/>
      <field name="perm_create" eval="True"/>
      <field name="perm_delete" eval="True"/>
    </record>
    
  </data>
</tryton>

actualizamos listado de modulos, si no se visualiza el modulo en la lista de modulos del sistema.

tryton-dev$ ./trytond/bin/trytond-admin -c tryton.ini -d bookmarks -m

una vez refrescado el modulo, en preferencias del usuario o al crear un usuario nuevo puede visualizar el grupo Bookmarks.

Documentacion

Iniciar servidor

tryton-dev$ ./trytond/bin/trytond -c tryton.ini -d bookmarks

Modulos

un modulo es un directorio en trytond/modules que contiene almenos los dos siguientes archivos:

  • init.py’ : un modulo Tryton es un modulo de Python
  • tryton.cfg : archivo de configuracion que describe el modulo Tryton

init.py’

debe contener un metodo llamada register() que debe registrar al Pool todos los objetos del modulo.

from trytond.pool import Pool

def register():
  Pool.register(..., module='....', type_='...')

tryton.cfg

el archivo de configuracion utiliza el formato ConfigParser, debe contener la seccion [tryton] y los posibles items son:

  • version : version del modulo.
  • depends : por linea lista de modulos que depende.
  • extras_depend : por linea lista de modulos que quizas necesita.
  • xml: por linea archivos XML del modulo, ellos van a ser cargados en el orden declarados.

ejemplo:

[tryton]
version=0.0.1
depends:
  ir
  res
xml:
 views.xml
 wizards.xml
 security.xml

VIstas

Record

..se utiliza para ingresar registros.. ..

API

Pool.register(klass, [type_])

module : modulo

type_: * model * wizard * report

FAQ

ERRORES

File "parser.pxi", line 1827, in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:106678)
ValueError: can only parse strings

algunos casos son: * no existe o ubica el archivo en view/, asegura que todo field:name de ir.ui.view existe el archivo correspondiente.

  File "/trytond/model/modelview.py", line 280, in fields_view_get
    tree = etree.fromstring(result['arch'], parser=parser)
  File "src/lxml/lxml.etree.pyx", line 3228, in lxml.etree.fromstring (src/lxml/lxml.etree.c:79609)
  File "src/lxml/parser.pxi", line 1847, in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:119109)
ValueError: can only parse strings
  • este error, es debido a que en el record del modelo ir.ui.view no encontro etiqueta field para name o arch.
  • si se desea que se genere dinamico, solo usar el record del modelo ir.action.act_window.

AGREGAR NUEVO ICONO

<record model="ir.ui.icon" id="presentation_icon">
  <field name="name">tryton-presentation</field>
  <field name="path"><**modulo**>/icons/tryton-presentation.svg</field>
</record>

Referencias