what's new in openerp v6 technical part - anybox · what's new in openerp v6 –...
Post on 10-Apr-2018
239 Views
Preview:
TRANSCRIPT
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
What's new in OpenERP v6
Technical Part
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Download
● http://www.openerp.com/downloads
● Bazaar - bzr
● Trunk (v6)
– Server: http://bazaar.launchpad.net/~openerp/openobject-server/trunk/
– Server Addons: http://bazaar.launchpad.net/~openerp/openobject-addons/trunk/
– Client: http://bazaar.launchpad.net/~openerp/openobject-client/trunk/
– Web: http://bazaar.launchpad.net/~openerp/openobject-client-web/trunk/
– Web Addons: http://bazaar.launchpad.net/~openerp/openobject-addons/share-web-addons/
● V 5.0
– Server: http://bazaar.launchpad.net/~openerp/openobject-server/5.0/
– Addons: http://bazaar.launchpad.net/~openerp/openobject-addons/5.0/
– Client: http://bazaar.launchpad.net/~openerp/openobject-client/5.0/
– Web: http://bazaar.launchpad.net/~openerp/openobject-client-web/5.0/
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Multi/Cross-company
● built-in in all applications
● Many models now have a direct or indirect link to a company (many2one or related field)
– e.g: sale.order.line -> sale.order -> shop -> company● Record visibility rules by default (ir.rule)
● multicompany module = demo
● in-depth review added specific internal operations:
● e.g. inventory push/pull flows, transit● Special group is needed to view multi-company fields
● Useability/Multi Companies● Users can belong to several companies, but select the one the
current context they work in, via Preferences
● Visibility of records depends on Company they belong to, current Company of user, and Record Rules.
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Technical zoom on new features
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Module structureAddons/idea/ # The module directory
demo/ # Demo and population data
idea_demo.xml # Demo data
idea_data.xml # Population data
i18n/ # Translation files
report/ # Report definitions
security/ # Declaration of groups and access rights
view/ # Views (forms,lists), menus, actions
idea_view.xml # Views (forms,lists), menus, actions
wizard/ # Wizards definitions
workflow/ # Workflow definitions
idea_workflow.xml # Workflow definitions
test/ # unit testing data
__init__.py # Python package initialization (required)
__terp__.py # module declaration (required)
__openerp__.py # module declaration (required)
idea.py # Python classes, the module's objects
installer.py # Installer Python declaration
idea_installer.xml # Installer XML declaration
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Module declaration
● __terp__.py is deprecated and replaced by
__openerp__.py● v6 Renamed / new keys
– 'init_xml', 'update_xml' → 'data'● Loading order : init_xml, update_xml, data● XML : <data noupdate="1">● CVS : always update except if in init_xml
– 'demo_xml' → 'demo'– 'test'– 'external_dependencies' : { 'python': ['mako','simplejson'],
'bin' : ['which'] }
● 'certificate' : '0123456789012'● 'installable' : True
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Module data files
● Data files● .csv
– many2xxx : reconnects via name_search()
– many2xxx:id : reconnects with object's xml_id
– many2xxx.id : reconnects with object's database id
– one2many/field : creates one2many destination record and sets field value
– Example:"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "access_idea_idea","idea.idea","model_idea_idea","base.group_user",1,0,0,0
● .xml
– XML RelaxNG validation
– OpenObject validates the syntax and structure of XML files, according to a RelaxNG grammar, found in server/bin/import_xml.rng.
– For manual check use xmllint: xmllint –relaxng /path/to/import_xml.rng <file>
● Test files● .yml
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
osv.osv attributes (1)
● Defaults may be literals (careful!)● dictionary: { field names → functions or literal
providing defaults }_defaults = {
#'active' : lambda *a : 1, 'active' : 1,
'start_date': lambda *a: time.strftime('%Y%m%d %H:%M:%S'),
'name': lambda self,cr,uid,context: 'eggs',
}
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
osv.osv attributes (2)
● Constraint msgs may be callables● list of tuples defining the Python constraints, in the form
(func_name, message, fields)● translations handled by ORM
def _construct_constraint_msg(self, cr, uid, ids, context=None):
#...
iban_country = self.browse(cr, uid, ids)[0].iban[:2]
if default_iban_check(iban_country): return _('The IBAN does not seems to be correct. You should have entered something like this %s')%iban_example
return _('The IBAN is invalid, It should begin with the country code')
_constraints = [(check_iban, _construct_constraint_msg, ["iban"])]
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
ORM fields (1)
● select– New search view– select=1, select=2 => select=True– create an index if select=True in ORM– insert in default search view if select=True if in ORM or XML
(form or list)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
ORM fields (2)
● Float– Stored in DB as numeric
– Don't store precision anymore
– fields.float('Duration',digits=(16,2)) #precision (total length) and scale (length right of the decimal point)
– Configurable scale : (precision hardcoded to 16)Import decimal_precision as dpfields.float('Duration',digits_compute=dp.get_precision('label'))+ record in table decimal precision<record id="decimal_sale" model="decimal.precision"> <field name="name">label</field> <field name="digits">2</field></record>
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
ORM methods (1)
● log(cr, uid, id, message, context=None)● To notify business events
– Displayed in the web client at the top● Create a record in res.log. Menu:
– Administration/Audit/Logs– Adminsitration/Reporting/Dashboard/Administration
Dashboard● Returns id of the new record in res.log● Parameters:
– message: message to write – context : if passed then context is stored in db
● self.log(cr, uid, st.id, _('Statement %s is confirmed, journal items are created.') % (st_number,))
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Logging
● import logging
logger = logging.getLogger('name of module')
logger.info(“loading %s”, filename)● Levels:
– critical
– error or except (also display traceback)
– warn
– info
– test
– debug
– debug_rpc
– debug_rpc_answer
– debug_sql
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
ORM methods (2a)
● read_group(cr, uid, domain, fields, groupby, offset=0, limit=None, context=None)● Get the list of records in list view grouped by the given ``groupby`` fields
● Returns: list of dictionaries (one dictionary for each record) containing:
– the values of fields grouped by the fields in ``groupby`` argument
– __domain: list of tuples specifying the search criteria
– __context: dictionary with argument like ``groupby``
● Parameters:
– domain: list specifying search criteria [['field_name', 'operator', 'value'], ...]
– fields: list of fields present in the list view specified on the object
– groupby: list of fields on which to groupby the records
– offset: optional number of records to skip
– limit: optional max number of records to return
● For numeric fields : new in ORM : group_operator='sum' # default
– Also : min,max,avg,... #all sql aggregators
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
ORM methods (2b)
Example: Sales/Address Book/Addresses: Returned data:
Group by Country & Type
[{'__context': {'group_by': ['type']}, 'country_id': (20, u'Belgium'), '__domain': [('country_id', '=', 20), ('is_customer_add', '=', '1')]},
{'__context': {'group_by': ['type']}, 'country_id': (46, u'China'), '__domain': [('country_id', '=', 46), ('is_customer_add', '=', '1')]},
{'__context': {'group_by': ['type']}, 'country_id': False, '__domain': [('country_id', '=', False), ('is_customer_add', '=', '1')]}]
Expand Belgium:
[{'__context': {'group_by': []}, 'type': False, '__domain': [('type', '=', False), ('country_id', '=', 20), ('is_customer_add', '=', '1'), ('is_customer_add', '=', '1')]},
{'__context': {'group_by': []}, 'type': u'delivery', '__domain': [('type', '=', u'delivery'), ('country_id', '=', 20), ('is_customer_add', '=', '1'), ('is_customer_add', '=', '1')]},
{'__context': {'group_by': []}, 'type': u'invoice', '__domain': [('type', '=', u'invoice'), ('country_id', '=', 20), ('is_customer_add', '=', '1'), ('is_customer_add', '=', '1')]}]
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Menus● Top level menu
● GTK : icon
● Web : web_icon, web_icon_hover
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views
● Type of views● Search + list● Form● Graph● Calendar● Gantt● Diagram
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : search (1)
● New type of view: <search>● <field name=”...”
filter_domain=”['|', ('location_to', '=', self), ('location_src', '=', self)]”/>
● <filter name=”current” string=”Current” icon=”terp-check” domain=”[(user_id,'=',uid)]” #filter context=”{'country_visible=”1”, #options 'group_by':'user_id'}” #group by help=”...”/>
● Also– <separator orientation=”vertical”>– <group expand=”0”>– <newline> # no 4 columns like in form view
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : search (2)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : search (3)
● Declaration : not mandatory
● Context● context: { 'default_field' = 'value' }
● context: { 'search_default_field' = 'value' }
● context: { 'search_default_filter' = True }
Examples:
● <field name="context" eval="{'search_default_name':'test', 'search_default_duration':3,'default_name':'toto'}"/>
● <field name="context">{'search_default_name':'test', 'search_default_duration':5,'default_name':'toto'}</field>
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : list (1)
● List views with aggregation● Multiple aggregations
context = { 'group_by': ['field_1','field_2'] }
● List views with buttons
● Dynamic List views (via context)context = { 'section_mode': True }
invisible = ”context.get('section_mode')”
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : list (2)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : list (3)
● When individual records are too low-level or irrelevant, hide them (e.g. stock moves)● context = {'group_by_no_leaf': 1}
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : form
● You can insert HTML code. However, it works only with web client.
● See html_view example module
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : diagram (1)
● editable workflow-like view
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Views : diagram (2)
● New in v6● diagram
● Node many2one(diagram)
– object– shape– bgcolor
● arrow– object– source– destination– label
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Workflows & Security
Some modifications:
● Security● Workflow : res.roles => res.groups● Admin still has all rights (c,r,w,u)● Admin do not have all groups anymore (views)
<record model="res.groups" id="group_no_one" context="{'noadmin':True}"> <field name="name">Useability / No One</field></record>
● ir.rule.group => ir.rule– Now controlled per mode: create/read/update/delete
– globals can't be bypassed
– group-local are OR'ed, and can provide more access
● Admin has access to all records● Private ORM method “_method()” are not accessible in RPC
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Security
● Example : project_security.xml
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Action - Wizard
● Action contexts now supports:● context: { 'default_field' = 'value' }
● context: { 'search_default_field' = 'value' }
● context: { 'search_default_filter' = True }
● Actions have new properties● help="Business description for this action"
● multi=True
● Simple XML declaration for wizards actions
Also view_id
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Installer (1)
● Like res.config● res.config.installer
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
● Create new form view inheriting base.res_config_installer:
Installer (2)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Installer (3)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Installer (4)
optional
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Installer (5)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Reports
● base_report_designer includes OOo plugin● Performance improvements in RML rendering
● <blockTable rowHeights="2cm" colWidths="11.0,7.0">● Better translation and with cache● Improved queries for accounting reports
● Report engine now has modules:● e.g. report_webkit from Camptocamp
see also report_webkit_sample
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Building Reporting Objects
...
...
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Translations
● Translation exports fixed● less spurious terms● additional terms exported by default
● Auto-synchronized with Launchpad's Rosetta● new terms imported automatically from .pot● new translations exported automatically to .po
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Developer goodies
● Improved views in Admin menus, with XML Ids● View logs shows XML ID● Debugging tooltips in GTK and Web● new logging levels: test, debug_rpc,
debug_rpc_answer, debug_sql● command-line instance creation: -d dbname -i
base
● Test reports./openerp-server.py -i sale -d mytestdb –test-report-directory=/tmp
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Filters and safe_eval
● ir.filters● 'ir_filter_id': fields.many2one('ir.filters', 'Filter', ondelete="restrict")
● tools.safe_eval● from tools.safe_eval import safe_eval as eval● criteria = eval(obj.ir_filter_id.domain)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Web module
● Easy distribution and sharing of web addons
● Web-side and server-side cooperation
● ./__openerp__.py, web=True● ./web/
● Structure
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Pad example
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Template
● Mako-based templates● Text manipulation of “raw” mako code● Register editor objects against lists of templates
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Controller
● Python-inherit from existing controller object
● Set same _cp_path to override existing controller
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Widget
● You can create your own widget● See addons wiki
– /web/widgets/
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Automated testingwith YAML
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
YAML
New YAML data serialization supported in v6
● Data-oriented syntax “YAML Ain't a Markup Language”
● Whitespace delimiters, Python-like
● Human-readable
● no quotes, brackets, braces, … by default● Featuring the following advantages:
● User friendly format as an alternative to our current XML data format.● Same system to load data or tests, integrated in modules.● Built in OpenERP so that you can develop complex Python tests.● Simpler for non developers to write functional tests.
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
YAML
Tests are executed when:
● you install the module and you are in demo mode (load demo data).
● run with –loglevel=test -d <db> -u <module>
● all tests of a module● run with –loglevel=test -d <db> --test-file=<file.yml>
● one test case of a module
When added to the test section of the module descriptor, tests are rolled back by default
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
sale/test/so.make_invoice.yml (1)
-
In order to test the 'Make Invoices' wizard of sale module in the Open-ERP, I create two Sale order,group them and create invoice.
-
I create a Sale Order for products Slider Mobile and LG Viewty Smart for qty 100 having order_policy manual.
-
!record {model: sale.order, id: sale_order_so4}:
date_order: '2010-08-02'
invoice_quantity: order
name: Test_SO004
order_line:
- name: Slider Mobile
price_unit: 200.0
- name: LG Viewty Smart
price_unit: 170.0
-
I confirm the Sale Order.
-
!workflow {model: sale.order, action: order_confirm, ref: sale_order_so4}
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
sale/test/so.make_invoice.yml (2)
-
I verify that an invoice has been created
-
!python {model: account.invoice}: |
inv = self.search(cr, uid, [('origin','=','Test_SO004|Test_SO003|')])
assert inv, "Invoices has not been generated"
-
I verify that an account invoice line with origin 'Test_SO004' is created
-
!python {model: account.invoice.line}: |
sale_order_obj = self.pool.get('sale.order')
acc_inv_obj = self.pool.get('account.invoice')
inv = acc_inv_obj.search(cr, uid, [('origin','=','Test_SO004|Test_SO003|')])
so = sale_order_obj.browse(cr, uid, ref("sale_order_so4"))
inv_line = self.search(cr, uid, [('origin','=',so.name),('name','=','Slider Mobile'),('invoice_id','=',inv)])
assert inv_line, "Account invoice line has not been created"
-
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
YAML Syntax
● OpenERP supports specific custom types:● !record {model: res_model, id: xml_id, context: {…} }● !python {model: res_model}● !workflow {model:res_model, action:signal, ref: xml_id}
● And equivalents for all XML supported elements● !act_window● !report● !function● !menuitem● !assert● etc.
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
YAML: Scenario Export
● base_module_record can now output YAML as well
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
YAML: Continuous Integration
● http://test.openobject.com (buildbot) builds each of our hundreds of YAML tests after each commit
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Module Certification
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Some rules (1)
● There is no warning during initialization. And it installs properly.
● Default language of the module (developpment, documentation and en-user language) is U.S. English. And it is entirely traductible.
● File structure of the module
● The “description” tag of the terp/openerp file contains at least 5 lines and 150 characters. Description is understandable and describe properly the module
● No dependencies to uncertified module. No group from uncertified modules. No inherited object or view from uncertified modules.
● Category is defined and consistent with already existing categories in OpenERP
● Contains security folder. Are groups of other modules this module depends on correctly used. Rules for menuitems and usefull. At least one security rule on each object.
● Does the code use unecessary SQL query? Do the SQL queries prevent SQL injection? Care must be taken not to introduce SQL injection vulnerabilities to SQL queries.
● Existing views are inherited and not overwritten.
● Overriden methods call to super()?
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Some rules (2)
● Exception properly raised : tuple and translatable.
● Proper comments. No TODO, TOFIX, TOCHECK...
● Have all the .py files a copyright ?
● Are the wizard implemented using osv_memory ineritancy
● Can we replace a read by a browse or the inverse?
● Have all the fields.function a help tooltip to explain how is computed the field?
● Does the module avoid unecessary queries like when we put a browse in a loop?
● Is the data model properly designed to prevent redudancy and ensure integrity?
● If you implemented workflows in the module, create demo data that passes most branches of your workflow
● For each workflow defined, is there at least one starting node and one ending node? No useless or unreachable nodes? Join and split modes are properly assigned. In case of conditions, all cases are covered.
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Some rules (3)
● No bug found during testing
● There are enough demo data
● All required fields are directly visible (not in hidden tabs) or prefilled with default value.
● Crud operations are all working. Duplication is working and fields are properly initialized (e.g. state, seq number, ...)
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Quality tests
● You can run automated quality tests with the wizard in the module view provided by the module “base_module_quality”
● Requires pylint
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Guidelines
http://doc.openerp.com/contribute/15_guidelines/coding_guidelines.html
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
OpenERP Guidelines (1)
● Bazaar is your historian● Do not comment out code if you want to disable it.
● Call your fish a fish● Give your variables a meaningful name all the time.
● Do not bypass the ORM● You should never use the database cursor directly when the ORM can do
the same thing! By doing so you are bypassing all the ORM features, possibly the transactions, access rights and so on.
● No SQL injections, please!● Care must be taken not to introduce SQL injections vulnerabilities when
using manual SQL queries. The vulnerability is present when user input is either incorrectly filtered or badly quoted which allow an attacker to introduce undesirable clauses to a SQL query
● The infamous context● Do not use mutable objects as default values for functions, because they
are created as constants
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
OpenERP Guidelines (2)
● There is better than lambda, sometimes● Instead of writing trivial lambda expression to extract items or attributes from a list
of data structures, learn to use list comprehension or operator.itemgetter and operator.attrgetter instead, which are often more readable and faster
● Keep your methods short/simple when possible● Functions and methods should not contain too much logic: having a lot of small
and simple methods is more advisable than having few large and complex methods.
● Never commit the transaction● The OpenERP/OpenObject framework is in charge of providing the transactional
context for all RPC calls. The principle is that a new database cursor is opened at the begining of each RPC call, and commited when the call has returned, just before transmitting the answer to the RPC client
● You should NEVER call cr.commit() yourself, UNLESS you have created your own database cursor explicitly!
● Use the gettext method correctly● OpenERP uses a GetText-like method named “underscore” _( ) to indicate that a
static string used in the code needs to be translated at runtime using the language of the context
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Other Guidelines
● Also Common Python Guidelineshttp://doc.openerp.com/contribute/15_guidelines/coding_guidelines_python.html
● Usability Book (including guidelines)● Usability guidelines are very important for 6.0!● Needed to maintain coherent look and feel● Help users directly locate what they need, when they
need it, to be efficient “Don't make me think”● Hide the complexity!
http://doc.openerp.com/usability_book/index.html#usability-link
What's new in OpenERP v6 – Technical Part v0.2 - December 2010Jacques-Etienne Baudoux
Questions
This document is published under the Creative Commons Attribution License 3.0 Unported by OpenERP SA. Feel free to copy, share, or modify it. For reference please see http://creativecommons.org/licenses/by/3.0/
top related