This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
Overview
Download & View Tinyerp - Technical Documentation / Book as PDF for free.
1 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: • python >= 2.3 : http://python.org • python2.3−xml • libxml2−python2.3 • libxslt1−python2.3 • PsycoPg : http://initd.org/projects/psycopg1 • Python Imaging Library : http://www.pythonware.com/products/pil/ (only if you use bitmap in reports) • Reportlab : http://reportlab.org • PostgreSQL: http://postgresql.org Optional, if you plan to render workflows: • PyParsing : http://pyparsing.sourceforge.net/ • PyDot : http://dkbza.org/pydot.html • graphviz: http://www.graphviz.org/ Have a look in the user manual for installation instructions.
1.1.2 The client The client depends on the following packages: • python >= 2.3 : http://python.org • GTK >= 2.6 : http://gtk.org Optional, for Windows users: • Win32Com : http://www.python.org/windows/win32com/
05/02/08
6
1.2 Installation Steps First check that all the required dependencies are installed. Then create the terp database. You have to make sure that your user has the correct credentials to create databases with postgres, for more information on this subject please refer to the PostgreSQL manual. $ createddb terp −−encoding=unicode 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
1.3 Upgrading Versions The upgrade of version is automatic and don't need any special script. In fact, the server is able to rebuild automatically the data base and the data from a disturbed version. 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: −> terp_server.py −−update=all
You can also upgrade one or several modules: −> terp_server.py −−update=account,base
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
1.4 Config files Two configuration files are available: • one for the client: ~/.terprc • one for the server: ~/.terp_serverrc Those files follow the convention used by python's ConfigParser module . 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
1.4.1 Server's config file: ~/.terp_serverrc The server configuration file @.terp_serverrc@ is used to save server statup options. Here is the list of the available options: 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. Here is the configuration by default for the server [options] verbose = False xmlrpc = True database = terp update = {} port = 8069 init = {} interface = 127.0.0.1 reportgz = False
05/02/08
10
1.4.2 Client's config file: ~/.terprc Here is the list of the available options:
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
file used by the logger verbose set the log level to INFO
client section default_path Default path used by the client when saving/loading datas.
1.5 Command line options The following options can be passed into the server with the line: −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.
Relatives langages options These options allows you to use Tiny ERP through other langage (than english). Go to the section Traductions for more details on the system of translations. −lLANGUAGE, −−language= LANGUAGE Specify the langage of the translation file. Use this option in combination with the option −−i18n−export or −−i18n−import . −−i18n−export= FILEPATH Export all the sentences to translate in the file FILEPATH. The option '−l' must also be used. −−i18n−import= FILEPATH Import the file CSV FILEPATH. This file must contain all the translations for the choosen langage by the parameter −l. Don't forget to select in the client preferences the choosen langage.
05/02/08
13
1.6 Shutting down the server The easiest way the shut down the server is to send the SIG INT signal to the server. $ kill −2 pid where pid is the processus ID of the server.
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:
How to change the langage of the interface user ? The langage is an user preference. To change the langage of the current user, you click on the menu: User > Preferences.
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".
Charge a file of translation on the server To import a file having translations, use the next order: ./tinyerp_server.py −−i18n−import=file.csv −lLANG
Note that the translation file must be encoded in UTF8!
05/02/08
15
2.1 Translation Reports TO get printscreen report in prefered language following methods are used. fields_view_get(cr, uid, view_type='tree', context=context) read(cr, uid, datas['ids'], result['fields'].keys(), context )
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 def read(self, cr, uid, ids, fields=None, context={}) Parameters * ids: the list ressources identifiers to read. * fields (optionnal): the list of the interested fields. If we don't introduce something for this parameter, the function check all the fields * context (optional): the actual context. Returns A list of dictionaries (a dictionary per resource asked) of the form [{'name_of_the_field': value, ...}, ...] 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
To translate a new langage To translate or modify the translation of a langage already translated, you have to:
1. Export all the sentences to translate in a file CSV To export this file, use the order: ./tinyerp_server.py −−i18n−export=file.csv −lLANG
or LANG is the langage in which you want to translate the program.
2. to translate the last column of the file You can make a translaion for a langaga which has already been translated or for a new one. If you ask for a langage already translated, the sentences already translated will be written in the last column. For example, here are the first lines of a translation file (dutch): type field field model model model
name "account.account,code" "account.account,name" "account.account,name" "account.account,name" "account.account,name"
res_id 0 0 2 25 61
src Code Name Assets Results Liabilities
value Code Name Aktiva Salden Verbindlichkeiten
3. Import this file to Tiny ERP (as explained at the precedent section) 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: createdb terp_test −−encoding=unicode terp_server.py −−database=terp_test −−init=all
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 [email protected] 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; tinyerp.py −drpc.result,rpc.request −lDEBUG
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. The available levels are: • 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.
File of configuration; Options The options are: 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
We can also use the next parameters to the order: • −l level ou −−log−level=level • −d channels ou −−log=channels Go to tinyerp.py −−help for more details...
Option verbose The option verbose allows you to log at the level 'INFO'. this option is the same as the option −−log−level=INFO In the file of onfiguration: logging.verbose: True
Also available as parameter at the line: −v, −−verbose
Examples Requests XML−RPC To log the requests XML−RPC but not the responses: ./terp.py −−log=rpc.request −−log−level=debug
To log all events XML−RPC (requests, responses, exceptions): ./terp.py −−log=rpc −−log−level=debug
To log the requests and the exceptions but not the responses: ./terp.py
−−log=rpc.request,rpc.exception −−log−level=info
05/02/08
19
4 Parameter settings of Modules The differents modules of Tiny ERP can be installed or not according to the needs. All the modules are in the repertoty: '/addons' of the server. You have to copy/delete the repertories to install or delete the Tiny ERP modules. Some modules depend on other modules. See the file addons/module/__terp__.py for more information on the dependences. Here is an example of __terp__.py: { »···"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
4.1.1 Management of access rights Three concepts are differentiated into Tiny ERP; 1. The users: person identified by his login/password 2. The groups: define the access rights of the ressources 3. The roles: determine the roles/duties of the users
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: • The reading access: reading of a file, • The writing access: recording & creation, • The execution access: the buttons of workflows or wizards. 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
4.1.2 The actions The actions define the reactions of a user in front of some events; login of a new user, double−click on an invoice, click on the action button, ... It exists different type of simple actions: • Window: opening of a new window • Report: The prints of a report ♦ Custom Report: The personalized reports ♦ RML Report: The XSL:RML reports • Wizard: The startings of a Wizard • Execute: The execution of a method on the server side • Group: Gather some actions in one group The actions are used for the next events; • 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: • Opening of a window when double−click in the menu • User connection Opening of the menu 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. 1. Search the action in the IR. 2. Execution of the action 1. If the action is the type Opening the Window; it indicates to the user that a new window must be opened for a selected object and it gives you the view (form or list) and the filed to use (only the pro−forma invoice). 2. The user asks the object and receives information necessary to trace a form; the fields description and the XML view. 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. The steps are: 1. Reading of a user file to obtain ACTION_ID 2. Reading of the action and execution of this one
05/02/08
24
The 'Window' actions They indicate at the user that he has to open a new window in a new 'onglet'.
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; • the field on which the test must be done
05/02/08
25 • the operator used for the test (<, >, =, like) • the tested value 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; • ir.sequence: Sequences of numbers • ir.sequence.type: Type of sequences These numbers are used, for example, to; • generate the number of the next invoice, • generate the number of the next refund, • generate continuous numbers for batches, ...
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
4.1.5 Customizing the menu It's easy (but tricky) to grant grained access to menu based on the user's groups. First of all, you should know that if a menu is not granted to any group then it is accessible to everybody ! If you want to grant access to some groups just go to Menu > Administration > 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
The parameter setting of the module CRM allows you to define; • the partners category • the sales canals • the customer's state of mind • the case categories • automatic segmentations
The partners categories The categories are used to segment/classify the various partners. A same partner can be included in several categories. These ones are defined according to the needs of the company. 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, ...
The states of mind The states of mind define a level of value on the partner option opposite to the company; Dissatisfied −> Normal −> Satisfied. At every state of mind, we assign a selected value in this level. These values are making calculations on a certain period. Level of value: Very dissatisfied Dissatisfied Normal Satisfied Very satisfied
0.0 2.0 5.0 6.3 10.0
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. So, if the customer has made three orders. 2005/01/01 2005/01/20 2005/03/10
Order 1 Sale opportunity 2 Helpdesk 3
Dissatisfied Normal Very satisfied
2.0 5.0 10.0
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.
The types of categories These categories classify sales opportunities and the technical tasks of the Helpdesk. Example of HelpDesk; • Problem with the hardware, • Problem of configuration, • Bug, ...
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 • the account charges • the various currencies used • the various applicable taxes
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. The used fields Relative Change Rate Relative change rate compared to the unity (in this case; the EURO). Digits Number of figures after the comma for the amounts written in this currency. Computational accuracy Precision of the amounts calculation in this currency.
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. It is possible to define a tax which regroup two others; • Taxe on the screen ♦ VAT (21%) ♦ Recup (1.52 EUR) 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.
The calculated taxes Some taxes are more difficul than a simple multiplication or addition. It is possible to transfer a python code that counts the value of the applicable taxes on the unit amount and/or a another python code that determines if the tax is
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: • price_unit : the unit price of the product • address : the address partner or False ___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. Code to calculate the taxe; result = 0.0 if price_unit>=50000: »··result=price_unit*0.05 elif price_unit>=2500: »··result=price_unit*0.1
Test code if applicable; result = price_unit>=2500
___Example___; The screen tax to calculate a tax pc with python_code
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
To define the tax in Main Menu go : Menu/Definitions/Accounting/Tax.
05/02/08
39
4.3.9 ModulesAdminAccountImputations Sale order Incomes account: product preference or the one by default Taxe account: taxe collected from the taxe definition Credit account: partners preferences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paid from the taxe definition Flow account: partners preferences
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. Stock account: product preference (inventory account) Stock losses account: product preference Stock benefit account: product preference
A payment Credit/flow account: partner preferences Cash account: manual
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, ... This representation allows you to manage uneasy situations in other ERP; • location of materiels • consigned stock • stored merchandises of our suppliers
To create a structural location The location are structured in tree. Thanks to the use of the double−entry system, you can/must also structure the customers and suppliers stock. As an exemple we will use this use case: • 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 A structure that can manage these situations; • 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). 1. the stock location; indicates the stock warehouse 2. the output location; indicates the place where the batches are getting out 3. the input location; indicates the place where arrive the batches
An example of the location structure for a warehouse; • 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
Modules that plan to use subscription All modules that plans to use subscription with references to their documents have to fill in these tables: • subscription.document: describe objects that can be referenced. • subscription.document.fields: describe fields of this object that have to be changed when copying. Example: account_subscription.xml: <delete model="subscription.document" search="[]"/> ======================================================= Subscription Views ======================================================= Invoiceaccount.invoice
05/02/08
45
Invoice Datedate_invoicedateInvoice Due Datedate_duedate
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
5.1 Program structure The core of the sever is formed by the following modules: • An object database (ORM) • A workflow system integrated with the Object database • A wizard system • A report system (based on reportlab) • A service to export the objects (XML−RPC)
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. for more infos on XML−RPC, consult:http://www.xml−rpc.com/
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. There are three types of action; • Open a window (form or tree) • Print a document • Execute a wizard
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: • account.transfer : a money transfer • account.invoice : an invoice • account.invoice.line : an invoice line Generally, the first word is the name of the module: account, stock, sale. Other advantages of an ORM; • simpler relations : invoice.partner.address[0].city • objects have properties and methods: invoice.pay(3400 EUR), • inheritance, high level constraints, ... It is easier to manipulate one object (example, a partner) than several tables (partner address, categories, events, ...) The Physical Objects Model of TinyERP version 3.0.3
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. The Physical Database Model of TinyERP • TinyERP version 3.2.0rc2 • TinyERP version 3.2.0rc1 • TinyERP version 3.0.3 • TinyERP version 2.1.3
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. There are two types of views: • form views • tree views Lists are simply a particular case of 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
To develop new objects The design of new objects is restricted to the minimum: create the objects and optionally create the views to represent them. The PostgreSQL tables do not have to be written by hand because the objects are able to automatically create them (or adapt them in case they already existed).
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: • a confirmed sale order must generate an invoice, according to certain conditions • an paid invoice must, only under certain conditions, start the shipping order 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.
In this graph, the nodes represent the actions to do:
05/02/08
57
• create an invoice, • cancel the sale order, • generate the shipping order, ... The arrows are the conditions; • waiting for the order validation, • invoice paid, • click on the cancel button, ... The squared nodes represent other Workflows; • 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
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: res_partner.send_email(... , [1, 5], ...)
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. Here is the list of the available types: 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). Parameters: fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, method=False, where • type is the function return type • method whether the field is computed by a method (of an object) or a global function • fcnt is the function or method that will compute the field value. If method is true, the signature of the method must be: def fnct(self, cr, uid, ids, field_name, arg, context) otherwise (if it is a global function), it should be: def fnct(cr, table, ids, field_name, arg, context) Either way, it must return a dictionary of values of the form {id: value, ...} selection a field which allows the user to make a selection between various predefined values Format of the selection parameter: tuple of tuples of strings of the form: (('key_or_value', 'string_to_display'), ... )
one2one Parameters: obj many2one Parameters: obj Optional parameters: • ondelete: What should happen when the resource this field points to is deleted. Predefined value: "cascade" Default value: "null" Example: 'commercial': fields.many2one('res.users', 'Commercial'),
many2many Parameters: obj, rel, id1, id2 where • obj is the other object which belongs to the relation • rel is the table that makes the link • id1 and id2 are the fields' names used in the relation table Example: 'category_id': fields.many2many( 'res.partner.category', 'res_partner_category_rel', 'partner_id', 'category_id', 'Categories'),
reference Parameters: string, selection, size where • string is the English name of the field • selection is either: ♦ a function that returns a list of tuple ♦ a list of tuples designating which model the field may reference to. Example: def _links_get(self, cr, uid): cr.execute('select object,name from res_request_link order by priority')
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: Project Taskproject.task
Common parameters to all field types change_default Whether or not the user can define default values on other fields depending on the value of this field. Default value: False. Example: (in res.partner.address) 'zip': fields.char('Zip', change_default=True, size=24), In this case, it means users will be able to set default values for any field of the contact form depending on the value of the 'zip' field. For example, the user could have the program automatically set the city field to 'Brussels' if the zip is 1200 and to 'Namur' if the zip is 5000. readonly Whether the field is editable or not. Default value: False. required Whether the field is required or not. The program will refuse to save a resource if a required field is left blank. Default value: False. states This parameter permits to define attributes for this field that will only be available in some states of the resource. Format: {'name_of_the_state': list_of_attributes} where list_of_attributes is a list of tuples of the form [('name_of_attribute', value), ...] Default value: {}. Example: (in account.transfer) 'partner_id': fields.many2one('res.partner', 'Partner', states={'posted':[('readonly',True)]}), string The label of the field. Default value: 'unknown'. Example: 'tested': fields.boolean('Tested'), Note: Strings containing non−ASCII characters must use python unicode objects. Example: (in french) 'tested': fields.boolean(u'Testé'),
05/02/08
71
translate Whether or not the content of this field should be translated (ie managed by the translation system). Default value: False.
6.2.2 Default values The special attribute _defaults allows you to define default values for one or several fields of an object. 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: [(method, 'error message', list_of_field_names), ...]
where • method is an object method used to check the constraint. This method must have the following signature: def _name_of_the_method(self, cr, uid, ids): −> True|False
• 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": def _constraint_sum(self, cr, uid, ids): cr.execute('SELECT a.currency_id FROM account_move m, account_move_line l, account_account a WHERE m.id=l.move_id AND l.account_id=a.id AND m.id IN ('+','.join(map(str, ids))+') GROUP BY a.currency_id') if len(cr.fetchall())>=2: return True cr.execute('SELECT abs(SUM(l.amount)) FROM account_move m LEFT JOIN account_move_line l ON (m.id=l.move_id) WHERE m.id IN ('+','.join(map(str, ids))+')') res = cr.fetchone()[0] return res<0.01 _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
def _credit_search(self, cr, uid, obj, name, args): if not len(args): return [] where = ' and '.join(map(lambda x: '(sum(amount)'+x[1]+str(x[2])+')',args)) cr.execute('select partner_id from account_move_line where account_id in (select id fr res = cr.fetchall() if not len(res): return [('id','=','0')] return [('id','in',map(lambda x:x[0], res))]
def _debit_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.payable')],[ cr.execute('select sum(amount) from account_move_line where account_id=%d and res[id]=cr.fetchone()[0] or 0.0 return res
def _debit_search(self, cr, uid, obj, name, args): if not len(args): return [] where = ' and '.join(map(lambda x: '(sum(amount)'+x[1]+str(x[2])+')',args)) cr.execute('select partner_id from account_move_line where account_id in (select id fr res = cr.fetchall() if not len(res): return [('id','=','0')] return [('id','in',map(lambda x:x[0], res))]
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.
def address_get(self, cr, uid, ids, adr_pref=['default']): cr.execute('select type,id from res_partner_address where partner_id in ('+','.join(ma res = cr.fetchall() adr = dict(res) result = {} for a in adr_pref: result[a] = adr.get(a, adr.get('default', res[0][1])) return result res_partner()
05/02/08
78
6.3 Access
05/02/08
79
6.3.1 Access types There are several ways to execute methods on the objects. All the ways share a common point: you have to get an access point first. The different available ways are: * to access it directly * by the netservice * by xmlrpc
Direct access (direct reference towards the object) In the osv's object methods, we may reach another object with the get method of the pool attribute. 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")
Calling an object method will then have the form: result = service.execute(user_id, object_name, method_name, parameters)
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')
and the call to the method itself is made by: result = sock.execute(user_id, password, object_name, method_name, parameters)
Réf: service/web_services.py
05/02/08
80
6.3.2 Predefined Methods The predefined methods may be classified into 5 categories: the basic methods, methods to manipulate default values, methods to get the permissions, methods to return the fields and views, and finally those naming the ressource.
Basic methods (create, search, read, browse, write, unlink) 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 • ids: the list ressources identifiers to read. • fields (optionnal): the list of the interested fields. If we don't introduce something for this parameter, the function check all the fields. • context (optional): the actual context.
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 'partner_id': fields.many2one('res.partner', 'Partner', required=True), 'name': fields.char('Contact Name', size=64), the most simple way to procced is to use the browse method: addr_obj = self.pool.get('res.partner.address').browse(cr, uid, contact_id) 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 • select: this parameter accept the datas of several types: ♦ a figure, identifier of a ressource ♦ a list if figures (liste of identifiers) • offset (optional): the number of result to pass. • limit (optional): the maximum number of results to return. 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 • ids: the ressources identifiers list to modify. • vals: a dictionary with values to write. This dictionary must be with the form: {'name_of_the_field': value, ...}. • context (optional): the actual contaxt. Returns True
unlink Description Delete one or several ressources Signature def unlink(self, cr, uid, ids) Parameters • ids: the identifiers ressources list to delete. Returns True
Methods to manipulate the default values(default_get, default_set) default_get Description Get back the value by default for one or several fields. Signature def default_get(self, cr, uid, fields, form=None, reference=None) Parameters • 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
def default_set(self, cr, uid, field, value, for_user=False) Parameters • 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
Methods to manipulate the permissions (perm_read, perm_write) perm_read Description Signature def perm_read(self, cr, uid, ids) Parameters • ids: an integer list Returns a list of dictionaries with the following keys • level : access level • uid : user id • gid : group id • create_uid : user who created the ressource • create_date: date when the ressourece was created • write_uid : last user who change the ressource • write_date : date of the last change to the ressource
Methods to generate the fields and the views (fields_get, distinct_field_get, fields_view_get) fields_get Description Signature
05/02/08
84
def fields_get(self, cr, uid, fields = None, context={}) Parameters • fields: a list of fields that interest us, if None, all the fields • context: context['lang'] Result
Methods concerning the name of the ressources (name_get, name_search) name_get Description Signature def name_get(self, cr, uid, ids, context={}) Parameters Result a list of tuples of the form [(id, name), ...] Example (in res.partner.address) def name_get(self, cr, user, ids, context={}): if not len(ids): return [] 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
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. It is good to keep the uses of Python; • the methods beginning with '_' are private
Where • ID1: the type of operation (link, create, remove, ...) • ID2: the ID of the ressource (in case of a modification or unlink) • ID3: the values Different values for ID1: • 0: create • 1: write • 2: unlink • 6: set the link to a list of IDs (ID3) Example:
Example in XML files Installation and Training, 8 hours75.008
where tva21 is the ref of a tax previously defined (id="tva21").
Example in objects: Link: self.create(cr, uid, {'invoice_line_tax_id': [(6, 0, [1234])]})
6.4 Property Fields Properties have been introduced in version 3.4 of Tiny ERP to replace the deprecated ir_set system. Properties are special fields of ressources that describe
Declaring a property A property is a special field: fields.property. class res_partner(osv.osv): _name = 'res.partner' _inherit = 'res.partner' _columns = { 'property_product_pricelist': fields.property( 'product.pricelist', type='many2one',. relation='product.pricelist',· string="Sale Pricelist",· method=True, view_load=True, group_name="Pricelists Properties"), } res_partner() Then you have to create the default value in a .XML file for this property: property_product_pricelist ="[('model','=','res.partner'), TIP: if the default value points to a ressource from another module, you can use the ref function like this:
Putting properties in forms To add properties in forms, just put the <properties/> tag in your form. This will automatically add all properties fields that are related to this object. The system will add properties depending on your rights. (some people will be able to change a specific property, others not). 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.
Using properties or normal fields 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: Normal fields extend the object, adding more features or data. A property is a concept that is attached to an object and have special features: • Different value for the same property depending on the company • Rights management per field • It's a link between ressources (many2one)
Example 1: Account Receivable The default "Account Receivable" for a specific partner is implemented as a property because: • 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
Example 2: Product Times The product expiry module implements all delays related to products: removal date, product usetime, ... This module is very useful for food industries. 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: 1. Create the SQL tables 2. Insert the different datas in the tables 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: • the definitions of the reports, • the object default values, • the form description of the interface client, • the relations between the menu and the client buttons, ... 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
7.2 Files loading Introduction All the modules of the program must be accompanied with __terp__.py file in its main directory. Here is an example of a __terp__.py file for the account module. { "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. Generally, there is: • the description of workflows (convention: modulename_workflow.xml) • some usefull data (convention: modulename_data.xml) • demonstration data (convention: modulename_demo.xml)
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: • the views description (convention: modulename_view.xml)
05/02/08
94
• the reports description (convention: modulename_report.xml)
05/02/08
95
7.3 XML files structure The XML files have the following structure: ...
05/02/08
96
7.3.1 The data
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 account.invoiceInvoices Listaccount.invoice.listaccount/report/invoice.xslaccount/report/invoice.xml
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
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
7.4 Importing from a CSV 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. (.)
Structure of the CSV file 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
Export demo data and import it from a module 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" If you want to create these two categories you can try : name,category_id Smith,"Customer,Supplier" This does not work as expected: a category "Customer, Supplier" is created. The solution is to create an empty line with only the second category: name,category_id Smith,Customer ,Supplier (Note the comma before "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.
Use record id like in xml file: It's possible to define an id for each line of the csv file. This allow to define references between records: id, name, parent_id:id record_one, Father, record_two, Child, record_one
05/02/08
101
7.5 XML data files convention The ressources are placed in different files according to their uses. By convention; modulename_workflow.xml modulename_view.xml modulename_data.xml modulename_report.xml modulename_demo.sql
the definitions of workflows the views the important datas to download the reports declarations the useful datas for the demo version
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
7.6 Managing updates
Managing updates and migrations Tiny ERP has a built'in migration and upgrade system which allows updates to be nearly (or often) automatic. This system also allows to easily incorporate custom modules.
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" tag).
Data Some data is automatically loaded at the installation of Tiny ERP: • views, actions, menus, • workflows, • demo data 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.
Things to care about during development Since version 3.0.2 of Tiny ERP, you can not use twice the same 'id="..."' during resource creation in your XML files, unless they are in two different modules. 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. Use explicit id declaration !, Example:
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 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. From now on, we will call these screen descriptions views. 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. There are two types of views: 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: • By default, each field is preceded by a label, with its name. • Fields are placed on the screen from left to right, and from top to bottom, according to the order in which they are declared in the view. • Every screen is divided into 4 columns, each column being able to contain either a label, or an "edition" field. As every edition field is preceded (by default) by a label with its name, there will be two fields (and their respective labels) on each line of the screen. The green and red zones on the screen−shot below, illustrate those 4 columns. They designate respectively the fields and their labels.
05/02/08
106
Views also support more advanced placement options: • 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;
Colors are separated by ';'.
05/02/08
108
8.2 File Format The common structure to all the XML files of Tiny ERP is described in the XMLFiles section The files describing the views are also of the form: [view definitions]
The view definitions contain mainly three types of tags: • tags with the attribute model="ir.ui.view", which contain the view definitions themselves • 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 : <shortcut name="Draft Purchase Order (Proposals)" model="purchase.order" logins="demo" menu="m"/> Note that you should add an id attribute on the menuitem which is refered by menu attribute. sale.order.formsale.orderform
Default value for the priority field : 16. When not specified the system will use the view with the lower priority.
Special tags 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"> ... defines a new notebook page for the view. • string: defines the name of the page.
05/02/08
109
force a return to the line even if all the columns of the view are not filled in. 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). add a button using the string attribute as label. When clicked, it can trigger methods on the object, workflow transitions or actions (reports, wizards, ...). • string: define the button's label • confirm: the message for the confirmation window, if needed. Eg: confirm="Are you sure?" • name: the name of the function to call when the button is pressed. In the case it's an object function, it must take 4 arguments: cr, uid, ids, *args. ♦ cr is a database cursor ♦ uid is the userID of the user who clicked the button ♦ ids is the record ID list ♦ *args is a tuple of additional arguments • states: a comma−separated list of states (from the state field or from the workflow) in which the button must appear. If the states attribute is not given, the button is always visible. • type: this attribute can have 3 values "workflow" (value by default): the function to call is a function of workflow "object": the function to call is a method of the object "action": call an action instead of a function groups several columns and split the group in as many columns as desired. • colspan: the number of columns to use • ''rowspan':: the number of rows to use • expand: if we should expand the group or not • col: the number of columns to provide (to its children) • string: (optional) If set, a frame will be drawn around the group of fields, with a label containing the string. Otherwise, the frame will be invisible.
attributes for the "field" tag select="1" mark this field as being one of the research critera for this ressource search view. colspan="4" the number of columns on which a field must extend. readonly="1" set the widget as readonly required="1" the field is marked as required. If a field is marked as required, a user has to fill it the system won't save the ressource if the field is not filled. This attribute superseed the required field value defined in the object. nolabel="1" hides the label of the field (but the field is not hidden in the search view). string="" change the field label. Note that this label is also used in the search view: see select attribute above). domain can restrict the domain. Example: domain="[('partner_id','=',partner_id)]"
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
8.3 Special Properties The on_change attribute defines a function that is called when the content of the view field has changed. This method takes at least arguments: cr, uid, ids. cr, uid and ids are the three classical arguments. You can add parameters to the function, these parameters are defined in a view. The example below is from the sale order view.
def onchange_shop_id(self, cr, uid, ids, shop_id): v={} if shop_id: shop=self.pool.get('sale.shop').browse(cr,uid,shop_id) v['project_id']=shop.project_id.id if shop.pricelist_id.id: v['pricelist_id']=shop.pricelist_id.id v['payment_default_id']=shop.payment_default_id.id return {'value':v}
When editing the shop_id form field, the onchange_shop_id method of the sale_order object is called and returns a dictionary where the 'value' key contains a dictionary of the new value to use in the 'project_id', 'pricelist_id' and 'payment_default_id' fields. Note that it is possible to change more than just the values of fields. For example, it is possible to change the value of some fields and the domain of other fields by returning a value of the form: return {'domain': d, 'value': value} context : in you can add a context field, whic will be pass to the action. See the example below :
name="name">account.account.tree1 name="res_model">account.account name="view_type">tree name="view_id" ref="v"/> name="domain">[('code','=','0')] name="context">{'project_id': active_id}
05/02/08
113
8.4 Inheritancy in Views
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: »···res.partner.form.inherit »···res.partner »··· »··· »···»···<notebook position="inside"> »···»···»···<page string="Relations"> »···»···»···»··· »···»···»··· »···»··· »···
The inheritancy engine will parse the existing view and search for the the root nodes of "". It will append or edit the content of this tag. If this tag has some attributes, it will look for the matching node, including the same attributes (unless position). This will add a page to the notebook of the res.partner.form view in the base module. You can use these values in the position attribute: • inside (default): your values will be appended inside this tag • after: add the content after this tag • before: add the content before this tag • replace: replace the content of the tag. Second Example: »···res.partner.form.inherit »···res.partner »··· »··· »···»···<page string="Extra Info" position="replace"> »···»···»··· »···»··· »···
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
8.5 Graphs in views A graph is a new mode of view for all views of type form. If, for example, a sale order line must be visible as list or as graph, define it like this in the action that open this sale order line: formtree,graph 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: sale.order.line.graphsale.order.linegraph
The graph view A view of type graph is just a list of fields for the 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
Defining real statistics on objects The easiest method to compute real statistics on objects is: 1. Define a statistic object wich is a postgresql view 2. Create a tree view and a graph view on this object You can get en example in all modules of the form: report_.... Example: report_crm. TODO: be more complete here...
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. The workflow system has multiple goals: • description of document evolution in time • automatic trigger of actions if some conditions are met • management of company roles and validation steps • management of interactions between the different objects/modules • graphical tool for visualization of document flows To understand its utility, see these two examples: • Discount on orders • Sale order and invoice
05/02/08
117
9.2 Example of Workflows • Edition of the order process • Dependences of the invoices and orders
05/02/08
118
9.2.1 Discount on orders The next diagram represent a very basic workflow of an order:
The order starts in the 'draft' state, when it is in redaction and not approved. When the user press on the 'Confirm' button, the invoice is created and the order comes into the 'CONFIRMED' state. Then, two operations are possible: 1. the order is done (shipped) 2. the order is canceled 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
9.2.2 Purchase order and Invoice A sale order that generates an invoice and a shipping order.
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: • condition on the user role • condition on another resource or workflow (trigger) • condition on values of fields of this resource
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
9.4 Define a Workflow Three types of ressources are necessary to desrcibe a workflow; • WKF: the workflow, • WKF_ACTIVITY: the activities (nodes), • WKF_TRANSITION: the transitions between the activities.
05/02/08
124
9.4.3 Workflow A WKF resource represents a given workflow. Its fields are : • name: name of the workflow • osv: resource model / type of document • on_create: True if the workflow must be instantiated automatically when the OSV resource is created.
05/02/08
125
9.4.4 The nodes (activities) The WKF_ACTIVITY resource represents the nodes of workflows. These nodes are the actions to be executed.
The fields split_mode:
• XOR: One necessary transition, takes the first one found • OR : Take only valid transitions (0 or more) • AND: All the transitions are launched at the same time (fork) In the OR and AND separation mode, certain workitems can be generated. 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:
• XOR: One transition necessary to continue to the destination activity. • AND: Waits for all transition conditions to be valid to execute the destination activity. kind: The type of the activity can take several values: • 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: def object_method(self, cr, uid, ids):
05/02/08
126
In the action though, they will be called by a statement like: 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 The workflow which this activity belongs to.
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 conditions are from different types: • role to satisfy by the user • button pressed in the interface • end of a subflow through a selected activity of subflow The roles and signals are evaluated before the expression. If a role or a signal is false, the expression will not be evaluated. Transition tests may not write values in objects.
The fields act_from Source activity. When this activity is over, the condition is tested to determinate if we can start the ACT_TO activity. act_to The destination activity. condition Expression to be satisfied if we want the transition done. signal When the operation of transition comes from a button pressed in the client form, signal tests the name of the pressed button. If signal is NULL, no button is necessary to validate this transition. role_id The role that a user must have to validate this transition.
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
9.6 The notion of Roles Roles can be attached to transitions. If a role is given for a transition, that transition can only be executed if the user who triggered it possess the necessary role. 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
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
9.7 Error handling As of this writing, there is no exception handling in workflows. 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: • a file describing the data to export (*.xml) • a file containing the presentation rules to apply to that data (*.xsl)
05/02/08
133
10.2 XSL:RML reports XSL:RML reports don't require programming (in python) but require writing an XML file and an XSL:RML style sheet per report type.
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 data file (generated) Tiny sprlpnk00ASUS
05/02/08
134
AgrolaitBanque Plein−Aux−AsChina ExportDitrib PCEcole de Commerce de LiegeElec ImportMaxtorMediapole SPRLOpensides sprlosTecsas sarl
Fore more information on the formats used: • 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 All these formats use XML: • http://www.w3.org/XML/
05/02/08
137
10.2.1 XML Template XML templates are simple XML files describing which fields among all available object fields are necessary for the report.
File format Tag names can be chosen arbitrarily (it must be valid XML though). In the XSL file, you will have to use those names. Most of the time, the name of a tag will be the same as the name of the object field it refers to. Nodes without type attribute are transferred identically into the XML destination file (the data file). Nodes with a type attribute will be parsed by the server and their content will be replaced by data coming from objects. In addition to the type attribute, nodes have other possible attributes. These attributes depend on the type of the node (each node type supports or needs different attributes). Most node types have a name attribute, which refers to the name of a field of the object on which we work. As for the "browse" method on objects, field names in reports can use a notation similar to the notation found in object oriented programming languages. It means that "relation fields" can be used as "bridges" to fetch data from other (related) objects. Let's use the "account.transfer" object as an example. It contains a partner_id field. This field is a relation field ("many to one") pointing to the "res.partner" object. Let's suppose that we want to create a report for transfers and in this report, we want to use the name of the recipient partner. This name could be accessed using the following expression as the name of the field: partner_id.name
Possible types 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: <partner type="zoom" name="partner_id">
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:
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:
Example Here is an example of XML file: <partner_id type="field" name="partner_id.name"/>
05/02/08
139
10.2.2 Introduction to RML For more information on the RML format, please refer to the official Reportlab documentation. • http://reportlab.com/docs/RML_UserGuide_1_0.pdf
Tips and tricks It is possible to put tables in RML tables. This is can be useful in some situations because it is not possible to extend a field on several columns (colspan in html).
05/02/08
140
10.2.3 XSL:RML Stylesheet There are two possibilities to do a XSL style sheet for a report. Either making everything by yourself, or use our predefined templates Either freestyle or use corporate_defaults + rml_template import rml_template.xsl required − − −
templates: frames? stylesheet story
optional templates:
Translations As 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.
10.3 Each reports with own header The need: Sales Quotation is printed on different paper from other documents. 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. • • • •
order.sxw is used as basis for quotation.sxw quotation.sxw can be made blank if order is not in 'draft' state quotation.rml produced in the standard way and put in an appropriate place add a new button to the Sales Order form, and link to the report (see TechnicalDocumentation/ReportsAddNew) - this can be done in the custom directory
• check that the button works and a report is shown - do this before messing with further customization • edit custom/corporate_rml_header.rml <pageTemplate> becomes <pageTemplate id="first"> (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 <pageTemplate id="first"> becomes <pageTemplate id="second"> • 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
10.4 Bar Codes
Barcodes in RML files Barcodes can be generated using the tag in RML files. The following formats are supported: • 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: SN12345678
05/02/08
145
10.5 OpenOffice Report
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.
Will produce the following document: 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. Duplicated flowables are: 1. table: will duplicate lines for each element of the list 2. document: will duplicate the document 3. section: will duplicate the section. 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: [[ len(objects) or 'No Invoice' ]] [[ objects.name ]] [[ objects.partner_id.name ]] [[ objects[0].name ]] [[ objects[0].partner_id.name ]]
Number of invoices to print or write 'No Invoice' Name of the invoice (will duplicate the line for each invoice if in a table) One line per invoice with the name of the partner Write the name of the first invoice Write the name of the partner of the first invoice
Lists of objects have some methods: 1. explodeIn('varname'): will explode the group (line of table, section, page) and set a variable named 'varname' that you can use.
Example of using explodeIn Subscribe report To subscribe a report, put this in a .xml file;
05/02/08
147
10.6 Reports TAGS
Code find in tags is python code. The context of the code (the variable's values you can use) is the following: python objects/variables, available when the report start: "objects" the list of objects to be printed (invoices for example) "data" comes from the wizard "time" see python documentation. "user" the user object launching the report. python functions you can use: "setlang('fr')" change the langage used in automated translation (fields...). "repeatIn(list,varname)" repeat the template (whole doc. or current paragraph?) for each object in the list. Use varname in the template's tags. "setTag('para','xpre')" change the enclosing RML tag (usually 'para') by an other (xpre is a preformatted paragraph), in the (converted from sxw)rml document (?) "removeParentNode"
Useful tags: [[ repeatIn(objects,'o') ]] objects to be printed [[ repeatIn(o.invoice_line,'l') ]] print every line [[ o.quantity * o.price ]] Operations are OK. [[ '%07d' % int(o.number) ]] number formating [[ reduce(lambda x obj: x+obj.qty , list , 0 ) ]] total qty of list (try "objects" as list) [[ user.name ]] user name. [[ setLang(o.partner_id.lang) ]] Localized printings [[ time.strftime('%d/%m/%Y') ]] format=dd MM YYYY, check python doc for more about "%d", ... [[ time.strftime(time.ctime()[0:10]) ]] [[ time.strftime(time.ctime()[-4:]) ]] prints only date.
05/02/08
148
[[ time.ctime() ]] it prints the actual date & time. [[ time.ctime().split()[3] ]] prints only time 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: 'create_uid': fields.many2one('res.users', 'User', readonly=1) 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: [[ (o.prop=='draft') and 'YES' or 'NO' ]] print YES or NO it works 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). // If o.prop=='draft' -> FALSE then it reads FALSE or 'No'. So 'No' is printed. One can use very comlpex structures. To learn more search for some pyhton reference regarding logical opertors.
05/02/08
149
10.7 How to add a new report To add a new report, you need to add it to the list of reports for its module (there is one such list for each module). Those lists are usually in files named modulename_report.xml which are located in each module directory.
modulename_report.xml format Here is, as an example, the declaration of the reports related to invoices. <delete model="ir.actions.report.xml" search="[('model','=','account.invoice')]"/>
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
10.8 Customize Report Customize Report Header Footer default custome report header and footer is applied from file “tiny-server/bin/addons/custom/corporate_rml_header.rml” You can change the content of the file to change the effect of the report header and the footer
Change line/text Color • - Change the Text Color • <stroke color="darkblue"/> - Change the Line Color Change the color according to the requirements after applying the color, you drawString that will take the color automatically
Add / Change Image • place image file in bin folder of the tiny server You can change the height and width of the image accordingly
Add String to Report • String drawString> You can set the x and y coordinate for setting the location of the string
Setting Font • <setFont name="Helvetica" size="10"/> 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: 1. A window is sent to the client (a form to be completed) 2. The client sends back the data from the fields which where filled in The server gets the result, usually execute a function and possibly sends another window/form to the client
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; 1. send a form to the client and some buttons 2. get the form result and the button pressed from the client 3. execute some actions 4. send a new action to the client (form, print, ...) To define a wizard, you have to create a class inheriting from wizard.interface and instantiate it. Each wizard must have a unique name, which can be chosen arbitrarily except for the fact it has to start with the module name (for example: account.move.line.reconcile). The wizard must define a dictionary named states which defines all its steps. Here is an example of such a class: class wiz_reconcile(wizard.interface): states = { 'init': { 'actions': [_trans_rec_get], 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, }, 'reconcile': { 'actions': [_trans_rec_reconcile], 'result': {'type': 'state', 'state':'end'} } } wiz_reconcile('account.move.line.reconcile');
The 'states' dictionary define all the states of the wizard. In this example; 'init' and 'reconcile'. There is another state which is named end which is implicit. A wizard always starts in the init state and ends in the end state. A state define two things: 1. a list of actions 2. a result
The list of actions Each step/state of a wizard defines a list of actions which are executed when the wizard enters the state. This list can be empty. The function (actions) must have the following signatures: def _trans_rec_get(self, uid, data, res_get=False):
Where: • self is the pointer to the wizard object • uid is the user ID of the user which is executing the wizard • data is a dictionary containing the following data: ♦ ids: the list of ids of resources selected when the user executed the wizard ♦ id: the id highlighted when the user executed the wizard ♦ form: a dictionary containing all the values the user completed in the preceding forms. If you change
05/02/08
'stat
156
values in this dictionary, the following forms will be pre−completed.
The result Here are some result examples:
Result: next step 'result': {'type': 'state', 'state':'end'}
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: 1. a form : with fields description and a form description 2. some buttons : on wich the user press after completing the form The form description (arch) is like in the views objects. Here is an example of form: _form = ''' '''
The fields description is similar to the fields described in the python ORM objects. Example:
Each step/state of a wizard can have several buttons. Those are located on the bottom right of the dialog box. The list of buttons for each step of the wizard is declared in the state key of its result dictionary. For example: 'state':[('reconcile','Reconcile'),('end','Cancel')] 1. the next step name (determine which state will be next) 2. the button string (to display for the client) Here is a screen−shot of this form:
05/02/08
157
Result: call a method to determine which state is next def _check_refund(self, uid, datas): return datas['form']['refund_id'] and 'wait_invoice' or 'end' ... 'result': {'type':'choice', 'next_state':_check_refund}
11.3 How to add a new wizard To create a new wizard, you must: • create the wizard definition in a .py file ♦ wizards are usually defined in the wizard subdirectory of their module as in server/bin/addons/module_name/wizard/your_wizard_name.py • add your wizard to the list of import statements in the __init__.py file of your module's wizard subdirectory. • declare your wizard in the database The declaration is needed to map the wizard with a key of the client; when to launch which client. To declare a new wizard, you need to add it to the module_name_wizard.xml file, which contains all the wizard declarations for the module. If the that file does not exist, you need to create it first. Here is an example of the accound_wizard.xml file; <delete model="ir.actions.wizard" search="[('wiz_name','like','account.')]"/> <wizard <wizard <wizard <wizard <wizard <wizard
Attributes for the wizard 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. • replace (optional): Whether or not the wizard should override all existing wizards for this model. Default value: False. • menu (optional): Whether or not to link the wizard with the 'gears' button. Default value: True. • keyword (optional): Bind the wizard to another action (print icon, gear icon, ...). Possible values for the keyword attribute are: ♦ client_print_multi: the print icon in a form ♦ client_action_multi: the 'gears' icon in a form ♦ tree_but_action: the 'gears' icon in a tree view (with the shortcuts on the left) ♦ tree_but_open: the double click on a branch of a tree (with the shortcuts on the left). For example, this is used, to bind wizards in the menu.
__terp__.py If the wizard you created is the first one of its module, you probably had to create the modulename_wizard.xml file yourself. In that case, it should be added to the update_xml field of the __terp__.py file of the module. Here is, for example, the __terp__.py file for the account module.
12 Events 12.1 Linking events to action The available type of events are: • • • •
client_print_multi (print from a list or form) client_action_multi (action from a list or form) tree_but_open (double click on the item of a tree, like the menu) tree_but_action (action on the items of a tree)
To map an events to an action: tree_but_openaccount.journal.periodOpen Journal 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. tree_but_openaccount.journal.periodOpen Journal 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... /> • So you usually do not need to add the mapping by yourself.
05/02/08
161
13 Information Repository (deprecated) The information repository is a semantics tree in which the datas that are not the ressources are stocked. We find in this structure: • the values by default • the conditional values; ♦ the state depends on the zip code, ♦ the payment method depends of the partner, ... • the reactions to the events client; ♦ click on the invoice menu, ♦ print an invoice, ♦ action on a partner, ... The IR has 3 methods; • add a value in the tree • delete a value in the tree • obtain all the values of a selected sheet
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.
The values by default The preferences per ressource (meta) the buttons actions structure in tree (key, key2, models, ..., value, ...) id | key
| key2
| models
| name | value | isobject | replace | meta |
1 3 20 24 30 36 78
| | | | | | |
|res.users | | | | | |
|.. | | | | | |
| | | | | | |
meta action action action meta action meta
lang tree_but_open client_print_multi client_action_multi res.partner tree_but_action product.product
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
Fix me! Make comment on replace and meta field
Setting the IR in xml files (version 3.1.1) • for buttons ♦
Description of the fields • keys: • args: • name: • value: • isobject: • replace: whether or not the action described should override an existing action or be appended to the list of actions. • def ir_get(cr, uid, keys, args=[], meta=False, context={}): res = ir.ir_get(cr, uid, [('default', self._name), ('field', False)], [('user_id',str(uid))]) account_payable = ir.ir_get(cr, uid, [('meta','res.partner'), ('name','account.payable')], opt)[0][2]
• def ir_del(cr, uid, id):
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__: • draft : not take into account • waiting : not take into account, waiting that an operation wake up this operation • confirmed : lots lines are reserved • done : unused, finnished • cancel : unused, canceled 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 a reservation is done it: • Finnish the associated picking list if completed • Allocate lot line to linked reservation and wake up this 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. You can create partial parcels with the wizard.
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. Procurement may have 2 methods: • make to stock : take products on the unreserved stock • make to order : buy or produce these products and after assign to this procurement Have a look at the workflow:
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
14.2 Bill of Materials Main BOM Infos active BOM active or not workcenter_id Workcenter to produce to transformations operations product_id Product generated with this BOM product_qty Qty of this product produced with all product lines bellow
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 The different modules are situated in the repertory: server/addons.
05/02/08
171
15.1 Writing a new module All the modules are situated in the repertory server/addons. You can delete or not a module durong the installation. The following steps are necessary to create a new module; • to create a repertory in the server/addons • to create a module description file: __terp__.py • to create the Python file containing the objects • to create .xml files that download the datas (views, workflows, ...) • eventually to create reports and wizards.
The __terp__.py file In the module repertory created, you must add a __terp__.py file. This file, with Python format, is responsible to determinate the files that will be executed at the starting of the server and also the dependences of this module. 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
The __init__.py file The __init__.py file is, like all the Python module,executed at the start of the program. It needs to import the Python files for being loaded. 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
15.2 Modules guidelines Guidelines for the name of a module: • You can use the '_' character as a separator • If your module is a submodule of another module, you can name it: module_submodule • Do not use uppercase, special characters or '.' in the name of a module Reports and boards: • • • • •
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
15.3 Network management For example, let's develop quickly a module that could manage the materiel installed to the customers. The specifications are the next ones; • a management of different materiels installed; computers, screens, switch, ... • a management of the relations between the components; ♦ computers running on a switch ♦ the print running on a computer • a mangement of configurations; users, passwords on every computer • differents components must be attached to the clients Few screenshots of our network management.
How to load data ? • Postgresql ♦ Simple, standard ♦ Does not respect the WORKFLOW !!! • XML files (avec –update=) • XML−RPC ♦ Script, same as website interface
How to backup/restore a Postgresql database? backup pg_dump terp >terp.sql restore createdb terp −−encoding=unicode psql terp
The objects methods • create({'field':'value'}) ♦ return ID created • search([('arg1','=','value1')...], offset=0, limit=1000) ♦ return [IDS] found • read([IDS], ['field1','field2',...]) ♦ return [{'id':1, 'field1':..., 'field2':..., ...}, ...] • write([IDS], {'field1':'value1','field2':3}) ♦ return True • unlink([IDS]) ♦ return True
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: XML−RPC Home Page XML−RPC for C and C++ The Apache XML−RPC Project Expat: The XML Parser
05/02/08
178
16.3 An example in PHP Here is an example on how to insert a new partner using PHP. This example makes use the phpxmlrpc library, available on sourceforge. include('xmlrpc.inc'); $arrayVal = array( 'name'=>new xmlrpcval('Fabien Pinckaers', "string") , 'vat'=>new xmlrpcval('BE477472701' , "string") ); $client = new xmlrpc_client("/xmlrpc/object", "localhost", 8069); $msg = new xmlrpcmsg('execute'); $msg−>addParam(new xmlrpcval("3", "int")); $msg−>addParam(new xmlrpcval("demo", "string")); $msg−>addParam(new xmlrpcval("res.partner", "string")); $msg−>addParam(new xmlrpcval("create", "string")); $msg−>addParam(new xmlrpcval($arrayVal, "struct")); $resp = $client−>send($msg); $val = $resp−>value(); $id = $val−>scalarval(); echo "Partner $id created!"; ?>
05/02/08
179
16.4 An example in Python Example of creation of a partner and his address. import xmlrpclib sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object') uid = 1 pwd = 'demo' partner = { 'title': 'Monsieur', 'name': 'Fabien Pinckaers', 'lang': 'fr', 'active': True, } partner_id = sock.execute(uid, pwd, 'res.partner', 'create', partner) 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', } sock.execute(uid, pwd, 'res.partner.address', 'create', address)
05/02/08
180
17.1 Load Balancing between servers
Load Balancing There is two ways to do load−balancing; • on the Tiny ERP server • on the relationnal database
Tiny ERP Server You can use any http proxy to make some load−balancing on Tiny ERP. All servers have to use the same database. For example, with balance (http://www.inlab.de/balance.html): ./balance 8070 serveur_1:8069 serveur_2:8069 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 Security in the client side The multi-company in Tiny ERP is very flexible. To edit companies, go to the menu; Administration > Users > Company Structure > Define Company
Companies are structured in trees. An example; • Tiny Group º Tiny Company 1 ■ Tiny Comp 1.1 º 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. Each users must be attached to a company. Ressources that doesn't have a company are visible for all companies.
On the server side 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.
Example with the account chart 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.
Different charts for different companies 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.
Different credit/debit account for your partners 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. It is in this directory that all your localisation works should go.
The __terp__.py file Here is an example __terp__.py file that you should add in the addons/l10n_be directory you just created. { "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).
The account creation At first you should take a look at the file addons/account/data/account_minimal.xml, this file is sourced on every update of the account module. It defines the minimal account tree needed by tinyerp to operate at its best.
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 Main Receivable40receivable 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.
How to make links 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.
The constraints on the account tree There is three constraints pertaining to the account tree creation: • You must not use an account type of view as a leaf, • The root of the account tree must use as its code, • and there must be at least one account type in the tree.
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.