Open Object Developer
Open Object Developer
Release 1.0
Tiny SPRL
2009-04-10
CONTENTS
i
ii
Open Object Developer Book, Release 1.0
I Forewords 5
1 Introduction 7
5 Development Environment 17
5.1 Working with Launchpad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.2 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
5.3 Command line options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.4 OpenERP Server and Web Client - Start/Stop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.5 Shutting down the server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
CONTENTS 1
Open Object Developer Book, Release 1.0
14 Reports 139
2 CONTENTS
Open Object Developer Book, Release 1.0
17 Dashboard 169
CONTENTS 3
Open Object Developer Book, Release 1.0
Index 209
4 CONTENTS
Part I
Forewords
5
CHAPTER
ONE
INTRODUCTION
Open ERP is a rich development environment. Thanks to its Python and PostgreSQL bindings, and above all, its
Object Relational Mapping (ORM), you can develop any arbitrary complex module in Open ERP.
7
Open Object Developer Book, Release 1.0
8 Chapter 1. Introduction
CHAPTER
TWO
9
Open Object Developer Book, Release 1.0
THREE
Book Contents
11
Open Object Developer Book, Release 1.0
FOUR
13
Open Object Developer Book, Release 1.0
15
CHAPTER
FIVE
DEVELOPMENT ENVIRONMENT
Installing Bazaar
17
Open Object Developer Book, Release 1.0
To work correctly, bzr version must be at least 1.3. Check it with the command:
bzr --version
If you don’t have at least 1.3 version, you can check this url: http://bazaar-vcs.org/Download On debian, in any
distribution, the 1.5 version is working, you can get it on this url: http://backports.org/debian/pool/main/b/bzr/bzr_1.5-
1~bpo40+1_i386.deb
If you experience problems with Bazaar, please read the F.A.Q on Bazaar version control system before asking any
questions.
The combination of Bazaar branch hosting and Launchpad’s teams infrastructure gives you a very powerful capability
to collaborate on code. Essentially, you can push a branch into a shared space and anyone on that team can then
commit to the branch.
This means that you can use Bazaar in the same way that you would use something like SVN, i.e. centrally hosting
a branch that many people commit to. You have the added benefit, though, that anyone outside the team can always
create their own personal branch of your team branch and, if they choose, upload it back to Launchpad.
This is the official and proposed way to contribute on OpenERP and OpenObject.
Quick Summary
To download the latest sources and create your own local branches of OpenERP, do this:
This will download all the component of openerp (server, client, addons) and create links of modules in addons in your
server so that you can use it directly. You can change the bzr_set.py file to select what you want to download exactly.
Now, you can edit the code and commit in your local branch.:
EDIT addons/account/account.py
cd addons
bzr ci -m "Testing Modifications"
Once your code is good enough and follow the Coding Guidelines, you can push your branch in launchpad. You may
have to create an account on launchpad first, register your public key, and subscribe to the openerp-community team.
Then, you can push your branch. Suppose you want to push your addons:
cd addons
bzr push lp:~openerp-community/openobject-addons/YOURLOGIN_YOURBRANCHNAME
bzr bind lp:~openerp-community/openobject-addons/YOURLOGIN_YOURBRANCHNAME
After having done that, your branch is public on Launchpad, in the OpenObject project, and commiters can work on
it, review it and propose for integration in the official branch. The last line allows you to rebind your branch to the
one which is on launchpad, after having done this, your commit will be applied on launchpad directly (unless you use
-local):
If your changes fixe a public bug on launchpad, you can use this to mark the bug as fixed by your branch:
Once your branch is mature, mark it as mature in the web interface of launchpad and request for merging in the official
release. Your branch will be reviewed by a commiter and then the quality team to be merged in the official release.
[Read more about Open ERP Team]
• You create a branch on launchpad on the project that interest you. It’s important that you create your branch on
launchpad and not on your local system so that we can easily merge, share code between projects and centralize
futur developments.
• You develop your own features or bugfixes in your own branch on launchpad. Don’t forget to set the status of
your branch (new, experimental, development, mature, ...) so that contributors knows what they can use or not.
• Once your code is good enough, you propose your branch for merging
• Your work will be evaluated by one responsible of the commiters team.
– If they accept your branch for integration in the official version, they will submit to the quality team that
will review and merge in the official branch.
– If the commiter team refuses your branch, they will explain why so that you can review your code to better
fits guidelines (problem for futur migrations, ...)
The extra-addons branch, that stores all extra modules, is directly accessible to all commiters. If you are a commiter,
you can work directly on this branch and commit your own work. This branch do not require a validation of the quality
team. You should put there your special modules for your own customers.
If you want to propose or develop new modules, we suggest you to create your own branch in the openobject-addons
project and develop within your branch. You can fill in a bug to request that your modules are integrated in one of the
two branches:
We invite all our partners and contributors to work in that way so that we can easily integrate and share the work done
between the different projects.
After having done that, your branch is public on Launchpad, in the OpenObject project, and commiters can work on
it, review it and propose for integration in the official branch. The last line allows you to rebind your branch to the
one which is on launchpad, after having done this, your commit will be applied on launchpad directly (unless you use
-local):
If your changes fixe a public bug on launchpad, you can use this to mark the bug as fixed by your branch:
Once your branch is mature, mark it as mature in the web interface of launchpad and request for merging in the official
release. Your branch will be reviewed by a commiter and then the quality team to be merged in the official release.
• You create a branch on launchpad on the project that interest you. It’s important that you create your branch on
launchpad and not on your local system so that we can easily merge, share code between projects and centralize
futur developments.
• You develop your own features or bugfixes in your own branch on launchpad. Don’t forget to set the status of
your branch (new, experimental, development, mature, ...) so that contributors knows what they can use or not.
• Once your code is good enough, you propose your branch for merging
The extra-addons branch, that stores all extra modules, is directly accessible to all commiters. If you are a commiter,
you can work directly on this branch and commit your own work. This branch do not require a validation of the quality
team. You should put there your special modules for your own customers.
If you want to propose or develop new modules, we suggest you to create your own branch in the openobject-addons
project and develop within your branch. You can fill in a bug to request that your modules are integrated in one of the
two branches:
We invite all our partners and contributors to work in that way so that we can easily integrate and share the work done
between the different projects.
We use launchpad on the openobject project to track all bugs and features request related to openerp and openobject.
the bug tracker is available here:
Every contributor can report bug and propose bugfixes for the bugs. The status of the bug is set according to the
correction.
When a particular branch fixes the bug, a commiter (member of the Commiter Team) can set the status to “Fix Com-
mited”. Only commiters have the right to change the status to “Fix Committed.”, after they validated the proposed
patch or branch that fixes the bug.
The Quality Team have a look every day to bugs in the status “Fix Commited”. They check the quality of the code and
merge in the official branch if it’s ok. To limit the work of the quality team, it’s important that only commiters can set
the bug in the status “Fix Commited”. Once quality team finish merging, they change the status to “Fix Released”.
5.1.4 Translation
Translations are managed by the Launchpad Web interface. Here, you’ll find the list of translatable projects.
Please read the FAQ before asking questions.
5.1.5 Blueprints
Blueprint is a lightweight way to manage releases of your software and to track the progress of features and ideas, from
initial concept to implementation. Using Blueprint, you can encourage contributions from right across your project’s
community, while targeting the best ideas to future releases.
Launchpad Blueprint helps you to plan future release with two tools:
Although only drivers can target blueprints to milestones and set them as series goals, anyone can propose a blueprint
as a series goal. As a driver or owner, you can review proposed goals by following the Set goals link on your project’s
Blueprint overview page.
By following the Subscribe yourself link on a blueprint page, you can ask Launchpad to send you email notification
of any changes to the blueprint. In most cases, you’ll receive notification only of changes made to the blueprint itself
in Launchpad and not to any further information, such as in an external wiki.
However, if the wiki software supports email change notifications, Launchpad can even notify you of changes to the
wiki page.
If you’re a blueprint owner and want Launchpad to know about updates to the related wiki page, ask the wiki admin
how to send email notifications. Notifications should go to [email protected].
The Buleprints for OpenERP are listed here:
• https://blueprints.launchpad.net/openerp
• https://blueprints.launchpad.net/~openerp-commiter
Development Environment
Windows
Linux
Mac-OS
Directory Structure
Server
GTK-Client
Web Client
Configuration
The server configuration file .terp_serverrc is used to save server startup options. For the version 5.X configuration file
is .openerp_serverrc. Here is the list of the available options:
You can create your own configuration file by specifying -s or –save on the server command line. If you would like to
write an alternativve configuration file, use -c <config file> or –config=<config file> Here is a basic configuration for
a server:
[options]
verbose = False
xmlrpc = True
database = terp
update = {}
port = 8069
init = {}
interface = 127.0.0.1
reportgz = False
[printer]
path = none
softpath_html = none
preview = True
softpath = none
[logging]
output = stdout
logger =
verbose = True
level = error
[help]
index = http://www.openerp.com/documentation/user-manual/
context = http://www.openerp.com/scripts/context_index.php
[form]
autosave = False
toolbar = True
[support]
recipient = [email protected]
support_id =
[tip]
position = 0
autostart = False
[client]
lang = en_US
default_path = /home/user
filetype = {}
theme = none
toolbar = icons
form_tab_orientation = 0
form_tab = top
[survey]
position = 3
[path]
pixmaps = /usr/share/pixmaps/openerp-client/
share = /usr/share/openerp-client/
[login]
db = eo2
login = admin
protocol = http://
port = 8069
server = localhost
GTK-Client Configuration
login section
path section
tip section
form section
autosave Will the client automatically save the change you made to a record
printer section
logging section
logger log channels to display. List values are: @common@, @common.message@, @view@,
@view.form@, @common.options@, @rpc.request@, @rpc.result@, @rpc.exception@
level logging level to show
output file used by the logger
verbose set the log level to INFO
client section
Default values:
[login]
login = admin
port = 8069
server = 192.168.1.4
[printer]
path = none
preview = True
softpath = none
[logging]
output = stdout
logger =
verbose = True
level = ERROR
[form]
autosave = False
[client]
default_path = /home/user
If you want to get a clone of the extra-addons repository, you can execute this command:
Currently the initialisation procedure of the server parameter –init=all to populate the database seems to be broken in
trunk.
It is recommended to create a new database via the gtk-client. Before that the web-client will not work.
Start OpenERP server like this:
./openerp-server.py --addons-path=/path/to/my/addons
The bin/addons will be considered as default addons directory which can be overriden by the
/path/to/my/addons/. That is if an addon exists in bin/addons as well as /path/to/my/addons (cus-
tom path) the later one will be given preference over the bin/addons (default path).
General Options
Internationalization options:
Use these options to translate Tiny ERP to another language.See i18n section of the user man-
ual. Option ‘-l’ is mandatory.
OpenERP 4.2
First check that all the required dependencies are installed. Then create the terp database. You have to make sure that
your user has the correct credentials to create databases with PostgreSQL. For more information on this subject please
refer to the PostgreSQL manual.:
Once the database created, you can start OpenERP. The content of the database will automatically be created at the
first start.:
$ ./tinyerp-server.py
• Make sure you are logged on as a user that has catalog admin rights in PostgreSQL. Refer to the PostgreSQL
manual for more info on this.
• Start the OpenERP Server
./openerp-server.py
* Finally connect to the server with the GTK Client or eTiny and use the Create Database option to c
The easiest way the shut down the server on a Linux type system is to send the SIG INT signal to the server.
5.2 Configuration
Two configuration files are available:
The server configuration file .terp_serverrc is used to save server startup options. For the version 5.X configuration file
is .openerp_serverrc. Here is the list of the available options:
You can create your own configuration file by specifying -s or –save on the server command line. If you would like to
write an alternativve configuration file, use -c <config file> or –config=<config file> Here is a basic configuration for
a server:
[options]
verbose = False
xmlrpc = True
database = terp
update = {}
port = 8069
init = {}
interface = 127.0.0.1
reportgz = False
[printer]
path = none
softpath_html = none
preview = True
softpath = none
[logging]
output = stdout
logger =
verbose = True
level = error
[help]
index = http://www.openerp.com/documentation/user-manual/
context = http://www.openerp.com/scripts/context_index.php
5.2. Configuration 29
Open Object Developer Book, Release 1.0
[form]
autosave = False
toolbar = True
[support]
recipient = [email protected]
support_id =
[tip]
position = 0
autostart = False
[client]
lang = en_US
default_path = /home/user
filetype = {}
theme = none
toolbar = icons
form_tab_orientation = 0
form_tab = top
[survey]
position = 3
[path]
pixmaps = /usr/share/pixmaps/openerp-client/
share = /usr/share/openerp-client/
[login]
db = eo2
login = admin
protocol = http://
port = 8069
server = localhost
login section
path section
tip section
form section
autosave Will the client automatically save the change you made to a record
printer section
logging section
logger log channels to display. List values are: @common@, @common.message@, @view@,
@view.form@, @common.options@, @rpc.request@, @rpc.result@, @rpc.exception@
level logging level to show
output file used by the logger
verbose set the log level to INFO
client section
Default values:
[login]
login = admin
port = 8069
server = 192.168.1.4
[printer]
path = none
preview = True
softpath = none
5.2. Configuration 31
Open Object Developer Book, Release 1.0
[logging]
output = stdout
logger =
verbose = True
level = ERROR
[form]
autosave = False
[client]
default_path = /home/user
If you want to get a clone of the extra-addons repository, you can execute this command:
Currently the initialisation procedure of the server parameter –init=all to populate the database seems to be broken in
trunk.
It is recommended to create a new database via the gtk-client. Before that the web-client will not work.
Start OpenERP server like this:
./openerp-server.py --addons-path=/path/to/my/addons
The bin/addons will be considered as default addons directory which can be overriden by the
/path/to/my/addons/. That is if an addon exists in bin/addons as well as /path/to/my/addons (cus-
tom path) the later one will be given preference over the bin/addons (default path).
Use these options to translate Tiny ERP to another language.See i18n section of the user man-
ual. Option ‘-l’ is mandatory.
First check that all the required dependencies are installed. Then create the terp database. You have to make sure that
your user has the correct credentials to create databases with PostgreSQL. For more information on this subject please
refer to the PostgreSQL manual.:
Once the database created, you can start OpenERP. The content of the database will automatically be created at the
first start.:
$ ./tinyerp-server.py
./openerp-server.py
* Finally connect to the server with the GTK Client or eTiny and use the Create Database option to c
SIX
• OpenERP uses XML for several purpose: describing data, view, reports, data transport (XML-RPC)
Server/client, XML-RPC
Open ERP is a based on a client/server architecture. The server and the client communicate using the XML-RPC
protocol. XML-RPC is a very simple protocol which allows the client to do remote procedure calls. The function
called, its arguments, and the result are sent HTTP and encoded using XML.
For more information on XML-RPC, please see: http://www.xml-rpc.com/
Since version 4.2, there is a new protocol between client/server that have been called net-rpc. It is based on the python
cPickle function, it is faster than the xml-rpc.
Client
The logic of Open ERP is entirely on the server side. The client is very simple; his work is to ask data (forms, lists,
trees) from the server and to send them back. With this approach, nearly all developments are made on the server side.
This makes Open ERP easier to develop and to maintain.
The client doesn’t understand what it posts. Even actions like ‘Click on the print icon’ are sent to the server to ask
how to react.
The client operation is very simple; when a user makes an action (save a form, open a menu, print, ...) it sends this
action to the server. The server then sends the new action to execute to the client.
35
Open Object Developer Book, Release 1.0
• Execute a wizard
Architecture
Server - Web-Services
The web-service module offer a common interface for all web-services
• SOAP
• XML-RPC
• NET-RPC
Business objects can also be accessed via the distributed object mechanism. They can all be modified via the client
interface with contextual views. Server - Workflow Engine
Workflows are graphs represented by business objects that describe the dynamics of the company. Workflows are also
used to track processes that evolve over time.
• Custom reports: those reports can be directly created via the client interface, no programming required. Those
reports are represented by business objects (ir.report.custom)
• High quality personalized reports using openreport: no programming required but you have to write 2 small XML files: –
a template which indicates the data you plan to report
– an XSL:RML stylesheet
• Hard coded reports
• OpenOffice Writer templates
• entries
• textboxes
• floating point numbers
• dates (with calendar)
• checkboxes
• ...
Widget have different appearances in different views. For example, the date widget in the search dialog represents two
normal dates for a range of date (from...to...).
Some widgets may have different representations depending on the context. For example, the one2many widget can
be represented as a form with multiple pages or a multi-columns list.
Events on the widgets module are processed with a callback mechanism. A callback mechanism is a process whereby
an element defines the type of events he can handle and which methods should be called when this event is triggered.
Once the event is triggered, the system knows that the event is bound to a specific method, and calls that method back.
Hence callback.
{
"name" : "Open TERP Accounting",
"version" : "1.0",
"author" : "Bob Gates - Not So Tiny",
"website" : "http://www.openerp.com/",
"category" : "Generic Modules/Others",
"depends" : ["base"],
"description" : """A
Multiline
Description
""",
"init_xml" : ["account_workflow.xml", "account_data.xml", "account_demo.xml"],
"demo_xml" : ["account_demo.xml"],
"update_xml" : ["account_view.xml", "account_report.xml", "account_wizard.xml"],
"active": False,
"installable": True
}
When initializing a module, the files in the init_xml list are evaluated in turn and then the files in the update_xml list
are evaluated. When updating a module, only the files from the update_xml list are evaluated.
6.3 Inheritance
Introduction
Objects may be inherited in some custom or specific modules. It is better to inherit an object to add/modify some
fields.
It is done with:
_inherit=’object.name’
Extension of an object
There are two possible ways to do this kind of inheritance. Both ways result in a new class of data, which holds parent
fields and behaviour as well as additional fielda and behaviour, but they differ in heavy programatical consequences.
While Example 1 creates a new subclass “custom_material” that may be “seen” or “used” by any view or tree which
handles “network.material”, this will not be the case for Example 2.
This is due to the table (other.material) the new subclass is operating on, which will never be recognized by previous
“network.material” views or trees.
Example 1:
class custom_material(osv.osv):
_name = ’network.material’
_inherit = ’network.material’
_columns = {
’manuf_warranty’: fields.boolean(’Manufacturer warranty?’),
}
_defaults = {
’manuf_warranty’: lambda *a: False,
}
custom_material()
Tip: Notice
_name == _inherit
In this example, the ‘custom_material’ will add a new field ‘manuf_warranty’ to the object ‘network.material’. New
instances of this class will be visible by views or trees operating on the superclasses table ‘network.material’.
This inheritancy is usually called “class inheritance” in Object oriented design. The child inherits data (fields) and
behavior (functions) of his parent.
Example 2:
class other_material(osv.osv):
_name = ’other.material’
_inherit = ’network.material’
_columns = {
’manuf_warranty’: fields.boolean(’Manufacturer warranty?’),
}
_defaults = {
’manuf_warranty’: lambda *a: False,
}
other_material()
Tip: Notice
_name != _inherit
In this example, the ‘other_material’ will hold all fields specified by ‘network.material’ and it will additionally hold a
new field ‘manuf_warranty’. All those fields will be part of the table ‘other.material’. New instances of this class will
therefore never been seen by views or trees operating on the superclasses table ‘network.material’.
This type of inheritancy is known as “inheritance by prototyping” (e.g. Javascript), because the newly created subclass
“copies” all fields from the specified superclass (prototype). The child inherits data (fields) and behavior (functions)
of his parent.
6.3. Inheritance 39
Open Object Developer Book, Release 1.0
Syntax ::
class tiny_object(osv.osv)
_name = ’tiny.object’
_table = ’tiny_object’
_inherits = { ’tiny.object’_1_ : name_col’_1_’, ’tiny.object’_2_ : name_col’_2_’, ..., ’tiny
(...)
The object ‘tiny.object’ inherits from all the columns and all the methods from the n objects ‘tiny.object’_1_, ...,
‘tiny.object’_n_.
To inherit from multiple tables, the technique consists in adding one column to the table tiny_object per inherited
object. This column will store a foreign key (an id from another table). The values name_col’_1_’ name_col’_2_’ ...
name_col’_n_’ are of type string and determine the title of the columns in which the foreign keys from ‘tiny.object’_1_,
..., ‘tiny.object’_n_ are stored.
This inheritance mechanism is usually called ” instance inheritance ” or ” value inheritance “. A resource (instance)
has the VALUES of its parents.
SEVEN
For example, in the diagram above, the solid lines for the arrows starting from the controller and going to both the
view and the model mean that the controller has a complete access to both the view and the model. The dashed line for
the arrow going from the view to the controller means that the view has a limited access to the controller. The reasons
of this design are :
• From View to Model : the model sends notification to the view when its data has been modified in order the
view to redraw its content. The model doesn’t need to know the inner workings of the view to perform this
operation. However, the view needs to access the internal parts of the controller.
• From View to Controller : the reason why the view has limited access to the controller is because the depen-
dencies from the view to the controller need to be minimal: the controller can be replaced at any moment.
41
Open Object Developer Book, Release 1.0
7.2 MVCSQL
7.2.1 Example 1
Suppose sale is a variable on a record of the sale.order object related to the ‘sale_order’ table. You can acquire such a
variable doing this.:
(where cr is the current row, from the database cursor, uid is the current user’s ID for security checks, and ID is the
sale order’s ID or list of IDs if we want more than one)
Suppose you want to get: the country name of the first contact of a partner related to the ID sale order. You can do the
following in Open ERP:
country_name = sale.partner_id.address[0].country_id.name
If you want to write the same thing in traditional SQL development, it will be in python: (we suppose cr is the cursor
on the database, with psycopg)
Of course you can do better if you develop smartly in SQL, using joins or subqueries. But you have to be smart and
most of the time you will not be able to make such improvements:
The first operation as an object call is much better for several reasons:
• It uses objects facilities and works with modules inheritances, overload, ...
• It’s simpler, more explicit and uses less code
• It’s much more efficient as you will see in the following examples
• Some fields do not directly correspond to a SQL field (e.g.: function fields in Python)
Suppose that later in the code, in another function, you want to access the name of the partner associated to your sale
order. You can use this:
partner_name = sale.partner_id.name
And this will not generate any SQL query as it has been prefetched by the object relational mapping engine of Open
ERP.
Suppose now that you want to compute the totals of 10 sales order by countries. You can do this in Open ERP within
a Open ERP object:
And, to print them as a good way, you can add this on your object:
The 2 functions will generate 4 SQL queries in total ! This is due to the SQL engine of Open ERP that does prefetching,
works on lists and uses caching methods. The 3 queries are:
That’s great because if you run this code on 1000 sales orders, you have the guarantee to only have 4 SQL queries.
Notes:
• If you run this code on 5000 sales orders, you may have 8 SQL queries because as SQL queries are not allowed
to take too much memory, it may have to do two separate readings.
7.2. MVCSQL 43
Open Object Developer Book, Release 1.0
Here is a complete example, from the Open ERP official distribution, of the function that does bill of material explosion
and computation of associated routings:
class mrp_bom(osv.osv):
...
def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
bom_result = False
# Why searching on ‘BoM without parent ?
cr.execute(’select id from mrp_bom where product_id=%d and bom_id is null
order by sequence’, (product_id,))
ids = map(lambda x: x[0], cr.fetchall())
max_prop = 0
result = False
for bom in self.pool.get(’mrp.bom’).browse(cr, uid, ids):
prop = 0
for prop_id in bom.property_ids:
if prop_id.id in properties:
prop+=1
if (prop>max_prop) or ((max_prop==0) and not result):
result = bom.id
return result
’cycle’: cycle,
’hour’: wc_use.hour_nbr + (
wc.time_start+wc.time_stop+cycle*wc.time_cycle) *
(wc.time_efficiency or 1
})
for bom2 in bom.bom_lines:
res = self._bom_explode(cr, uid, bom2, factor, properties,
addthis=True, level=level+10)
result = result + res[0]
result2 = result2 + res[1]
return result, result2
7.2. MVCSQL 45
Open Object Developer Book, Release 1.0
47
CHAPTER
EIGHT
Open ERP is a Python based client/server program for Enterprise Resource Planning. It consist of a client “tinyerp-
client” and a server “tinyerp-server” while the persistence is provided by Postgresql. Open ERP currently uses XML-
RPC for communication over a network. Once installed Open ERP has a modular structure that allows modules to be
added as needed.
• base : The most basic module. Defines ir.property, res.company, res.request, res.currency, res.user, res.partner
• crm : Customer & Supplier Relationship Management.
New modules can be programed easily, and require a little practice of XML and Python.
The Modules
1. Introduction
2. Files & Directories (a) __terp__.py
(b) __init__.py
(c) XML Files i. Actions
ii. Menu Entries
iii. Reports
iv. Wizards
3. Profiles
49
Open Object Developer Book, Release 1.0
• workflows declaration.
General structure of Tiny ERP XML files is more detailed in the section Data Loading Files XML. Look here if you are
interested in learning more about initialization and demonstration data declaration XML files. The following section
are only related to XML specific to actions, menu entries, reports, wizards and workflows declaration.
import module
In the created module directory, you must add a __terp__.py file. This file, which must be in Python format, is
responsible to
1. determine the XML files that will be parsed during the initialization of the server, and also to
This file must contain a Python dictionary with the following values:
name
version
description
author
website
license
depends
List of modules on which this module depends. The base module must almost always be in the dependen-
cies because some necessary data for the views, reports, ... are in the base module.
init_xml
List of .xml files to load when the server is launched with the “–init=module” argument. Filepaths must
be relative to the directory where the module is. Open ERP XML File Format is detailed in this section.
update_xml
List of .xml files to load when the server is launched with the “–update=module” launched. Filepaths must
be relative to the directory where the module is. Open ERP XML File Format is detailed in this section.
installable
active
True or False (default: False). Determines the modules that are installed on the database creation.
Example
Here is an example of __terp__.py file for the product module:
{
"name" : "Products & Pricelists",
"version" : "1.0",
"author" : "Open",
"category" : "Generic Modules/Inventory Control",
"depends" : ["base", "account"],
"init_xml" : [],
"demo_xml" : ["product_demo.xml"],
"update_xml" : ["product_data.xml","product_report.xml", "product_wizard.xml","product_view.xml"
"installable": True,
"active": True
}
The files that must be placed in init_xml are the ones that relate to the workflow definition, data to load at the installa-
tion of the software and the data for the demonstrations.
The files in update_xml concern: views, reports and wizards.
Objects
All Tiny ERP resources are objects: menus, actions, reports, invoices, partners, ... Tiny ERP is based on an ob-
ject relational mapping of a database to control the information. Object names are hierarchical, as in the following
examples:
Generally, the first word is the name of the module: account, stock, sale.
Other advantages of an ORM;
It is easier to manipulate one object (example, a partner) than several tables (partner address, categories, events, ...)
PostgreSQL The ORM of Open ERP is constructed over PostgreSQL. It is thus possible to query the object used by
Open ERP using the object interface or by directly using SQL statements.
But it is dangerous to write or read directly in the PostgreSQL database, as you will shortcut important steps like
constraints checking or workflow modification.
Note: The Physical Database Model of OpenERP
Pre-Installed Data
%define=lightblue color=#27adfb%
Data can be inserted or updated into the PostgreSQL tables corresponding to the Tiny ERP objects using XML files.
The general structure of a Tiny ERP XML file is as follows:
<?xml version="1.0"?>
<terp>
<data>
<record model="model.name_1" id="id_name_1">
<field name="field1">
%lightblue%"field1 content"
</field>
<field name="field2">
%lightblue%"field2 content"
</field>
(...)
</record>
<record model="model.name_2" id="id_name_2">
(...)
</record>
(...)
</data>
</terp>
Fields content are strings that must be encoded as UTF-8 in XML files.
Let’s review an example taken from the TinyERP source (base_demo.xml in the base module):
->The field @@company_id@@ is a many-to-one relation from the user object to the company object, and
main_company is the id of to associate.
• The eval attribute allows to put some python code in the xml: here the groups_id field is a many2many. For
such a field, “[(6,0,[group_admin])]” means : Remove all the groups associated with the current user and use
the list [group_admin] as the new associated groups (and group_admin is the id of another record).
• The search attribute allows to find the record to associate when you do not know its xml id. You can thus specify
a search criteria to find the wanted record. The criteria is a list of tuples of the same form than for the predefined
search method. If there are several results, an arbitrary one will be chosen (the first one):
->This is a classical example of the use of @@search@@ in demo data: here we do not really care about which
partner we want to use for the test, so we give an empty list. Notice the model attribute is currently mandatory.
field tag
The attributes for the field tag are the following:
function tag
• model:
• name:
• eval o should evaluate to the list of parameters of the method to be called, excluding cr and uid
Example
getitem tag
Takes a subset of the evaluation of the last child node of the tag.
Example
Evaluates to the first element of the list of ids returned by the function node
i18n
Improving Translations
Translating in launchpad
Translations are managed by the Launchpad Web interface. Here, you’ll find the list of translatable projects.
Please read the FAQ before asking questions.
Translating your own module
Changed in version 5.0. Contrary to the 4.2.x version, the translations are now done by module. So, instead of an
unique i18n folder for the whole application, each module has its own i18n folder. In addition, OpenERP can now
deal with .po 1 files as import/export format. The translation files of the installed languages are automatically loaded
when installing or updating a module. OpenERP can also generate a .tgz archive containing well organised .po files
for each selected module.
Process
Defining the process Thourgh the interface and module recorder Then, put the generated XML in your own module
Views
(:title Technical Specifications - Architecture - Views:) Views are a way to represent the objects on the client side.
They indicate to the client how to lay out the data coming from the objects on the screen.
There are two types of views:
• form views
• tree views
Usage example When you open an invoice, here is the chain of operations followed by the client:
• An action asks to open the invoice (it gives the object’s data (account.invoice), the view, the domain (e.g. only
unpaid invoices) ).
• The client asks (with XML-RPC) to the server what views are defined for the invoice object and what are the
data it must show.
• The client displays the form according to the view
1 http://www.gnu.org/software/autoconf/manual/gettext/PO-Files.html#PO-Files
To develop new objects The design of new objects is restricted to the minimum: create the objects and optionally
create the views to represent them. The PostgreSQL tables do not have to be written by hand because the objects are
able to automatically create them (or adapt them in case they already exist).
Reports
Open ERP uses a flexible and powerful reporting system. Reports are generated either in PDF or in HTML. Reports
are designed on the principle of separation between the data layer and the presentation layer.
Reports are described more in details in the Reporting chapter.
Wizards
<?xml version="1.0"?>
<terp>
<data>
<wizard string="Employee Info"
model="hr.employee"
name="employee.info.wizard"
id="wizard_employee_info"/>
</data>
</terp>
A wizard is declared using a wizard tag. See “Add A New Wizard” for more information about wizard XML.
also you can add wizard in menu using following xml entry
<?xml version="1.0"?>
<terp>
<data>
<wizard string="Employee Info"
model="hr.employee"
name="employee.info.wizard"
id="wizard_employee_info"/>
<menuitem
name="Human Resource/Employee Info"
action="wizard_employee_info"
type="wizard"
id="menu_wizard_employee_info"/>
</data>
</terp>
Workflow
The objects and the views allow you to define new forms very simply, lists/trees and interactions between them. But it
is not enough : you have to define the dynamics of these objects.
A few examples:
The workflows describe these interactions with graphs. One or several workflows may be associated to the objects.
Workflows are not mandatory; some objects don’t have workflows.
Below is an example workflow used for sale orders. It must generate invoices and shipments according to certain
conditions.
• create an invoice,
• cancel the sale order,
• generate the shipping order, ...
• invoice paid,
• click on the cancel button, ...
• the invoice
• the shipping
Normal Module
In the created module directory, you must add a __terp__.py file. This file, which must be in Python format, is
responsible to
1. determine the XML files that will be parsed during the initialization of the server, and also to
2. determine the dependencies of the created module.
This file must contain a Python dictionary with the following values:
name
version
description
author
website
license
depends
List of modules on which this module depends. The base module must almost always be in the dependen-
cies because some necessary data for the views, reports, ... are in the base module.
init_xml
List of .xml files to load when the server is launched with the “–init=module” argument. Filepaths must
be relative to the directory where the module is. Open ERP XML File Format is detailed in this section.
update_xml
List of .xml files to load when the server is launched with the “–update=module” launched. Filepaths must
be relative to the directory where the module is. Open ERP XML File Format is detailed in this section.
installable
active
True or False (default: False). Determines the modules that are installed on the database creation.
Example
{
"name" : "Products & Pricelists",
"version" : "1.0",
"author" : "Open",
"category" : "Generic Modules/Inventory Control",
"depends" : ["base", "account"],
"init_xml" : [],
"demo_xml" : ["product_demo.xml"],
"update_xml" : ["product_data.xml","product_report.xml", "product_wizard.xml","product_view.xml"
"installable": True,
"active": True
}
The files that must be placed in init_xml are the ones that relate to the workflow definition, data to load at the installa-
tion of the software and the data for the demonstrations.
The files in update_xml concern: views, reports and wizards.
Profile Module
The purpose of a profile is to initialize Open ERP with a set of modules directly after the database has been created.
A profile is a special kind of module that contains no code, only dependencies on other modules.
In order to create a profile, you only have to create a new directory in server/addons (you should call this folder
profile_modulename), in which you put an empty __init__.py file (as every directory Python imports must contain an
__init__.py file), and a __terp__.py whose structure is as follows :
{
"name":"’’Name of the Profile’’,
"version":"’’Version String’’",
"author":"’’Author Name’’",
"category":"Profile",
"depends":[’’List of the modules to install with the profile’’],
"demo_xml":[],
"update_xml":[],
"active":False,
"installable":True,
}
Example
Here’s the code of the file server/bin/addons/profile_manufacturing/__terp__.py, which corresponds to the manufac-
turing industry profile in Open ERP.
{
"name":"Manufacturing industry profile",
"version":"1.0",
"author":"Open",
"category":"Profile",
"depends":["mrp", "crm", "sale", "delivery"],
"demo_xml":[],
"update_xml":[],
"active":False,
"installable":True,
}
Creating a new module is quickly done by copying the module called “simple” or “custom” (depending on your
OpenERP version) into a new directory.
As an example on Ubuntu:
$ cd /usr/lib/tinyerp-server/addons/
$ sudo cp -r custom travel
You will need to give yourself permissions over that new directory if you want to be able to modify it:
You got yourself the directory for a new module there, and a skeleton structure, but you still need to change a few
things inside the module’s definition...
To change the default settings of the custom module (which is now the “travel” module), get yourself into the “travel”
directory and edit __terp__.py. gedit, in the following example, is just a simple text editor. Feel free to use another
one.
$ cd travel
$ gedit __terp__.py
#
# Use the custom module to put your specific code in a separate module.
#
{
"name" : "Module for custom developments",
"version" : "1.0",
"author" : "Tiny",
"category" : "Generic Modules/Others",
"website": "http://www.tinyerp.com",
"description": "Sample custom module where you can put your customer specific developments."
"depends" : ["base"],
"init_xml" : [],
"update_xml" : ["custom_view.xml"],
"active": False,
"installable": True
}
You will want to change whichever settings you feel right and get something like this:
{
"name" : "Travel agency module",
"version" : "1.0",
"author" : "Tiny",
"category" : "Generic Modules/Others",
"website": "http://www.tinyerp.com",
"description": "A module to manage hotel bookings and a few other useful features.",
"depends" : ["base"],
"init_xml" : [],
"update_xml" : ["custom_view.xml"],
"active": True,
"installable": True
}
Now you need to update the custom.py script to suit the needs of your module. We suggest you follow the Flash
tutorial for this or download the travel agency module from the 20 minutes tutorial page.
The documentation below is overlapping the two next step in this wiki tutorial,
so just consider them as a help and head towards the next two pages first...
The custom.py file should initially look like this (intentionally removing the comments):
The ‘#’ signs represent comments. You’ll have to remove them, rename the class and its attributes to something like
this:
class travel_hostel(osv.osv):
_name = ’travel.hostel’
_inherit = ’res.partner’
_columns = {
’rooms_id’: fields.one2many(’travel.room’, ’hostel_id’, ’Rooms’),
’quality’: fields.char(’Quality’, size=16),
}
_defaults = {
}
travel_hostel()
Ideally, you would copy that bunch of code several times to create all the entities you need (travel_airport, travel_room,
travel_flight). This is what will hold the database structure of your objects, but you don’t really need to worry too much
about the database side. Just filling this file will create the system structure for you when you install the module.
You can now move on to editing the views. To do this, edit the custom_view.xml file. It should first look like this:
<terp>
<data>
<record model="res.groups" id="group_compta_user">
<field name="name">grcompta</field>
</record>
<record model="res.groups" id="group_compta_admin">
<field name="name">grcomptaadmin</field>
</record>
<menuitem name="Administration" groups="admin,grcomptaadmin" icon="terp-stock" id="menu_admi
</data>
</terp>
This is, as you can see, an example taken from an accounting system (French people call accounting “comptabilité”,
which explains the compta bit).
Defining a view is defining the interfaces the user will get when accessing your module. Just defining a bunch of fields
here should already get you started on a complete interface. However, due to the complexity of doing it right, we
recommend, once again, that you take a look at the 20 minutes Flash tutorial or download the travel agency module
example.
Next you should be able to create different views using other files to separate them from your basic/admin view.
If you double click on a journal/period (object: account.journal.period), this will open the selected wizard.
(id=”action_move_journal_line_form_select”).
You can use a res_id field to allow this action only if the user click on a specific object.
The action will be triggered if the user clicks on the account.journal.period n°3.
When you declare wizard, report or menus, the ir.values creation is automatically made with these tags:
• <wizard... />
• <menuitem... />
• <report... />
NINE
9.1.1 Introduction
All the ERP’s pieces of data are accessible through “objects”. As an example, there is a res.partner object to access
the data concerning the partners, an account.invoice object for the data concerning the invoices, etc...
Please note that there is an object for every type of resource, and not an object per resource. We have thus a res.partner
object to manage all the partners and not a @@res.partner@@ object per partner. If we talk in “object oriented” terms,
we could also say that there is an object per level.
The direct consequences is that all the methods of objects have a common parameter: the “ids” parameter. This
specifies on which resources (for example, on which partner) the method must be applied. Precisely, this parameter
contains a list of resource ids on which the method must be applied.
For example, if we have two partners with the identifiers 1 and 5, and we want to call the res_partner method
“send_email”, we will write something like:
We will see the exact syntax of object method calls further in this document.
In the following section, we will see how to define a new object. Then, we will check out the different methods of
doing this.
For developers:
• Open ERP “objects” are usually called classes in object oriented programming.
• A Open ERP “resource” is usually called an object in OO programming, instance of a class.
It’s a bit confusing when you try to program inside Open ERP, because the language used is Python, and Python is a
fully object oriented language, and has objects and instances ...
Luckily, an Open ERP “resource” can be converted magically into a nice Python object using the “browse” class
method (Open ERP object method).
67
Open Object Developer Book, Release 1.0
To define a new object, you have to define a new Python class then instantiate it. This class must inherit from the osv
class in the osv module.
The first line of the object definition will always be of the form:
class name_of_the_object(osv.osv):
_name = ’name.of.the.object’
_columns = { ... }
...
name_of_the_object()
An object is defined by declaring some fields with predefined names in the class. Two of them are required (_name
and _columns), the rest is optional. The predefined fields are:
_auto
Determines whether a corresponding PostgreSQL table must be generated automatically from the object. Setting
_auto to False can be useful in case of Open ERP objects generated from PostgreSQL views. See the “Reporting From
PostgreSQL Views” section for more details.
_columns (required)
_constraints
The constraints on the object. See the constraints section for details.
_sql_constraints
The SQL Constraint on the object. See theconstraints SQL section for more details.
_defaults
The default values for some of the object’s fields. See the default value section for details.
_inherit
The name of the osv object which the current object inherits from. See the object inheritance section (first form) for
details.
_inherits
The list of osv objects the object inherits from. This list must be given in a python dictionary of the form:
{‘name_of_the_parent_object’: ‘name_of_the_field’, ...}. See the object inheritance section (second form) for de-
tails. Default value: {}.
_log_access
Determines whether or not the write access to the resource must be logged. If true, four fields will be created in the
SQL table: create_uid, create_date, write_uid, write_date. Those fields represent respectively the id of the user who
created the record, the creation date of record, the id of the user who last modified the record, and the date of that last
modification. This data may be obtained by using the perm_read method.
_name (required)
_order
Name of the fields used to sort the results of the search and read methods.
Default value: ‘id’.
Examples:
_order = "name"
_order = "date_order desc"
_rec_name
Name of the field in which the name of every resource is stored. Default value: ‘name’. Note: by default, the name_get
method simply returns the content of this field.
_sequence
Name of the SQL sequence that manages the ids for this object. Default value: None.
_sql
SQL code executed upon creation of the object (only if _auto is True)
_table
Name of the SQL table. Default value: the value of the _name field above with the dots ( . ) replaced by underscores
( _ ).
9.4.1 Introduction
Objects may be inherited in some custom or specific modules. It is better to inherit an object to add/modify some
fields.
It is done with:
_inherit=’object.name’
There are two possible ways to do this kind of inheritance. Both ways result in a new class of data, which holds parent
fields and behaviour as well as additional fielda and behaviour, but they differ in heavy programatical consequences.
While Example 1 creates a new subclass “custom_material” that may be “seen” or “used” by any view or tree which
handles “network.material”, this will not be the case for Example 2.
This is due to the table (other.material) the new subclass is operating on, which will never be recognized by previous
“network.material” views or trees.
Example 1:
class custom_material(osv.osv):
_name = ’network.material’
_inherit = ’network.material’
_columns = {
’manuf_warranty’: fields.boolean(’Manufacturer warranty?’),
}
_defaults = {
’manuf_warranty’: lambda *a: False,
}
custom_material()
Tip: Notice
_name == _inherit
In this example, the ‘custom_material’ will add a new field ‘manuf_warranty’ to the object ‘network.material’. New
instances of this class will be visible by views or trees operating on the superclasses table ‘network.material’.
This inheritancy is usually called “class inheritance” in Object oriented design. The child inherits data (fields) and
behavior (functions) of his parent.
Example 2:
class other_material(osv.osv):
_name = ’other.material’
_inherit = ’network.material’
_columns = {
’manuf_warranty’: fields.boolean(’Manufacturer warranty?’),
}
_defaults = {
’manuf_warranty’: lambda *a: False,
}
other_material()
Tip: Notice
_name != _inherit
In this example, the ‘other_material’ will hold all fields specified by ‘network.material’ and it will additionally hold a
new field ‘manuf_warranty’. All those fields will be part of the table ‘other.material’. New instances of this class will
therefore never been seen by views or trees operating on the superclasses table ‘network.material’.
This type of inheritancy is known as “inheritance by prototyping” (e.g. Javascript), because the newly created subclass
“copies” all fields from the specified superclass (prototype). The child inherits data (fields) and behavior (functions)
of his parent.
Syntax ::
class tiny_object(osv.osv)
_name = ’tiny.object’
_table = ’tiny_object’
_inherits = { ’tiny.object’_1_ : name_col’_1_’, ’tiny.object’_2_ : name_col’_2_’, ..., ’tiny
(...)
The object ‘tiny.object’ inherits from all the columns and all the methods from the n objects ‘tiny.object’_1_, ...,
‘tiny.object’_n_.
To inherit from multiple tables, the technique consists in adding one column to the table tiny_object per inherited
object. This column will store a foreign key (an id from another table). The values name_col’_1_’ name_col’_2_’ ...
name_col’_n_’ are of type string and determine the title of the columns in which the foreign keys from ‘tiny.object’_1_,
..., ‘tiny.object’_n_ are stored.
This inheritance mechanism is usually called ” instance inheritance ” or ” value inheritance “. A resource (instance)
has the VALUES of its parents.
boolean
Syntax:
integer
An integer.
Syntax:
float
Syntax:
Note: The optional parameter digits defines the precision and scale of the number. The scale being the number of dig-
its after the decimal point whereas the precision is the total number of significant digits in the number (before and after
the decimal point). If the parameter digits is not present, the number will be a double precision floating point number.
Warning: these floating-point numbers are inexact (not any value can be converted to its binary representation) and
this can lead to rounding errors. You should always use the digits parameter for monetary amounts.
Example
‘rate’ : fields.float(‘Relative Change rate’, digits=(12,6) [, Optional Parameters]),
char
A string of limited length. The required size parameter determines its size.
Syntax:
Example
‘city’ : fields.char(‘City Name’, size=30, required=True),
text
Syntax:
date
A date.
Syntax:
datetime
Allows to store a date and the time of day in the same field.
Syntax:
binary
A binary chain
selection
A field which allows the user to make a selection between various predefined values.
Syntax:
fields.selection(((’n’,’Unconfirmed’), (’c’,’Confirmed’)),
’Field Name’ [, Optional Parameters]),
Note: Format of the selection parameter: tuple of tuples of strings of the form:
Example
Using relation fields many2one with selection. In fields definitions add:
...,
’my_field’: fields.many2one(’mymodule.relation.model’, ’Title’, selection=_sel_func),
...,
And then define the _sel_func like this (but before the fields definitions):
one2one
A one2one field expresses a one:to:one relation between two objects. It is deprecated. Use many2one instead.
syntax:
many2one
Associates this object to a parent object via this Field. For example Department an Employee belongs to would Many
to one. i.e Many employees will belong to a Department
syntax:
• Optional parameters: – ondelete: What should happen when the resource this field points to is deleted. *
Predefined value: “cascade”, “set null”
* Default value: “set null”
– required: True
– readonly: True
– select: True - (creates an index on the Foreign Key field)
Example
‘commercial’: fields.many2one(‘res.users’, ‘Commercial’, ondelete=’cascade’),
one2many
TODO
syntax:
Example
‘address’: fields.one2many(‘res.partner.address’, ‘partner_id’, ‘Contacts’),
many2many
TODO
syntax:
fields.many2many(’other.object.name’,
’relation object’,
’other.object.id’,
’actual.object.id’,
’Field Name’)
Example:
’category_id’:
fields.many2many(
’res.partner.category’,
’res_partner_category_rel’,
’partner_id’,
’category_id’,
’Categories’),
related
Sometimes you need to refer the relation of a relation. For example, supposing you have objects: City <- State <-
Country, and you need to refer Country in a City, you can define a field as below in the City object:
Functional Field
A functional field is a field whose value is calculated by a function (rather than being stored in the database).
Parameters: fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type=”%green%float%black%”,
fnct_search=None, obj=None, method=False, store=True
where
• type is the field type name returned by the function. It can be any field type name except function.
Either way, it must return a dictionary of values of the form {id’_1_’: value’_1_’, id’_2_’: value’_2_’,...}.
The values of the returned dictionary must be of the type specified by the type argument in the field declaration.
• fnct_inv is the function or method that will allow writing values in that field.
The return value is a list countaining 3-part tuplets which are used in search funtion:
return [(’id’,’in’,[1,3,5])]
class hr_contract(osv.osv):
_name = ’hr.contract’
_description = ’Contract’
_columns = {
’name’ : fields.char(’Contract Name’, size=30, required=True),
’employee_id’ : fields.many2one(’hr.employee’, ’Employee’, required=True),
’function’ : fields.many2one(’res.partner.function’, ’Function’),
}
hr_contract()
If we want to add a field that retrieves the function of an employee by looking its current contract, we use a functional
field. The object hr_employee is inherited this way:
class hr_employee(osv.osv):
_name = "hr.employee"
_description = "Employee"
_inherit = "hr.employee"
_columns = {
• type =’many2one’ is because the function field must create a many2one field; function is declared as a many2one
in hr_contract also.
• obj =”res.partner.function” is used to specify that the object to use for the many2one field is res.partner.function.
• We called our method _get_cur_function_id because its role is to return a dictionary whose keys are ids of
employees, and whose corresponding values are ids of the function of those employees. The code of this method
is:
cr.execute(sql_req)
sql_res = cr.dictfetchone()
The id of the function is retrieved using a SQL query. Note that if the query returns no result, the value of
sql_res[’func_id’] will be None. We force the False value in this case value because XML:RPC (communication
between the server and the client) doesn’t allow to transmit this value.
store={...} Enhancement
Property Fields
Declaring a property
A property is a special field: fields.property.
class res_partner(osv.osv):
_name = "res.partner"
_inherit = "res.partner"
_columns = {
’property_product_pricelist’: fields.property(
’product.pricelist’,
type=’many2one’,·
relation=’product.pricelist’,·
string="Sale Pricelist",·
method=True,
view_load=True,
group_name="Pricelists Properties"),
}
Then you have to create the default value in a .XML file for this property:
Tip: if the default value points to a resource from another module, you can use the ref function like this:
<field name=”value” eval=“‘product.pricelist,’+str(ref(‘module.data_id’))”/>
• This is a concept related to the account chart and not to the partner, so it is an account property that is visible on
a partner form. Rights have to be managed on this fields for accountants, these are not the same rights that are
applied to partner objects. So you have specific rights just for this field of the partner form: only accountants
may change the account receivable of a partner.
• This is a multi-company field: the same partner may have different account receivable values depending on the
company the user belongs to. In a multi-company system, there is one account chart per company. The account
receivable of a partner depends on the company it placed the sale order.
• The default account receivable is the same for all partners and is configured from the general property menu (in
administration).
Note: One interesting thing is that properties avoid “spaghetti” code. The account module depends on the partner
(base) module. But you can install the partner (base) module without the accounting module. If you add a field that
points to an account in the partner object, both objects will depend on each other. It’s much more difficult to maintain
and code (for instance, try to remove a table when both tables are pointing to each others.)
class product_product(osv.osv):
_inherit = ’product.product’
_name = ’product.product’
_columns = {
product_product()
This module adds simple fields to the product.product object. We did not use properties because:
• We extend a product, the life_time field is a concept related to a product, not to another object.
• We do not need a right management per field, the different delays are managed by the same people that manage
all products.
Description
• vals: a dictionary of values for every field. This dictionary must use this form:
{‘name_of_the_field’: value, ...}
• context (optional): the actual context dictionary.
Returns: the id of the newly created resource.
Example:
id = pooler.get_pool(cr.dbname).get(’res.partner.event’).create(cr, uid,
{’name’: ’Email sent through mass mailing’,
’partner_id’: partner.id,
’description’: ’The Description for Partner Event’})
search
Description
• args: a list of tuples containing the search criteria. This list must be of the form: [(‘name_of_the_field’, ‘operator’, value)
=, >, <, <=, >=
– IN (sql)
– LIKE, ILIKE (sql)
– child_of
• offset (optional): do not return the “offset” first results.
• limit (optional): maximum number of results to return.
This example will return a list with all the partners that have the category ‘Customer’.
read
Description
Example:
values = pooler.get_pool(cr.dbname).get(’res.partner’).
read(cr, uid, ids, [’name’,’category_id’], context=context)
browse
Description
Return one or several resources with the objects form. These object fields can be reached directly with the pointed
notation (“object.name_of_the_field”). The “relations” fields are also automatically evaluated to allow you to recover
the values in the “neighbors” objects.
Example
Let’s consider the case of a partner (object ‘res.partner’) and of a partner contact (object ‘res.partner.address’). Let’s
suppose that we know the identifier of a partner contact (name contact_id) and we want to recover his name and the
account number of the company he works for.
Knowing that the object res.partner contains the field:
’bank’:fields.char(’Bank account’,size=64),
so, to recover the two fields that interest us, you have to write:
name = addr_obj.name
account_num = addr_obj.partner_id.bank
Note: This method is only useful locally (on the server itself) and not with the other interfaces !!
write
Description
Example:
unlink
Description
Example:
self.pool.get(’sale.order’).unlink(cr,uid, ids)
default_get
Description
Returns: dictionary of the default values of the form {‘field_name’: value, ... }
Example:
default_set
Description
• field: the name of the field that we want to change the value by default.
• value: the value by default.
• for_user (optional): boolean that determines if the new default value must be available only for the
current user or for all users.
Returns: True
Example:
TODO
perm_read
perm_write
Example:
fields_get
Example:
In payment.line in account_payment module
fields_view_get
Example:
In membership module [product.product]:
distinct_field_get
Description Signature: def distinct_field_get(self, cr, uid, field, value, args=[], offset=0, limit=2000)
Parameters:
Result:
Example:
TODO
name_get
Example:
In res.partner.address:
addr += ’, ’
addr += str(r[’zip’] or ’’) + ’ ’ + str(r[’city’] or ’’)
res.append((r[’id’], addr))
return res
name_search
Description Signature: def name_search(self, cr, uid, name=, args=[], operator=’ilike’, context={})
‘Parameters:
Result:
Example:
In res.country:
TEN
Views describe how each object (type of resource) is displayed. More precisely, for each object, we can define one (or
several) view(s) to describe which fields should be drawn and how.
There are two types of views:
1. form views
2. tree views
Note: Since Open ERP 4.1, form views can also contain graphs.
87
Open Object Developer Book, Release 1.0
• Every screen is divided into 4 columns, each column being able to contain either a label, or an “edition” field. As
every edition field is preceded (by default) by a label with its name, there will be two fields (and their respective
labels) on each line of the screen. The green and red zones on the screen-shot below, illustrate those 4 columns.
They designate respectively the labels and their corresponding fields.
• A view field can use several columns. For example, on the screen-shot below, the zone in the blue frame is, in
fact, the only field of a “one to many”. We will come back later on this note, but let’s note that it uses the whole
width of the screen and not only one column.
• We can also make the opposite operation: take a columns group and divide it in as many columns as desired.
The surrounded green zones of the screen above are good examples. Precisely, the green framework up and on
the right side takes the place of two columns, but contains 4 columns.
As we can see below in the purple zone of the screen, there is also a way to distribute the fields of an object on
different tabs.
The different options of those views will be detailed into the next section.
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
Then, the user will be able to switch from one view to the other. Unlike forms and trees, Tiny ERP is not able to
automatically create a view on demand for the graph type. So, you must define a view for this graph:
The default type of the graph is a pie chart - to change it to a barchart change <graph string=”Sales Order Lines”>
to <graph string=”Sales Order Lines” type=”bar”> You also may change the orientation.
:Example :
The first field is the X axis. The second one is the Y axis and the optionnal third one is the Z axis for 3 dimensional
graphs. You can apply a few attributes to each field/axis:
• group: if set to true, the client will group all item of the same value for this field. For each other field, it will
apply an operator
• operator: the operator to apply is another field is grouped. By default it’s ‘+’. Allowed values are: –
+: addition
– *: multiply
– **: exponent
– min: minimum of the list
– max: maximum of the list
You can get en example in all modules of the form: report_.... Example: report_crm.
Example
<?xml version="1.0"?>
<terp>
<data>
[view definitions]
</data>
</terp>
• <record> tags with the attribute model=”ir.ui.view”, which contain the view definitions themselves
• <record> tags with the attribute model=”ir.actions.act_window”, which link actions to these views
• <menuitem> tags, which create entries in the menu, and link them with actions
New : You can precise groups for whom the menu is accessible using the groups attribute in menuitem tag.
New : You can now add shortcut using the shortcut tag.
Example
Note that you should add an id attribute on the menuitem which is refered by menu attribute.
Default value for the priority field : 16. When not specified the system will use the view with the lower priority.
Separator
Example
The string attribute defines its label and the colspan attribute defines his horizontal size (in number of columns).
Notebook
<notebook>: With notebooks you can distribute the view fields on different tabs (each one defined by a page tag). You
can use the tabpos properties to set tab at: up, down, left, right.
Example
<notebook colspan="4">....</notebook>
Group
<group>: groups several columns and split the group in as many columns as desired.
• string: (optional) If set, a frame will be drawn around the group of fields, with a label containing the string.
Otherwise, the frame will be invisible.
Example
Page
Example
Field
• select=”1”: mark this field as being one of the research criteria for this resource search view.
Example
Here’s the source code of the view of a sale order object. This is the same object as the object shown on the screen
shots of the presentation.
Example
<?xml version="1.0"?>
<terp>
<data>
<record id="view_partner_form" model="ir.ui.view">
<field name="name">res.partner.form</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Partners">
<group colspan="4" col="6">
<field name="name" select="1"/>
<field name="ref" select="1"/>
<field name="customer" select="1"/>
<field domain="[(’domain’, ’=’, ’partner’)]" name="title"/>
<field name="lang" select="2"/>
<field name="supplier" select="2"/>
</group>
<notebook colspan="4">
<page string="General">
<field colspan="4" mode="form,tree" name="address"
nolabel="1" select="1">
<form string="Partner Contacts">
<field name="name" select="2"/>
<field domain="[(’domain’, ’=’, ’contact’)]" name="title
<field name="function"/>
<field name="type" select="2"/>
<field name="street" select="2"/>
<field name="street2"/>
<newline/>
<field name="zip" select="2"/>
<field name="city" select="2"/>
<newline/>
<field completion="1" name="country_id" select="2"/>
<field name="state_id" select="2"/>
<newline/>
<field name="phone"/>
<field name="fax"/>
<newline/>
<field name="mobile"/>
<field name="email" select="2" widget="email"/>
</form>
<tree string="Partner Contacts">
<field name="name"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="phone"/>
<field name="email"/>
</tree>
</field>
<separator colspan="4" string="Categories"/>
<field colspan="4" name="category_id" nolabel="1" select="2"/>
</page>
<page string="Sales & Purchases">
<separator string="General Information" colspan="4"/>
<field name="user_id" select="2"/>
<field name="active" select="2"/>
<field name="website" widget="url"/>
<field name="date" select="2"/>
<field name="parent_id"/>
<newline/>
94 </page> Chapter 10. Views and Events
<page string="History">
<field colspan="4" name="events" nolabel="1" widget="one2many_li
</page>
Open Object Developer Book, Release 1.0
Button
<button/>: add a button using the string attribute as label. When clicked, it can trigger methods on the object, workflow
transitions or actions (reports, wizards, ...).
Example
Label
Example
<label string="Test"/>
New Line
Force a return to the line even if all the columns of the view are not filled in.
Example
<newline/>
Example
The inheritance engine will parse the existing view and search for the the root nodes of
It will append or edit the content of this tag. If this tag has some attributes, it will look for the matching node, including
the same attributes (unless position).
This will add a page to the notebook of the res.partner.form view in the base module.
You can use these values in the position attribute:
Second Example
Will replace the content of the Extra Info tab of the notebook by one ‘relation_ids’ field.
The parent and the inherited views are correctly updated with –update=all argument like any other views.
To delete a field from a form, an empty element with position=”replace” atribute is used. Example:
</field>
</record>
Take into account that only one position=”replace” attribute can be used per inherited view so multiple inherited views
must be created to make multiple replacements.
10.7 Events
10.7.1 On Change
The on_change attribute defines a method that is called when the content of a view field has changed.
This method takes at least arguments: cr, uid, ids, which are the three classical arguments and also the context dictio-
nary. You can add parameters to the method. They must correspond to other fields defined in the view, and must also
be defined in the XML with fields defined this way:
v={}
if shop_id:
shop=self.pool.get(’sale.shop’).browse(cr,uid,shop_id)
v[’project_id’]=shop.project_id.id
if shop.pricelist_id.id:
v[’pricelist_id’]=shop.pricelist_id.id
v[’payment_default_id’]=shop.payment_default_id.id
return {’value’:v}
When editing the shop_id form field, the onchange_shop_id method of the sale_order object is called and returns a
dictionary where the ‘value’ key contains a dictionary of the new value to use in the ‘project_id’, ‘pricelist_id’ and
‘payment_default_id’ fields.
Note that it is possible to change more than just the values of fields. For example, it is possible to change the value of
some fields and the domain of other fields by returning a value of the form: return {‘domain’: d, ‘value’: value}
context in <record model=”ir.actions.act_window” id=”a”> you can add a context field, which will be
pass to the action.
10.7. Events 97
Open Object Developer Book, Release 1.0
view_type:
tree = (tree with shortcuts at the left), form = (switchaable view form/list)
view_mode:
Description
Example:
default_set
Description
Returns: True
Example:
TODO
10.7. Events 99
Open Object Developer Book, Release 1.0
ELEVEN
11.1 Menus
Here’s the template of a menu item :
<menuitem id="menuitem_id"
name="Position/Of/The/Menu/Item/In/The/Tree"
action="action_id"
icon="NAME_FROM_LIST"
groups="groupname"
sequence="<integer>"/>
Where
• id specifies the identifier of the menu item in the menu items table. This identifier must be unique. Mandatory
field.
• name defines the position of the menu item in the menu hierarchy. Elements are separated by slashes (“/”). A
menu item name with no slash in its text is a top level menu. Mandatory field.
• action specifies the identifier of the action that must have been defined in the action table (ir.actions.act_window).
Note that this field is not mandatory : you can define menu elements without associating actions to them. This
is useful when defining custom icons for menu elements that will act as folders (for example this is how custom
icons for “Projects”, “Human Resources” in Open ERP are defined).
• icon specifies which icon will be displayed for the menu item using the menu item. The default icon is STOCK_OPEN.
The available icons are : 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,
101
Open Object Developer Book, Release 1.0
11.1.1 Example
11.2 Actions
11.2.1 Introduction
The actions define the behavior of the system in response to the actions of the users ; login of a new user, double-click
on an invoice, click on the action button, ...
There are different types of simple actions:
• User connection,
• The user double-clicks on the menu,
In Open ERP, all the actions are described and not configured. Two examples:
When the user open the option of the menu “Operations > Partners > Partners Contact”, the next steps are done to give
the user information on the action to undertake.
User connection
When a new user is connected to the server, the client must search the action to use for the first screen of this user.
Generally, this action is: open the menu in the ‘Operations’ section.
The steps are:
The fields
The view describes how the edition form or the data tree/list appear on screen. The views can be of ‘Form’ or ‘Tree’
type, according to whether they represent a form for the edition or a list/tree for global data viewing.
A form can be called by an action opening in ‘Tree’ mode. The form view is generally opened from the list mode (like
if the user pushes on ‘switch view’).
This parameter allows you to regulate which resources are visible in a selected view.(restriction)
For example, in the invoice case, you can define an action that opens a view that shows only invoices not paid.
The domains are written in python; list of tuples. The tuples have three elements;
For example, if you want to obtain only ‘Draft’ invoice, use the following domain; [(‘state’,’=’,’draft’)]
In the case of a simple view, the domain define the resources which are the roots of the tree. The other resources, even
if they are not from a part of the domain will be posted if the user develop the branches of the tree.
Actions are explained in more detail in section “Administration Modules - Actions”. Here’s the template of an action
XML record :
Where
• view_mode is only considered if view_type is form, and ignored otherwise. The four possibilities are : –
form,tree : the view is first displayed as a form, the list view can be displayed by clicking the
“alternate view button” ;
– tree,form : the view is first displayed as a list, the form view can be displayed by clicking the “alter-
nate view button” ;
– form : the view is displayed as a form and there is no way to switch to list view ;
– tree : the view is displayed as a list and there is no way to switch to form view.
They indicate at the user that he has to open a new window in a new ‘tab’.
Administration > Custom > Low Level > Base > Action > Window Actions
Examples of actions
<?xml version="1.0"?>
<terp>
<data>
<wizard string="Employee Info"
model="hr.employee"
name="employee.info.wizard"
id="wizard_employee_info"/>
</data>
</terp>
A wizard is declared using a wizard tag. See “Add A New Wizard” for more information about wizard XML.
also you can add wizard in menu using following xml entry
<?xml version="1.0"?>
<terp>
<data>
<wizard string="Employee Info"
model="hr.employee"
name="employee.info.wizard"
id="wizard_employee_info"/>
<menuitem
name="Human Resource/Employee Info"
action="wizard_employee_info"
type="wizard"
id="menu_wizard_employee_info"/>
</data>
</terp>
Report declaration
Reports in Open ERP are explained in chapter “Reports Reporting”. Here’s an example of a XML file that declares a
RML report :
<?xml version="1.0"?>
<terp>
<data>
<report id="sale_category_print"
string="Sales Orders By Categories"
model="sale.order"
name="sale_category.print"
rml="sale_category/report/sale_category_report.rml"
menu="True"
auto="False"/>
</data>
</terp>
A report is declared using a report tag inside a “data” block. The different arguments of a report tag are :
11.3 Security
Three concepts are differentiated into Tiny ERP;
The users
They represent physical persons. These are identified with a login and a password. A user may belong to several
groups and may have several roles.
A user must have an action set up. This action is executed when the user connects to the program with his login and
password. An example of action would be to open the menu at ‘Operations’.
The preferences of the user are available with the preference icon. You can, for example, through these preferences,
determine the working language of this user. English is set by default.
A user can modify his own preferences while he is working with Tiny ERP. To do that, he clicks on this menu: User >
Preferences. The Open ERP administrator can also modify some preferences of each and every user.
The groups
The groups determine the access rights to the different resources. There are three types of right:
A user can belong to several groups. If he belongs to several groups, we always use the group with the highest rights
for a selected resource.
The roles
The roles define a hierarchical structure in tree. They represent the different jobs/roles inside the company. The biggest
role has automatically the rights of all the inferior roles.
Example:
CEO
• Technical manager
– Chief of projects
* Developers
* Testers
• Commercial manager
– Salesmen
– ...
If we want to validate the test of a program (=role Testers), it may be done by a user having one of the following roles:
Testers, Chief of the project, Technical manager, CEO.
The roles are used for the transition of Workflow actions into confirmation, choice or validation actions. Their impli-
cations will be detailed in the Workflow section.
It’s easy (but risky) to grant grained access to menu based on the user’s groups.
First of all, you should know that if a menu is not granted to any group then it is accessible to everybody ! If you
want to grant access to some groups just go to Menu > Administration > Security > Define access to Menu-items
and select the groups that can use this menu item.
Beware ! If the Administrator does not belong to one of the group, he will not be able to reach this menu again.
111
CHAPTER
TWELVE
WORKFLOW-BUSINESS PROCESS
12.1 Introduction
The workflow system in Open ERP is a very powerful mechanism that can describe the evolution of documents (model)
in time.
Workflows are entirely customizable, they can be adapted to the flows and trade logic of almost any company. The
workflow system makes Tiny ERP very flexible and allows it to easily support changing needs without having to
program new functionalities.
Goals
113
Open Object Developer Book, Release 1.0
The order starts in the ‘draft’ state, when it is in redaction and not approved. When the user press on the ‘Confirm’
button, the invoice is created and the order comes into the ‘CONFIRMED’ state.
Then, two operations are possible:
Let’s suppose a company has a need not implemented in OpenERP. For example, suppose their sales staff can only
offer discounts of 15% or less. Every order having a discount above 15% must be approved by the sales manager.
This modification in the sale logic doesn’t need any line of python code! A simple modification of the workflow allows
us to take this new need into account and add the extra validation step.
The workflow is thus modified as above and the orders will react as we want to. We then only need to modify the order
form view and add a validation button at the desired location.
We could then further improve this workflow by sending a request to the sales manager when an order enters the
‘Validation’ state. Workflow nodes can execute object methods; only two lines of Python are needed to send a request
asking the sales manager to validate or not the order.
12.1.2 WkfExample2: A sale order that generates an invoice and a shipping order.
<?xml version="1.0"?>
<terp>
<data>
<record model="workflow" id=workflow_id>
<field name="name">workflow.name</field>
<field name="osv">resource.model</field>
<field name="on_create">True | False</field>
</record>
</data>
</terp>
Where
• id (here “workflow_id”) is a workflow identifier. Each workflow must have an unique identifier.
• name (here “workflow.name”) is the name of the workflow. The name of the workflow must respect the Open
ERP syntax of “dotted names”.
• osv (here “resource.model”) is the name of the Tiny object we use as a model [-(Remember a Open object
inherits from osv.osv, hence the ‘<field name=”osv”>’)-].
• on_create is True if workflow.name must be instantiated automatically when resource.model is created, and
False otherwise.
Example
The workflow “sale.order.basic” defined in addons/sale/sale_workflow.xml follows exactly this model, the code of
its workflow tag is :
<field name="name">sale.order.basic</field>
<field name="osv">sale.order</field>
<field name="on_create">True</field>
</record>
12.4 Activity
12.4.1 Introduction
The wkf_activity class represents the nodes of workflows. These nodes are the actions to be executed.
12.4.3 split_mode
• XOR: One necessary transition, takes the first one found (default).
• OR : Take only valid transitions (0 or more) in sequential order.
• AND: All valid transitions are launched at the same time (fork).
• AND: Waits for all transition conditions to be valid to execute the destination activity.
12.4.4 kind:
The type of the activity can take several values • DUMMY: Do nothing (default).
• FUNCTION: Execute the function selected by an action.
• SUBFLOW: Execute a sub-workflow SUBFLOW_ID. The action method must return the ID
of the concerned resource by the subflow ! If the action returns False, the workitem disappears
!
• STOPALL:
A sub-workflow is executed when an activity is of the type SUBFLOW. This activity ends when the sub-workflow has
finished. While the sub-workflow is active, the workitem of this activity is frozen.
12.4.5 action:
The action indicates the method to execute when a workitem comes into this activity. The method must be defined in
a object which belongs this workflow and have the following signature:
object_method()
signal_send
flow_start
Indicates if the node is a start node. When a new instance of a workflow is created, a workitem is activated for each
activity marked as a flow_start.
Be warned to not use this flag unless your activity really is a “flow start”. There are tiny versions that do not care
about the tags contents like “true” or “false”. Using such tag and tiny version, you will always end up whith an activity
which is tagged as “flow start = true”, leaving u with a nasty hunt to find out where your workflowdesign could be
wrong.
12.4.6 flow_stop
Indicates if the node is an ending node. When all the active workitems for a given instance come in the node marked
by flow_stop, the workflow is finished.
Be warned to not use this flag unless your activity really is a “flow stop”. There are tiny versions that do not care
about the tags contents like “true” or “false”. Using such tag and tiny version, you will always end up whith an activity
which is tagged as “flow stop = true”, leaving u with a nasty hunt to find out where your workflowdesign could be
wrong.
12.4.7 wkf_id
The workflow which this activity belongs to. Defining activities using XML files
<field name="action">’’(...)’’</field>
<field name="signal_send">’’(...)’’</field>
<field name="flow_start">True | False</field>
<field name="flow_stop">True | False</field>
</record>
The first two arguments wkf_id and name are mandatory. Be warned to not use flow_start and flow_stop unless your
activity really is a flow start or flow_stop. There are tiny versions that do not care about the tags contents like “True”
or “False”.
Examples
There are too many possibilities of activity definition to choose from using this definition. We recommend you to have
a look at the file server/bin/addons/sale/sale_workflow.xml for several examples of activity definitions.
12.5 Transition
12.5.1 Introduction
Workflow transitions are the conditions to be satisfied to go from one activity to the next one. They are represented by
one-way arrows joining two activities.
The conditions are of different types:
The roles and signals are evaluated before the expression. If a role or a signal is false, the expression will not be
evaluated.
Transition tests may not write values in objects. The fields
act_from
Source activity. When this activity is over, the condition is tested to determine if we can start the ACT_TO activity.
act_to
condition
signal
When the operation of transition comes from a button pressed in the client form, signal tests the name of the pressed
button.
If signal is NULL, no button is necessary to validate this transition.
role_id
The role that a user must have to validate this transition. Defining Transitions Using XML Files
The general structure of a transition record is as follows
<field name="signal">(...)</field>
<field name="role_id" ref="role_id’_1_’"/>
<field name="condition">(...)</field>
<field name="trigger_model">(...)</field>
<field name="trigger_expr_id">(...)</field>
</record>
12.6 Expressions
Expressions are written as in python:
• True
• 1==1
• ‘hello’ in [’hello’,’bye’]
Any field from the resource the workflow refers to can be used in these expressions. For example, if you were creating
a workflow for partner addresses, you could use expressions like:
• zip==1400
• phone==mobile
Each user can have one or several roles. Roles are defined in a tree of roles, parent roles having the rights of all their
children.
Example:
CEO
• Technical manager
– Lead developper
* Developpers
* Testers
• Sales manager
– Commercials
– ...
Let’s suppose we handle our own bug database and that the action of marking a bug as valid needs the Testers role.
In the example tree above, marking a bug as valid could be done by all the users having the following roles: Testers,
Lead developper, Technical manager, CEO.
Steps for creating a simple state-changing workflow for a custom module called mymod
The first step is to define the States your object can be in. We do this by adding a ‘state’ field to our object, in the
_columns collection
_columns = {
...
’state’: fields.selection([
(’new’,’New’),
(’assigned’,’Assigned’),
(’negotiation’,’Negotiation’),
(’won’,’Won’),
(’lost’,’Lost’)], ’Stage’, readonly=True),
}
Add the following additional methods to your object. These will be called by our workflow buttons
Obviously you would extend these methods in the future to do something more useful! Create your Workflow XML
file —————————–
There are three types of records we need to define in a file called mymod_workflow.xml
<field name="name">negotiation</field>
<field name="kind">function</field>
<field name="action">mymod_negotiation()</field>
</record>
12.9.3 Testing
Now use the Module Manager to install or update your module. If you have done everything correctly you shouldn’t get
any errors. You can check if your workflow is installed in Administration -> Customisation -> Workflow Definitions
When you are testing, remember that the workflow will only apply to NEW records that you create.
12.9.4 Troubleshooting
If your buttons do not seem to be doing anything, one of the following two things are likely:
1. The record you are working on does not have a Workflow Instance record associated with it (it was probably
created before you defined your workflow)
2. You have not set the “osv” field correctly in your workflow XML file
THIRTEEN
13.1 Introduction
Wizards describe interaction sequences between the client and the server.
Here is, as an example, a typical process for a wizard:
3. The server gets the result, usually execute a function and possibly sends another window/form to the client
127
Open Object Developer Book, Release 1.0
Here is a screenshot of the wizard used to reconcile transactions (when you click on the gear icon in an account chart):
2. get the form result and the button pressed from the client
3. execute some actions
4. send a new action to the client (form, print, ...)
To define a wizard, you have to create a class inheriting from wizard.interface and instantiate it. Each wizard must
have a unique name, which can be chosen arbitrarily except for the fact it has to start with the module name (for
example: account.move.line.reconcile). The wizard must define a dictionary named states which defines all its steps.
Here is an example of such a class:
class wiz_reconcile(wizard.interface):
states = {
’init’: {
’actions’: [_trans_rec_get],
’result’: {’type’: ’form’,
’arch’: _transaction_form,
’fields’: _transaction_fields,
’state’:[(’reconcile’,’Reconcile’),(’end’,’Cancel’)]}
},
’reconcile’: {
’actions’: [_trans_rec_reconcile],
’result’: {’type’: ’state’, ’state’:’end’}
}
}
wiz_reconcile(’account.move.line.reconcile’);
The ‘states’ dictionary define all the states of the wizard. In this example; init and reconcile. There is another state
which is named end which is implicit.
A wizard always starts in the init state and ends in the end state.
A state define two things:
1. a list of actions
2. a result
Each step/state of a wizard defines a list of actions which are executed when the wizard enters the state. This list can
be empty.
The function (actions) must have the following signatures:
Where:
The result
Here are some result examples:
Result: next step
Indicate that the wizard has to continue to the next state: ‘end’. If this is the ‘end’ state, the wizard stops.
Result: new dialog for the client
The type=form indicate that this step is a dialog to the client. The dialog is composed of:
2. some buttons : on wich the user press after completing the form
The form description (arch) is like in the views objects. Here is an example of form:
The fields description is similar to the fields described in the python ORM objects. Example:
_transaction_fields = {
’trans_nbr’: {’string’:’# of Transaction’, ’type’:’integer’, ’readonly’:True},
’credit’: {’string’:’Credit amount’, ’type’:’float’, ’readonly’:True},
’debit’: {’string’:’Debit amount’, ’type’:’float’, ’readonly’:True},
’writeoff’: {’string’:’Write-Off amount’, ’type’:’float’, ’readonly’:True},
’writeoff_acc_id’: {’string’:’Write-Off account’,
’type’:’many2one’,
’relation’:’account.account’
},
}
Each step/state of a wizard can have several buttons. Those are located on the bottom right of the dialog box. The list
of buttons for each step of the wizard is declared in the state key of its result dictionary.
For example:
4. a boolean, if true the button is set as the default action (since 4.2)
...
...
’actions’: [_get_invoice_id],
’result’: {’type’:’print’,
’report’:’account.invoice’,
’get_id_from_action’: True,
’state’:’check_refund’}
...
The result of the function must be an all the fields of an ir.actions.* Here it is an ir.action.act_window, so the client
will open an new tab for the objects account.invoice For more information about the fields used click here.
It is recommended to use the result of a read on the ir.actions object like this:
...
13.3 Specification
13.3.1 Form
13.3.2 Fields
Standard
_fields = {
’str_field’: {’string’:’product name’, ’type’:’char’, ’readonly’:True},
}
Relational
_fields = {
’field_id’: {’string’:’Write-Off account’, ’type’:’many2one’, ’relation’:’account.account’}
}
• create the wizard definition in a .py file – wizards are usually defined in the wizard subdirectory of their
module as in server/bin/addons/module_name/wizard/your_wizard_name.py
• add your wizard to the list of import statements in the __init__.py file of your module’s wizard subdirectory.
• declare your wizard in the database
The declaration is needed to map the wizard with a key of the client; when to launch which client. To declare a new
wizard, you need to add it to the module_name_wizard.xml file, which contains all the wizard declarations for the
module. If that file does not exist, you need to create it first.
Here is an example of the account_wizard.xml file;
<?xml version="1.0"?>
<terp>
<data>
<delete model="ir.actions.wizard" search="[(’wiz_name’,’like’,’account.’)]" />
<wizard string="Reconcile Transactions" model="account.move.line"
name="account.move.line.reconcile" />
<wizard string="Verify Transac steptions" model="account.move.line"
name="account.move.line.check" keyword="tree_but_action" />
<wizard string="Verify Transactions" model="account.move.line"
name="account.move.line.check" />
<wizard string="Print Journal" model="account.account"
name="account.journal" />
<wizard string="Split Invoice" model="account.invoice"
name="account.invoice.split" />
<wizard string="Refund Invoice" model="account.invoice"
name="account.invoice.refund" />
</data>
</terp>
• id (optional):
• string: The string which will be displayed if there are several wizards for one resthe user will be presented a list
with wizards names).
• model: The name of the model where the data needed by the wizard is.
• name: The name of the wizard. It is used internally and should be unique.
• replace (optional): Whether or not the wizard should override all existing wizards for this model. Default value:
False.
• menu (optional): Whether or not (True|False) to link the wizard with the ‘gears’ button (i.e. show the button or
not). Default value: True.
• keyword (optional): Bind the wizard to another action (print icon, gear icon, ...). Possible values for the keyword attribute
client_print_multi: the print icon in a form
– client_action_multi: the ‘gears’ icon in a form
– tree_but_action: the ‘gears’ icon in a tree view (with the shortcuts on the left)
– tree_but_open: the double click on a branch of a tree (with the shortcuts on the left). For example,
this is used, to bind wizards in the menu.
__terp__.py
If the wizard you created is the first one of its module, you probably had to create the modulename_wizard.xml file
yourself. In that case, it should be added to the update_xml field of the __terp__.py file of the module.
Here is, for example, the __terp__.py file for the account module.
{
"name": Open ERP Accounting",
"version": "0.1",
"depends": ["base"],
"init_xml": ["account_workflow.xml", "account_data.xml"],
"update_xml": ["account_view.xml","account_report.xml", "account_wizard.xml"],
}
<field name="target">new</field>
It means the object will open in a new window instead of the current one.
• On a button, you can use <button special=”cancel” .../> to close the window.
class config_compute_remaining(osv.osv_memory):
_name=’config.compute.remaining’
def _get_remaining(self,cr, uid, ctx):
if ’active_id’ in ctx:
return self.pool.get(’project.task’).browse(cr,uid,ctx[’active_id’]).remaining_hours
return False
_columns = {
’remaining_hours’ : fields.float(’Remaining Hours’, digits=(16,2),),
}
_defaults = {
’remaining_hours’: _get_remaining
}
def compute_hours(self, cr, uid, ids, context=None):
if ’active_id’ in context:
remaining_hrs=self.browse(cr,uid,ids)[0].remaining_hours
self.pool.get(’project.task’).write(cr,uid,context[’active_id’],
{’remaining_hours’ : remaining_hrs})
return {
’type’: ’ir.actions.act_window_close’,
}
config_compute_remaining()
Example :
• Action is also same as normal action (don’t forget to add target attribute)
Example :
FOURTEEN
REPORTS
• OpenOffice.org reports
• RML reports
• custom reports (based on PostgreSQL views and displayed within the interface)
This chapter mainly describes OpenOffice.org reports, and then XSL:RML reports. Custom reports are described in
section Advanced Modeling - Reporting With PostgreSQL Views.
139
Open Object Developer Book, Release 1.0
• We use a .SXW file for the template, which is the OpenOffice 1.0 format. The template includes expressions in
brackets or OpenOffice fields to point where the data from the Open ERP server will be filled in. This document
is only used for developers, as a help-tool to easily generate the .RML file. Open ERP does not need this .SXW
file to print reports.
• We generate a .RML file from the .SXW file using Open SXW2RML. A .RML file is a XML format that
represent a .PDF document. It can be converted to a .PDF after. We use RML for more easy processing: XML
syntax seems to be more common than PDF syntax.
• The Open Report Engine process the .RML file inserting data from the database at each expression.
in the .RML file will be replaced by the name of the country of the partner of the printed invoice. This report engine
produce the same .RML file where all expressions have been replaced by real data.
The final document
• Finaly the .RML file is converted to PDF or HTML according to the need, using OpenReport’s scripts.
You can design reports using OpenOffice. Here’s, as an example, the file
@@server/bin/addons/sale/report/order.sxw@@.
Dynamic content
In the .SXW/.RML reports, you can put some Python code that accesses the Open ERP objects in brackets. The
context of the code (the variable’s values you can use) is the following:
Available variables
Here are Python objects/variables available:
• setTag(‘para’,’xpre’) : change the enclosing RML tag (usually ‘para’) by an other (xpre is a preformatted
paragraph), in the (converted from sxw)rml document (?)
• removeParentNode(‘tr’) : removes the parent node of type ‘tr’, this parameter is usually used together with a
conditional (see examples below)
14.1.3 SXW2RML
About
In order to use the ‘tiny_sxw2rml.py’ Python script you need the following packages installed:
• Pyhton (http://www.python.org)
• ReportLab (http://www.reportlab.org)/(Installation)
• Libxml for Python (http://users.skynet.be/sbi/libxml-python)
Ensure normalized_oo2rml.xsl is available to tiny_sxw2rml otherwise you will get an error like:
Running tiny_sxw2rml
When you have all that installed just edit your report template and run the script with the following command:
Note: tiny_sxw2rml.py help suggests that you specify the output file with: “-o OUTPUT” but this does not seem to
work as of V0.9.3
About
To generate the pdf from the rml file, OpenERP needs a rml parser.
Parser
The parsers are generally put into the folder report of the module. Here is the code for the sale order report:
import time from report import report_sxw
class order(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context):
super(order, self).__init__(cr, uid, name, context)
self.localcontext.update({
’time’: time,
})
report_sxw.report_sxw(’report.sale.order’, ’sale.order’,
’addons/sale/report/order.rml’, parser=order, header=True)
The parser inherit from the report_sxw.rml_parse object and it add to the localcontext, the function time so it will be
possible to call it in the report.
After an instance of report_sxw.report_sxw is created with the parameters:
To be visible from the client, the report must be declared in an xml file (generally: “module_name”_report.xml) that
must be put in the __terp__.py file
Here is an example for the sale order report:
<?xml version="1.0"?>
<terp>
<data>
<report
id="report_sale_order"
string="Print Order"
model="sale.order"
name="sale.order"
rml="sale/report/order.rml"
auto="False"/>
header="False"/>
</data>
</terp>
The role of the XML template is to describe which fields of the resource have to be exported (by the server). The
XSL:RML style sheet deals with the layout of the exported data as well as the “static text” of reports. Static text is
referring to the text which is common to all reports of the same type (for example, the title of table columns).
Example
Here is, as an example, the different files for the simplest report in the ERP.
XML Template
<?xml version="1.0"?>
<ids>
<id type="fields" name="id">
</id>
</ids>
<?xml version="1.0"?>
<ids>
<id>
<name>Tiny sprl</name>
<ref>pnk00</ref>
</id><id>
<name>ASUS</name>
<ref></ref>
</id><id>
<name>Agrolait</name>
<ref></ref>
</id><id>
<name>Banque Plein-Aux-As</name>
<ref></ref>
</id><id>
<name>China Export</name>
<ref></ref>
</id><id>
<name>Ditrib PC</name>
<ref></ref>
</id><id>
</id><id>
<name>Elec Import</name>
<ref></ref>
</id><id>
<name>Maxtor</name>
<ref></ref>
</id><id>
<name>Mediapole SPRL</name>
<ref></ref>
</id><id>
<name>Opensides sprl</name>
<ref>os</ref>
</id><id>
<name>Tecsas sarl</name>
<ref></ref>
</id>
</ids>
XSL stylesheet
<xsl:template match="/">
<xsl:apply-templates select="ids"/>
</xsl:template>
<xsl:template match="ids">
<document>
<template pageSize="21cm,29.7cm">
<pageTemplate>
</pageTemplate>
</template>
<stylesheet>
<blockTableStyle id="ids">
</blockTableStyle>
</stylesheet>
<story>
<tr>
<td t="1">Ref.</td>
<td t="1">Name</td>
</tr>
<xsl:apply-templates select="id"/>
</blockTable>
</story>
</document>
</xsl:template>
<xsl:template match="id">
<tr>
<td><xsl:value-of select="ref"/></td>
<td><para><xsl:value-of select="name"/></para></td>
</tr>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0"?>
<document>
...
<story>
<tr>
<td t="1">Ref.</td>
<td t="1">Name</td>
</tr>
<tr>
<td>pnk00</td>
<td><para>Tiny sprl</para></td>
</tr>
<tr>
<td></td>
<td><para>ASUS</para></td>
</tr>
<tr>
<td></td>
<td><para>Agrolait</para></td>
</tr>
<tr>
<td></td>
<td><para>Banque Plein-Aux-As</para></td>
</tr>
<tr>
<td></td>
<td><para>China Export</para></td>
</tr>
<tr>
<td></td>
<td><para>Ditrib PC</para></td>
</tr>
<tr>
<td></td>
<td><para>Ecole de Commerce de Liege</para></td>
</tr>
<tr>
<td></td>
<td><para>Elec Import</para></td>
</tr>
<tr>
<td></td>
<td><para>Maxtor</para></td>
</tr>
<tr>
<td></td>
<td><para>Mediapole SPRL</para></td>
</tr>
<tr>
<td>os</td>
<td><para>Opensides sprl</para></td>
</tr>
<tr>
<td></td>
<td><para>Tecsas sarl</para></td>
</tr>
</blockTable>
</story>
</document>
• RML : http://reportlab.com/docs/RML_UserGuide_1_0.pdf
• XSL - Specification : http://www.w3.org/TR/xslt
• http://www.w3.org/XML/
XML templates are simple XML files describing which fields among all available object fields are necessary for the
report.
File format
Tag names can be chosen arbitrarily (it must be valid XML though). In the XSL file, you will have to use those names.
Most of the time, the name of a tag will be the same as the name of the object field it refers to.
Nodes without type attribute are transferred identically into the XML destination file (the data file). Nodes with a type
attribute will be parsed by the server and their content will be replaced by data coming from objects. In addition to the
type attribute, nodes have other possible attributes. These attributes depend on the type of the node (each node type
supports or needs different attributes). Most node types have a name attribute, which refers to the name of a field of
the object on which we work.
As for the “browse” method on objects, field names in reports can use a notation similar to the notation found in object
oriented programming languages. It means that “relation fields” can be used as “bridges” to fetch data from other
(related) objects.
Let’s use the “account.transfer” object as an example. It contains a partner_id field. This field is a relation field
(“many to one”) pointing to the “res.partner” object. Let’s suppose that we want to create a report for transfers and
in this report, we want to use the name of the recipient partner. This name could be accessed using the following
expression as the name of the field:
partner_id.name
Possible types
• field: It is the simplest type. For nodes of this type, the server replaces the node content
by the value of the field whose name is given in the name attribute.
• fields: when this type of node is used, the server will generate a node in the XML data file
for each unique value of the field whose name is given in the name attribute.
Notes:
** This node type is often used with “id” as its name attribute. This has the effect
of creating one node for each resource selected in the interface by the user. ** The
semantics of a node <node type=”fields” name=”field_name”> is similar to an SQL
statement of the form “SELECT FROM object_table WHERE id in identifier_list
GROUP BY field_name” where identifier_list is the list of ids of the resources se-
lected by the ::user (in the interface).
• eval: This node type evaluate the expression given in the expr attribute. This expression
may be any Python expression and may contain objects fields names.
• zoom: This node type allows to “enter” into the resource referenced by the relation field
whose name is given in the name attribute. It means that its child nodes will be able to
access the fields of that resource without having to prefix them with the field name that
makes the link with the other object. In our example above, we could also have accessed
the field name of the partner with the following:
</partner>
In this precise case, there is of course no point in using this notation instead of the standard
The zoom type is only useful when we want to recover several fields in the same object.
• function: returns the result of the call to the function whose name is given in the name attribute. This function
must be part of the list of predefined functions. For the moment, the only available function is today, which
returns the current date.
• call: calls the object method whose name is given in the name attribute with the arguments given in the args
attribute. The result is stored into a dictionary of the form {‘name_of_variable’: value, ... } and can be accessed
through child nodes. These nodes must have a value attribute which correspond to one of the keys of the
dictionary returned by the method.
Example:
<name value="name"/>
<amount value="amount"/>
</cost>
TODO: documenter format methode appellée def compute_buyer_costs(self, cr, uid, ids, *args):
• attachment: extract the first attachment of the resource whose id is taken from the field whose name is given in
the name attribute, and put it as an image in the report.
Example
Here is an example of XML file:
</transfer>
</transfer-list>
For more information on the RML format, please refer to the official Reportlab documentation.
• http://www.reportlab.com/docs/RML_UserGuide.pdf
There are two possibilities to do a XSL style sheet for a report. Either making everything by yourself, or use our
predefined templates
Either freestyle or use corporate_defaults + rml_template
import rml_template.xsl
required templates:
• frames?
• stylesheet
• story
optional templates:
Translations
As Open ERP can be used in several langages, reports must be translatable. But in a report, everything doesn’t have
to be translated : only the actual text has to be translated, not the formatting codes. A field will be processed by the
translation system if the XML tag which surrounds it (whatever it is) has a t=”1” attribute. The server will translate all
the fields with such attributes in the report generation process.
Useful links:
<xsl:import href="../../custom/corporate_defaults.xsl"/>
<xsl:import href="../../base/report/rml_template.xsl"/>
<xsl:variable name="page_format">a4_normal</xsl:variable>
<xsl:template match="/">
<xsl:call-template name="rml"/>
</xsl:template>
<xsl:template name="stylesheet">
</xsl:template>
<xsl:template name="story">
<xsl:apply-templates select="transfer-list"/>
</xsl:template>
<xsl:template match="transfer-list">
<xsl:apply-templates select="transfer"/>
</xsl:template>
<xsl:template match="transfer">
<setNextTemplate name="other_pages"/>
<para>
</para><para>
</para><para>
</para><para>
</para><para>
</para><para>
</para>
<xsl:if test="number(change)>0">
<para>
</para>
</xsl:if>
<setNextTemplate name="first_page"/>
<pageBreak/>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="/">
<xsl:call-template name="rml"/>
</xsl:template>
<xsl:template name="stylesheet">
</xsl:template>
<xsl:template name="story">
<xsl:apply-templates select="transfer-list"/>
</xsl:template>
<xsl:template match="transfer-list">
<xsl:apply-templates select="transfer"/>
</xsl:template>
<xsl:template match="transfer">
<setNextTemplate name="other_pages"/>
<para>
Document: <xsl:value-of select="name"/>
</para><para>
Type: <xsl:value-of select="type"/>
</para><para>
Reference: <xsl:value-of select="reference"/>
</para><para>
Partner ID: <xsl:value-of select="partner_id"/>
</para><para>
Date: <xsl:value-of select="date"/>
</para><para>
Amount: <xsl:value-of select="amount"/>
</para>
<xsl:if test="number(change)>0">
<para>
Change: <xsl:value-of select="change"/>
</para>
</xsl:if>
<setNextTemplate name="first_page"/>
<pageBreak/>
</xsl:template>
</xsl:stylesheet>
<xsl:import href="../../custom/corporate_defaults.xsl"/>
<xsl:import href="../../base/report/rml_template.xsl"/>
<xsl:variable name="page_format">a4_normal</xsl:variable>
.....................
</xsl:template>
</xsl:stylesheet>
Barcodes can be generated using the <barcode> tag in RML files. The following formats are supported:
• codabar
• code11
• code128 (default if no ‘code’ specified’)
• standard39
• standard93
• i2of5
• extended39
• extended93
• msi
• fim
• postnet
You can change the following attributes for rendering your barcode:
• ‘code’: ‘char’
• ‘ratio’:’float’
• ‘xdim’:’unit’
• ‘height’:’unit’
• ‘checksum’:’bool’
• ‘quiet’:’bool’
Examples:
Administration -> Custom -> Low Level -> Base->Actions -> ir.actions.report.xml
The context of the code (the variable’s values you can use) is the following:
python objects/variables, available when the report start:
“objects” the list of objects to be printed (invoices for example)
“data” comes from the wizard
“time” see python documentation.
“user” the user object launching the report.
python functions you can use:
“setlang(‘fr’)” change the langage used in automated translation (fields...).
“repeatIn(list,varname)” repeat the template (whole doc. or current paragraph?) for each object in the list. Use
varname in the template’s tags.
“setTag(‘para’,’xpre’)” change the enclosing RML tag (usually ‘para’) by an other (xpre is a preformatted paragraph),
in the (converted from sxw)rml document (?)
“removeParentNode”
one more interesting tag: if you want to print out the creator of an entry (create_uid) or the last one who wrote on an
entry (write_uid) you have to add something like this to the class your report refers to:
and then in your report it’s like this to print out the corresponding name:
o.create_uid.name
Sometimes you might want to print out something only if a certain condition is fullfilled. You can construct it with the
pyhton logical operators “not”, “and” and “or”. Because every object in python has a logical value (TRUE or FALSE)
you can construct something like this:
• in a subdirectory of the OpenERP installation and give that path to ReportLab during the font registration.
In the server/bin/report/render/rml2pdf/__init__.py
import reportlab.rl_config
reportlab.rl_config.warnOnMissingFontGlyphs = 0
enc = ’UTF-8’
trml2pdf.py should be modified to load this if invoked from the command line.
All the xsl and rml files have to be modified
A list of possible alternatives:
’Times-Roman’, ’DejaVuSerif.ttf’
’Times-BoldItalic’, ’DejaVuSerif-BoldItalic.ttf’
’Times-Bold’, ’DejaVuSerif-Bold.ttf’
’Times-Italic’, ’DejaVuSerif-Italic.ttf’
’Helvetica’, ’DejaVuSans.ttf’
’Helvetica-BoldItalic’, ’DejaVuSans-BoldOblique.ttf’
’Helvetica-Bold’, ’DejaVuSans-Bold.ttf’
’Helvetica-Italic’, ’DejaVuSans-Oblique.ttf’
’Courier’, ’DejaVuSansMono.ttf’
’Courier-Bold’, ’DejaVuSansMono-Bold.ttf’
’Courier-BoldItalic’,’DejaVuSansMono-BoldOblique.ttf’
’Courier-Italic’, ’DejaVuSansMono-Oblique.ttf’
’Helvetica-ExtraLight’, ’DejaVuSans-ExtraLight.ttf’
’TimesCondensed-Roman’, ’DejaVuSerifCondensed.ttf’
’TimesCondensed-BoldItalic’, ’DejaVuSerifCondensed-BoldItalic.ttf’
’TimesCondensed-Bold’, ’DejaVuSerifCondensed-Bold.ttf’
’TimesCondensed-Italic’, ’DejaVuSerifCondensed-Italic.ttf’
’HelveticaCondensed’, ’DejaVuSansCondensed.ttf’
’HelveticaCondensed-BoldItalic’, ’DejaVuSansCondensed-BoldOblique.ttf’
’HelveticaCondensed-Bold’, ’DejaVuSansCondensed-Bold.ttf’
’HelveticaCondensed-Italic’, ’DejaVuSansCondensed-Oblique.ttf
FIFTEEN
I18N - INTERNATIONALIZATION
15.1 Introduction
159
Open Object Developer Book, Release 1.0
161
CHAPTER
SIXTEEN
SERVER ACTION
16.1 Introduction
Server action is an new feature to the OpenERP available since the version 5.0 beta, This is the interesting features for
the customizer, to full fill the customers requirements, This features enables to provides the quick and easy configura-
tion some process which is day to day requirements. Like send email on confirmation of the sale order, or confirmation
of the Invoice, log the operation of the invoice (confirm, cancel, etc..). or need to develope some system which runs
wizard / report on the confirmation of the sales, purchase, or invoice. So Server action is the only one answer to solve
all this kind of problems without doing any development, just a few configuration and the system is ready to answer
few of above questions.
Following are the list of action types which are supplied under the Server Action.
• Client Action
• Trigger
• Email
• SMS
• Create Object
• Write Object
• Multi Action
Each type of action have the special features and different configuration parameters. We will see one by one all type
of action how to configure and list of parameters that affect the system
163
Open Object Developer Book, Release 1.0
Object Select the object on which we want to implement the Server Action when work flow will execute
on this object
Client Action Select the client action that is to execute at client side. Any of the following types.
• ir.actions.report.custom
• ir.actions.report.xml
• ir.actions.act_window
• ir.actions.wizard
• ir.actions.url
16.3 Trigger
Trigger is an really excellent when we want to deal with the work flow of the other object which working the work
flow of the first object. For example we want to configure the system like when we confirm the purchase order and
create the invoice that newly created invoice should confirm it self automatically by the server action.
This is the easy configuration for the trigger to have the system where the created invoice will confirm it self.
Important fields are
Object Select the object on which we want to implement the Server Action when work flow will execute
on this object
Work-flow on Here we select invoice, need to select the model on which the automatic workflow will be
called by the action system
Trigger On We need to provide the id of the newly record, here in this case, Purchase order store the id
of the Invoice after creating of the invoice in invoice_id field.
Trigger Name This is the signal name which we want to generate on the newly created object.
Object Select the object on which we want to implement the Server Action when work flow will execute
on this object
Contact We need to select the fields from which action will select the email address to whom we would
like to send the email, system will display all the fields related to the current object selected in the
Object field
Message You can provide the message template with the fields that related to the current object. And it
will be merge when it is going to send the email. This is the same language then the rml which is
used to design the report here we can use the [[ ]] + html tage to design in the html format Working
with You can select the any fields from the current object, like here we select the [[ ]] invoice in the
object.
For example to get the partner name we can use [[ object.partner_id.name ]]like the same, object refers to the current
object and we can access any fields which exist in the model.
After confirmation the invoice we get the confirmation email from the action.
Create Object action have the easy but tricky configuration, for the movement you have to remember the fields name
or check it out from the code it self, in future we will develop the expression builder inside OpenERP so you can build
the complex expression.
Important fields are
Object Select the object on which we want to implement the Server Action when work flow will execute
on this object
Model This is the target model where the new object is to be created, if its empty it refers to the current
object and allow to select the fields from the same, but its advisable to provide the model in all case
if different or if the same.
Fields Mapping Need to provide the 3 values
You must select the all required fields from the object
Record Id After creating the new record where the id of the new record if going to store. So in future we
can refer the same for the other operations.
• Print Invoice
• Invoice Confirmation Email !!
• Multi Action
The only problem with the Multi Action is that it will execute many actions at the server side, but only one client
action will be executed.
For example we would like to print report + execute the wizard this 2 operation is not allowd in the one multi action.
Object Select the object on which we want to implement the Server Action when work flow will execute
on this object
Other Actions We need to select the server action in this fields, we are free to select the as many as
actions as we can. Just we need to take care for the problem of the multi action, other things is very
easy.
Here in this example I added the Action to print the Invoice, when the Invoice will be confirmed.
SEVENTEEN
DASHBOARD
Open ERP objects can be created from PostgreSQL views. The technique is as follows :
1. Declare your _columns dictionary. All fields must have the flag readonly=True.
2. Specify the parameter _auto=False to the Open ERP object, so no table corresponding to the _columns dictio-
nnary is created automatically.
3. Add a method init(self, cr) that creates a PostgreSQL View matching the fields declared in _columns.
class report_crm_case_user(osv.osv):
_name = "report.crm.case.user"
_description = "Cases by user and section"
_auto = False
_columns = {
’name’: fields.date(’Month’, readonly=True),
’user_id’:fields.many2one(’res.users’, ’User’, readonly=True, relate=True),
’section_id’:fields.many2one(’crm.case.section’, ’Section’, readonly=True, relate=True),
’amount_revenue’: fields.float(’Est.Revenue’, readonly=True),
’amount_costs’: fields.float(’Est.Cost’, readonly=True),
’amount_revenue_prob’: fields.float(’Est. Rev*Prob.’, readonly=True),
’nbr’: fields.integer(’# of Cases’, readonly=True),
’probability’: fields.float(’Avg. Probability’, readonly=True),
’state’: fields.selection(AVAILABLE_STATES, ’State’, size=16, readonly=True),
’delay_close’: fields.integer(’Delay to close’, readonly=True),
}
_order = ’name desc, user_id, section_id’
169
Open Object Developer Book, Release 1.0
171
CHAPTER
EIGHTEEN
18.1.1 Introduction
There are different methods to import your data into Open ERP:
The best way to import data in Open ERP is to build a module that integrates all the data you want to import. So,
when you want to import all the data, you just have to install the module and Open ERP manages the different creation
operations. When you have lots of different data to import, we sometimes create different modules.
So, let’s create a new module where we will store all our datas. To do this, from the addons directory, create a new
module called data_yourcompany.
• mkdir data_yourcompany
• cd data_yourcompany
• touch __init__.py
You must also create a file called __terp__.py in this new module. Write the following content in this module file
description.
{
’name’: ’Module for Data Importation’,
’version’: ’1.0’,
’category’: ’Generic Modules/Others’,
’description’: "Sample module for data importation.",
’author’: ’Tiny’,
’website’: ’http://www.openerp.com’,
’depends’: [’base’],
’init_xml’: [
173
Open Object Developer Book, Release 1.0
’res.partner.csv’,
’res.partner.address.csv’
],
’update_xml’: [],
’installable’: True,
’active’: False,
}
Once this module is created, you must load data from your old application to .CSV file that will be loaded in Open
ERP. Open ERP has a builtin system to manage identifications columns of the original software.
For this exercice, we will load data from another Open ERP database called old. As this database is in SQL, it’s quite
easy to export the data using the command line postgresql client: psql. As to get a result that looks like a .CSV fiel,
we will use the following arguments of psql:
• –pset footer : don’t write the latest line that looks like “(21 rows)”
When you import a .CSV file in Open ERP, you can provide a ‘id’ column that contains a uniq identification number
or string for the record. We will use this ‘id’ column to refer to the ID of the record in the original application. As to
refer to this record from a many2one field, you can use ‘FIELD_NAME:id’. Open ERP will re-create the relationship
between the record using this uniq ID.
So let’s start to export the partners from our database using psql: ::
This creates a res.partner.csv file containing a structure that looks like this:
id,name
partner_2,ASUStek
partner_3,Agrolait
partner_4,Camptocamp
partner_5,Syleam
By doing this, we generated data from the res.partner object, by creating a uniq identification string for each record,
which is related to the old application’s ID.
Now, we will export the table with addresses (or contacts) that are linked to partners through the relation field: part-
ner_id. We will proceed in the same way to export the data and put them into our module:
This should create a file called res.partner.address with the following data:
When you will install this module, Open ERP will automatically import the partners and then the address and recreate
efficiently the link between the two records. When installing a module, Open ERP will test and apply the constraints
for consistency of the data. So, when you install this module, it may crash, for example, because you may have
different partners with the same name in the system. (due to the uniq constraint on the name of a partner). So, you
have to clean your data before importing them.
If you plan to upload thousands of records through this technique, you should consider using the argument ‘-P’ when
running the server.
This method provides a faster importation of the data and, if it crashes in the middle of the import, it will continue at
the same line after rerunning the server. This may preserves hours of testing when importing big files.
The next version of Open ERP will include an ETL module to allow you to easily manages complex import jobs. If you
are interrested in this system, you can check the complete specifications and the available prototype at this location:
NINETEEN
The upgrade from version to version is automatic and doesn’t need any special scripting on the user’s part. In fact, the
server is able to automatically rebuild the database and the data from a previously installed version.
The tables are rebuilt from the current module definitions. To rebuild the tables, the server uses the definition of the
objects and adds / modifies database fields as necessary.
To invoke a database upgrade after installing a new verion, you need to start the server with the –update=all argument
:
tinyerp-server.py --update=all
tinyerp-server.py --update=account,base
The database is rebuilt according to information provided in XML files and Python Classes. For more information on
these functionalities, go to the section XML files and Defining Objects.
You can also execute the server with –init=all. The server will then rebuild the database according to the existing
XML files on the system, delete all existing data and return Open ERP to its basic configuration.
177
Open Object Developer Book, Release 1.0
179
CHAPTER
TWENTY
backup
pg_dump terp >terp.sql
restore
createdb terp –encoding=unicode psql terp < terp.sql or psql -d terp -f terp.sql
181
Open Object Developer Book, Release 1.0
TWENTYONE
XML-RPC is known as a web service. Web services are a set of tools that let one build distributed applications on
top of existing web infrastructures. These applications use the Web as a kind of “transport layer” but don’t offer a
direct human interface via the browser.[1] Extensible Markup Language (XML) provides a vocabulary for describing
Remote Procedure Calls (RPC), which is then transmitted between computers using the HyperText Transfer Protocol
(HTTP). Effectively, RPC gives developers a mechanism for defining interfaces that can be called over a network.
These interfaces can be as simple as a single function call or as complex as a large API.
XML-RPC therefore allows two or more computers running different operating systems and programs written in
different languages to share processing. For example, a Java application could talk with a Perl program, which in
turn talks with Python application that talks with ASP, and so on. System integrators often build custom connections
between different systems, creating their own formats and protocols to make communications possible, but one can
often end up with a large number of poorly documented single-use protocols. The RPC approach spares programmers
the trouble of having to learn about underlying protocols, networking, and various implementation details.
XML-RPC can be used with Python, Java, Perl, PHP, C, C++, Ruby, Microsoft’s .NET and many other programming
languages. Implementations are widely available for platforms such as Unix, Linux, Windows and the Macintosh.
An XML-RPC call is conducted between two parties: the client (the calling process) and the server (the called process).
A server is made available at a particular URL (such as http://example.org:8080/rpcserv/).
The above text just touches the surface of XML-RPC. I recommend O’Reilly’s “Programming Web Service with
XML-RPC” for further reading. One may also wish to review the following links:
XML-RPC Home Page\ XML-RPC for C and C++\ The Apache XML-RPC Project\ Expat: The XML Parser\
21.1 Interfaces
21.1.1 XML-RPC
XML-RPC Architecture
Open ERP is a based on a client/server architecture. The server and the client(s) communicate using the XML-RPC
protocol. XML-RPC is a very simple protocol which allows the client to do remote procedure calls. The called
183
Open Object Developer Book, Release 1.0
function, its arguments, and the result of the call are transported using HTTP and encoded using XML. For more
information on XML-RPC, please see: http://www.xml-rpc.com.
Architecture
The diagram below synthesizes the client server architecture of Open ERP. Open ERP server and Open ERP clients
communicate using XML-RPC.
Client
The logic of Open ERP is configured on the server side. The client is very simple; it is only used to post data (forms,
lists, trees) and to send back the result to the server. The updates and the addition of new functionality don’t need the
clients to be frequently upgraded. This makes Open ERP easier to maintain.
The client doesn’t understand what it posts. Even actions like ‘Click on the print icon’ are sent to the server to ask
how to react.
The client operation is very simple; when a user makes an action (save a form, open a menu, print, ...) it sends this
action to the server. The server then sends the new action to execute to the client.
There are three types of action;
• Execute a wizard
21.1.2 Python
Demo script
import xmlrpclib
partner = {
’name’: ’Fabien Pinckaers’,
’lang’: ’fr_FR’,
}
address = {
’partner_id’: partner_id,
’type’ : ’default’,
’street’: ’Chaussée de Namur 40’,
’zip’: ’1367’,
’city’: ’Grand-Rosière’,
’phone’: ’+3281813700’,
’fax’: ’+3281733501’,
}
• Search a partner
• Delete partner
# ids : list of id
result = sock.execute(dbname, uid, pwd, ’res.partner’, ’unlink’, ids)
21.1.3 PHP
• Login
function connect() {
var $user = ’admin’;
var $password = ’admin’;
var $dbname = ’db_name’;
var $server_url = ’http://localhost:8069/xmlrpc/’;
if(isset($_COOKIE["user_id"]) == true) {
if($_COOKIE["user_id"]>0) {
return $_COOKIE["user_id"];
}
}
• Search
/**
* $client = xml-rpc handler
* $relation = name of the relation ex: res.partner
* $attribute = name of the attribute ex:code
* $operator = search term operator ex: ilike, =, !=
* $key=search for
*/
function search($client,$relation,$attribute,$operator,$keys) {
var $user = ’admin’;
var $password = ’admin’;
var $userId = -1;
var $dbname = ’db_name’;
var $server_url = ’http://localhost:8069/xmlrpc/’;
if($userId<=0) {
connect();
}
$resp = $client->send($msg);
$val = $resp->value();
$ids = $val->scalarval();
return $ids;
}
• Create
TODO
• Write
TODO
21.1.4 JAVA
• Find Databases
import java.net.URL;
import java.util.Vector;
import org.apache.commons.lang.StringUtils;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
xmlrpcDb.setConfig(xmlrpcConfigDb);
try {
//Retrieve databases
Vector<Object> params = new Vector<Object>();
Object result = xmlrpcDb.execute("list", params);
Object[] a = (Object[]) result;
• Login
import java.net.URL;
import org.apache.commons.lang.StringUtils;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
public int Connect(String host, int port, String tinydb, String login, String password)
{
XmlRpcClient xmlrpcLogin = new XmlRpcClient();
xmlrpcConfigLogin.setEnabledForExtensions(true);
xmlrpcConfigLogin.setServerURL(new URL("http",host,port,"/xmlrpc/common"));
xmlrpcLogin.setConfig(xmlrpcConfigLogin);
try {
//Connect
params = new Object[] {tinydb,login,password};
Object id = xmlrpcLogin.execute("login", params);
if (id instanceof Integer)
return (Integer)id;
return -1;
}
catch (XmlRpcException e) {
logger.warn("XmlException Error while logging to TinyERP: ",e);
return -2;
}
catch (Exception e)
{
logger.warn("Error while logging to TinyERP: ",e);
return -3;
}
}
• Search
TODO
• Create
TODO
• Write
TODO
import xmlrpclib
sock = xmlrpclib.ServerProxy(’http://localhost:8069/xmlrpc/object’)
uid = 1
pwd = ’demo’
partner = {
’title’: ’Monsieur’,
’name’: ’Fabien Pinckaers’,
’lang’: ’fr’,
’active’: True,
}
address = {
’partner_id’: partner_id,
’type’: ’default’,
’street’: ’Rue du vieux chateau, 21’,
’zip’: ’1457’,
’city’: ’Walhain’,
’phone’: ’(+32)10.68.94.39’,
’fax’: ’(+32)10.68.94.39’,
}
To get the UID of a user, you can use the following script:
sock = xmlrpclib.ServerProxy(’http://localhost:8069/xmlrpc/common’)
UID = sock.login(’terp3’, ’admin’, ’admin’)
CRUD example:
"""
:The login function is under
:: http://localhost:8069/xmlrpc/common
:For object retrieval use:
:: http://localhost:8069/xmlrpc/object
"""
import xmlrpclib
user = ’admin’
pwd = ’admin’
dbname = ’terp3’
model = ’res.partner’
sock = xmlrpclib.ServerProxy(’http://localhost:8069/xmlrpc/common’)
uid = sock.login(dbname ,user ,pwd)
sock = xmlrpclib.ServerProxy(’http://localhost:8069/xmlrpc/object’)
# CREATE A PARTNER
partner_data = {’name’:’Tiny’, ’active’:True, ’vat’:’ZZZZZ’}
partner_id = sock.execute(dbname, uid, pwd, model, ’create’, partner_data)
# SEARCH PARTNERS
args = [(’vat’, ’=’, ’ZZZZZ’),]
ids = sock.execute(dbname, uid, pwd, model, ’search’, args)
values = {’vat’:’ZZ1ZZ’}
results = sock.execute(dbname, uid, pwd, model, ’write’, ids, values)
PRINT example:
1. PRINT INVOICE
2. IDS is the invoice ID, as returned by:
3. ids = sock.execute(dbname, uid, pwd, ‘account.invoice’, ‘search’, [(‘number’, ‘ilike’, invoicenumber), (‘type’,
‘=’, ‘out_invoice’)])
import time
import base64
printsock = xmlrpclib.ServerProxy(’http://server:8069/xmlrpc/report’)
model = ’account.invoice’
id_report = printsock.report(dbname, uid, pwd, model, ids, {’model’: model, ’id’: ids[0], ’report_ty
time.sleep(5)
state = False
attempt = 0
while not state:
report = printsock.report_get(dbname, uid, pwd, id_report)
state = report[’state’]
if not state:
time.sleep(1)
attempt += 1
if attempt>200:
print ’Printing aborted, too long delay !’
string_pdf = base64.decodestring(report[’result’])
file_pdf = open(’/tmp/file.pdf’,’w’)
file_pdf.write(string_pdf)
file_pdf.close()
<?
include(’xmlrpc.inc’);
$arrayVal = array(
’name’=>new xmlrpcval(’Fabien Pinckaers’, "string") ,
’vat’=>new xmlrpcval(’BE477472701’ , "string")
);
$resp = $client->send($msg);
if ($resp->faultCode())
else
?>
193
CHAPTER
TWENTYTWO
RAD TOOLS
22.1 DIA
The uml_dia module helps to develop new modules after an UML description using the DIA tool
(http://www.gnome.org/projects/dia).
It’s not a typical module in the sense that you don’t have to install it on the server as another module. The contents of
the module are just a python script for dia (codegen_openerp.py), a test dia diagram and the module generated by the
test.
The module is located in the extra_addons branch: https://code.launchpad.net/openobject-addons
To use the module you need to make codegen_openerp.py accesible from dia, usually in your /usr/share/dia/python
directory and make sure that it gets loaded once. To do it, just open dia and open a Python Console from the
Dialog Menu, and type there “import codegen_openerp”. If everything goes alright you will have a new option in
your “Export...” dialog named “PyDia Code Generation (OpenERP)” that will create a zip module from your UML
diagram.
To install win Dia in windows, first install Python-2.2, then when you install Dia, you will have an option to install
the python plug-in. After this, put the codegen_openerp.py file in C:Program FilesDia and you will have the export
function in Dia.
If you find that the zip file is corrupt, use DiskInternals ZipRepair utility to repair the zip file before you’ll be able to
import it - make sure the zip file you import has the same name you saved as.
• modify existing reports which will then replace the originals in your Open ERP database,
• create new reports for the selected object.
To modify an existing report, select Tiny Report > Modify Existing Report . Choose the report Request for
Quotation in the Modify Existing Report dialog box and then click Save to Temp Directory .
195
Open Object Developer Book, Release 1.0
The older reports haven’t all been converted into the new form supported by Open ERP. The data expressions in the
old format are shown within double brackets and not in OpenOffice.org fields.
You can transform an old report format to the new format from the OpenOffice.org menu Tiny Report > Convert
Bracket–Fields.
From the Tiny toolbar in OpenOffice.org it’s possible to:
field.
• add an expression: enter an expression in the Python language to calculate values from any fields in the selected
object.
Using the Expression button you can enter expressions in the Python language. These expressions can use all of the
object’s fields for their calculations.
For example if you make a report on an order you can use the following expression:
In this example, amount_total is a field from the order object. The result will be 90% of the total of the order, formatted
to two decimal places.
Tiny Report > Send to server Technical Name Report Name Sale Order Mod Corporate Header Send
Report to Server
You can check the result in Open ERP using the menu Sales Management > Sales Orders > All Orders .
Tiny Report > Open a new report Sale Order Open New Report Use Model in Report
The general template is made up of loops (such as the list of selected orders) and fields from the object, which can also
be looped. Format them to your requirements then save the template.
The existing report templates make up a rich source of examples. You can start by adding the loops and several fields
to create a minimal template.
When the report has been created, send it to the server by clicking Tiny Report > Send to server , which brings up the
Send to server dialog box. Enter the Technical Name of sale.order , to make it appear beside the other sales order
reports. Rename the template as Sale Order New in Report Name , check the checkbox Corporate Header and
finally click Send Report to Server .
To send it to the server, you can specify if you prefer Open ERP to produce a PDF when the user prints the document,
or if Open ERP should open the document for editing in OpenOffice.org Writer before printing. To do that choose
PDF or SXW (a format of OpenOffice.org documents) in the field Select Report Type
Open ERP objects can be created from PostgreSQL views. The technique is as follows :
1. Declare your _columns dictionary. All fields must have the flag readonly=True.
2. Specify the parameter _auto=False to the Open ERP object, so no table corresponding to the _columns diction-
nary is created automatically.
3. Add a method init(self, cr) that creates a PostgreSQL View matching the fields declared in _columns.
report_crm_case_user(osv.osv):
_name = "report.crm.case.user"
_description = "Cases by user and section"
_auto = False
_columns = {
’name’: fields.date(’Month’, readonly=True),
’user_id’:fields.many2one(’res.users’, ’User’, readonly=True, relate=True),
’section_id’:fields.many2one(’crm.case.section’, ’Section’, readonly=True, relate=True),
’amount_revenue’: fields.float(’Est.Revenue’, readonly=True),
’amount_costs’: fields.float(’Est.Cost’, readonly=True),
’amount_revenue_prob’: fields.float(’Est. Rev*Prob.’, readonly=True),
’nbr’: fields.integer(’# of Cases’, readonly=True),
’probability’: fields.float(’Avg. Probability’, readonly=True),
’state’: fields.selection(AVAILABLE_STATES, ’State’, size=16, readonly=True),
’delay_close’: fields.integer(’Delay to close’, readonly=True),
}
_order = ’name desc, user_id, section_id’
Part 8 : Appendices
199
CHAPTER
TWENTYTHREE
APPENDICES INDEX
def foo(a=None):
if a is None:
a=[]
# ...
This is what is [in the Python documentation]. In addition it is good practice to avoid modifying objects that you
receive as arguments if it is not specified. If you want to do so, prefer to copy the object first. A list can easily be
copied with the syntax
copy = original[:]
/module/
/__init__.py
/__terp__.py
/module.py
/module_other.py
/module_view.xml
201
Open Object Developer Book, Release 1.0
/module_wizard.xml
/module_report.xml
/module_data.xml
/module_demo.xml
/wizard/
/__init__.py
/wizard_name.py
/report/
/__init__.py
/report_name.sxw
/report_name.rml
/report_name.py
• modules: modules must be written in lower case, with underscores. The name of the module is the name of the directory i
sale
– sale_commission
• objects: the name of an object must be of the form name_of_module.name1.name2.name3.... The namei part of the object
sale.order
– sale.order.line
– sale.shop
– sale_commission.commission.rate
• fields: field must be in lowercase, separated by underscores. Try to use commonly used names for fields: name, state, activ
many2one: must end by ‘_id’ (eg: partner_id, order_line_id)
– many2many: must end by ‘_ids’ (eg: category_ids)
– one2many: must end by ‘_ids’ (eg: line_ids
23.2.1 Introduction
The Open Forge has tools to help your team collaborate, like message forums, tasks tracker and mailing lists; tools
to create and control access to Source Code Management repositories. It is the central repository of collaborative
developments for Open ERP.
23.3 Translations
Open ERP is multilingual. You can add as many languages as you wish. Each user may work with the interface in his
own language. Moreover, some resources (the text of reports, product names, etc.) may also be translated.
This section explains how to change the language of the program shown to individual users, and how to add new
languages to Open ERP.
Nearly all the labels used in the interface are stored on the server. In the same way, the translations are also stored on
the server. By default, the English dictionary is stored on the server, so if the users want to try Open ERP in a language
other than English, then you have to store these languages definitions on the server.
However, it is not possible to store “everything” on the server. Indeed, the user gets some menus, buttons, etc... that
must contain some text even before being connected to the server. These few words and sentences are translated using
GETTEXT. The chosen language by default for these is the language of the computer from which the user connects.
The translation system of Open ERP is not limited to interface texts; it also works with reports and the “content” of
some database fields. Obviously, not all the database fields need to be translated. The fields where the content is
multilingual are marked thus by a flag :
The language is a user preference. To change the language of the current user, click on the menu: User > Preferences.
An administrator may also modify the preferences of a user (including the language of the interface) in the menu:
Administration > Users > Users. He merely has to choose a user and toggle on “preferences”.
where LANG is the language of the translation data in the CSV file.
Note that the translation file must be encoded in UTF8!
Please keep in mind to use the same translation string for identical sources . Launchpad Online Translation may
give helpful hints.
More information on accelerators on this website: http://translate.sourceforge.net/wiki/guide/translation/accelerators
To translate or modify the translation of a language already translated, you have to:
where LANG is the language to which you want to translate the program.
You can make a translation for a language, which has already been translated or for a new one. If you ask for a
language already translated, the sentences already translated will be written in the last column.
For example, here are the first lines of a translation file (Dutch):
type name res_id src value
field “account.account,code” 0 Code Code
field “account.account,name” 0 Name Name
model “account.account,name” 2 Assets Aktiva
model “account.account,name” 25 Results Salden
model “account.account,name” 61 Liabilities Verbindlichkeiten
3. Import this file into Open ERP (as explained in the preceding section)
Notes
• You should perform all these tasks on an empty database, so as to avoid over-writing data.
Alternatively, you could also delete your current database with these:
3. translate <new po file> using poedit, kbabel (KDE) (a) some programs (like kbabel) allow using dictionar-
ies to create rough translations.
(b) It is especially useful to create a complete dictionary from existing translations to reuse existing terms related to th
In OpenERP load most/all of the modules
ii. Load your language
iii. export all modules of your language as po file and use this one as dictionary. Depending on
context of the module this creates 30-80% exact translations.
4. the <new po file> must not contain <fuzzy> comments inserted by kbabel for rough translation (a) grep -v
fuzzy <new po file> > <po file>
5. check for correct spelling (a) msgfmt <po file> -o <mo file>
6. check your translation for correct context (a) import the <po file> (for modules)
(b) install the <mo file> and restart the application (for applications)
7. adjust the translation Online in OpenERP (a) check context
(b) check length of strings
(c) export <po file>
8. upload <po file> to Launchpad (a) keep in mind that Launchpad / Rosetta uses some tags (not sure which) in the
header section of the exported <po file> to recognize the imported <po file> as valid.
(b) after some time (hours) you will receive a confirmation E-Mail (success / error)
The context dictionary is explained in details in section “The Objects - Methods - The context Dictionary”. If an
additional language is installed using the Administration menu, the context dictionary will contain an additional key :
lang. For example, if you install the French language then select it for the current user, his or her context dictionary
will contain the key lang to which will be associated the value fr_FR.
B
Bazaar
installation, 17
I
Installation
Bazaar, 17
R
Report
Modify, 195
New, 197
209