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_419
#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_419

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:

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’

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:

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

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