Professional Documents
Culture Documents
1. Installation
1. Dependencies
2. Installation Steps
3. Upgrading Versions
4. Config files
1. Server's config file: ~/.terp_serverrc
2. Client's config file: ~/.terprc
5. Command line options
6. Shutting down the server
2. Translations
1. Translation in Reports
3. The client
4. Parameter settings of Modules
1. ModulesAdminBase
1. Management of access rights
2. The actions
3. Sequences
4. Requests
5. Customizing the menu
2. ModulesAdminCRM
3. ModulesAdminAccount
1. ModulesAdminAccountChart
2. ModulesAdminAccountCurrency
3. ModulesAdminAccountTax
4. ModulesAdminAccountImputations
4. ModulesAdminStock
5. ModulesAdminSale
6. Subscriptions
7. Audittrail
8. Configuring Projects
5. Architecture
1. Program structure
2. Objects
3. Views
4. Workflows
5. Reports
6. Services
6. Objects
1. Introduction
2. Definition
1. Fields
2. Default values
3. Constraints
4. Methods
5. Example
6. Inheritance
3. Access
1. Access types
2. Predefined Methods
3. Object Specific Methods
4. ObjectsWritingValues
4. Property Fields
7. Data loading
1. Introduction
2. Files loading
05/02/08
2
3. XML files structure
1. The data
2. IR Use
4. CSV Files
5. XML data files convention
6. Managing updates
8. Views
1. Introduction
2. File Format
3. Special Properties
4. Inheritancy in Views
5. Graphs in forms
9. Workflow
1. Introduction
2. Example of Workflows
1. Discount on orders
2. Purchase order and Invoice
3. Terminology
4. Define a Workflow
1. Workflow
2. The nodes (activities)
3. Transitions
5. Expressions
6. The notion of Roles
7. Error handling
10. Reports
1. Introduction
2. XSL:RML reports
1. XML Template
2. Introduction to RML
3. XSL:RML Stylesheet
3. Each reports with its own corporate header
4. Bar Codes
5. OpenOffice Report
6. Usual TAGS
7. How to add a new report
8. Customize report Header
11. Wizards
1. Introduction
2. Principles
3. How to add a new wizard
12. Events
1. Linking client events to actions
13. Information Repository (deprecated)
1. Introduction
2. Structure
3. Access Methods
14. MRPModule
1. Workcenters
2. Bill of Materials
3. Production Order
15. Modules
1. Writing a new module
2. Modules guidelines
3. Network management
05/02/08
3
16. Webservices
1. WebservicesIntro
2. WebservicesXMLRPC
3. An example in PHP
4. An example in Python
17. Misc
1. Load Balancing between servers
18. Multi − Company
19. Internationalization
05/02/08
4
1 Installation
05/02/08
5
1.1 Dependencies
1.1.1 The server
The Tiny ERP server depends on the following packages:
• PyParsing : http://pyparsing.sourceforge.net/
• PyDot : http://dkbza.org/pydot.html
• graphviz: http://www.graphviz.org/
• Win32Com : http://www.python.org/windows/win32com/
05/02/08
6
Then create the terp database. You have to make sure that your user has the correct credentials to create databases
with postgres, for more information on this subject please refer to the PostgreSQL manual.
Once the database created, you can start Tiny ERP. The content of the data base will automatically be created at the
first start.
$ ./tinyerp−server
05/02/08
7
The tables are rebuilt from the actual data base in production. To rebuild these tables, the server uses the description
of the objets and adds, modify or delete new fields.
To ask the data base upgrade while having a new version installed, you need to do:
The data base are rebuilt because of the XML files. For more information on these functionalities, go to the section
concerning XML files.
You can also execute the server with −−init=all. The server rebuilds then the data of beginning and deletes all existing
datas to come back in his basic configuration.
05/02/08
8
Those files are not necessary. If they are not found, the programs will start with the default configuration.
The client configuration file is automatically generated upon the first start. That of the server can be automatically
created with the command:
tinyerp_server.py −s
05/02/08
9
interface
Address the server will be bound to
port
Port the server will listen on
database
Name of the database to use
user
Username used when connecting to the database
translate_in
File used to translate tinyerp to your language
translate_out
File used to export the language tinyerp use
language
Use this languages as the server's language. This must be specified as the language two letter code.
verbose
Will used debugged output
This file is written on the disk when the option "−s" ou "−−save" has been put in parameter at the first start of the
server.
[options]
verbose = False
xmlrpc = True
database = terp
update = {}
port = 8069
init = {}
interface = 127.0.0.1
reportgz = False
05/02/08
10
login section
login
login name to use to connect to TinyERP server
server
address used by the server
port
port used by the server
path section
share
path used to find tinyERP's file
pixmaps
path used to fin tinyERP's pixmaps file
tip section
autostart
Should the client display tips at startup
position
Tip number the client will display
form section
autosave
Will the client automatically save the change you made to a record
printer section
preview
Preview report before printing
softpath
Path to the pdf previewer
softpath_html
Path to the html previewer
path
Command used to print
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
05/02/08
11
client section
default_path
Default path used by the client when saving/loading datas.
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/ged
05/02/08
12
−h, −−help
Show this help screen and leave the program.
−−version
Show the number of the version and leave.
−cFILE, −−config= FILE
Use an alternative configuration file. By default, the server use the file ~/.terp_serverrc
−s, −−save
Save the server configurationin the file ~/.terp_serverrc. By default, the options given to the line order are not
saved.
−pPORT, −−port= PORT
Specify the TCP port that the server must use. By default, the server uses the port 8069.
−−interface= INTERFACE
Specify the network interface that the server must use. In most of the case, you have to use the IP address of
the computer on which is the server. By default, the server uses the interface localhost and can accept the
connections only from the clients who are on the same computer.
−dDATABASE, −−database= DATABASE
Specify the name of the data base.
−iMODULE, −−init= MODULE
Initialize a module (use "all" for all modules). Attention: this option will delete all datas linked to this
module!
−uMODULE, −−update= MODULE
Upgrade a module (use "all" for all modules).
−v, −−verbose
Active the verbose mode.
05/02/08
13
$ kill −2 pid
05/02/08
14
2 Translations
Tiny ERP is multi−langage. You can add as much as langages you want. Each user can work with the interface in his
langage. Moreover, some ressources (text of reports, name of the products, ...) may also be translated.
This section explain how to change the langage of the program for the users and how to add new langages to Tiny
ERP.
First of all, you have to know that all the labels of interface is stored in the server. In the same way, the translations
are also stored in the server. By default, the english dictionnary is charged in the server, so if the users want to try
Tiny ERP in other langage, you have to charge these langages (in the server). The method for this is explain in the
next paragraph "To charge a translation file on the server".
But it is not possible to store "everything" on the server. Indeed, the user gots some menus, buttons, etc... that must
contain some text even before being connected to a server. These few words and sentences are translated using
GETTEXT. The choosen langage by default for these ones is the langage of the computer in which the user is
subscribe.
As I said before, the system of translation in Tiny ERP isn't limited to interface texts: it works also for reports and the
"content" of some fields (of data base). Obviously, all the data base fileds must not be translated. The fields where the
content is multi−langage are marked by a flag:
An administrator may also modify the preferences of the user (including the langage of the interface) in the menu:
Administration > Users > Users. He has to choose an user and notch on "préférences".
05/02/08
15
fields_view_get
Description: This method is use for getting all fields from the current view.
Signature
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context={})
Parameters
* context is a dictionary that contains: {'lang':'prefered language'}
read
Description
List of fields ressources values.
Signature
Parameters
Returns
all methods on objects (fields_view_get, read) process context before sending the result using values in the
ir.translation table.
05/02/08
16
For example, here are the first lines of a translation file (dutch):
Notes
• It is advised to start these orders when the data base is empty because you could publish directly the file of
translation.
To create a new data base (named 'terp_test'), use the next orders:
Alternatively, you could also delete your current data base using this order:
dropdb terp
createdb terp −−encoding=unicode
terp_server.py −−init=all
• If you translate the program in a new lanage or modify a current translation, send the translation file to
fp@tiny.be attached by a file credits.txt. We will publish this translation on our website (http://tinyerp.org).
• It is possible to translate only a part of Tiny ERP, to continu a translation already begun, or even to correct
few sentences of a translation file.
05/02/08
17
3 The client
The type of message
Differents types of messages can be analysed:
• rpc
♦ exception
♦ request
♦ result
• common
♦ message
♦ option
• view
♦ form
♦ tree
If you want to analyse several types of message, you can divide them with a comma in the arguments of the user;
This request can post the XML−RPC tasks sent to the server and the results given back.
Levels of logs
Some levels are available. You can, for example, log only the messages that are coming from an alert or an error.
• DEBUG
• INFO
• WARNING
• ERROR
• CRITICAL
The user argument concern the minimum level to log. So, if you select DEBUG, you obtain all the messages. If you
select WARNING, you obtain the messages from the following levels; WARNING, ERROR, CRITICAL.
logging.logger
list of the namespace to log separated by a comma. Value by default: none.
logging.level
level of log. Value by default: WARNING
Example (~/.terprc):
[logging]
output = stdout
logger = rpc.request,common.message
verbose = False
05/02/08
18
level = INFO
• −l level ou −−log−level=level
• −d channels ou −−log=channels
Option verbose
The option verbose allows you to log at the level 'INFO'. this option is the same as the option −−log−level=INFO
logging.verbose: True
−v, −−verbose
Examples
Requests XML−RPC
To log the requests XML−RPC but not the responses:
To log the requests and the exceptions but not the responses:
05/02/08
19
Some modules depend on other modules. See the file addons/module/__terp__.py for more information on the
dependences.
{
»···"name" : "Tiny TERP Accounting",
»···"version" : "1.0",
»···"depends" : ["base"],
»···"init_xml" : [ "account_workflow.xml", "account_data.xml", "account_demo.xml"],
»···"update_xml" : [ "account_view.xml", "account_report.xml", "account_wizard.xml"],
}
When initializing a module, the files in the init_xml list are evaluated in turn the the files in update_xml are evaluated.
When updating a module only the files from update_xml are evaluated.
05/02/08
20
4.1 ModulesAdminBase
This module is the only one who needs to be installed in Tiny ERP (mandatory).
05/02/08
21
The users
They represent physical persons. These are identified with a login and a password. A user may belong to severel
groups and may have several roles.
A user must have an action. This action is executed when the user connects to the program with his login and
password. An exampleof action would; 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 langage of work of this user. By default, English is set up.
A user can modify his own preferences while he's working on Tiny ERP. For that, he clicks on this menu; User >
Preference.
The groups
The groups determine the access rights to the different ressources. There is three types of right:
A user can belong to several groups. If he belongs to some groups, we use always the group with the highest rights for
a selected ressource.
The roles
The roles define a hierarchical structure in tree. They represent the different posts of the company. The bigest role has
automatically the rights of all its wires. Example:
• CEO
05/02/08
22
♦ Technical manager
◊ Chief of the project
⋅ Developpers
⋅ Testers
♦ Commercial manager
◊ Salers
◊ ...
If we want to valid 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 transitions of Workflow actions in the confirmation actions, choice or validation. Their
implication will be detailed in the Workflow section.
05/02/08
23
• User connection
• The user double−click on the menu
• The user click on the icon 'print' or 'action'
Example of events
Into Tiny ERP, all the actions are described and not configured. Two examples are briefly describe here:
When the user open the option of the menu "Operations > Partners > Partners Contact"., the next steps are done to
give to the user information on the action to undertake.
User connection
When a new user is connected to the sever, 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.
05/02/08
24
The fields
Action Name
The action name
Action Type
Always 'ir.actions.act_window'
View Ref
The view used for showing the object
Model
The model of the objet to post
Type of View
The type of view (Tree/Form)
Domain Value
The domaine that decreases the visible datas with this view
The view
The view describes how is traced the edition form or the datas tree/list. The views are from type 'Form' or 'Tree'
according to whether they represent a form for the edition or a list/tree for a global datas view.
A form can be asked by an action opening in mode 'Tree'. The form is open in the list mode (like the user push on
'switch view').
The domain
This notion allows you to regulate which ressourcesare 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;
05/02/08
25
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 ressources which are the roots of the tree. The other ressources,
even if they are not from a part of the domain will be posted if the user develop the branches of the tree.
05/02/08
26
4.1.3 Sequences
Two objects are defined to generate sequences of numbers;
Principle
The ressources which need a sequence number record a name and a code in the types of sequences. This operation is
generally done once by the installation, at the first start of the server.
Several sequences can be defined for every type. When the ressource (invoice, location, ...) needs a sequence number,
this one is generated by an active sequence corresponding to the asked type.
The fields
Sequence Name
the name of the sequence,
Sequence Code
the defined code in the types of sequences
Active
As all forms having an 'active' case, the numbers of sequences can be activated or unactivated notching or not
this case.
Next Number
The next number
Increment Number
The step for the sequence
Example;
• prefix = LOT
• nextnumber = 1000
• suffix = TS
• increment = 10
The next generated number will be 'LOT1000TS'. The next one will be 'LOT1010TS', ...
05/02/08
27
4.1.4 Requests
Objects defined
res_request
The requests
res_request_link
The objects linked to requests
res_request_history
The history of all the requests
Principle
Sometimes, you just wish that an action inform a user that something has happened. When you have to do this, just
use the request object. This object allows you to send small pieces of information to any user about ongoing events. It
also allows you to link an object to the request you wish to sent.
The fields
In the table request you can find the following fields:
name
The request description
active
Activate or deactivate the request
req_ref
The request reference
req_type
The type of the request, must be one of the following: 'remind' or 'info'.
req_priority
The priority of the request. It must be one of the following: '0', '1' or '2', the higher the number the higher the
priority.
req_summary
A small summary of the request
state
The state of the request, must be one of the following: 'draft', 'wait', 'active', 'close'.
act_from
the user who emitted the request
act_to
the user who received the request
act_title
the action's result
act_date
the when the action was triggered
ref_partner_id
the partner the request is about
ref_doc1
the first reference for the request
ref_doc2
the second reference for the request
req_history
all the request history
05/02/08
28
Creating a request is just as simple as (taken from the project module) :
request = self.pool.get('res.request')
request.create(cr, uid,
{'name' : "Task '%s' closed" % task.name,
'req_ref' : 'project:task:%d' % task.id,
'req_type' : 'info',
'req_priority' : str(task.priority),
'state' : 'wait',
'req_summary' : "Task '%s' closed by %s" % (task.name, task.user_id.name),
'act_from' : task.user_id.id,
'act_to' : task.project_id.manager.id,
'ref_doc1' : 'project.task,%d'% (task.id,),
'ref_doc2' : 'project.project,%d'% (task.project_id.id,),
'act_title' : "Task '%s' closed" % (task.name,),
})
05/02/08
29
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 > Menus > Grant Access to
menu and select the groups that can use this menu item.
Beware ! If the superuser does not belong to one of ther group, he will not be able to reach this menu again.
05/02/08
30
4.2 ModulesAdminCRM
Few examples;
• Customer Activity 1
• Very Important Customer
• Textile Supplier
• Newsletter Tiny ERP
The categories can be assigned manually (Newsletter, Textile Supplier) or automatically with the automatic
segmentations tool. An example would 'Very Important Customer', based on the amount of the partner's purchase.
Once the categories are defined, they can be used in the partners files.
05/02/08
31
The sale canals
The sale canals indicate the various methods of sale and communication between the partner and the company. Few
examples;
• Website / Email
• Shop 1
• Phone
• A residence
The canals are used when there are contacts with the partner in the form like the sale/ppurchase opportunities and the
Help−desk. You can, for example, make some analysis on different canals; sales pipeline, ...
Level of value:
In the different interactions with the customer, the Tiny ERP users can marked his state of mind according to their
contacts having with the customer. The program is able to calculate the staat of mind of the customer depending on
the last interactions with the same customer.
The program can calculate, with the automatic segmentation, that the customer has a state of mind of 6.3. This number
is taking part of other parameters, the intervals between the tasks, the reductions, ...
This system can be very useful to send, for example, a promotion every month to the customers who are not happy.
Example of HelpDesk;
05/02/08
32
Example of sales opportunities;
• Website,
• ERP integration,
• store, ...
05/02/08
33
4.3 ModulesAdminAccount
Three ressources must be configured for the accounting
05/02/08
34
4.3.6 ModulesAdminAccountChart
The best method to create a new account chart is to write it directly in the XML file. So, this one will be simply
reusable. With this intention, consult the section on the data download. Tiny ERP may used several account charts at
the same time. With this intention, we use some different views of the same accounts.
Into Tiny ERP, the accounts are structured hierarchically with the type of accounts 'VIEW'. These can't be used in the
transactions, they are only used to structure the real accounts.
So, it is possible to make two different views of the same accounts. Here is a simple example;
• Shop 1 (*)
♦ Actives Mg 1 (*)
◊ Fortis Bank 1
◊ Cash 1
♦ Incomes Mg 1
♦ Expenses Mg 1
• Shop 2 (*)
♦ Actives Mg 2 (*)
◊ Fortis Bank 2
◊ Cash 2
♦ Incomes Mg 2
♦ Expenses Mg 2
These accounts may be presented differently with other accounts views but using the same real accounts; income,
expenses, money cash.
• Actives (*)
♦ Shop 1 (*)
◊ Fortis Bank 1
◊ Cash 1
• Shop 2 (*)
♦ Fortis Bank 2
♦ Cash 2
• Incomes (*)
♦ Incomes Mg 1
♦ Incomes Mg 2
• Expenses (*)
♦ Expenses Mg 2
♦ Expenses Mg 2
In these representations, the accounts marked with (*) are from the type 'view'.
05/02/08
35
4.3.7 ModulesAdminAccountCurrency
The accounting is multi−currencies. As a preliminary every currencies must be defined with their rate relative to the
default one. An account can be only in one currency.
It is better to have an account tree by currency but you can mix several accounts of different currencies in the
accounting.
05/02/08
36
4.3.8 ModulesAdminAccountTax
The taxes are used, by example, in the definition of a product or in the invoice lines.
The various taxes must be encode beforehand. There exists several modes of calculation for the taxes;
• Percentage
• Fixe
• Code Python
• None
The taxes can be applicable or not according to some criteria: partners, amount, number of pieces. Two application
tests are available;
• Always applicable
• Applicable according to a python code which will be executed for the test.
The dependencies
In some countries, the taxes can be composed or depended of another taxe. We can define a relation between the
different taxes. During a sale, if we specify a tax, the sub−taxes are also calculated.
On the invoice, you can specify 'Taxe on the screen' and the two taxes will automatically get inside the accounting.
You have to define:
|Name of the taxe|Type|Amount|Parent| |Taxe on the screen|None|0|| |VAT (21%)|Percentage|0.21|Taxe on the screen|
|Recup (1.52 EUR)|Fixed|1.52|Taxe on the screen|
You can also use this system if one tax must be ventilated on several accounts.
The accounts
Two accounts are mandatory for the taxes; paid and collected taxes. These two may be identical for the same tax or
not.
Taxes domain
Some taxes are only effective under certain forms. We define then their own domain.
For example, the auction houses in Belgium are subjected at the law of the margin sale. The VAT is not deductible
and can be invoiced with sale expenses. (percentage of adjudication). These taxes are then defined with the domain
'auction'. They are only used by the auction module.
05/02/08
37
applicable or not.
The value of the variable result is used as the amount of the tax and as the boolean indicating if the tax must be
computed. Some informations are accessible:
___Example___; the SACEM impose royalties on the works allocated more than 2500 EUR of 10% and of 5% if the
price is over 50.000 EUR.
result = 0.0
if price_unit>=50000:
»··result=price_unit*0.05
elif price_unit>=2500:
»··result=price_unit*0.1
result = price_unit>=2500
05/02/08
38
The compute code to calculate tax (at the end the result variable is the tax):
result =0.0
if price_unit>100:
result = 5 + price_unit * 0.06
else:
result= price_unit * 0.10
05/02/08
39
4.3.9 ModulesAdminAccountImputations
Sale order
Purchase order
Inventory
The movements
The accounts inventory from the movements are taken in account only in the case of an exit ot an entry in the location
that belongs to the company. The internal movements don't generate transactions.
A payment
05/02/08
40
4.4 ModulesAdminStock
Introduction to the stock management
Tiny ERP introduce a new concept for the stock management. As for the accounting, the stock management uses a
double−entry system; there is no batches apparition in the stock but only moves from a location to another. The stock
manages the suppliers and the customers.
So, when you order merchandise to the supplier, the supplier location receives automaticaly the batch. When you
receive the merchandise of the supplier the batch moves from the supplier location to the "My Stock" location.
Moreover, the stock management of Tiny ERP is represented by a tree. We can deliver baches in a specify warehouse
or deliver batches in the rack 3 that is situated at the level 2 of the warehouse. Even, the suppliers and customers
locations may be structured according to their geographical localization, their volume, ...
• location of materiels
• consigned stock
• stored merchandises of our suppliers
• different suppliers
• different customers with some of them renting merchandise
• two warehouses
♦ one with two stages
♦ the other with a consigned stock in the warehouse 1 of the carrefour customer
• suppliers
♦ textile suppliers
♦ it suppliers
• customers
♦ european customers
♦ non european customers
• company
♦ rented materials
♦ warehouse 1
◊ output
◊ stock 1
⋅ level 1
⋅ level 2
◊ stock 2
⋅ carrefour stock
⋅ others materials
05/02/08
41
You can obviously make prints or stock valorisations on each account (called locations).
The warehouses
Tiny ERP is multi−warehouses. Into Tiny ERP, the batches are stored in the locations. The warehouses define which
locations are in a warehouse. A warehouse uses 3 different locations (that may be the same or not).
• Warehouse
♦ OUTPUT
♦ STOCK
◊ Level 1
⋅ Room 1.1
⋅ Room 1.2
◊ Level 2
◊ INPUT
05/02/08
42
4.5 ModulesAdminSale
Tiny ERP is multi points of sale. A point of sale is different from a warehouse. It is a place where the sale transactions
are done, a warehouse is the article location and may be different from a point of sale.
The attributes
Shop Name
the name of a point of sale
Pricelist
the pricelist by default of this point of sale. This list is partner dependent.
Project
A point of sale can be associated to a project. (not mandatory)
Default Payment
The default payment method for this point of sale. This method is used in several circumstances; into an order
form for this point of sale, if any method of payment is specified, this one will be used in this POS by default.
Account Payment
All methods of payment accepted for this POS. A method of payment is an account, generally with the cash
type of your account chart.
Warehouse
05/02/08
43
A POS is associated to a warehouse. When there is an order in this POS, the merchandise is delivered from
this location.
05/02/08
44
4.6 Subscriptions
Example:
account_subscription.xml:
<?xml version="1.0"?>
<terp>
<data>
<delete model="subscription.document" search="[]"/>
=======================================================
Subscription Views
=======================================================
<record model="subscription.document" id="doc_invoice">
<field name="name">Invoice</field>
<field name="model">account.invoice</field>
</record>
<record model="subscription.document.fields">
05/02/08
45
</data>
</terp>
05/02/08
46
4.7 Audittrail
Module Audittrail
TinyERP gives you the opportunity to trace any call (read/write/create/unlink) to the database. This can be usefull to
check who tempered with the stock or deleted invoices.
How it works: when registering an audittrail rule, TinyERP replaces the traced method by a method that will first
create an audittrail log line and then execute the regular method. Audittrail rules can be registered for any object
(except audittrail.log) on all or any specific user(s)
For example, to trace any modification on invoices, go the the administration menu −> Audittrail −> Rules
Let's create a new rule named "test" which will record any write/create/delete on account.invoice for users "admin"
and "demo".
05/02/08
47
To make a rule active (so that it logs events) its state has to be "Subscribed"
(don't forget to make a few modifications on an invoice to feed the logs in this test)
Now, if we take a look at the administration menu −> Audittrail −> Logs, and search for all log lines we will see all
modifications made to invoices
05/02/08
48
If you subscribe a rule on, an object that is part of a module which normally loads after audittrail, don't forget to
update Audittrail depends to include that module or next time the server starts, the rule's state will be set back to
draft
If you make changes on a subscribed rule, unsubcribe it and subcribe it again after having saved the changes or
those changes won't apply
05/02/08
49
5 Architecture
05/02/08
50
Server/client, XML−RPC
Tiny ERP is a based on a client/server architecture. They 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, his arguments, and
the result are transported using HTTP and encoded using XML.
Client
The logic of Tiny ERP is configured on the server side. The client is very simple; it is only used to post datas (forms,
lists, trees) and to send back the result to the server. The updates and the addition of new functionalities don't need the
clients to be frequently upgraded, it makes Tiny ERP easier to maintain.
The client doesn't understand what he 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, ...) he sends this
action to the server. This one sends to the client the new action to execute.
05/02/08
51
5.2 Objects
All Tiny ERP ressources are objects: menus, actions, reports, invoices, partners, ... Tiny ERP is based on a object
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.
It is easier to manipulate one object (example, a partner) than several tables (partner address, categories, events, ...)
05/02/08
52
05/02/08
53
PostgreSQL
The ORM of Tiny ERP is constructed over PostgreSQL. It is thus possible to query the object used by Tiny 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.
05/02/08
54
5.3 Views
Views are a way to represent the objects on the client side. It indicates to the client how to lay out the data coming
from the objects on the screen.
• form views
• tree views
The same object may have several views; thus, the products have several different views according to the product
variants or not, the first defined view of a kind will be used as the default for its kind, that way you can have a default
tree view (that will as the view of a one2many) and a specialized view with more or less information that will appear
when some double click on a menu item. Views are described in XML.
If no view has been defined for an object, the object is able to generate a view to represent itself. This can limit the
developper's work but result in less ergonomic 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 datas on the object (account.invoice), the view, the domain
(only invoices not paid)
• The client asks (with XML−RPC) to the server what views are defined for the invoice object and what are the
datas he must show.
• The client displays the form according to the view
05/02/08
55
05/02/08
56
5.4 Workflows
The objects and the views allow you to define very simply new forms, lists/trees and interactions between them. But it
is not enough you have to define the dynamics of these objects.
Few examples:
The workflows describe with graphs these interactions. One or several workflows may be associated to the objetcs.
Workflow are not mandatory some objects don't have workflows.
Below, an example workflow use for sale orders. It must generate invoices and shippings according to some
conditions.
05/02/08
57
• create an invoice,
• cancel the sale order,
• generate the shipping order, ...
• the invoice
• the shipping
05/02/08
58
5.5 Reports
Tiny ERP uses a flexible and powerfull 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.
We first generate an XML file containing all the data needed by the report. This file is then processed using an XSL
stylesheet to produce an RML file which, in turn, is processed to produce either a PDF document or an HTML file.
05/02/08
59
5.6 Services
Services are python code methods. Some services are grouped
List of services
Service: workflow
• Object: workflow.wkf_service.workflow_service
♦ Methods
◊ workflow_service.trg_write
◊ workflow_service.trg_delete
◊ workflow_service.trg_create
◊ workflow_service.trg_validate
05/02/08
60
05/02/08
61
⋅ report_projects.args_get
⋅ report_projects.result
• Service: report.res.partner.address
♦ Object: report.interface.report_rml
◊ Methods
⋅ method report_rml.create
⋅ method report_rml.args_get
⋅ method report_rml.result
• Service: report.res.partner.ids
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.account.invoice
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.account.invoice.list
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.account.transfer
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.network.material.list
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.network.network.list
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.network.network.list2
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.stock.packing.list
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
05/02/08
62
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.stock.shipping.list
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.stock.lot.overview
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.stock.location.overview
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.purchase.order
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.sale.order
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
• Service: report.project.report
♦ Object: report.interface.report_rml
◊ Methods
⋅ report_rml.create
⋅ report_rml.args_get
⋅ report_rml.result
Service: wizard.res.partner.sms_send
• Object: base.res.partner.wizard.wizard_sms.part_sms
♦ Methods
◊ part_sms.execute
Service: wizard.res.partner.spam_send
• Object: base.res.partner.wizard.wizard_spam.part_email
♦ Methods
◊ part_email.execute
05/02/08
63
Service: wizard.res.partner.clear_ids
• Object: base.res.partner.wizard.wizard_clear_ids.wiz_clear_ids
♦ Methods
◊ wiz_clear_ids.execute
Service: wizard.account.journal
• Object: account.wizard.wizard_journal.wiz_journal
♦ Methods
◊ wiz_journal.execute
Service: wizard.account.move.line.reconcile
• Object: account.wizard.wizard_reconcile.wiz_reconcile
♦ Methods
◊ wiz_reconcile.execute
Service: wizard.account.invoice.split
• Object: account.wizard.wizard_invoice_split.wiz_reconcile
♦ Methods
◊ wiz_reconcile.execute
Service: wizard.account.invoice.refund
• Object: account.wizard.wizard_refund.wiz_refund
♦ Methods
◊ wiz_refund.execute
Service: wizard.account.move.line.check
• Object: account.wizard.wizard_verify.wiz_check
♦ Methods
◊ wiz_check.execute
Service: wizard.hr.si_so
• Object: hr.wizard.sign_in_out.wiz_si_so
♦ Methods
◊ wiz_si_so.execute
Service: wizard.campaign.partner.email_send
• Object: marketing.campaign.wizard.wizard_campaign_email.part_email
♦ Methods
◊ method part_email.execute
05/02/08
64
Service: name:wizard.campaign.partner.add
• Object: marketing.campaign.wizard.wizard_campaign_partner_add.part_add
♦ Methods
◊ part_add.execute
Service: wizard.campaign.partner.sms_send
• Object: marketing.campaign.wizard.wizard_campaign_sms.part_sms
♦ Methods
◊ part_sms.execute
Service: wizard.project.wiz_bill
• Object: project.wizard.billing.wiz_bill
♦ Methods
◊ wiz_bill.execute
Service: wizard.project.wiz_copy
• Object: project.wizard.proj_copy.wiz_copy
♦ Methods
◊ wiz_copy.execut
Service: wizard.project.wiz_copy
• Object: project.wizard.proj_copy.wiz_copy
♦ Methods
◊ wiz_copy.execute
Service: wizard.project.wiz_deactivate
• Object: project.wizard.proj_inactivate.wiz_deactivate
♦ Methods
◊ wiz_deactivate.execute
05/02/08
65
6 Objects
05/02/08
66
6.1 Introduction
All the ERP datas are accessible as "objects". As an exemple, there is a res.partner object to access the datas
concerning the partners, an account.invoice object for the ones concerning the invoices, etc...
Please note that there is an object for every type of ressource, and not an object per ressource. We have thus a
res.partner object to manage all the partners and not a res.partner object per partner. If we talk in "oriented
object" terms, we could also say that there is an object per level.
The direct consequences is that all the objects methods have a common parameter: the "ids" parameter. This one
specify on which ressources (for example: on which partner) the method must be applicated. Precisily, this parameter
contains a list of ressource 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 applicate the res_partner's
method "send_email", we will write something like:
We will see the exactly syntax of object method calls further in this document.
In the following operation, we will see how to define a new object. Then, we will check out the different methods to
do it.
05/02/08
67
6.2 Definition
To define a new object, you have to describe a new python class. 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):
...
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
Default value: True.
_columns (required)
The object fields. See the fields section for details.
_constraints
The constraints on the object. See the constraints section for details.
_defaults
The default values for some of the object's fields. See the default value section for details.
_name (required)
Name of the object. Default value: None.
_table
Name of the SQL table. Default value: the value of the _name field above of which the dots ( . ) are replaced
by underscores ( _ ).
_rec_name
Name of the field in which the name of every ressource is stored. Default value: 'name'. Note: by default, the
name_get method simply returns the content of this field.
_order
Name of the fields used to sort the results of the search and read methods. Default value: 'id'.
_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', ...}. Default value: {}.
_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 (if _auto is True)
_log_access
Determine whether or not the write access to the ressource 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.
05/02/08
68
6.2.1 Fields
Objects may contain different types of fields. Those types can be divided into two categories: simple types and
relation types. The simple types are integers, floats, booleans, strings, etc. The relation types are used to represent
relations between objects.
boolean
a boolean (true, false)
integer
an integer
float
a floating point number. The optional parameter digits define the precision and scale of the number. The scale
being the number of digits 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 every
value can be converted to their 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)),
char
a string of limited length. The size parameter determines its size.
text
a text field with no limit in length.
date
a date
datetime
allows to store a date and the time of day in the same field.
binary
a binary chain
function
a field whose value is calculated by a function (rather than being stored in the database).
selection
a field which allows the user to make a selection between various predefined values
05/02/08
69
Example:
'state': fields.selection((('n','Unconfirmed'),('c','Confirmed')),'State', required=True)
one2one
Parameters: obj
many2one
Parameters: obj
Optional parameters:
• ondelete: What should happen when the resource this field points to is deleted.
many2many
Example:
'category_id':
fields.many2many(
'res.partner.category',
'res_partner_category_rel',
'partner_id',
'category_id',
'Categories'),
reference
Example:
def _links_get(self, cr, uid):
cr.execute('select object,name from res_request_link order by priority')
05/02/08
70
return cr.fetchall()
...
'ref':fields.reference('Document Ref 2', selection=_links_get, size=128),
...
Note: If you wish to use the reference field in one of your modules, you must add your object to the list of
referencable objects. This action is done using a few lines of xml as in:
<record model="res.request.link">
<field name="name">Project Task</field>
<field name="object">project.task</field>
</record>
readonly
Whether the field is editable or not.
required
Whether the field is required or not. The program will refuse to save a resource if a required field is left blank.
states
This parameter permits to define attributes for this field that will only be available in some states of the
resource.
string
The label of the field.
05/02/08
71
translate
Whether or not the content of this field should be translated (ie managed by the translation system).
size
priority
Default value: 0.
domain
invisible
Default value:
context
selection
05/02/08
72
Because default values do not have to be static (they could, for example, depend on the values of resources already
created), you have to define "default values functions" and not simple "scalar" default values.
To define default values for an object fields, you have to define a dictionary of the form: {'name_of_the_field':
function, ...}
These functions take 4 parameters (obj, cr, uid, context) and can return any simple type (boolean, integer, string, etc.).
• obj: the osv object corresponding to the type of resource being created
• cr: a database cursor
• uid: the user ID of the person creating the record
• context: the current context (given by the client)
Default values are usually declared by using python's anonymous functions (also known as lambda functions). If you
want more information about lambda functions, please refer to the python tutorial or python manual.
Example
Here is the declaration of default values for the "sale.order" object:
_defaults = {
'date_order': lambda *a: time.strftime('%Y−%m−%d'),
'state': lambda *a: 'draft',
'user_id': lambda obj, cr, uid, context: uid
}
05/02/08
73
6.2.3 Constraints
Tiny ERP objects can optionally have custom integrity/validity constraints. In case such a constraint is transgressed,
the data modification which broke the constraint is cancelled and an error message appears on the screen.
Format
Constraints can be defined by using the _constraints attribute. If defined, it should be a list of tuples (a tuple per
constraint) of the form:
where
• method is an object method used to check the constraint. This method must have the following signature:
• error message is the error message displayed to the user if the constraint check fails.
• list_of_field_names is the list of the names of the fields that will be added to the error message. It should help
the user understand why the constraint check failed.
Example
Here is the definition of the integrity constraint for the object "account.move":
_constraints = [
(_constraint_sum, 'Error: the sum of all amounts should be zero.', ['name'])
]
05/02/08
74
6.2.4 Methods
All objects define a set of methods as we will see in the section "6.3.2 predefined methods". Of course, you can also
add your own methods to a particular object.
To define such a method you only have to add it to source of the python class you're writing.
All the defined methods on an object are accessible from the outside (other objects or RPC), but don't forget the
python convention that makes private method prefixed with an underscore
05/02/08
75
6.2.5 Example
Here is an example of the definition of an object. More particulary, here is the definition of the object res.partner.
class res_partner(osv.osv):
def _credit_get(self, cr, uid, ids, prop, unknow_none, unknow_dict):
res={}
for id in ids:
acc = ir.ir_get(cr, uid, [('meta','res.partner'),('name','account.receivable')],[
cr.execute('select sum(amount) from account_move_line where account_id=%d and pa
res[id]=cr.fetchone()[0] or 0.0
return res
_name = "res.partner"
_auto=True
_columns = {
'name': fields.char('Name', size=64, required=True),
'name2': fields.char('Corporation Type', size=64),
'parent_id': fields.many2one('res.partner','Main Company'),
'commercial': fields.many2one('res.users','Commercial'),
'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'),
'ref': fields.char('Identifiant', size=64),
'lang':fields.char('Langage',size=2),
'vat':fields.char('VAT',size=32),
'bank':fields.char('Bank account',size=64),
'website':fields.char('Website',size=64),
'comment':fields.text('Notes'),
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'),
'category_id': fields.many2many('res.partner.category', 'res_partner_category_rel', 'p
'events': fields.one2many('res.partner.event', 'partner_id', 'events'),
'credit': fields.function(_credit_get, fnct_search=_credit_search, method=True, string
'debit': fields.function(_debit_get, fnct_search=_debit_search, method=True, string='D
}
05/02/08
76
6.2.6 Inheritancy
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'
Example:
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()
In this example, the 'custom_material' will add a new field 'manuf_warranty' to the object 'network.material'.
You can also modify the definition of an already existing field from the parent object.
05/02/08
77
05/02/08
78
6.3 Access
05/02/08
79
* to access it directly
* by the netservice
* by xmlrpc
self.pool.get('name_of_the_object')
for example:
partner_object = self.pool.get('res.partner')
Calling a method on the partner object we just recovered has the following form:
partner_object.name_of_the_method(parameters_for_that_method)
This kind of access is only possible inside objects, and thus, locally on the server.
Ref: osv/orm.py
Netservice access
Outside of an object but inside the server nevertheless (for example in the reports and wizards), we can reach an object
access point with the command:
service = netsvc.LocalService("object_proxy")
Ref: osv/osv.py
XML−RPC access
From the network (LAN or internet), to reach an access point to the objects use the following command:
sock = xmlrpclib.ServerProxy('http://server_address:port_number/xmlrpc/object')
Réf: service/web_services.py
05/02/08
80
create
Description
Create a new ressource
Signature
def create(self, cr, uid, vals, context={})
Parameters
• vals: a dictionary of values for every field. This dictionary must use this form: {'name_of_the_field': value,
...}
• context (optional): the actual context.
Returns
the id of the newly created resource.
search
Description
Search all the resources which satisfy certain criteria
Signature
def search(self, cr, uid, args, offset=0, limit=2000)
Parameters
• args: a list of tuples containing the search criteria. This list must be of the form: [('name_of_the_field',
'operator', value), ...]. The available operators are:
♦ =, >, <, <=, >=
♦ like, ilike
♦ child_of
• offset (optional): does not return the first offset results.
• limit (optional): the maximum number of results to gat back.
Returns
the list of ids of matching resources
read
Description
List of fields ressources values.
Signature
def read(self, cr, uid, ids, fields=None, context={})
Parameters
05/02/08
81
Returns
A list of dictionaries (a dictionary per resource asked) of the form [{'name_of_the_field': value, ...}, ...]
browse
Description
Return one or several ressources 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 take 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 (amongst other things) the field
'bank':fields.char('Bank account',size=64),
and the object res.partner.address contains (amongst other thing) the fields
so, to recover the two fields that interest us, you have to write:
nom = addr_obj.name
compte = addr_obj.partner_id.bank
Signature
def browse(self, cr, uid, select, offset=0, limit=2000)
Parameters
Returns
• if an integer (identifier) has been passed as select parameter, return an object having the properties described
here above.
• if a list of integer (identifiers) has been passed, return the object list.
Remarque
This method is only useful locally (on the server itself) and not with the other intefaces !!
05/02/08
82
write
Description
Writes values in one or several fields of one or several ressources
Signature
def write(self, cr, uid, ids, vals, context={})
Parameters
Returns
True
unlink
Description
Delete one or several ressources
Signature
def unlink(self, cr, uid, ids)
Parameters
Returns
True
• fields: the fields list which we want to recover the value by default.
• form (optional): TODO
• reference (optional): TODO
Returns
dictionary of the default values of the form {'field_name': value, ... }
default_set
Description
Change the default value for one or several fields.
Signature
05/02/08
83
• 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 determinates if the new default vlaue must be available only for the current
user or for all the users.
Returns
True
Returns
a list of dictionaries with the following keys
perm_write
Description
Signature
def perm_write(self, cr, uid, ids, fields)
Parameters
Returns
05/02/08
84
• fields: a list of fields that interest us, if None, all the fields
• context: context['lang']
Result
fields_view_get
Description
Signature
def fields_view_get(self, cr, uid, view_id=None, view_type='form',
context={})
Parameters
Result
distinct_field_get
Description
Signature
def distinct_field_get(self, cr, uid, field, value, args=[], offset=0,
limit=2000)
Parameters
Result
res = []
for r in self.read(cr, user, ids, ['name','zip','city']):
addr = str(r['name'] or '')
if r['name'] and (r['zip'] or r['city']):
addr += ', '
addr += str(r['zip'] or ) + ' ' + str(r['city'] or )
res.append((r['id'], addr))
return res
05/02/08
85
name_search
Description
Signature
def name_search(self, cr, uid, name='', args=[], operator='ilike',
context={})
Parameters
Result
05/02/08
86
To use a specific method to an object is making by the same way of the predefined methods. The different types of
access sawn before stay available.
05/02/08
87
6.3.4 ObjectsWritingValues
Where
• 0: create
• 1: write
• 2: unlink
• 6: set the link to a list of IDs (ID3)
Example:
Example in objects:
Link:
Create:
05/02/08
88
Declaring a property
Then you have to create the default value in a .XML file for this property:
TIP: if the default value points to a ressource from another module, you can use the ref function like this:
<field name="value" eval="'product.pricelist,'+str(ref('module.data_id'))"/>
Properties are displayed by section, depending on the group_name attribute. (It is rendered in the client like a
separator tag).
05/02/08
89
How does this work ?
The fields.property class inherits from fields.function and override the read and write method. The type of this
field is many2one, so in the form a property is represented like a many2one function.
But the value of a property is stored in the ir.property class/table as a complete record. The stored value is a
field of type reference (not many2one) because each property may points to a different object. If you edit
properties values (from the administration menu), these are represented like a field of type reference.
When you read a property, the program gives you the property attached to the instance of object you are
reading. It this object has no value, the system will give you the default property.
The definition of a property is stored in the ir.model.fields class like any other fields. In the definition of the
property, you can add groups that are allowed to change to property.
When you want to add a new feature, you will have to choose to implement it as a property or as normal field.
Use a normal field when you inherit from an object and want to extend this object. Use a property when the
new feature is not related to the object but to an external concept.
Here are a few tips to help you choose between a normal field or a property:
• 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 interresting think is that properties avoid spagetthi codes. 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.)
05/02/08
90
This module inherits from the product.product object and add new fields to it:
class product_product(osv.osv):
_inherit = 'product.product'
_name = 'product.product'
_columns = {
'life_time': fields.integer('Product lifetime'),
'use_time': fields.integer('Product usetime'),
'removal_time': fields.integer('Product removal time'),
'alert_time': fields.integer('Product alert time'),
}
product_product()
This module add simple fields to the product.product object. We did not used 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 fields, the different delays are managed by the same people
that manage all products.
05/02/08
91
7 Data loading
05/02/08
92
7.1 Introduction
At the Tiny ERP installation, two steps are necessary to create and feed the data base:
The creation (or modification in the case of an upgrade) of SQL tables is automated thanks to the description of
objects in the server.
Into Tiny ERP, all the logic of the application is stored in the data base. We find for example:
There must be a mecanism to describe, modify and reload the different datas. These datas are represented into a set of
XML files that can eventually be loaded at the start of the program in order to fill in the tables.
05/02/08
93
{
"name" : "Tiny TERP Accounting",
"version" : "0.1",
"depends" : ["base"],
"init_xml" : [ "account_workflow.xml", "account_data.xml"],
"update_xml" : [ "account_view.xml", "account_report.xml"],
}
The fields
name
The name of the module, an arbitrary english name.
version
The version of the module. This version is not the same version as the Tiny ERP version. The modules evolve
indenpendently of the program.
depends
The list of modules this module depends upon. The dependencies are very important because they determinate the
order in which the different modules are loaded.
For example, if the workflow of the sale order (module:sale) uses the workflow of an invoice (module:account), it is
important to create the account workflow before the sale order workflow.
init_xml
The XML files loaded when the server is started with the argument: −−init=module.
update_xml
The XML files to loaded when the server is started with the argument: −−update=modulename. If the server is
executed with the argument −−init=modulename, the files 'update_xml' are also downloaded.
We generally find:
05/02/08
94
05/02/08
95
<?xml version="1.0"?>
<terp>
<data>
...
</data>
</terp>
05/02/08
96
record tag
The addition of new data is made with the record tag. This one takes a mandatory attribute : model. Model is the
object name where the insertion has to be done. The tag record can also take an optional attribute: id. If this attribute is
given, a variable of this name will be able to be used later on in the same file to make reference to the new created
ressource ID.
A record tag may contain field tags. They indicate the record's fields value. If a field is not specified the default
value will be used.
Example
<record model="ir.actions.report.xml" id="l0">
<field name="model">account.invoice</field>
<field name="name">Invoices List</field>
<field name="report_name">account.invoice.list</field>
<field name="report_xsl">account/report/invoice.xsl</field>
<field name="report_xml">account/report/invoice.xml</field>
</record>
field tag
The attributes that may conatain the field tag are the followings:
name
mandatory attribute indicating the field name
eval
python expression that indicating the value to add
ref
reference to an id defined in this file
function tag
model
name
eval
should evaluate to the list of parameters of the method to be called, excluding cr and uid
Example
<function model="ir.ui.menu" name="search" eval=""/>
getitem tag
Takes a subset of the evaluation of the last child node of the tag.
type
int or list
index
int or string (a key of a dictionary)
05/02/08
97
Example
Evaluates to the first element of the list of ids returned by the function node
05/02/08
98
7.3.2 IR Use
The ir_set tag allows you to insert new values in the information repository. This tag must contain several field tags.
field tags
attributes: name and eval
The attributes are those defined by the access methods to the information repository. We must provide him with
several attributes: keys, args, name, value, // isobject, replace, meta. optional fields
Example
<ir_set>
<field name="keys" eval="[('action','client_print_multi'),('res_model','account.invoice')]"/>
<field name="args" eval="[]"/>
<field name="name">Print Invoices</field>
<field name="value" eval="'ir.actions.report.xml,'+str(l0)"/>
<field name="isobject" eval="True"/>
<field name="replace" eval="False"/>
</ir_set>
05/02/08
99
Instead of using .XML file, you can import .CSV files. It is simpler but the migration system does not migrate
the data imported from the .CSV files. It is like the noupdate attribute in .XML files. It is also more difficult to
keep track of relations between resources and it is slower at the installation of the server.
Use this only for demo data that will never been upgraded from one version of Tiny ERP to another.
The name of the object is the name of the file before the first dot. You must use one file per object to import.
For example, to import a file with partners (including their multiple contacts and events), the file must be
named like one of the following example:
• res_partner.csv
• res_partner.tiny_demo.csv
• res_partner.tiny.demo.csv
For the name of the object, use underscores (_) instead of dots. (.)
Have a look at the user manual for a complete description on how to construct your .CSV file.
Usefull info:
• Separator of field: ,
• Quote of fields: "
• Encoding to use: UTF-8
You can import .CSV file that have been exported from the Tiny ERP client. This is interesting to create your
own demo module. But both formats are not exactly the same, mainly due to the conversion: Structured Data
-> Flat Data -> Structured Data.
• The name of the column (first line of the .CSV file) use the end user term in his own language when you
export from the client. If you want to import from a module, you must convert the first column using the
fields names. Example, from the partner form:
Name,Code,Contacts/Contact Name,Contacts/Street,Contacts/Zip
becomes
name,ref,address/name,address/street,address/zip
05/02/08
100
• When you export from the Tiny ERP client, you can select any many2one fields and their children
relation. When you import from a module, Tiny ERP tries to recreate the relation between the two
resources. For example, do not export something like this from a sale order form - otherwise Tiny ERP
will not be able to import your file: Order
Description,Partner/Name,Partner/Payable,Partner/Address/Name
• To find the link for a many2one or many2many field, the server use the name_search function when
importing. So, for a many2one field, it is better to export the field 'name' or 'code' of the related resource
only. Use the more unique one. Be sure that the field you export is searchable by the name_search
function. (the 'name' column is always searchable). Order Description,Partner/Code
• Change the title of the column for all many2many or many2one fields. It's because you export the
related resource and you import a link on the resource. Example from a sale order: Partner/Code should
become partner_id and not partner_id/code.
• Many2many fields. If all the exported data contains 0 or 1 relation on each many2many fields, there will
be no problem. Otherwise, the export will result in one line per many2many. The import function expect
to get all many2many relations in one column, separated by a comma. So, you have to make to
transformation. For example, if the categories "Customer" and "Supplier" already exists :
name,category_id Smith,"Customer,Supplier"
• Readonly fields. Do not try to import readonly fields like the amount receivable or payable for a partner.
Otherwise, Tiny ERP will not accept to import your file.
• Exporting trees. You can export and import tree structures using the parent field. You just have to take
care of the import order. The parent have to be created before his children.
05/02/08
101
The workflow files have to be loaded before the datas ! Otherwise, the ressource created won't be integrated inside the
workflow because the later is not yet defined.
05/02/08
102
Table/Object structure
When you run tinyerp−server with option −−init or −−update, the table structure are updated to match the new
description that is in .py files. Fields that are removed are not removed in the postgresql database not to lose data.
So, simply running −−update or −−init, will upgrade your table structure.
It's important to run −−init=module the first time you install the module. Next time, you must use the
−−update=module argument instead of the init one. This is because init loads ressources that are loaded only once and
never upgraded (eg: ressources with no id="" attribute or within a noupdate="1" <data> tag).
Data
Some data is automatically loaded at the installation of Tiny ERP:
This data is also migrated to a new version if you run −−update or −−init.
Workflows
Workflows are also upgraded automatically. If some activities are removed, the documents states evolves
automatically to the preceding activities. That ensure that all documents are always in valid states.
You can freely remove activities in your XML files with the unlink method. If workitems are in this activity, they will
evolve to the preceding unlinked activity. And after the activity will be removed.
Resources which don't contain an id are created (and updated) only once; at the installation of the module or when you
use the −−init argument.
If a resource has an id and this resource is not present anymore in the next version of the XML file, Tiny ERP will
automatically remove it from the database. If this resource is still present, Tiny ERP will update the modifications to
this resource.
If you use a new id, the resource will be automatically created at the next update of this module.
05/02/08
103
• view_invoice_form,
• view_move_line_tree,
• action_invoice_form_open, ...
It is important to put id="...." to all record that are important for the next version migrations. For example, do not
forget to put some id="..." on all workflows transitions. This will allows Tiny ERP to know which transition has been
removed and which transition is new or updated.
Custom modules
For example, if you want to override the view of an object named 'invoice_form' in your xml file (id="invoice_form").
All you have to do is redefine this view in your custom module with the same id. You can prefix ids with the name of
the module to reference an id defined in another module.
Example:
This will override the invoice form view. You do not have to delete the old view, like in 3.0 versions of Tiny ERP.
Note that it is often better to use view inherytancy instead of overwritting views.
In this migration system, you do not have to delete any ressource. The migration system will detect if it is an update or
a delete using id="..." attributes. This is important to preserve references duing migrations.
Demo datas
Demo datas do not have to be upgraded; because they are probably modified, deleted, ... by users. So, to avoid demo
data to be upgraded, you can put a noupdate="1" attribute in the <data> tag of your .xml data files.
05/02/08
104
8 Views
Views are used to represent objects on the client side.
05/02/08
105
8.1 Introduction
As all data of the program is stored in objects, as explained in the Objects section, how are these objects exposed to
the user ? We will try to answer this question in this section.
First of all, let's note that every resource type uses its own interface. For example, the screen to modify a partner's data
is not the same as the one to modify an invoice.
Then, you have to know that the Tiny ERP user interface is dynamic, it means that it is not described "statically" by
some code, but dynamically built from XML descriptions of the client screens.
A notable characteristic of these views is that they can be edited at any moment (even during the program execution).
After a modification to a displayed view has occurred, you simply need to close the tab corresponding to that 'view'
and re−open it for the changes to appear.
Views principles
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.
1. form views
2. tree views
Form views
The field disposition in a form view always follows the same principle. Fields are distributed on the screen following
the rules below:
05/02/08
106
• A view field can take the place of several columns. For example, the light blue zone on the screen−shot below
is, in fact, the only field of a "one to many". We will come back later on this note, but let's see that he takes
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 blue zones of the screen above are good examples. Precisely, the blue framework up and on
the right side takes the place of two columns, but contains 4 columns.
As we can see it below in the orange zone of the screen, there is also a way to distribute the fields of an object on
different tabs.
05/02/08
107
Tree views
These views are used when we work in list mode (in order to visualize several resources at once) and in the search
screen. These views are simpler than the form views and thus have less options.
The different options of views will be detailed into the next section.
Tree views can define colors for each row of the tree. To do this, you have to add the attribute color like this sample;
05/02/08
108
<?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.
Special tags
<form string="Sale Order"> ... </form>
The form tag defines a new view form, it takes a string attribute that defines the tab name for this view.
<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.
<page string="Order Line"> ... </page>
defines a new notebook page for the view.
05/02/08
109
<newline/>
force a return to the line even if all the columns of the view are not filled in.
<label string="Test"/>
adds a simple label using the string attribute as caption.
<separator string="Links" colspan="4"/>
add a separator line. The string attribute defines its label and the colspan attribute defines his horizontal size
(in number of columns).
<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, ...).
<group>
groups several columns and split the group in as many columns as desired.
05/02/08
110
widget
can change the widget.
Example: widget="one2many_list"
on_change
define a function that is called when the content of the field changes.
Example: on_change="onchange_partner(type,partner_id)"
See ViewsSpecialProperties for details
<?xml version="1.0"?>
<terp>
<data>
...
05/02/08
111
</page>
<page string="Notes">
<field name="note" colspan="3" string=""/>
</page>
<page string="Links">
<field name="invoice_id" select="1" colspan="4"/>
<field name="shipping_id" colspan="4"/>
</page>
</notebook>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="a">
<field name="name">sale.order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
<field name="view_type">form</field>
<field name="view_id" ref="v"/>
</record>
<menuitem name="Operations/Sales/Order" action="a"/>
...
</data>
</terp>
05/02/08
112
You can add parameters to the function, these parameters are defined in a view.
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, whic will be pass to the
action. See the example below :
<field name="name">account.account.tree1</field>
<field name="res_model">account.account</field>
<field name="view_type">tree</field>
<field name="view_id" ref="v"/>
<field name="domain">[('code','=','0')]</field>
<field name="context">{'project_id': active_id}</field>
</record>
05/02/08
113
Inheritancy in views
When you create and inherit objects in some custom or specific modules, it is better to inherit (than to replace) from
an existing view to add/modify/delete some fields and preserve the others.
Example:
The inheritancy engine will parse the existing view and search for the the root nodes of "<field name="arch"
type="xml">". 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.
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.
05/02/08
114
<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 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.
05/02/08
115
9 Workflow
05/02/08
116
9.1 Introduction
The workflow system in Tiny 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.
• Discount on orders
• Sale order and invoice
05/02/08
117
05/02/08
118
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.
Let's suppose a company has a need not implemented in TinyERP. For example, suppose their commercials can only
offer discounts of 15% or less. Every order having a discount above 15% must be approved by the commercial
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.
05/02/08
119
We could then further improve this workflow by sending a request to the sale 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 sale manager to validate or not the order.
05/02/08
120
05/02/08
121
05/02/08
122
9.3 Terminology
Workflow
A workflow is a graph of actions/conditions associated to a type of resource/document (model). You can have several
workflows defined for the same document; in that case, they are all executed at the same time.
Activity
Activities represent the different possible states of a document. They are the nodes of a workflow. Actions can
optionally be associated with each activity so that they are executed when a document enters into that activity.
Transition
Transitions are conditions to be satisfied in order to pass from one activity to another. Transitions are represented by
arcs between two activities. These conditions can be of several types:
Instance
An instance is the state of the workflow for a particular document.
Workitem
A workitem represents an activity being executed for a given instance. It is possible to have several active workitems
at the same time in a single workflow instance.
05/02/08
123
05/02/08
124
9.4.3 Workflow
A WKF resource represents a given workflow.
05/02/08
125
The fields
split_mode:
In the AND mode, the activity waits for all transitions to be valid, even if some of them are already valid. They are all
triggered at the same time.
join_mode:
kind:
• DUMMY: Do nothing
• FUNCTION: Execute the function selected by an action.
• SUBFLOW: Execute a sub−workflowSUBFLOW_ID. The action method must return the ID of the concerned
resource by the subflow ! If the action returns False, the workitem disappears !
A sub−workflow is executed when an activity is of the type SUBFLOW. This activity will end when the
sub−workflow will be finished. While the sub−workflow is active, the workitem of this activity is frozen.
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:
05/02/08
126
object_method()
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.
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.
wkf_id
05/02/08
127
9.4.5 Transitions
Workflow transitions are the conditions to be satisfied to go from one activity to the next. They are represented by
one−way arrows joining two activities.
The roles and signals are evaluated before the expression. If a role or a signal is false, the expression will not be
evaluated.
The fields
act_from
Source activity. When this activity is over, the condition is tested to determinate 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.
role_id
05/02/08
128
9.5 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
05/02/08
129
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.
05/02/08
130
Workflows being made of several actions executed in batch, they can't trigger exceptions. In order to improve the
execution efficiency and to release a maximum of locks, workflows commit at the end of each activity. This approach
is reasonable because an activity is only started if the conditions of the transactions are satisfied.
The only problem comes from exceptions due to programming errors; in that case, only transactions belonging to the
entirely terminated activities are executed. Other transactions are "rollbacked".
05/02/08
131
10 Reports
05/02/08
132
10.1 Introduction
There are mainly three types of reports in Tiny ERP: custom reports (made within the interface), RML reports, and
hand−coded reports. This documentation mainly concerns RML reports.
RML reports don't require programming but require two simple XML files to be written:
05/02/08
133
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 for 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">
<name type="field" name="name"/>
<ref type="field" name="ref"/>
</id>
</ids>
<?xml version="1.0"?>
<ids>
<id>
<name>Tiny sprl</name>
<ref>pnk00</ref>
</id><id>
<name>ASUS</name>
<ref></ref>
</id><id>
05/02/08
134
<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>
<name>Ecole de Commerce de Liege</name>
<ref></ref>
</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>
<frame id="col1" x1="2cm" y1="2.4cm" width="8cm" height="26cm"/>
<frame id="col2" x1="11cm" y1="2.4cm" width="8cm" height="26cm"/>
</pageTemplate>
</template>
<stylesheet>
<blockTableStyle id="ids">
<!−− title −−>
<blockFont name="Helvetica−BoldOblique" size="12" start="0,0" stop="−1,0"/>
<lineStyle kind="BOX" colorName="black" start="0,0" stop="−1,0"/>
05/02/08
135
<story>
<blockTable colWidths="2cm, 6cm" repeatRows="1" style="ids">
<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>
<blockTable colWidths="2cm, 6cm" repeatRows="1" style="ids">
<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>
05/02/08
136
<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
• XSL − Tutorial :http://www.zvon.org/xxl/XSLTutorial/Books/Output/contents.html
• http://www.w3.org/XML/
05/02/08
137
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
Here is the list of available field 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 selected 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:
05/02/08
138
In this precise case, there is of course no point in using this notation instead of the standard notation below:
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:
<cost type="call" name="compute_seller_costs" args="">
<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:
<image type="attachment" name="id"/>
Example
Here is an example of XML file:
05/02/08
139
• http://reportlab.com/docs/RML_UserGuide_1_0.pdf
05/02/08
140
import rml_template.xsl
required templates:
− frames?
− stylesheet
− story
optional templates:
Translations
As Tiny ERP can be used in several langages, reports must be translatable. But in a report, everything mustn't be
translated: only the actual text and 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:
• RML UserGuide (pdf) (reportlab.com)
• XSL Tutorial (zvon.org)
• XSL Reference (zvon.org)
• XSL tutorial and references (W3Schools)
• XSL Specification (W3C)
<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"/>
05/02/08
141
<para>
<b t="1">Document</b>: <i><xsl:value−of select="name"/></i>
</para><para>
<b t="1">Type</b>: <i><xsl:value−of select="type"/></i>
</para><para>
<b t="1">Reference</b>: <i><xsl:value−of select="reference"/></i>
</para><para>
<b t="1">Partner ID</b>: <i><xsl:value−of select="partner_id"/></i>
</para><para>
<b t="1">Date</b>: <i><xsl:value−of select="date"/></i>
</para><para>
<b t="1">Amount</b>: <i><xsl:value−of select="amount"/></i>
</para>
<xsl:if test="number(change)>0">
<para>
<b t="1">Change</b>: <i><xsl:value−of select="change"/></i>
</para>
</xsl:if>
<setNextTemplate name="first_page"/>
<pageBreak/>
</xsl:template>
</xsl:stylesheet>
Example (yourself):
05/02/08
142
The general approach: (note that I'm not giving step by step instructions for just this function because in the
actual implementation it's part of a larger group of customizations).
It requires edits to server-side python, xml and rml. This was done in 4.1.0 but seems to be the same in 4.0.x.
<report id="report_sale_quotation"
string="Print Booking"
model="sale.order"
name="sale.booking"
rml="custom/report/quotation.rml"
auto="True" />
• check that the button works and a report is shown - do this before messing with further customization
• edit custom/corporate_rml_header.rml
(note that the string "first" shouldn't be messed with) and then make a second template and call it "second" or
something.
• edit quotation.rml
• check that the button still works and a report is shown - should still work with the old template
• now edit tinyerp-server/bin/report/report_sxw.py (note that this is not update-safe)
º method _add_header(self, node) needs a slight change, which matches id attributes to the
pageTemplate tag
if tag.nodeType==tag.ELEMENT_NODE:
found = self._find_node(node, tag.localName)
becomes
05/02/08
143
if tag.nodeType==tag.ELEMENT_NODE:
# ++ if tag id non-null make pageTemplate id tags match
found = self._find_node(node, tag.localName)
if found:
if not tag.getAttribute('id') or
(tag.getAttribute('id') == found.getAttribute('id')):
found.parentNode.replaceChild(tag, found)
• now check that the two reports come out with different templates
05/02/08
144
• codabar
• code11
• code128 (default if not '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:
05/02/08
145
RML Report
Introductions
You can design reports using OpenOffice directly. To do this, write a report in OpenOffice and save it as a .SXW.
(OpenOffice 1 default format).
To put datas in your report, you have to write expressions enclosed with a double bracket. These expressions are in
Python. Example: [[ 2*4+3 ]] will write 11 in the generated document. When the report is generated in Tiny ERP, a
variable named 'objects' is defined. It is the list of all selected objects in the ERP.
Attach:report_oo_sample1.pdf
Expressions
Objects are used like any other objects. For example, if you are creating a report for invoices, when the report is
generated, you can use the variable 'objects' that represent the list of selected invoices. Lists (or one2many fields) can
05/02/08
146
be used like any other many2one relations. Except that if you put a list in a document, it will duplicate the flowable
for each element of the list.
Sections in OpenOffice are created using the red icons in the following screenshot. Sections can be nested.
Examples
If you are writing a report on invoices. You can do this:
1. explodeIn('varname'): will explode the group (line of table, section, page) and set a variable named 'varname'
that you can use.
<report
string="Partner List"
model="res.partner"
name="partner.list"
rml="base/res/partner/report/partner_list.rml"
/>
05/02/08
147
"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"
Useful tags:
[[ repeatIn(objects,'o') ]] objects to be printed
[[ reduce(lambda x obj: x+obj.qty , list , 0 ) ]] total qty of list (try "objects" as list)
[[ time.strftime('%d/%m/%Y') ]] format=dd MM YYYY, check python doc for more about "%d", ...
05/02/08
148
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:
and: first value is TRUE then print out the second value. First value is FALSE print out first value.
or: first value is TRUE then print out the first value. First value is FALSE print out second value.
in this example if o.prop=='draft' -> TRUE then (o.prop=='draft') and 'YES' reads 'Yes'. Next step is 'Yes' or
'No' which leads to a printed 'YES' (because a string's logical value is TRUE). //
One can use very comlpex structures. To learn more search for some pyhton reference regarding logical
opertors.
05/02/08
149
modulename_report.xml format
Here is, as an example, the declaration of the reports related to invoices.
<?xml version="1.0"?>
<terp>
<data>
<delete model="ir.actions.report.xml" search="[('model','=','account.invoice')]"/>
Fields description
• delete attributes:
♦ model:
♦ search:
• Attributes for the report tag:
♦ id (optional):
♦ string: The string which will be displayed if there are several reports for one ressource (the user will
be presented a list with reports names).
♦ model: The name of the model where the data needed by the report is.
♦ name: The name of the report. It is used internally and should be unique.
♦ xml (optional): The path to the XML template (relative to the addons directory).
♦ xsl (optional): The path to the XSL stylesheet (relative to the addons directory).
♦ auto (optional): Whether the report should be automatically instanciated. This is should be set to False
for handcoded reports. Default value: True.
♦ replace (optional): Whether or not the report should override all existing reports for this model.
Default value: False.
♦ menu (optional): Whether or not to link the report with the print button. Default value: True.
__terp__.py
If the report you created is the first one of its module, you probably had to create the modulename_report.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" : "Tiny 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"],
}
05/02/08
150
Handcoded Reports
The steps to register a new handcoded report are roughly the same as for RML reports. In this case, the xml and xsl
attributes of the report tag are useless and can be omitted. One should not forget to add its report (python) source file
to the __init__.py of the module.
05/02/08
151
You can change the content of the file to change the effect of the report header and the footer
You can set the x and y coordinate for setting the location of the string
Setting Font
Using the tag you can set the font size and name
05/02/08
152
11 Wizards
Wizards describe interaction sequences between the client and the server.
05/02/08
153
11.1 Introduction
Here is, as an example, a typical process for a wizard:
Here is a screenshot of the wizard used to reconcile transactions (when you click on the gear icon in an account chart):
05/02/08
154
05/02/08
155
11.2 Principles
A wizard is a succession of steps. A step is composed of several actions;
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.
class wiz_reconcile(wizard.interface):
states = {
'init': {
'actions': [_trans_rec_get],
'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'stat
},
'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.
1. a list of actions
2. a result
Where:
05/02/08
156
The result
Here are some result examples:
Indicate that the wizard has to continue to the next state: 'end'. If this is the 'end' state, the wizard stops.
The type=form indicate that this step is a dialog to the client. The dialog is composed of:
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.
05/02/08
157
...
'result': {'type':'choice', 'next_state':_check_refund}
...
'actions': [_get_invoice_id],
'result': {'type':'print', 'report':'account.invoice', 'get_id_from_action':True, 'state':'check_refund'}
05/02/08
158
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 the that file does not exist, you need to create it first.
<?xml version="1.0"?>
<terp>
<data>
<delete model="ir.actions.wizard" search="[('wiz_name','like','account.')]"/>
__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.
05/02/08
159
{
"name" : "Tiny 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"],
}
05/02/08
160
12 Events
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... />
05/02/08
161
05/02/08
162
13.1 Introduction
The Information Repository (IR) is a datas structure where you can stock some information: the values by default, the
preferences per ressource (meta) and the actions todo when certain buttons are pushed down.
◊
<ir_set>
<field name="key" eval="'action'"/>
<field name="key2" eval="'client_action_multi'"/>
<field name="models" eval="['stock.tracking']"/>
<field name="name">Tracability: Article composition</field>
<field name="value" eval="'ir.actions.act_window,'+str(action2)"/>
<field name="isobject" eval="True"/>
<field name="replace" eval="False"/>
</ir_set>
• for raports
<ir_set>
<field name="key" eval="'action'"/>
05/02/08
163
• for meta
♦ 'lang'
<ir_set>
<field name="key" eval="'meta'"/>
<field name="key2" eval="'lang'"/>
<field name="models" eval="['res.users']"/>
<field name="name">lang</field>
<field name="value" eval="False"/>
<field name="meta" eval="{'type':'selection', 'string':'Language', 'selection':[(False
<field name="replace" eval="True"/>
</ir_set>
◊ 'account.receivable'
<ir_set>
<field name="key" eval="'meta'"/>
<field name="key2" eval="'account.receivable'"/>
<field name="models" eval="['res.partner']"/>
<field name="name">account.receivable</field>
<field name="value" eval="a_recv"/>
<field name="meta" eval="{'type':'many2one', 'string':'Account Receivable', 'relation'
</ir_set>
◊ 'account.payable'
<ir_set>
<field name="key" eval="'meta'"/>
<field name="key2" eval="'account.payable'"/>
<field name="models" eval="['res.partner']"/>
<field name="name">account.payable</field>
<field name="value" eval="a_pay"/>
<field name="meta" eval="{'type':'many2one', 'string':'Account Payable', 'relation':'a
</ir_set>
05/02/08
164
13.2 Structure
05/02/08
165
05/02/08
166
14 MRPModule
Stock Concepts
Reservation
Reservation for a quantity of one products (wich may be several lot_lines with the same products).
__States__:
The key point for make to order operations. Reservations may be linked: once the operation is done, the lots are
automatically linked to another reservations for another operations. (for eg, a waiting reservation)
Once some lots are reserved they can not be used for another operation.
Reservation may be placed on a location and assigned later to the right lots.
Picking list:
Takes some reservations. Once that reservations are all confirmed, the picking list can be processed. When you
process the picking list, it takes all reserved lots lines and create a new lot with these reserved products.
MRP Concepts
Procurement
The form that allows the program to produce, buy or reserve products. (=procure products). Then these products are
assigned to a reservation.
05/02/08
167
Procurements may wait in the states 'make to stock' or 'make to order'. Then the 'calcul des besoins net' processor will
run all procurements one by one ordered by priorities.
Attach:mrp_reservation.png
05/02/08
168
14.1 Workcenters
capacity_hour
Capacity per hour. default: 1.0. Eg: If 5 concurrent operations can be done at one time: capacity = 5 (because
5 employees for example).
unit_per_cycle
Units produce in one cycle. default 1.0.
time_efficiency
Time efficiency for production. Factor default to 1.0
costs_hour
Costs for one hour of production in this workcenter.
costs_cycle
Fixed cost for each cycle of production,
cost_setup
Fixed cost for the setup of the workcenter
05/02/08
169
BOM Lines
Materials used to compose a product_qty of these products.
05/02/08
170
15 Modules
The structure of Tiny ERP are divided into modules. The available modules are;
• account
• auction
• base
• crm
• marketing
• product
• purchase
• sale
• stock
• project
• hr
• subscription
• Audittrail
05/02/08
171
This file must contain a Python dictionnary with the following values:
name
The name of (english) the module
version
The version of the developped module
depends
List of modules which depends this module. The basic module must be in the dependences because certain
necessary datas for the views, reports, ... are in the basic module.
init_xml
List of .xml files to download when the server is launched with the "−−init=module" argument. The way is
relative to the repertory of the concerned module.
update_xml
List of .xml files to download when the server is launched with the "−−update=module" launched. The way is
relative to the concerned module repertory.
active
True or False. Determinate if the module is active or not.
Example
Here is an un example of __terp__.py file for the Account module.
{
"name" : "Tiny TERP Accounting",
"version" : "1.0",
"depends" : ["base"],
"init_xml" : [ "account_workflow.xml", "account_data.xml", "account_demo.xml"],
"update_xml" : [ "account_view.xml", "account_report.xml", "account_wizard.xml"],
}
The files that must be placed in init_xml, are the ones that concern the workflow definition, the datas to download at
the installation of the softwaredu logiciel and the datas for the demonstrations.
The files in "update_xml" concern: the views, the reports and the wizards.
05/02/08
172
So, if you create a "module.py" file, containing the description of your objects, you have to write one line in
__init__.py; import module
05/02/08
173
• Extra statistical reports on a specific module should go in modules of the form: 'report_mymodule'
• Boards module must go in modules like 'board_mymodule'
• Reports are structured by module: account, sandwich, sale, purchase
• Board are structured by poste: executive, accountant, sales manager, project manager, ...
• A board might depend on different report modules. Some examples:
º Executive (report_sale,report_account,report_purchase)
º Developper (report_project, report_sandwich, report_crm for support).
05/02/08
174
05/02/08
175
16 Webservices
1. WebservicesIntro
2. WebservicesXMLRPC
3. WebserviceExemplePHP
4. WebserviceExemplePython
05/02/08
176
16.1 WebservicesIntro
05/02/08
177
16.2 WebservicesXMLRPC
• XML−RPC
♦ standard: http://xmlrpc.org
♦ RPC Over HTTP
♦ Function Parameters & Result encoded in XML
• Principe;
♦ appels aux méthodes des objets;
◊ read, write
◊ create
◊ unlink (=delete)
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:
05/02/08
178
<?
include('xmlrpc.inc');
$arrayVal = array(
'name'=>new xmlrpcval('Fabien Pinckaers', "string") ,
'vat'=>new xmlrpcval('BE477472701' , "string")
);
$resp = $client−>send($msg);
$val = $resp−>value();
$id = $val−>scalarval();
echo "Partner $id created!";
?>
05/02/08
179
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',
}
05/02/08
180
Load Balancing
There is two ways to do load−balancing;
Then, if you connect with the Tiny ERP client on the server you started balance, on port 8070, requests are
load−balanced between serveur_1 and serveur_2. If can start different balance server if you need redundancy for
QOS.
Postgresql
Have a look at postgresql replication system. Each Tiny ERP server uses one and only one postgresl database. You
need to configure each server to use one replicated database.
05/02/08
181
18. Multi−Company
º Tiny Company 2
Someone in the 'Tiny Company 1' will get an access of ressources that are to these companies; 'Tiny
Company 1', 'Tiny Comp 1.1'. He will not see the ressources of 'Tiny Group' or 'Tiny Company 2'.
Some fields, records or reports have different values depending on the company you work in. Some example;
• The credit / debit in the account chart is for your own company.
• The report header my be different for different companies
• Some records will available to some companies only.
Ressources that doesn't have a company are visible for all companies.
When you want to filters records automatically according to your company context, just add a 'company_id'
fields in the object that have to be filtered. If the company_id field is null for a record, this record will be
available for every body.
By default, all companies write account move line in the same account charts. Account.move.line may have a
compay_id fields, wich id defined like this:
'company_id': fields.many2one('res.company', 'Company')
and with a default value:
'company_id': lambda self,cr,uid,context: context.get('company', False)
05/02/08
182
If you browse the account chart, you will see different credit/debit according to your company. This is
because the sum of your visible move.line are different.
If you plan to have differents account charts for differents companies, you must add a company_id field in the
account.account object. So, companies may share most of the account charts and have specific accounts for
their own use.
If you want to use a different credit account for your partners for different companies, add a company_id field
in your 'ir.values' object. So, all preferences of your partners will depend of the company.
05/02/08
183
19. Internationalization
Module creation
To customize tinyerp to suit your local needs you must first create a directory where all your local
configuration will take place. So you should at first create this directory:
cd $TINYERP_DIRECTORY
mkdir addons/l10n_be
You should change the last two letters of the directory name (here the be of l10n_be) by the ISO Code for
your country.
{
"name" : "Localisation for Belgium",
"version" : "1.0",
"author" : "Tiny",
"category" : "Localisation/Europe",
"depends" : ["base", "account"],
"init_xml" : [],
"demo_xml" : ['account_demo.xml'],
"update_xml" : ['account_pcmn_belgium.xml'],
"active": False
}
Of course you should also change all the reference to Belgium that are found in the example above.
See WritingANewModule
In the demo_xml field there is a list of file that are read on the first startup of tinyerp and that insert some data
into the database. More interesting is the update_xml field. This list defines a number of files that are
reloaded each time tinyerp is invoked with the --update switch positioned on one of the dependency of the
current module (or on this module of course).
05/02/08
184
Above everything there is one thing to take into account when you are writing your own account tree. You
must keep the ids used in account_minimal.xml in your own version of the account tree. Another
important thing is that every acount defined in account_minimal should be defined in your own account
tree.
That is, in your xml file defining your country account tree the record defining the Main receivable account
should look like
In this example, you can see that the id associated to the account that is created is account.a_recv which
match the id defined for the Main receivable account in account_minimal.xml.
Account types
There is nine account types: receivable, payable, view, income, expense, tax, cash, asset and equity.
The only particular type is the view type wich is used to structure the account tree.
As yoou might have noticed an account can have more than one father. That's why we are using a special
syntax to create links from the account to its father:
This line means that the current node should be linked to the node identified by the id a5.
Taxes
If your not living in some financial paradise, you have to define some taxes. Here are the fields that you must
use to define the taxes.
name
This is the name of the tax, it will appear on the invoice.
05/02/08
185
type
This is the type of the tax, it can be percent, fixed, none or code. Percent apply this percentage to the
total amount, fixed add a fixed amount to the total amount, none means that the tax is not applied
(but its
child might be), code means that the amount is computed using python code.
amount
This is the amount used by the tax. eg.: a tax with an amount of 21 and a type of percent will add 21%
to the total amount of the invoice.
account_collected_id
This is the account that will collect the amount this peculiar tax
account_paid_id
This is the account that is used to pay the amount of this peculiar tax
Example
You should have a look at the "addons/l10n_belgium" directory for a detailed example.
05/02/08