Professional Documents
Culture Documents
N. Évrard
B2 CK
Outline
2 A basic module
4 Additional topics
Global overview
Technical Overview
Written in Python
Three tiers architecture
PyGTK client (tryton) and a JavaScript client (sao) in its infancy
Application Server (trytond)
dbms, usually PostgreSQL but you can use SQLite or even MySQL
Modularity of business functionalities
Configuring mercurial
In order to activate both the hgnested and MQ extensions of Mercurial we will need to
add those lines to your .hgrc
Example
[extensions]
hgext.mq =
hgnested =
Installing trytond
Example
[database]
path = /home/training/databases
$ export TRYTOND_CONFIG=$HOME/.trytond.conf
Example
$ touch ~/databases/test.sqlite
$ ./bin/trytond -d test -u ir res
trytond will ask you for the admin password at the end of the installation process.
$ cd trytond/modules
$ hg init training
$ cd training/.hg
$ hg clone http://hg.tryton.org/training -b 3.4 patches
Outline
2 A basic module
A minimal module
Defining some object / tables
Different fields
Defining a tree and a form view
Default values and on_change calls
4 Additional topics
Example
[tryton]
version=0.0.1
depends:
ir
res
Creating a model
A trytond model is a python class inheriting from Model. To enable the SQL
persistence the model must inherit of ModelSQL.
Example
from t r y t o n d . model import ModelSQL
__all__ = [ ’ Opportunity ’ ]
c l a s s O p p o r t u n i t y ( ModelSQL ) :
’ Opportunity ’
__name__ = ’ t r a i n i n g . o p p o r t u n i t y ’
In order for your object to be "known" by trytond they must be registered into the pool.
Example
from t r y t o n d . p o o l import P o o l
from . o p p o r t u n i t y import ∗
def r e g i s t e r ( ) :
Pool . r e g i s t e r (
Opportunity ,
module = ’ t r a i n i n g ’ , t y p e _ = ’ model ’ )
Example
c l a s s O p p o r t u n i t y ( ModelSQL ) :
’ Opportunity ’
__name__ = ’ t r a i n i n g . o p p o r t u n i t y ’
_rec_name = ’ d e s c r i p t i o n ’
d e s c r i p t i o n = f i e l d s . Char ( ’ D e s c r i p t i o n ’ ,
r e q u i r e d =True )
s t a r t _ d a t e = f i e l d s . Date ( ’ S t a r t Date ’ ,
r e q u i r e d =True )
e n d _ d a t e = f i e l d s . D a t e ( ’ End D a t e ’ )
p a r t y = f i e l d s . Many2One ( ’ p a r t y . p a r t y ’ ,
’ P a r t y ’ , r e q u i r e d =True )
comment = f i e l d s . T e x t ( ’ Comment ’ )
fields arguments
fields.Char
In a tryton module:
Example
d e s c r i p t i o n = f i e l d s . Char ( ’ D e s c r i p t i o n ’ , r e q u i r e d = T r u e )
In the interface:
Example
In SQL:
Example
d e s c r i p t i o n VARCHAR NOT NULL
fields.Date
In a tryton module:
Example
s t a r t _ d a t e = f i e l d s . Date ( ’ S t a r t Date ’ , r e q u i r e d =True )
In the interface:
Example
In SQL:
Example
s t a r t _ d a t e DATE NOT NULL
fields.Text
In a tryton module:
Example
comment = f i e l d s . T e x t ( ’ Comment ’ )
In the interface:
Example
In SQL:
Example
comment TEXT
fields.Many2One
In a tryton module:
Example
p a r t y = f i e l d s . Many2One ( ’ p a r t y . p a r t y ’ , ’ P a r t y ’ , r e q u i r e d = T r u e )
In the interface:
Example
In SQL:
Example
p a r t y i n t e g e r NOT NULL,
FOREIGN KEY( p a r t y ) REFERENCES p a r t y _ p a r t y ( i d )
fields.One2Many
fields.Many2Many
fields.One2One
Displaying data
To use the presentation layer your model must inherit from ModelView
Example
c l a s s O p p o r t u n i t y ( ModelSQL , ModelView ) :
You must also add the xml presentation file in the tryton.cfg configuration file
Example
xml:
opportunity.xml
Defining a view
A tree view
Example
< r e c o r d model = " i r . u i . view " i d = " o p p o r t u n i t y _ v i e w _ l i s t " >
< f i e l d name= " model " > t r a i n i n g . o p p o r t u n i t y < / f i e l d >
< f i e l d name= " t y p e " > t r e e < / f i e l d >
< f i e l d name= " name " > o p p o r t u n i t y _ l i s t < / f i e l d >
</ record>
Example
< t r e e s t r i n g =" O p p o r t u n i t i e s ">
< f i e l d name= " p a r t y " / >
< f i e l d name= " d e s c r i p t i o n " / >
< f i e l d name= " s t a r t _ d a t e " / >
< f i e l d name= " e n d _ d a t e " / >
</ tree>
A form view
Example
< r e c o r d model = " i r . u i . view " i d = " o p p o r t u n i t y _ v i e w _ f o r m " >
< f i e l d name= " model " > t r a i n i n g . o p p o r t u n i t y < / f i e l d >
< f i e l d name= " t y p e " > form < / f i e l d >
< f i e l d name= " name " > o p p o r t u n i t y _ f o r m < / f i e l d >
</ record>
Example
< form s t r i n g = " O p p o r t u n i t y " >
< l a b e l name= " p a r t y " / >
< f i e l d name= " p a r t y " / >
< l a b e l name= " d e s c r i p t i o n " / >
< f i e l d name= " d e s c r i p t i o n " / >
< l a b e l name= " s t a r t _ d a t e " / >
< f i e l d name= " s t a r t _ d a t e " / >
< l a b e l name= " e n d _ d a t e " / >
< f i e l d name= " e n d _ d a t e " / >
< s e p a r a t o r name= " comment " c o l s p a n = " 4 " / >
< f i e l d name= " comment " c o l s p a n = " 4 " / >
< / form >
Example
< r e c o r d model = " i r . a c t i o n . a c t _ w i n d o w "
id =" a c t _ o p p o r t u n i t y _ f o r m ">
< f i e l d name= " name " > O p p o r t u n i t i e s < / f i e l d >
< f i e l d name= " r e s _ m o d e l " > t r a i n i n g . o p p o r t u n i t y < / f i e l d >
</ record>
< r e c o r d model = " i r . a c t i o n . a c t _ w i n d o w . view "
id =" act_opportunity_form_view1 ">
< f i e l d name= " s e q u e n c e " e v a l = " 10 " / >
< f i e l d name= " view " r e f = " o p p o r t u n i t y _ v i e w _ t r e e " / >
< f i e l d name= " a c t _ w i n d o w " r e f = " a c t _ o p p o r t u n i t y _ f o r m " / >
</ record>
< r e c o r d model = " i r . a c t i o n . a c t _ w i n d o w . view "
id =" act_opportunity_form_view2 ">
< f i e l d name= " s e q u e n c e " e v a l = " 20 " / >
< f i e l d name= " view " r e f = " o p p o r t u n i t y _ v i e w _ f o r m " / >
< f i e l d name= " a c t _ w i n d o w " r e f = " a c t _ o p p o r t u n i t y _ f o r m " / >
</ record>
Example
< menuitem name= " T r a i n i n g " i d = " m e n u _ t r a i n i n g " / >
< menuitem p a r e n t = " m e n u _ t r a i n i n g "
a c t i o n =" a c t _ o p p o r t u n i t y _ f o r m "
id =" menu_opportunity_form " / >
Tryton provides a way to react on the user input by changing the value of other fields.
This mechanism is call the on_changes.
They come in two flavours:
on_change_<field name> When a field is changed a list of value is sent to the server.
The server sends the new values of some fields
on_change_with_<field name> A field value is computed when any field in a list of
fields is modified. The computation occurs on the server.
In both case the list of fields sent to the server is specified thanks to the decorator
@fields.depends.
on_change
Example
@fields . depends ( ’ p a r t y ’ )
def on_change_party ( s e l f ) :
a d d r e s s = None
if self . party :
a d d r e s s = s e l f . p a r t y . a d d r e s s _ g e t ( type= ’ i n v o i c e ’ )
return {
’ address ’ : address ,
}
on_change_with
Example
@fields . depends ( ’ s t a r t _ d a t e ’ )
def on_change_with_end_date ( s e l f ) :
if self . start_date :
return s e l f . s t a r t _ d a t e + d a t e t i m e . t i m e d e l t a ( days =7)
r e t u r n None
Outline
2 A basic module
4 Additional topics
Defining a workflow
@classmethod
@ModelView . b u t t o n
@Workflow . t r a n s i t i o n ( ’ c o n v e r t e d ’ )
def convert ( cls , o p p o r t u n i t i e s ) :
...
Let’s add the state and the buttons to the opportunity view. There is also a way of
grouping widgets
Example
<group co l =" 2 " colspan =" 2 " id =" s t a t e ">
< l a b e l name= " s t a t e " / >
< f i e l d name= " s t a t e " / >
< / group>
<group co l =" 2 " colspan =" 2 " id =" b u t t o n s ">
< b u t t o n name= " l o s t " s t r i n g = " L o s t " i c o n = " t r y t o n−c a n c e l " / >
< b u t t o n name= " c o n v e r t " s t r i n g = " C o n v e r t " i c o n = " t r y t o n−go−n e x t " / >
< / group>
A function field is a field that is computed into python its data is not persistently store
into the database.
Example
duration = f i e l d s . Function ( f i e l d s . Integer ( ’ Duration ’ ) , ’ get_duration ’ )
d e f g e t _ d u r a t i o n ( s e l f , name=None ) :
i f n o t s e l f . s t a r t _ d a t e or n o t s e l f . e n d _ d a t e :
r e t u r n None
return ( s e l f . end_date − s e l f . s t a r t _ d a t e ) . days
Any tryton type can be used. Note also that since this signature is the same as the one
of an on_change_with then the same function can be used for both.
Of course a setter and a searcher can also be defined in order to modify or search
corresponding data.
@classmethod
d e f g e t _ d e s c r i p t i o n _ l e n g t h ( c l s , o p p o r t u n i t i e s , name ) :
cursor = Transaction . cursor ()
return d i c t ( c u r s o r . f e t c h a l l ( ) )
Sometime you want to add functionalities to a model that do not suite the use of a
button. For this kind of use case the wizard is your solution.
A wizard is composed of two things:
A set of views that represent the form to gather the user input
A "state machine" that define what should be done
Wizard views
Those are standard ModelView, you can define on_change, on_change_with and
default values on them.
Example
c l a s s C o n v e r t O p p o r t u n i t i e s S t a r t ( ModelView ) :
’ Convert O p p o r t u n i t i e s ’
__name__ = ’ t r a i n i n g . o p p o r t u n i t y . c o n v e r t . s t a r t ’
This is a class that inherits from Wizard. You’ll be able to define different states on it.
Example
from t r y t o n d . w i z a r d import Wizard , S t a t e V i e w , S t a t e T r a n s i t i o n , B u t t o n
c l a s s C o n v e r t O p p o r t u n i t i e s ( Wizard ) :
’ Convert O p p o r t u n i t i e s ’
__name__ = ’ t r a i n i n g . o p p o r t u n i t y . c o n v e r t ’
s t a r t = StateView ( ’ t r a i n i n g . o p p o r t u n i t y . convert . s t a r t ’ ,
’ training . opportunity_convert_start_view_form ’ , [
B u t t o n ( ’ C a n c e l ’ , ’ end ’ , ’ t r y t o n−c a n c e l ’ ) ,
B u t t o n ( ’ C o n v e r t ’ , ’ c o n v e r t ’ , ’ t r y t o n−ok ’ , d e f a u l t = T r u e ) ,
])
convert = StateTransition ()
def t r a n s i t i o n _ c o n v e r t ( s e l f ) :
pool = Pool ( )
Opportunity = pool . get ( ’ t r a i n i n g . opportuni ty ’ )
o p p o r t u n i t i e s = O p p o r t u n i t y . browse (
Transaction ( ) . context [ ’ active_ids ’ ])
Opportunity . convert ( opportunities )
r e t u r n ’ end ’
Example
< r e c o r d model = " i r . a c t i o n . w i z a r d " i d = " a c t _ c o n v e r t _ o p p o r t u n i t i e s " >
< f i e l d name= " name " > C o n v e r t O p p o r t u n i t i e s < / f i e l d >
< f i e l d name= " wiz_name " > t r a i n i n g . o p p o r t u n i t y . c o n v e r t < / f i e l d >
< f i e l d name= " model " > t r a i n i n g . o p p o r t u n i t y < / f i e l d >
</ record>
< r e c o r d model = " i r . a c t i o n . keyword " i d = " a c t _ c o n v e r t _ o p p o r t u n i t i e s _ k e y w o r d " >
< f i e l d name= " keyword " > f o r m _ a c t i o n < / f i e l d >
< f i e l d name= " model " > t r a i n i n g . o p p o r t u n i t y ,−1< / f i e l d >
< f i e l d name= " a c t i o n " r e f = " a c t _ c o n v e r t _ o p p o r t u n i t i e s " / >
</ record>
Extending models
__all__ = [ ’ Party ’ ]
_ _ m e t a c l a s s _ _ = PoolMeta
class Party :
__name__ = ’ p a r t y . p a r t y ’
o p p o r t u n i t i e s = f i e l d s . One2Many (
’ training . opportunity ’ , ’ party ’ ,
’ Opportunities ’ )
Modifing existing view is done by adding record in the XML files that specify an
xpath and what to do with the resulting node.
Example
< r e c o r d model = " i r . u i . view " i d = " p a r t y _ v i e w _ f o r m " >
< f i e l d name= " model " > p a r t y . p a r t y < / f i e l d >
< f i e l d name= " i n h e r i t " r e f = " p a r t y . p a r t y _ v i e w _ f o r m " / >
< f i e l d name= " name " > p a r t y _ f o r m < / f i e l d >
</ record>
Example
<data>
< x p a t h e x p r = " / form / n o t e b o o k / p a g e [ @id = ’ a c c o u n t i n g ’ ] "
p o s i t i o n =" a f t e r ">
< p a g e name= " o p p o r t u n i t i e s " c o l = " 1 " >
< s e p a r a t o r name= " o p p o r t u n i t i e s " / >
< f i e l d name= " o p p o r t u n i t i e s " / >
< / page>
< / xpath>
</ data>
Outline
2 A basic module
4 Additional topics
Reporting
proteus
Table Queries
Adding a report
To add a report you must create an odt template that is compatible with relatorio
(http://relatorio.openhex.org/)
This report engine allows you to create opendocument files from templates.
You must define a python class that will register a report object
Example
from t r y t o n d . r e p o r t import R e p o r t
c l a s s OpportunityReport ( Report ) :
pass
You might want to overload the parse method to define a specific behavior.
Example
< r e c o r d model = " i r . a c t i o n . r e p o r t " i d = " r e p o r t _ o p p o r t u n i t y " >
< f i e l d name= " name " > O p p o r t u n i t y < / f i e l d >
< f i e l d name= " model " > t r a i n i n g . o p p o r t u n i t y < / f i e l d >
< f i e l d name= " r e p o r t _ n a m e " > t r a i n i n g . o p p o r t u n i t y < / f i e l d >
< f i e l d name= " r e p o r t " > t r a i n i n g / o p p o r t u n i t y . o d t < / f i e l d >
</ record>
< r e c o r d model = " i r . a c t i o n . keyword "
id =" r e p o r t _ o p p o r t u n i t y _ k e y w o r d ">
< f i e l d name= " keyword " > f o r m _ p r i n t < / f i e l d >
< f i e l d name= " model " > t r a i n i n g . o p p o r t u n i t y ,−1< / f i e l d >
< f i e l d name= " a c t i o n " r e f = " r e p o r t _ o p p o r t u n i t y " / >
</ record>
You might want to script your tryton server. For this there is the proteus library. It will
allow you to interact with it in a python way.
With models you will be able to use the find, edit, delete and create objects
You will also have the possiblity to use the wizards.
Setting up proteus
An example of proteus
Example
import csv
import datetime
import sys
from p r o t e u s i m p o r t c o n f i g , Model
d e f main ( a r g s ) :
P a r t y = Model . g e t ( ’ p a r t y . p a r t y ’ )
O p p o r t u n i t y = Model . g e t ( ’ t r a i n i n g . o p p o r t u n i t y ’ )
c s v _ f i l e = c s v . D i c t R e a d e r ( open ( a r g s [ 1 ] , ’ r ’ ) )
for line in c s v _ f i l e :
p a r t i e s = P a r t y . f i n d ( [ ( ’ name ’ , ’ = ’ , l i n e [ ’ P a r t y ’ ] ) ] )
i f not p a r t i e s :
p a r t y = P a r t y ( name= l i n e [ ’ P a r t y ’ ] )
party . save ( )
else:
party = parties [0]
l i n e _ d a t e = d a t e t i m e . d a t e t i m e . s t r p t i m e ( l i n e [ ’ Date ’ ] ,
’%d/%m/%y ’ ) . d a t e ( )
new_opportunity = Opportunity ( party =party ,
description=line [ ’ Description ’ ] , start_date =line_date )
new_opportunity . save ( )
i f __name__ == ’ __main__ ’ :
c o n f i g . s e t _ t r y t o n d ( p a s s w o r d = ’ admin ’ , d a t a b a s e _ n a m e = ’ t r a i n i n g ’ )
main ( s y s . a r g v )
p a r t y = f i e l d s . Many2One ( ’ p a r t y . p a r t y ’ , ’ P a r t y ’ , r e a d o n l y = T r u e )
o p p o r t u n i t y _ c o u n t = f i e l d s . I n t e g e r ( ’ O p p o r t u n i t y count ’ , r e a d o n l y =True )
@classmethod
def t a b l e _ q u e r y ( c l s ) :
pool = Pool ( )
o p p o r t u n i t y _ t a b l e = pool . get ( ’ t r a i n i n g . opportunit y ’ ) . __table__ ( )
columns = [
Min ( o p p o r t u n i t y _ t a b l e . i d ) . a s _ ( ’ i d ’ ) ,
Max ( o p p o r t u n i t y _ t a b l e . c r e a t e _ u i d ) . a s _ ( ’ c r e a t e _ u i d ’ ) ,
Max ( o p p o r t u n i t y _ t a b l e . c r e a t e _ d a t e ) . a s _ ( ’ c r e a t e _ d a t e ’ ) ,
Max ( o p p o r t u n i t y _ t a b l e . w r i t e _ u i d ) . a s _ ( ’ w r i t e _ u i d ’ ) ,
Max ( o p p o r t u n i t y _ t a b l e . w r i t e _ d a t e ) . a s _ ( ’ w r i t e _ d a t e ’ ) ,
opportunity_table . party ,
Count ( o p p o r t u n i t y _ t a b l e . i d ) . a s _ ( ’ o p p o r t u n i t y _ c o u n t ’ ) ,
]
group_by = [ o p p o r t u n i t y _ t a b l e . p a r t y ]
r e t u r n o p p o r t u n i t y _ t a b l e . s e l e c t ( ∗ columns , g r o u p _ b y = g r o u p _ b y )