[tryton-debian-vcs] tryton-server branch upstream updated. upstream/3.2.3-1-g61781f3

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Thu Oct 23 12:19:46 UTC 2014


The following commit has been merged in the upstream branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/tryton-server.git;a=commitdiff;h=upstream/3.2.3-1-g61781f3

commit 61781f32a142e12d249411d63b65026bb1079865
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Oct 21 11:29:26 2014 +0200

    Adding upstream version 3.4.0.
    
    Signed-off-by: Mathias Behrle <mathiasb at m9s.biz>

diff --git a/CHANGELOG b/CHANGELOG
index be6f898..6a2811c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,13 +1,28 @@
-Version 3.2.3 - 2014-09-29
+Version 3.4.0 - 2014-10-20
 * Bug fixes (see mercurial logs for details)
 * Use literal_eval instead of safe_eval (CVE-2014-6633)
 * Prevent double underscore in safe_eval (CVE-2014-6633)
-
-Version 3.2.2 - 2014-08-03
-* Bug fixes (see mercurial logs for details)
-
-Version 3.2.1 - 2014-07-01
-* Bug fixes (see mercurial logs for details)
+* Add pre-validation on button
+* Model and Field access checked only if _check_access is set
+* Add check_access to RPC
+* Add check_access to Wizard and Report
+* Add support for domain_<field name> method
+* Refactor configuration file and command line
+* Use the context of the relation field for instanciation
+* Use a configuration field for logging
+* Add translated descriptor for Selection field
+* Add tree_state attribute on tree view
+* Allow to sync XML data
+* Remove on_change calls in Model.default_get
+* Add group call to on_change
+* Add UnionMixin
+* Allow to disable sorting of dictionary field's selection
+* Add active field to views of action window
+* Make global cache depends on explicit context keys
+* Don't add to global cache Binary fields
+* Add MatchMixin
+* Add image widget to tree
+* Remove context, current_date and time from record rule evaluation
 
 Version 3.2.0 - 2014-04-21
 * Bug fixes (see mercurial logs for details)
diff --git a/MANIFEST.in b/MANIFEST.in
index 15940c4..b5f249f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,7 +8,6 @@ include doc/*
 recursive-include doc *.rst
 recursive-include doc *.po
 recursive-include doc *.pot
-include etc/*
 include trytond/backend/*/init.sql
 include trytond/ir/tryton.cfg
 include trytond/ir/*.xml
diff --git a/PKG-INFO b/PKG-INFO
index 40f97fa..c6ba62e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: trytond
-Version: 3.2.3
+Version: 3.4.0
 Summary: Tryton server
 Home-page: http://www.tryton.org/
 Author: Tryton
 Author-email: issue_tracker at tryton.org
 License: GPL-3
-Download-URL: http://downloads.tryton.org/3.2/
+Download-URL: http://downloads.tryton.org/3.4/
 Description: trytond
         =======
         
diff --git a/bin/trytond b/bin/trytond
index d8b84ef..6f03fe7 100755
--- a/bin/trytond
+++ b/bin/trytond
@@ -21,72 +21,39 @@ def parse_commandline():
 
     parser.add_argument('--version', action='version',
         version='%(prog)s ' + VERSION)
-    parser.add_argument("-c", "--config", dest="config",
-        help="specify config file")
-    parser.add_argument('--debug', dest='debug_mode', action='store_true',
-        help='enable debug mode (start post-mortem debugger if exceptions'
-        ' occur)')
+    parser.add_argument("-c", "--config", dest="configfile", metavar='FILE',
+        default=os.environ.get('TRYTOND_CONFIG'), help="specify config file")
+    parser.add_argument('--dev', dest='dev', action='store_true',
+        help='enable development mode')
     parser.add_argument("-v", "--verbose", action="store_true",
         dest="verbose", help="enable verbose mode")
 
-    parser.add_argument("-d", "--database", dest="db_name",
-        help="specify the database name")
-    parser.add_argument("-i", "--init", dest="init",
-        help="init a module (use \"all\" for all modules)")
-    parser.add_argument("-u", "--update", dest="update",
-        help="update a module (use \"all\" for all modules)")
+    parser.add_argument("-d", "--database", dest="database_names", nargs='+',
+        default=[], metavar='DATABASE', help="specify the database name")
+    parser.add_argument("-u", "--update", dest="update", nargs='+', default=[],
+        metavar='MODULE', help="update a module")
+    parser.add_argument("--all", dest="update", action="append_const",
+        const="ir", help="update all installed modules")
 
-    parser.add_argument("--pidfile", dest="pidfile",
+    parser.add_argument("--pidfile", dest="pidfile", metavar='FILE',
         help="file where the server pid will be stored")
-    parser.add_argument("--logfile", dest="logfile",
-        help="file where the server log will be stored")
-    parser.add_argument("--disable-cron", dest="cron",
-        action="store_false", help="disable cron")
-
-    parser.epilog = ('The first time a database is initialized with "-i" admin'
-        ' password is read from file defined by TRYTONPASSFILE '
-        'environment variable or interactively ask user. '
+    parser.add_argument("--logconf", dest="logconf", metavar='FILE',
+        help="logging configuration file (ConfigParser format)")
+    parser.add_argument("--cron", dest="cron", action="store_true",
+        help="enable cron")
+
+    parser.epilog = ('The first time a database is initialized admin '
+        'password is read from file defined by TRYTONPASSFILE '
+        'environment variable or interactively ask user.\n'
         'The config file can be specified in the TRYTOND_CONFIG '
+        'environment variable.\n'
+        'The database URI can be specified in the TRYTOND_DATABASE_URI '
         'environment variable.')
 
-    opt = parser.parse_args()
-
-    if opt.config:
-        options['configfile'] = opt.config
-    else:
-        # No config file speficified, it will be guessed
-        options['configfile'] = None
-
-    for arg in (
-            'verbose',
-            'debug_mode',
-            'pidfile',
-            'logfile',
-            'cron',
-            ):
-        if getattr(opt, arg) is not None:
-            options[arg] = getattr(opt, arg)
-
-    db_name = []
-    if opt.db_name:
-        for i in opt.db_name.split(','):
-            db_name.append(i)
-    options['db_name'] = db_name
-
-    init = {}
-    if opt.init:
-        for i in opt.init.split(','):
-            if i != 'test':
-                init[i] = 1
-    options['init'] = init
-
-    update = {}
-    if opt.update:
-        for i in opt.update.split(','):
-            if i != 'test':
-                update[i] = 1
-    options['update'] = update
+    options = parser.parse_args()
 
+    if not options.database_names and options.update:
+        parser.error('Missing database option')
     return options
 
 
diff --git a/doc/conf.py b/doc/conf.py
index 71c121e..18e62c7 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,9 +48,9 @@ copyright = (u'2008-2011, Bertrand Chenal, Cédric Krier, Ian Wilson, '
 # built documents.
 #
 # The short X.Y version.
-version = '3.2'
+version = '3.4'
 # The full version, including alpha/beta/rc tags.
-release = '3.2'
+release = '3.4'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/doc/index.rst b/doc/index.rst
index aa1c79b..5ee2f0d 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -24,7 +24,9 @@ First steps
 ===========
 
     * **Installation:**
-      :ref:`Installation <topics-install>`
+      :ref:`Installation <topics-install>` |
+      :ref:`Configuration <topics-configuration>` |
+      :ref:`Setup a database <topics-setup-database>`
 
 The model layer
 ===============
@@ -32,7 +34,8 @@ The model layer
     * **Models:**
       :ref:`Model syntax <topics-models>` |
       :ref:`Field types <ref-models-fields>` |
-      :ref:`Domain syntax <topics-domain>`
+      :ref:`Domain syntax <topics-domain>` |
+      :ref:`Access rights <topics-access_rights>`
 
 The view layer
 ==============
diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst
index 85fd0ca..82b4b1e 100644
--- a/doc/ref/models/fields.rst
+++ b/doc/ref/models/fields.rst
@@ -129,8 +129,12 @@ client will also read these fields even if they are not defined on the view.
 
 .. attribute:: Field.context
 
-A dictionary which will update the current context when opening a *relation
-field*.
+A dictionary which will update the current context for *relation field*.
+
+.. warning::
+    The context could only depend on direct field of the record and without
+    context.
+..
 
 ``loading``
 -----------
@@ -184,6 +188,18 @@ Default value
 
 See :ref:`default value <topics-fields_default_value>`
 
+Searching
+=========
+
+A class method could be defined for each field which must return a SQL
+expression for the given domain instead of the default one.
+The method signature is::
+
+    domain_<field name>(domain, tables)
+
+Where ``domain`` is the simple :ref:`domain <topics-domain>` clause and
+``tables`` is a nested dictionary, see :meth:`~Field.convert_domain`.
+
 Ordering
 ========
 
@@ -413,6 +429,13 @@ A string field with limited values to choice.
     If true, the human-readable values will be translated. Default value is
     ``True``.
 
+Instance methods:
+
+.. method:: Selection.translated([name])
+
+    Returns a descriptor for the translated value of the field. The descriptor
+    must be used on the same class as the field.
+
 Reference
 ---------
 
@@ -600,6 +623,10 @@ This field accepts as written value a list of tuples like the :class:`One2Many`.
     An integer or a PYSON expression denoting the maximum number of records
     allowed in the relation.
 
+.. attribute:: Many2Many.add_remove
+
+    An alias to the :attr:`domain` for compatibility with the :class:`One2Many`.
+
 Instance methods:
 
 .. method:: Many2Many.get_target()
diff --git a/doc/ref/models/models.rst b/doc/ref/models/models.rst
index 5ac7918..110636e 100644
--- a/doc/ref/models/models.rst
+++ b/doc/ref/models/models.rst
@@ -76,13 +76,12 @@ Class methods:
         warning states by users.
     ..
 
-.. classmethod:: Model.default_get(fields_names[, with_rec_name[, with_on_change]])
+.. classmethod:: Model.default_get(fields_names[, with_rec_name])
 
-    Return a dictionary with the default values for each field in
+    Returns a dictionary with the default values for each field in
     ``fields_names``. Default values are defined by the returned value of each
     instance method with the pattern ``default_`field_name`()``.
     ``with_rec_name`` allow to add `rec_name` value for each many2one field.
-    ``with_on_change`` allow to add ``on_change`` value for each default value.
 
 .. classmethod:: Model.fields_get([fields_names])
 
@@ -90,6 +89,15 @@ Class methods:
 
 Instance methods:
 
+.. method:: Model.on_change(fieldnames)
+
+    Returns the list of changes by calling `on_change` method of each field.
+
+.. method:: Model.on_change_with(fieldnames)
+
+    Returns the new values of all fields by calling `on_change_with` method of
+    each field.
+
 .. method:: Model.pre_validate()
 
     This method is called by the client to validate the instance.
@@ -301,6 +309,8 @@ CLass methods:
     Return a list of list of values for each ``records``.
     The list of values follows ``fields_names``.
     Relational fields are defined with ``/`` at any depth.
+    Descriptor on fields are available by appending ``.`` and the name of the
+    method on the field that returns the descriptor.
 
 .. classmethod:: ModelStorage.import_data(fields_names, data)
 
@@ -520,6 +530,10 @@ Class attributes are:
     couple of key and label when the type is `selection`.
     The format is a key/label separated by ":" per line.
 
+.. attribute:: DictSchemaMixin.selection_sorted
+
+    If the :attr:`selection` must be sorted on label by the client.
+
 .. attribute:: DictSchemaMixin.selection_json
 
     The definition of the :class:`trytond.model.fields.Function` field to
@@ -543,5 +557,58 @@ Instance methods:
 
     Getter for the :attr:`selection_json`.
 
+==========
+MatchMixin
+==========
+
+.. class:: MatchMixin
+
+A mixin_ to add to a :class:`Model` a match method on pattern.
+The pattern is a dictionary with field name as key and the value to compare.
+The record matches the pattern if for all dictionary entries, the value of the
+record is equal or not defined.
+
+Instance methods:
+
+.. method:: MatchMixin.match(pattern)
+
+    Return if the instance match the pattern
+
+==========
+UnionMixin
+==========
+
+.. class:: UnionMixin
+
+A mixin_ to create a :class:`ModelSQL` which is the UNION_ of some
+:class:`ModelSQL`'s. The ids of each models are sharded to be unique.
+
+Static methods:
+
+.. staticmethod:: UnionMixin.union_models()
+
+    Return the list of :class:`ModelSQL`'s names
+
+Class methods:
+
+.. classmethod:: UnionMixin.union_shard(column, model)
+
+    Return a SQL expression that shards the column containing record id of
+    model name.
+
+.. classmethod:: UnionMixin.union_unshard(record_id)
+
+    Return the original instance of the record for the sharded id.
+
+.. classmethod:: UnionMixin.union_column(name, field, table, Model)
+
+    Return the SQL column that corresponds to the field on the union model.
+
+.. classmethod:: UnionMixin.union_columns(model)
+
+    Return the SQL table and columns to use for the UNION for the model name.
+
+
 .. _mixin: http://en.wikipedia.org/wiki/Mixin
 .. _JSON: http://en.wikipedia.org/wiki/Json
+.. _UNION: http://en.wikipedia.org/wiki/Union_(SQL)#UNION_operator
diff --git a/doc/ref/rpc.rst b/doc/ref/rpc.rst
index ba0e6f9..d34e26f 100644
--- a/doc/ref/rpc.rst
+++ b/doc/ref/rpc.rst
@@ -5,7 +5,7 @@
 RPC
 ===
 
-.. class:: RPC([readonly[, instantiate[, result]]])
+.. class:: RPC([readonly[, instantiate[, result[, check_access]]]])
 
 RPC is an object to define the behavior of Remote Procedur Call.
 
@@ -22,3 +22,8 @@ Instance attributes are:
 .. attribute:: RPC.result
 
     The function to transform the result
+
+.. attribute:: RPC.check_access
+
+    Set `_check_access` in the context to activate the access right on model
+    and field. Default is `True`.
diff --git a/doc/topics/access_rights.rst b/doc/topics/access_rights.rst
new file mode 100644
index 0000000..96c0d22
--- /dev/null
+++ b/doc/topics/access_rights.rst
@@ -0,0 +1,35 @@
+.. _topics-access_rights:
+
+=============
+Access Rights
+=============
+
+There are 3 levels of access rights: model, field, button and record.
+Every access right is based on the groups of the user.
+The model and field access rights are checked for every RPC call for which
+:attrs:`RPC.check_access` is set. The others are always enforced.
+
+Model Access
+============
+
+They are defined by records of `ir.model.access` which define for each couple
+of model and group, the read, write, create and delete permission. If any group
+of the user has the permission activated, then the user is granted this
+permission.
+
+Field Access
+============
+
+Same as for model access but applied on the field. It uses records of
+`ir.model.field.access`.
+
+Button
+======
+
+For each button of a model the records of 'ir.model.button` define the list of
+groups that are allowed to call it.
+
+Record Rule
+===========
+
+.. TODO
diff --git a/doc/topics/configuration.rst b/doc/topics/configuration.rst
new file mode 100644
index 0000000..a187c0b
--- /dev/null
+++ b/doc/topics/configuration.rst
@@ -0,0 +1,193 @@
+.. _topics-configuration:
+
+=============================
+Configuration file for Tryton
+=============================
+
+The configuration file control some aspects of the behavior of Tryton.
+The file uses a simple ini-file format. It consists of sections, led by a
+`[section]` header and followed by `name = value` entries:
+
+    [database]
+    uri = postgresql://user:password@localhost/
+    path = /var/lib/trytond
+
+For more information see ConfigParser_.
+
+.. _ConfigParser: http://docs.python.org/2/library/configparser.html
+
+Sections
+========
+
+This section describes the different main sections that may appear in a Tryton
+configuration file, the purpose of each section, its possible keys, and their
+possible values.
+Some modules could request the usage of other sections for which the guideline
+asks them to be named like their module.
+
+jsonrpc
+-------
+
+Defines the behavior of the JSON-RPC_ network interface.
+
+listen
+~~~~~~
+
+Defines a comma separated list of couple of host (or IP address) and port numer
+separeted by a colon to listen on.
+The default value is `localhost:8000`.
+
+hostname
+~~~~~~~~
+
+Defines the hostname for this network interface.
+
+data
+~~~~
+
+Defines the root path to retrieve data for `GET` request.
+
+xmlrpc
+------
+
+Defines the behavior of the XML-RPC_ network interface.
+
+listen
+~~~~~~
+
+Same as for `jsonrpc` except it have no default value.
+
+webdav
+------
+
+Define the behavior of the WebDAV_ network interface.
+
+listen
+~~~~~~
+
+Same as for `jsonrpc` except it have no default value.
+
+database
+--------
+
+Defines how database is managed.
+
+uri
+~~~
+
+Contains the URI to connect to the SQL database. The URI follows the RFC-3986_.
+The typical form is:
+
+    database://username:password@host:port/
+
+The default available databases are:
+
+PostgreSQL
+**********
+
+`pyscopg2` supports two type of connections:
+
+    - TCP/IP connection: `postgresql://user:password@localhost:5432/`
+    - Unix domain connection: `postgresql://username:password@/`
+
+SQLite
+******
+
+The only possible URI is: `sqlite://`
+
+MySQL
+*****
+
+Same as for PostgreSQL.
+
+path
+~~~~
+
+The directory where Tryton should store files and so the user running `trytond`
+must have write access on this directory.
+The default value is `/var/lib/trytond/`.
+
+list
+~~~~
+
+A boolean value (default: `True`) to list available databases.
+
+retry
+~~~~~
+
+The number of retries when a database operation error occurs during a request.
+
+language
+~~~~~~~~
+
+The main language (default: `en_US`) of the database that will be stored in the
+main table for translatable fields.
+
+ssl
+---
+
+Activates the SSL_ on all network protocol.
+
+privatekey
+~~~~~~~~~~
+
+The path to the private key.
+
+certificate
+~~~~~~~~~~~
+
+The path to the certificate.
+
+email
+-----
+
+uri
+~~~
+
+The SMTP-URL_ to connect to the SMTP server which is extended to support SSL_
+and STARTTLS_.
+The available protocols are:
+
+    - `smtp`: simple SMTP
+    - `smtp+tls`: SMTP with STARTTLS
+    - `smtps`: SMTP with SSL
+
+The default value is: `smtp://localhost:25`
+
+from
+~~~~
+
+Defines the default `From` address when Tryton send emails.
+
+session
+-------
+
+timeout
+~~~~~~~
+
+The time in second before a session expires.
+
+super_pwd
+~~~~~~~~~
+
+Theserver password uses to authenticate database management from the client.
+It is encrypted using using the Unix `crypt(3)` routine.
+Such password can be generated using this command line::
+
+    python -c 'import getpass,crypt,random,string; print crypt.crypt(getpass.getpass(), "".join(random.sample(string.ascii_letters + string.digits, 8)))'
+
+report
+------
+
+unoconv
+~~~~~~~
+
+The parameters for `unoconv`.
+
+.. _JSON-RPC: http://en.wikipedia.org/wiki/JSON-RPC
+.. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC
+.. _WebDAV: http://en.wikipedia.org/wiki/WebDAV
+.. _RFC-3986: http://tools.ietf.org/html/rfc3986
+.. _SMTP-URL: http://tools.ietf.org/html/draft-earhart-url-smtp-00
+.. _SSL: http://en.wikipedia.org/wiki/Secure_Sockets_Layer
+.. _STARTTLS: http://en.wikipedia.org/wiki/STARTTLS
diff --git a/doc/topics/index.rst b/doc/topics/index.rst
index bae4107..939dacd 100644
--- a/doc/topics/index.rst
+++ b/doc/topics/index.rst
@@ -10,11 +10,15 @@ Introduction to all the key parts of trytond:
     :maxdepth: 1
 
     install
+    configuration
+    setup_database
+    logs
     models/index
     models/fields_default_value
     models/fields_on_change
     domain
     pyson
+    access_rights
     actions
     views/index
     views/extension
diff --git a/doc/topics/install.rst b/doc/topics/install.rst
index a5eb993..0836c30 100644
--- a/doc/topics/install.rst
+++ b/doc/topics/install.rst
@@ -8,7 +8,7 @@ Prerequisites
 =============
 
     * Python 2.7 or later (http://www.python.org/)
-    * lxml 2.0 or later (http://codespeak.net/lxml/)
+    * lxml 2.0 or later (http://lxml.de/)
     * relatorio 0.2.0 or later (http://code.google.com/p/python-relatorio/)
     * python-dateutil (http://labix.org/python-dateutil)
     * polib (https://bitbucket.org/izi/polib/wiki/Home)
diff --git a/doc/topics/logs.rst b/doc/topics/logs.rst
new file mode 100644
index 0000000..ccd4e6d
--- /dev/null
+++ b/doc/topics/logs.rst
@@ -0,0 +1,49 @@
+.. _topics-logs:
+
+=====================
+Logging configuration
+=====================
+
+Without any configuration, trytond write INFO messages to standard output.
+
+Logs can be configured using a `configparser-format`_ file. The filename can
+be specified using trytond ``logconf`` parameter.
+
+.. _`configparser-format`: https://docs.python.org/2/library/logging.config.html#configuration-file-format
+
+Example
+=======
+
+This example allows to write INFO messages on standard output and on a disk log
+file rotated every day.
+
+.. highlight:: ini
+
+::
+
+    [formatters]
+    keys: simple
+
+    [handlers]
+    keys: rotate, console
+
+    [loggers]
+    keys: root
+
+    [formatter_simple]
+    format: %(asctime)s] %(levelname)s:%(name)s:%(message)s
+    datefmt: %a %b %d %H:%M:%S %Y
+
+    [handler_rotate]
+    class: handlers.TimedRotatingFileHandler
+    args: ('/tmp/tryton.log', 'D', 1, 30)
+    formatter: simple
+
+    [handler_console]
+    class: StreamHandler
+    formatter: simple
+    args: (sys.stdout,)
+
+    [logger_root]
+    level: INFO
+    handlers: rotate, console
diff --git a/doc/topics/setup_database.rst b/doc/topics/setup_database.rst
new file mode 100644
index 0000000..c1fa543
--- /dev/null
+++ b/doc/topics/setup_database.rst
@@ -0,0 +1,26 @@
+.. _topics-setup-database:
+
+=======================
+How to setup a database
+=======================
+
+The database section of the `configuration <topics-configuration>` must be set
+before starting.
+
+Create a database
+=================
+
+Depending of the database backend choosen, you must create a database (see the
+documentation of the choosen backend). The user running `trytond` must be
+granted the priviledge to create tables. For backend that has the option, the
+encoding of the database must be set to `UTF-8`.
+
+Initialize a database
+=====================
+
+A database can be initialized using this command line::
+
+    trytond -c <config file> -d <database name> --all
+
+At the end of the process, `trytond` will ask to set the password for the
+`admin` user.
diff --git a/doc/topics/views/index.rst b/doc/topics/views/index.rst
index 04203c5..0f79504 100644
--- a/doc/topics/views/index.rst
+++ b/doc/topics/views/index.rst
@@ -113,6 +113,9 @@ List of attributes shared by many form elements:
         * ``icon``: Only for button, it must return the icon name to use or
           False.
 
+        * ``pre_validate``: Only for button, it contains a domain to apply
+          on the record before calling the button.
+
     .. _common-attributes-help:
 
     * ``help``: The string that will be displayed when the cursor hovers over
@@ -491,6 +494,9 @@ Each tree view must start with this tag.
     * ``keyword_open``: A boolean to specify if the client should look for a
       tree_open action on double click instead of switching view.
 
+    * ``tree_state``: A boolean to specify if the client should save the state
+      of the tree.
+
 field
 ^^^^^
 
diff --git a/etc/trytond.conf b/etc/trytond.conf
deleted file mode 100644
index f70a6d6..0000000
--- a/etc/trytond.conf
+++ /dev/null
@@ -1,88 +0,0 @@
-#This file is part of Tryton.  The COPYRIGHT file at the top level of
-#this repository contains the full copyright notices and license terms.
-[options]
-
-# Activate the json-rpc protocol
-jsonrpc = localhost:8000
-#ssl_jsonrpc = False
-
-# This is the hostname used when generating tryton URI
-#hostname_jsonrpc =
-
-# Configure the path of json-rpc data
-#jsondata_path = /var/www/localhost/tryton
-
-# Activate the xml-rpc protocol
-#xmlrpc = *:8069
-#ssl_xmlrpc = False
-
-# Activate the webdav protocol
-#webdav = *:8080
-#ssl_webdav = False
-
-# This is the hostname used when generating WebDAV URI
-#hostname_webdav =
-
-# Configure the database type
-# allowed values are postgresql, sqlite, mysql
-#db_type = postgresql
-
-# Configure the database connection
-## Note: Only databases owned by db_user will be displayed in the connection dialog
-## of the Tryton client. db_user must have create permission for new databases
-## to be able to use automatic database creation with the Tryton client.
-#db_host = False
-#db_port = False
-#db_user = False
-#db_password = False
-#db_minconn = 1
-#db_maxconn = 64
-
-# Configure the postgresql path for the executable
-#pg_path = None
-
-# Configure the Tryton server password
-#admin_passwd = admin
-
-# Configure the path of the files for the pid and the logs
-#pidfile = False
-#logfile = False
-
-#privatekey = server.pem
-#certificate = server.pem
-
-# Configure the SMTP connection
-#smtp_server = localhost
-#smtp_port = 25
-#smtp_ssl = False
-#smtp_tls = False
-#smtp_password = False
-#smtp_user = False
-#smtp_default_from_email = False
-
-# Configure the path to store attachments and sqlite database
-#data_path = /var/lib/trytond
-
-# Allow to run more than one instance of trytond
-#multi_server = False
-
-# Configure the session timeout (inactivity of the client in sec)
-#session_timeout = 600
-
-# Enable auto-reload of modules if changed
-#auto_reload = True
-
-# Prevent database listing
-#prevent_dblist = False
-
-# Enable cron
-# cron = True
-
-# unoconv connection
-#unoconv = pipe,name=trytond;urp;StarOffice.ComponentContext
-
-# Number of retries on database operational error
-# retry = 5
-
-# Default language code
-# language = en_US
diff --git a/setup.py b/setup.py
index 793dac8..8fee48d 100644
--- a/setup.py
+++ b/setup.py
@@ -91,4 +91,5 @@ setup(name=PACKAGE,
     zip_safe=False,
     test_suite='trytond.tests',
     test_loader='trytond.test_loader:Loader',
+    tests_require=['mock'],
     )
diff --git a/trytond.egg-info/PKG-INFO b/trytond.egg-info/PKG-INFO
index 40f97fa..c6ba62e 100644
--- a/trytond.egg-info/PKG-INFO
+++ b/trytond.egg-info/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: trytond
-Version: 3.2.3
+Version: 3.4.0
 Summary: Tryton server
 Home-page: http://www.tryton.org/
 Author: Tryton
 Author-email: issue_tracker at tryton.org
 License: GPL-3
-Download-URL: http://downloads.tryton.org/3.2/
+Download-URL: http://downloads.tryton.org/3.4/
 Description: trytond
         =======
         
diff --git a/trytond.egg-info/SOURCES.txt b/trytond.egg-info/SOURCES.txt
index d3ca803..f5a045e 100644
--- a/trytond.egg-info/SOURCES.txt
+++ b/trytond.egg-info/SOURCES.txt
@@ -21,11 +21,15 @@ doc/ref/models/fields.rst
 doc/ref/models/index.rst
 doc/ref/models/models.rst
 doc/ref/tools/singleton.rst
+doc/topics/access_rights.rst
 doc/topics/actions.rst
+doc/topics/configuration.rst
 doc/topics/domain.rst
 doc/topics/index.rst
 doc/topics/install.rst
+doc/topics/logs.rst
 doc/topics/pyson.rst
+doc/topics/setup_database.rst
 doc/topics/wizard.rst
 doc/topics/models/fields_default_value.rst
 doc/topics/models/fields_on_change.rst
@@ -34,7 +38,6 @@ doc/topics/modules/index.rst
 doc/topics/reports/index.rst
 doc/topics/views/extension.rst
 doc/topics/views/index.rst
-etc/trytond.conf
 trytond/__init__.py
 trytond/cache.py
 trytond/config.py
@@ -110,6 +113,7 @@ trytond/ir/locale/cs_CZ.po
 trytond/ir/locale/de_DE.po
 trytond/ir/locale/es_AR.po
 trytond/ir/locale/es_CO.po
+trytond/ir/locale/es_EC.po
 trytond/ir/locale/es_ES.po
 trytond/ir/locale/fr_FR.po
 trytond/ir/locale/nl_NL.po
@@ -177,6 +181,8 @@ trytond/ir/view/model_access_form.xml
 trytond/ir/view/model_access_list.xml
 trytond/ir/view/model_button_form.xml
 trytond/ir/view/model_button_list.xml
+trytond/ir/view/model_data_form.xml
+trytond/ir/view/model_data_list.xml
 trytond/ir/view/model_field_access_form.xml
 trytond/ir/view/model_field_access_list.xml
 trytond/ir/view/model_field_form.xml
@@ -230,11 +236,13 @@ trytond/ir/view/ui_view_tree_width_form.xml
 trytond/ir/view/ui_view_tree_width_list.xml
 trytond/model/__init__.py
 trytond/model/dictschema.py
+trytond/model/match.py
 trytond/model/model.py
 trytond/model/modelsingleton.py
 trytond/model/modelsql.py
 trytond/model/modelstorage.py
 trytond/model/modelview.py
+trytond/model/union.py
 trytond/model/workflow.py
 trytond/model/fields/__init__.py
 trytond/model/fields/binary.py
@@ -281,6 +289,7 @@ trytond/res/locale/cs_CZ.po
 trytond/res/locale/de_DE.po
 trytond/res/locale/es_AR.po
 trytond/res/locale/es_CO.po
+trytond/res/locale/es_EC.po
 trytond/res/locale/es_ES.po
 trytond/res/locale/fr_FR.po
 trytond/res/locale/nl_NL.po
@@ -299,6 +308,7 @@ trytond/tests/__init__.py
 trytond/tests/access.py
 trytond/tests/copy_.py
 trytond/tests/export_data.py
+trytond/tests/field_context.py
 trytond/tests/history.py
 trytond/tests/import_data.py
 trytond/tests/import_data.xml
@@ -311,6 +321,7 @@ trytond/tests/test_access.py
 trytond/tests/test_cache.py
 trytond/tests/test_copy.py
 trytond/tests/test_exportdata.py
+trytond/tests/test_field_context.py
 trytond/tests/test_fields.py
 trytond/tests/test_history.py
 trytond/tests/test_importdata.py
@@ -318,12 +329,14 @@ trytond/tests/test_mixins.py
 trytond/tests/test_modelsingleton.py
 trytond/tests/test_modelsql.py
 trytond/tests/test_mptt.py
+trytond/tests/test_protocols.py
 trytond/tests/test_pyson.py
 trytond/tests/test_sequence.py
 trytond/tests/test_tools.py
 trytond/tests/test_transaction.py
 trytond/tests/test_trigger.py
 trytond/tests/test_tryton.py
+trytond/tests/test_union.py
 trytond/tests/test_user.py
 trytond/tests/test_wizard.py
 trytond/tests/test_workflow.py
@@ -348,6 +361,7 @@ trytond/webdav/locale/cs_CZ.po
 trytond/webdav/locale/de_DE.po
 trytond/webdav/locale/es_AR.po
 trytond/webdav/locale/es_CO.po
+trytond/webdav/locale/es_EC.po
 trytond/webdav/locale/es_ES.po
 trytond/webdav/locale/fr_FR.po
 trytond/webdav/locale/nl_NL.po
diff --git a/trytond/backend/__init__.py b/trytond/backend/__init__.py
index 5620291..6b9abc4 100644
--- a/trytond/backend/__init__.py
+++ b/trytond/backend/__init__.py
@@ -1,15 +1,20 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 import sys
+import urlparse
 
-from trytond.config import CONFIG
+from trytond.config import config
 
-__all__ = ['get']
+__all__ = ['name', 'get']
 
 
-def get(name):
-    db_type = CONFIG['db_type']
+def name():
+    return urlparse.urlparse(config.get('database', 'uri', '')).scheme
+
+
+def get(prop):
+    db_type = name()
     modname = 'trytond.backend.%s' % db_type
     __import__(modname)
     module = sys.modules[modname]
-    return getattr(module, name)
+    return getattr(module, prop)
diff --git a/trytond/backend/database.py b/trytond/backend/database.py
index 2979c66..dae7908 100644
--- a/trytond/backend/database.py
+++ b/trytond/backend/database.py
@@ -105,29 +105,19 @@ class CursorInterface(object):
     Define generic interface for database cursor
     '''
     IN_MAX = 1000
+    cache_keys = {'language', 'fuzzy_translation', '_datetime'}
 
     def __init__(self):
         self.cache = {}
 
-    def get_cache(self, context=None):
-        '''
-        Return cache for the context
-
-        :param context: the context
-        :return: the cache dictionary
-        '''
+    def get_cache(self):
         from trytond.cache import LRUDict
         from trytond.transaction import Transaction
         user = Transaction().user
-        if context is None:
-            context = {}
-        cache_ctx = context.copy()
-        for i in ('_timestamp', '_delete', '_create_records',
-                '_delete_records'):
-            if i in cache_ctx:
-                del cache_ctx[i]
-        return self.cache.setdefault((user, repr(cache_ctx)),
-            LRUDict(MODEL_CACHE_SIZE))
+        context = Transaction().context
+        keys = tuple(((key, context[key]) for key in sorted(self.cache_keys)
+                if key in context))
+        return self.cache.setdefault((user, keys), LRUDict(MODEL_CACHE_SIZE))
 
     def execute(self, sql, params=None):
         '''
diff --git a/trytond/backend/mysql/database.py b/trytond/backend/mysql/database.py
index f61136f..96627d5 100644
--- a/trytond/backend/mysql/database.py
+++ b/trytond/backend/mysql/database.py
@@ -1,7 +1,7 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 from trytond.backend.database import DatabaseInterface, CursorInterface
-from trytond.config import CONFIG
+from trytond.config import config, parse_uri
 import MySQLdb
 import MySQLdb.cursors
 import MySQLdb.converters
@@ -91,14 +91,16 @@ class Database(DatabaseInterface):
             'charset': 'utf8',
             'conv': conv,
         }
-        if CONFIG['db_host']:
-            args['host'] = CONFIG['db_host']
-        if CONFIG['db_port']:
-            args['port'] = int(CONFIG['db_port'])
-        if CONFIG['db_user']:
-            args['user'] = CONFIG['db_user']
-        if CONFIG['db_password']:
-            args['passwd'] = CONFIG['db_password']
+        uri = parse_uri(config.get('database', 'uri'))
+        assert uri.scheme == 'mysql'
+        if uri.hostname:
+            args['host'] = uri.hostname
+        if uri.port:
+            args['port'] = uri.port
+        if uri.username:
+            args['user'] = uri.username
+        if uri.password:
+            args['passwd'] = uri.password
         conn = MySQLdb.connect(**args)
         cursor = Cursor(conn, self.database_name)
         cursor.execute('SET time_zone = `UTC`')
@@ -120,20 +122,21 @@ class Database(DatabaseInterface):
 
     @staticmethod
     def dump(database_name):
-        from trytond.tools import exec_pg_command_pipe
+        from trytond.tools import exec_command_pipe
 
         cmd = ['mysqldump', '--no-create-db']
-        if CONFIG['db_user']:
-            cmd.append('--user=' + CONFIG['db_user'])
-        if CONFIG['db_host']:
-            cmd.append('--host=' + CONFIG['db_host'])
-        if CONFIG['db_port']:
-            cmd.append('--port=' + CONFIG['db_port'])
-        if CONFIG['db_password']:
-            cmd.append('--password=' + CONFIG['db_password'])
+        uri = parse_uri(config.get('database', 'uri'))
+        if uri.username:
+            cmd.append('--user=' + uri.username)
+        if uri.hostname:
+            cmd.append('--host=' + uri.hostname)
+        if uri.port:
+            cmd.append('--port=' + uri.port)
+        if uri.password:
+            cmd.append('--password=' + uri.password)
         cmd.append(database_name)
 
-        pipe = exec_pg_command_pipe(*tuple(cmd))
+        pipe = exec_command_pipe(*tuple(cmd))
         pipe.stdin.close()
         data = pipe.stdout.read()
         res = pipe.wait()
@@ -143,7 +146,7 @@ class Database(DatabaseInterface):
 
     @staticmethod
     def restore(database_name, data):
-        from trytond.tools import exec_pg_command_pipe
+        from trytond.tools import exec_command_pipe
 
         database = Database().connect()
         cursor = database.cursor(autocommit=True)
@@ -152,14 +155,15 @@ class Database(DatabaseInterface):
         cursor.close()
 
         cmd = ['mysql']
-        if CONFIG['db_user']:
-            cmd.append('--user=' + CONFIG['db_user'])
-        if CONFIG['db_host']:
-            cmd.append('--host=' + CONFIG['db_host'])
-        if CONFIG['db_port']:
-            cmd.append('--port=' + CONFIG['db_port'])
-        if CONFIG['db_password']:
-            cmd.append('--password=' + CONFIG['db_password'])
+        uri = parse_uri(config.get('database', 'uri'))
+        if uri.username:
+            cmd.append('--user=' + uri.username)
+        if uri.hostname:
+            cmd.append('--host=' + uri.hostname)
+        if uri.port:
+            cmd.append('--port=' + uri.port)
+        if uri.password:
+            cmd.append('--password=' + uri.password)
         cmd.append(database_name)
 
         fd, file_name = tempfile.mkstemp()
@@ -171,7 +175,7 @@ class Database(DatabaseInterface):
 
         args2 = tuple(cmd)
 
-        pipe = exec_pg_command_pipe(*args2)
+        pipe = exec_command_pipe(*args2)
         pipe.stdin.close()
         res = pipe.wait()
         os.remove(file_name)
@@ -192,7 +196,7 @@ class Database(DatabaseInterface):
     @staticmethod
     def list(cursor):
         now = time.time()
-        timeout = int(CONFIG['session_timeout'])
+        timeout = config.getint('session', 'timeout')
         res = Database._list_cache
         if res and abs(Database._list_cache_timestamp - now) < timeout:
             return res
@@ -338,4 +342,4 @@ class Cursor(CursorInterface):
 
     def update_auto_increment(self, table, value):
         self.cursor.execute('ALTER TABLE `%s` AUTO_INCREMENT = %%s' % table,
-                value)
+                (value,))
diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py
index ca444cd..8323557 100644
--- a/trytond/backend/postgresql/database.py
+++ b/trytond/backend/postgresql/database.py
@@ -1,7 +1,7 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 from trytond.backend.database import DatabaseInterface, CursorInterface
-from trytond.config import CONFIG
+from trytond.config import config, parse_uri
 from psycopg2.pool import ThreadedConnectionPool
 from psycopg2.extensions import cursor as PsycopgCursor
 from psycopg2.extensions import ISOLATION_LEVEL_REPEATABLE_READ
@@ -54,14 +54,15 @@ class Database(DatabaseInterface):
             return self
         logger = logging.getLogger('database')
         logger.info('connect to "%s"' % self.database_name)
-        host = CONFIG['db_host'] and "host=%s" % CONFIG['db_host'] or ''
-        port = CONFIG['db_port'] and "port=%s" % CONFIG['db_port'] or ''
+        uri = parse_uri(config.get('database', 'uri'))
+        assert uri.scheme == 'postgresql'
+        host = uri.hostname and "host=%s" % uri.hostname or ''
+        port = uri.port and "port=%s" % uri.port or ''
         name = "dbname=%s" % self.database_name
-        user = CONFIG['db_user'] and "user=%s" % CONFIG['db_user'] or ''
-        password = (CONFIG['db_password']
-            and "password=%s" % CONFIG['db_password'] or '')
-        minconn = int(CONFIG['db_minconn']) or 1
-        maxconn = int(CONFIG['db_maxconn']) or 64
+        user = uri.username and "user=%s" % uri.username or ''
+        password = uri.password and "password=%s" % uri.password or ''
+        minconn = config.getint('database', 'minconn', 1)
+        maxconn = config.getint('database', 'maxconn', 64)
         dsn = '%s %s %s %s %s' % (host, port, name, user, password)
         self._connpool = ThreadedConnectionPool(minconn, maxconn, dsn)
         return self
@@ -106,18 +107,25 @@ class Database(DatabaseInterface):
 
     @staticmethod
     def dump(database_name):
-        from trytond.tools import exec_pg_command_pipe
+        from trytond.tools import exec_command_pipe
 
         cmd = ['pg_dump', '--format=c', '--no-owner']
-        if CONFIG['db_user']:
-            cmd.append('--username=' + CONFIG['db_user'])
-        if CONFIG['db_host']:
-            cmd.append('--host=' + CONFIG['db_host'])
-        if CONFIG['db_port']:
-            cmd.append('--port=' + CONFIG['db_port'])
+        env = {}
+        uri = parse_uri(config.get('database', 'uri'))
+        if uri.username:
+            cmd.append('--username=' + uri.username)
+        if uri.hostname:
+            cmd.append('--host=' + uri.hostname)
+        if uri.port:
+            cmd.append('--port=' + uri.port)
+        if uri.password:
+            # if db_password is set in configuration we should pass
+            # an environment variable PGPASSWORD to our subprocess
+            # see libpg documentation
+            env['PGPASSWORD'] = uri.password
         cmd.append(database_name)
 
-        pipe = exec_pg_command_pipe(*tuple(cmd))
+        pipe = exec_command_pipe(*tuple(cmd), env=env)
         pipe.stdin.close()
         data = pipe.stdout.read()
         res = pipe.wait()
@@ -127,7 +135,7 @@ class Database(DatabaseInterface):
 
     @staticmethod
     def restore(database_name, data):
-        from trytond.tools import exec_pg_command_pipe
+        from trytond.tools import exec_command_pipe
 
         database = Database().connect()
         cursor = database.cursor(autocommit=True)
@@ -136,12 +144,16 @@ class Database(DatabaseInterface):
         cursor.close()
 
         cmd = ['pg_restore', '--no-owner']
-        if CONFIG['db_user']:
-            cmd.append('--username=' + CONFIG['db_user'])
-        if CONFIG['db_host']:
-            cmd.append('--host=' + CONFIG['db_host'])
-        if CONFIG['db_port']:
-            cmd.append('--port=' + CONFIG['db_port'])
+        env = {}
+        uri = parse_uri(config.get('database', 'uri'))
+        if uri.username:
+            cmd.append('--username=' + uri.username)
+        if uri.hostname:
+            cmd.append('--host=' + uri.hostname)
+        if uri.port:
+            cmd.append('--port=' + uri.port)
+        if uri.password:
+            env['PGPASSWORD'] = uri.password
         cmd.append('--dbname=' + database_name)
         args2 = tuple(cmd)
 
@@ -153,7 +165,7 @@ class Database(DatabaseInterface):
             args2.append(' ' + tmpfile)
             args2 = tuple(args2)
 
-        pipe = exec_pg_command_pipe(*args2)
+        pipe = exec_command_pipe(*args2, env=env)
         if not os.name == "nt":
             pipe.stdin.write(data)
         pipe.stdin.close()
@@ -175,23 +187,14 @@ class Database(DatabaseInterface):
     @staticmethod
     def list(cursor):
         now = time.time()
-        timeout = int(CONFIG['session_timeout'])
+        timeout = config.getint('session', 'timeout')
         res = Database._list_cache
         if res and abs(Database._list_cache_timestamp - now) < timeout:
             return res
-        db_user = CONFIG['db_user']
+        uri = parse_uri(config.get('database', 'uri'))
+        db_user = uri.username
         if not db_user and os.name == 'posix':
             db_user = pwd.getpwuid(os.getuid())[0]
-        if not db_user:
-            cursor.execute("SELECT usename "
-                "FROM pg_user "
-                "WHERE usesysid = ("
-                    "SELECT datdba "
-                    "FROM pg_database "
-                    "WHERE datname = %s)",
-                (CONFIG["db_name"],))
-            res = cursor.fetchone()
-            db_user = res and res[0]
         if db_user:
             cursor.execute("SELECT datname "
                 "FROM pg_database "
diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py
index 9f132a0..7ee1e2b 100644
--- a/trytond/backend/sqlite/database.py
+++ b/trytond/backend/sqlite/database.py
@@ -1,7 +1,7 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 from trytond.backend.database import DatabaseInterface, CursorInterface
-from trytond.config import CONFIG
+from trytond.config import config
 import os
 from decimal import Decimal
 import datetime
@@ -22,7 +22,8 @@ except ImportError:
     from sqlite3 import IntegrityError as DatabaseIntegrityError
     from sqlite3 import OperationalError as DatabaseOperationalError
 from sql import Flavor, Table
-from sql.functions import Function, Extract, Position, Now, Substring, Overlay
+from sql.functions import (Function, Extract, Position, Now, Substring,
+    Overlay, CharLength)
 
 __all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError',
     'Cursor']
@@ -133,6 +134,11 @@ class SQLiteOverlay(Function):
         return string[:from_ - 1] + placing_string + string[from_ - 1 + for_:]
 
 
+class SQLiteCharLength(Function):
+    __slots__ = ()
+    _function = 'LENGTH'
+
+
 def sign(value):
     return math.copysign(1, value)
 
@@ -142,6 +148,7 @@ MAPPING = {
     Position: SQLitePosition,
     Substring: SQLiteSubstring,
     Overlay: SQLiteOverlay,
+    CharLength: SQLiteCharLength,
     }
 
 
@@ -167,7 +174,7 @@ class Database(DatabaseInterface):
             path = ':memory:'
         else:
             db_filename = self.database_name + '.sqlite'
-            path = os.path.join(CONFIG['data_path'], db_filename)
+            path = os.path.join(config.get('database', 'path'), db_filename)
             if not os.path.isfile(path):
                 raise IOError('Database "%s" doesn\'t exist!' % db_filename)
         if self._conn is not None:
@@ -209,7 +216,7 @@ class Database(DatabaseInterface):
         else:
             if os.sep in database_name:
                 return
-            path = os.path.join(CONFIG['data_path'],
+            path = os.path.join(config.get('database', 'path'),
                     database_name + '.sqlite')
         with sqlite.connect(path) as conn:
             cursor = conn.cursor()
@@ -222,7 +229,7 @@ class Database(DatabaseInterface):
             return
         if os.sep in database_name:
             return
-        os.remove(os.path.join(CONFIG['data_path'],
+        os.remove(os.path.join(config.get('database', 'path'),
             database_name + '.sqlite'))
 
     @staticmethod
@@ -231,7 +238,7 @@ class Database(DatabaseInterface):
             raise Exception('Unable to dump memory database!')
         if os.sep in database_name:
             raise Exception('Wrong database name!')
-        path = os.path.join(CONFIG['data_path'],
+        path = os.path.join(config.get('database', 'path'),
                 database_name + '.sqlite')
         with open(path, 'rb') as file_p:
             data = file_p.read()
@@ -243,7 +250,7 @@ class Database(DatabaseInterface):
             raise Exception('Unable to restore memory database!')
         if os.sep in database_name:
             raise Exception('Wrong database name!')
-        path = os.path.join(CONFIG['data_path'],
+        path = os.path.join(config.get('database', 'path'),
                 database_name + '.sqlite')
         if os.path.isfile(path):
             raise Exception('Database already exists!')
@@ -255,7 +262,7 @@ class Database(DatabaseInterface):
         res = []
         listdir = [':memory:']
         try:
-            listdir += os.listdir(CONFIG['data_path'])
+            listdir += os.listdir(config.get('database', 'path'))
         except OSError:
             pass
         for db_file in listdir:
diff --git a/trytond/cache.py b/trytond/cache.py
index c6d7adb..6ccd039 100644
--- a/trytond/cache.py
+++ b/trytond/cache.py
@@ -1,12 +1,12 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import datetime
 from threading import Lock
 from collections import OrderedDict
 
+from sql import Table
+from sql.functions import Now
+
 from trytond.transaction import Transaction
-from trytond.config import CONFIG
-from trytond import backend
 
 __all__ = ['Cache', 'LRUDict']
 
@@ -74,18 +74,13 @@ class Cache(object):
 
     @staticmethod
     def clean(dbname):
-        if not CONFIG['multi_server']:
-            return
-        database = backend.get('Database')(dbname).connect()
-        cursor = database.cursor()
-        try:
-            cursor.execute('SELECT "timestamp", "name" FROM ir_cache')
+        with Transaction().new_cursor():
+            cursor = Transaction().cursor
+            table = Table('ir_cache')
+            cursor.execute(*table.select(table.timestamp, table.name))
             timestamps = {}
             for timestamp, name in cursor.fetchall():
                 timestamps[name] = timestamp
-        finally:
-            cursor.commit()
-            cursor.close()
         for inst in Cache._cache_instance:
             if inst._name in timestamps:
                 with inst._lock:
@@ -96,36 +91,30 @@ class Cache(object):
 
     @staticmethod
     def reset(dbname, name):
-        if not CONFIG['multi_server']:
-            return
         with Cache._resets_lock:
             Cache._resets.setdefault(dbname, set())
             Cache._resets[dbname].add(name)
 
     @staticmethod
     def resets(dbname):
-        if not CONFIG['multi_server']:
-            return
-        database = backend.get('Database')(dbname).connect()
-        cursor = database.cursor()
-        try:
+        with Transaction().new_cursor():
+            cursor = Transaction().cursor
+            table = Table('ir_cache')
             with Cache._resets_lock:
                 Cache._resets.setdefault(dbname, set())
                 for name in Cache._resets[dbname]:
-                    cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
-                        (name,))
+                    cursor.execute(*table.select(table.name,
+                            where=table.name == name))
                     if cursor.fetchone():
                         # It would be better to insert only
-                        cursor.execute('UPDATE ir_cache SET "timestamp" = %s '
-                            'WHERE name = %s', (datetime.datetime.now(), name))
+                        cursor.execute(*table.update([table.timestamp],
+                                [Now()], where=table.name == name))
                     else:
-                        cursor.execute('INSERT INTO ir_cache '
-                            '("timestamp", "name") '
-                            'VALUES (%s, %s)', (datetime.datetime.now(), name))
+                        cursor.execute(*table.insert(
+                                [table.timestamp, table.name],
+                                [[Now(), name]]))
                 Cache._resets[dbname].clear()
-        finally:
             cursor.commit()
-            cursor.close()
 
 
 class LRUDict(OrderedDict):
diff --git a/trytond/config.py b/trytond/config.py
index ba67332..99e3057 100644
--- a/trytond/config.py
+++ b/trytond/config.py
@@ -1,18 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import sys
-try:
-    import cdecimal
-    # Use cdecimal globally
-    if 'decimal' not in sys.modules:
-        sys.modules['decimal'] = cdecimal
-except ImportError:
-    import decimal
-    sys.modules['cdecimal'] = decimal
 import os
 import ConfigParser
-import getpass
-import socket
+import urlparse
+
+__all__ = ['config', 'get_hostname', 'get_port', 'split_netloc',
+    'parse_listen', 'parse_uri']
 
 
 def get_hostname(netloc):
@@ -24,119 +17,80 @@ def get_hostname(netloc):
         return netloc
 
 
-def get_port(netloc, protocol):
+def get_port(netloc):
     netloc = netloc.split(']')[-1]
-    if ':' in netloc:
-        return int(netloc.split(':')[1])
-    else:
-        return {
-            'jsonrpc': 8000,
-            'xmlrpc': 8069,
-            'webdav': 8080,
-        }.get(protocol)
-
-
-class ConfigManager(object):
-    def __init__(self, fname=None):
-        self.options = {
-            'jsonrpc': [('localhost', 8000)],
-            'ssl_jsonrpc': False,
-            'hostname_jsonrpc': None,
-            'xmlrpc': [],
-            'ssl_xmlrpc': False,
-            'jsondata_path': '/var/www/localhost/tryton',
-            'webdav': [],
-            'ssl_webdav': False,
-            'hostname_webdav': None,
-            'db_type': 'postgresql',
-            'db_host': False,
-            'db_port': False,
-            'db_name': False,
-            'db_user': False,
-            'db_password': False,
-            'db_minconn': 1,
-            'db_maxconn': 64,
-            'pg_path': None,
-            'admin_passwd': 'admin',
-            'verbose': False,
-            'debug_mode': False,
-            'pidfile': None,
-            'logfile': None,
-            'privatekey': '/etc/ssl/trytond/server.key',
-            'certificate': '/etc/ssl/trytond/server.pem',
-            'smtp_server': 'localhost',
-            'smtp_port': 25,
-            'smtp_ssl': False,
-            'smtp_tls': False,
-            'smtp_user': False,
-            'smtp_password': False,
-            'smtp_default_from_email': '%s@%s' % (
-                getpass.getuser(), socket.getfqdn()),
-            'data_path': '/var/lib/trytond',
-            'multi_server': False,
-            'session_timeout': 600,
-            'auto_reload': True,
-            'prevent_dblist': False,
-            'init': {},
-            'update': {},
-            'cron': True,
-            'unoconv': 'pipe,name=trytond;urp;StarOffice.ComponentContext',
-            'retry': 5,
-            'language': 'en_US',
-        }
-        self.configfile = None
-
-    def update_cmdline(self, cmdline_options):
-        self.options.update(cmdline_options)
-
-        # Verify that we want to log or not,
-        # if not the output will go to stdout
-        if self.options['logfile'] in ('None', 'False'):
-            self.options['logfile'] = False
-        # the same for the pidfile
-        if self.options['pidfile'] in ('None', 'False'):
-            self.options['pidfile'] = False
-        if self.options['data_path'] in ('None', 'False'):
-            self.options['data_path'] = False
-
-    def update_etc(self, configfile=None):
-        if configfile is None:
-            configfile = os.environ.get('TRYTOND_CONFIG')
-            if not configfile:
-                prefixdir = os.path.abspath(os.path.normpath(os.path.join(
-                    os.path.dirname(sys.prefix), '..')))
-                configfile = os.path.join(prefixdir, 'etc', 'trytond.conf')
-                if not os.path.isfile(configfile):
-                    configdir = os.path.abspath(os.path.normpath(os.path.join(
-                        os.path.dirname(__file__), '..')))
-                    configfile = os.path.join(configdir, 'etc', 'trytond.conf')
-                if not os.path.isfile(configfile):
-                    configfile = None
-
-        self.configfile = configfile
-        if not self.configfile:
+    return int(netloc.split(':')[1])
+
+
+def split_netloc(netloc):
+    return get_hostname(netloc).replace('*', ''), get_port(netloc)
+
+
+def parse_listen(value):
+    for netloc in value.split(','):
+        yield split_netloc(netloc)
+
+
+def parse_uri(uri):
+    return urlparse.urlparse(uri)
+
+
+class TrytonConfigParser(ConfigParser.RawConfigParser):
+
+    def __init__(self):
+        ConfigParser.RawConfigParser.__init__(self)
+        self.add_section('jsonrpc')
+        self.set('jsonrpc', 'listen', 'localhost:8000')
+        self.set('jsonrpc', 'data', '/var/www/localhost/tryton')
+        self.add_section('xmlrpc')
+        self.add_section('webdav')
+        self.add_section('database')
+        self.set('database', 'uri',
+            os.environ.get('TRYTOND_DATABASE_URI', 'sqlite://'))
+        self.set('database', 'path', '/var/lib/trytond')
+        self.set('database', 'list', 'True')
+        self.set('database', 'retry', 5)
+        self.set('database', 'language', 'en_US')
+        self.add_section('ssl')
+        self.add_section('email')
+        self.set('email', 'uri', 'smtp://localhost:25')
+        self.add_section('session')
+        self.set('session', 'timeout', 600)
+        self.add_section('report')
+        self.set('report', 'unoconv',
+            'pipe,name=trytond;urp;StarOffice.ComponentContext')
+
+    def update_etc(self, configfile=os.environ.get('TRYTOND_CONFIG')):
+        if not configfile:
             return
+        self.read(configfile)
+
+    def get(self, section, option, default=None):
+        try:
+            return ConfigParser.RawConfigParser.get(self, section, option)
+        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+            return default
+
+    def getint(self, section, option, default=None):
+        try:
+            return ConfigParser.RawConfigParser.getint(self, section, option)
+        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError,
+                TypeError):
+            return default
+
+    def getfloat(self, section, option, default=None):
+        try:
+            return ConfigParser.RawConfigParser.getfloat(self, section, option)
+        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError,
+                TypeError):
+            return default
+
+    def getboolean(self, section, option, default=None):
+        try:
+            return ConfigParser.RawConfigParser.getboolean(
+                self, section, option)
+        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError,
+                AttributeError):
+            return default
 
-        parser = ConfigParser.ConfigParser()
-        with open(self.configfile) as fp:
-            parser.readfp(fp)
-        for (name, value) in parser.items('options'):
-            if value == 'True' or value == 'true':
-                value = True
-            if value == 'False' or value == 'false':
-                value = False
-            if name in ('xmlrpc', 'jsonrpc', 'webdav') and value:
-                value = [(get_hostname(netloc).replace('*', ''),
-                    get_port(netloc, name)) for netloc in value.split(',')]
-            self.options[name] = value
-
-    def get(self, key, default=None):
-        return self.options.get(key, default)
-
-    def __setitem__(self, key, value):
-        self.options[key] = value
-
-    def __getitem__(self, key):
-        return self.options[key]
-
-CONFIG = ConfigManager()
+config = TrytonConfigParser()
diff --git a/trytond/convert.py b/trytond/convert.py
index 378da55..8d5515c 100644
--- a/trytond/convert.py
+++ b/trytond/convert.py
@@ -2,8 +2,6 @@
 #this repository contains the full copyright notices and license terms.
 import time
 from xml import sax
-from decimal import Decimal
-import datetime
 import logging
 import traceback
 import sys
@@ -13,7 +11,7 @@ from itertools import izip
 from collections import defaultdict
 
 from .version import VERSION
-from .tools import safe_eval
+from .tools import safe_eval, grouped_slice
 from .transaction import Transaction
 
 CDATA_START = re.compile('^\s*\<\!\[cdata\[', re.IGNORECASE)
@@ -354,7 +352,6 @@ class Fs2bdAccessor:
             self.browserecord[module][model_name][model.id] = model
 
     def fetch_new_module(self, module):
-        cursor = Transaction().cursor
         self.fs2db[module] = {}
         module_data_ids = self.ModelData.search([
                 ('module', '=', module),
@@ -377,12 +374,11 @@ class Fs2bdAccessor:
                 continue
             Model = self.pool.get(model_name)
             self.browserecord[module][model_name] = {}
-            for i in range(0, len(record_ids[model_name]), cursor.IN_MAX):
-                sub_record_ids = record_ids[model_name][i:i + cursor.IN_MAX]
+            for sub_record_ids in grouped_slice(record_ids[model_name]):
                 with Transaction().set_context(active_test=False):
                     records = Model.search([
-                        ('id', 'in', sub_record_ids),
-                        ])
+                        ('id', 'in', list(sub_record_ids)),
+                        ], order=[('id', 'ASC')])
                 with Transaction().set_context(language='en_US'):
                     models = Model.browse(map(int, records))
                 for model in models:
@@ -493,7 +489,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
                 module, model = key
                 self.write_records(module, model, *actions)
             self.grouped_write.clear()
-        if self.grouped_model_data:
+        if name == 'data' and self.grouped_model_data:
             self.ModelData.write(*self.grouped_model_data)
             del self.grouped_model_data[:]
 
@@ -604,10 +600,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
             if not old_values:
                 old_values = {}
             else:
-                old_values = safe_eval(old_values, {
-                    'Decimal': Decimal,
-                    'datetime': datetime,
-                    })
+                old_values = self.ModelData.load_values(old_values)
 
             for key in old_values:
                 if isinstance(old_values[key], str):
@@ -691,6 +684,9 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
             else:
                 self.write_records(module, model, record, to_update,
                     old_values, fs_id, mdata_id)
+            self.grouped_model_data.extend(([self.ModelData(mdata_id)], {
+                        'fs_values': self.ModelData.dump_values(values),
+                        }))
         else:
             if self.grouped:
                 self.grouped_creations[model][fs_id] = values
@@ -713,7 +709,8 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
                     'model': model,
                     'module': self.module,
                     'db_id': record.id,
-                    'values': str(values),
+                    'values': self.ModelData.dump_values(values),
+                    'fs_values': self.ModelData.dump_values(values),
                     'noupdate': self.noupdate,
                     })
 
@@ -725,7 +722,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
                     'db_id': record.id,
                     'model': model,
                     'id': mdata.id,
-                    'values': str(values),
+                    'values': self.ModelData.dump_values(values),
                     })
         self.fs2db.reset_browsercord(self.module, model,
             [r.id for r in records])
@@ -774,8 +771,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
                             'model': model,
                             'module': module,
                             'db_id': record.id,
-                            'values': str(values),
-                            'date_update': datetime.datetime.now(),
+                            'values': self.ModelData.dump_values(values),
                             }))
 
         # reset_browsercord to keep cache memory low
diff --git a/trytond/ir/action.py b/trytond/ir/action.py
index 873a131..606796a 100644
--- a/trytond/ir/action.py
+++ b/trytond/ir/action.py
@@ -170,11 +170,7 @@ class ActionKeyword(ModelSQL, ModelView):
     def models_get():
         pool = Pool()
         Model = pool.get('ir.model')
-        models = Model.search([])
-        res = []
-        for model in models:
-            res.append([model.model, model.name])
-        return res
+        return [(m.model, m.name) for m in Model.search([])]
 
     @classmethod
     def delete(cls, keywords):
@@ -241,6 +237,7 @@ class ActionKeyword(ModelSQL, ModelView):
 
 class ActionMixin(ModelSQL):
     _order_name = 'action'
+    _action_name = 'name'
 
     @classmethod
     def __setup__(cls):
@@ -298,6 +295,8 @@ class ActionMixin(ModelSQL):
     @classmethod
     def create(cls, vlist):
         pool = Pool()
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
         Action = pool.get('ir.action')
         ir_action = cls.__table__()
         new_records = []
@@ -336,11 +335,15 @@ class ActionMixin(ModelSQL):
         pool = Pool()
         ActionKeyword = pool.get('ir.action.keyword')
         super(ActionMixin, cls).write(records, values, *args)
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
         ActionKeyword._get_keyword_cache.clear()
 
     @classmethod
     def delete(cls, records):
         pool = Pool()
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
         Action = pool.get('ir.action')
         actions = [x.action for x in records]
         super(ActionMixin, cls).delete(records)
@@ -360,10 +363,23 @@ class ActionMixin(ModelSQL):
                     default=default))
         return new_records
 
+    @classmethod
+    def get_groups(cls, name, action_id=None):
+        # TODO add cache
+        domain = [
+            (cls._action_name, '=', name),
+            ]
+        if action_id:
+            domain.append(('id', '=', action_id))
+        actions = cls.search(domain)
+        groups = {g.id for a in actions for g in a.groups}
+        return groups
+
 
 class ActionReport(ActionMixin, ModelSQL, ModelView):
     "Action report"
     __name__ = 'ir.action.report'
+    _action_name = 'report_name'
     model = fields.Char('Model')
     report_name = fields.Char('Internal Name', required=True)
     report = fields.Char('Path')
@@ -491,10 +507,9 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
                     where=outputformat.format == 'pdf'))
 
             ids = [x[0] for x in cursor.fetchall()]
-            with Transaction().set_user(0):
-                cls.write(cls.browse(ids), {'extension': 'pdf'})
-                ids = cls.search([('id', 'not in', ids)])
-                cls.write(cls.browse(ids), {'extension': 'odt'})
+            cls.write(cls.browse(ids), {'extension': 'pdf'})
+            ids = cls.search([('id', 'not in', ids)])
+            cls.write(cls.browse(ids), {'extension': 'odt'})
 
             table.drop_column("output_format")
             TableHandler.dropTable(cursor, 'ir.action.report.outputformat',
@@ -902,12 +917,17 @@ class ActionActWindowView(ModelSQL, ModelView):
             ondelete='CASCADE')
     act_window = fields.Many2One('ir.action.act_window', 'Action',
             ondelete='CASCADE')
+    active = fields.Boolean('Active', select=True)
 
     @classmethod
     def __setup__(cls):
         super(ActionActWindowView, cls).__setup__()
         cls._order.insert(0, ('sequence', 'ASC'))
 
+    @staticmethod
+    def default_active():
+        return True
+
     @classmethod
     def __register__(cls, module_name):
         TableHandler = backend.get('TableHandler')
@@ -960,6 +980,7 @@ class ActionActWindowDomain(ModelSQL, ModelView):
 class ActionWizard(ActionMixin, ModelSQL, ModelView):
     "Action wizard"
     __name__ = 'ir.action.wizard'
+    _action_name = 'wiz_name'
     wiz_name = fields.Char('Wizard name', required=True)
     action = fields.Many2One('ir.action', 'Action', required=True,
             ondelete='CASCADE')
diff --git a/trytond/ir/attachment.py b/trytond/ir/attachment.py
index d261509..0195c70 100644
--- a/trytond/ir/attachment.py
+++ b/trytond/ir/attachment.py
@@ -5,7 +5,7 @@ import hashlib
 from sql.operators import Concat
 
 from ..model import ModelView, ModelSQL, fields
-from ..config import CONFIG
+from ..config import config
 from .. import backend
 from ..transaction import Transaction
 from ..pyson import Eval
@@ -117,7 +117,7 @@ class Attachment(ModelSQL, ModelView):
             filename = self.digest
             if self.collision:
                 filename = filename + '-' + str(self.collision)
-            filename = os.path.join(CONFIG['data_path'], db_name,
+            filename = os.path.join(config.get('database', 'path'), db_name,
                     filename[0:2], filename[2:4], filename)
             if name == 'data_size' or format_ == 'size':
                 try:
@@ -140,7 +140,7 @@ class Attachment(ModelSQL, ModelView):
         cursor = Transaction().cursor
         table = cls.__table__()
         db_name = cursor.dbname
-        directory = os.path.join(CONFIG['data_path'], db_name)
+        directory = os.path.join(config.get('database', 'path'), db_name)
         if not os.path.isdir(directory):
             os.makedirs(directory, 0770)
         digest = hashlib.md5(value).hexdigest()
@@ -191,22 +191,19 @@ class Attachment(ModelSQL, ModelView):
         return (self.write_date if self.write_date else self.create_date
             ).replace(microsecond=0)
 
-    @classmethod
-    def get_last_user(cls, attachments, name):
-        with Transaction().set_user(0):
-            return dict(
-                (x.id, x.write_uid.rec_name
-                    if x.write_uid else x.create_uid.rec_name)
-                for x in cls.browse(attachments))
+    def get_last_user(self, name):
+        return (self.write_uid.rec_name if self.write_uid
+            else self.create_uid.rec_name)
 
     @classmethod
     def check_access(cls, ids, mode='read'):
         pool = Pool()
         ModelAccess = pool.get('ir.model.access')
-        if Transaction().user == 0:
+        if ((Transaction().user == 0)
+                or not Transaction().context.get('_check_access')):
             return
         model_names = set()
-        with Transaction().set_user(0):
+        with Transaction().set_context(_check_access=False):
             for attachment in cls.browse(ids):
                 if attachment.resource:
                     model_names.add(attachment.resource.__name__)
diff --git a/trytond/ir/configuration.py b/trytond/ir/configuration.py
index abe72db..4ee7307 100644
--- a/trytond/ir/configuration.py
+++ b/trytond/ir/configuration.py
@@ -2,7 +2,7 @@
 #this repository contains the full copyright notices and license terms.
 from ..model import ModelSQL, ModelSingleton, fields
 from ..cache import Cache
-from ..config import CONFIG
+from ..config import config
 
 __all__ = ['Configuration']
 
@@ -15,7 +15,7 @@ class Configuration(ModelSingleton, ModelSQL):
 
     @staticmethod
     def default_language():
-        return CONFIG['language']
+        return config.get('database', 'language')
 
     @classmethod
     def get_language(cls):
@@ -25,6 +25,6 @@ class Configuration(ModelSingleton, ModelSQL):
         config = cls(1)
         language = config.language
         if not language:
-            language = CONFIG['language']
+            language = config.get('database', 'language')
         cls._get_language_cache.set(None, language)
         return language
diff --git a/trytond/ir/cron.py b/trytond/ir/cron.py
index 10f7e3f..438a0d2 100644
--- a/trytond/ir/cron.py
+++ b/trytond/ir/cron.py
@@ -14,7 +14,7 @@ from ..tools import get_smtp_server
 from ..transaction import Transaction
 from ..pool import Pool
 from .. import backend
-from ..config import CONFIG
+from ..config import config
 
 __all__ = [
     'Cron',
@@ -133,7 +133,7 @@ class Cron(ModelSQL, ModelView):
             (cron.name, cron.__url__, tb_s),
             raise_exception=False)
 
-        from_addr = CONFIG['smtp_default_from_email']
+        from_addr = config.get('email', 'from')
         to_addr = cron.request_user.email
 
         msg = MIMEText(body, _charset='utf-8')
diff --git a/trytond/ir/gen_time_locale.py b/trytond/ir/gen_time_locale.py
index b0f5eaa..9f78290 100644
--- a/trytond/ir/gen_time_locale.py
+++ b/trytond/ir/gen_time_locale.py
@@ -195,6 +195,7 @@ if __name__ == '__main__':
                 'de_DE',
                 'en_US',
                 'es_AR',
+                'es_EC',
                 'es_ES',
                 'es_CO',
                 'fr_FR',
diff --git a/trytond/ir/lang.py b/trytond/ir/lang.py
index f5fd4f0..c0e175b 100644
--- a/trytond/ir/lang.py
+++ b/trytond/ir/lang.py
@@ -10,11 +10,14 @@ from ..tools import datetime_strftime
 from ..transaction import Transaction
 from ..pool import Pool
 from .time_locale import TIME_LOCALE
+from ..backend.database import CursorInterface
 
 warnings.filterwarnings('ignore', "", ImportWarning)
 from locale import CHAR_MAX
 warnings.resetwarnings()
 
+CursorInterface.cache_keys.add('translate_name')
+
 __all__ = [
     'Lang',
     ]
diff --git a/trytond/ir/lang.xml b/trytond/ir/lang.xml
index 7be3725..00b333d 100644
--- a/trytond/ir/lang.xml
+++ b/trytond/ir/lang.xml
@@ -48,6 +48,14 @@ this repository contains the full copyright notices and license terms. -->
             <field name="decimal_point">,</field>
             <field name="thousands_sep">.</field>
         </record>
+        <record model="ir.lang" id="lang_ec">
+            <field name="code">es_EC</field>
+            <field name="name">Spanish (Ecuador)</field>
+            <field name="date">%d/%m/%Y</field>
+            <field name="grouping">[3, 3, 0]</field>
+            <field name="decimal_point">.</field>
+            <field name="thousands_sep">,</field>
+        </record>
         <record model="ir.lang" id="lang_es">
             <field name="code">es_ES</field>
             <field name="name">Spanish (Spain)</field>
diff --git a/trytond/ir/locale/bg_BG.po b/trytond/ir/locale/bg_BG.po
index 56ebd1b..4209638 100644
--- a/trytond/ir/locale/bg_BG.po
+++ b/trytond/ir/locale/bg_BG.po
@@ -23,7 +23,7 @@ msgstr "Нямате права да изтривате този запис."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 
 msgctxt "error:domain_validation_record:"
@@ -570,6 +570,11 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Действие"
 
+#, fuzzy
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Активен"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Създадено на"
@@ -1394,14 +1399,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Създадено от"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Начална дата"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Дата на обновяване"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID на ресурс"
@@ -1410,6 +1407,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Идентификатор от файлова система"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr ""
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1426,6 +1427,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "Без обновяване"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Име"
@@ -2356,6 +2361,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Действие"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr ""
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Активен"
@@ -2870,6 +2879,11 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Бутони"
 
+#, fuzzy
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Данни"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Достъп до полета"
@@ -2982,6 +2996,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr ""
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr ""
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Изглед на действие на активния на прозорец"
@@ -3055,6 +3079,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Немски"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr ""
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Англииски"
@@ -3299,6 +3327,11 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Бутони"
 
+#, fuzzy
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Данни"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Достъп до полета"
@@ -3758,6 +3791,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Бутони"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr ""
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr ""
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Достъп до поле"
diff --git a/trytond/ir/locale/ca_ES.po b/trytond/ir/locale/ca_ES.po
index 1f2be61..9e9483f 100644
--- a/trytond/ir/locale/ca_ES.po
+++ b/trytond/ir/locale/ca_ES.po
@@ -25,16 +25,18 @@ msgstr "No podeu eliminar aquest registre."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
-"El nombre de dígits \"%(digits)s\" del camp \"%(field)s\" de %\"(value)s\" "
-"és superior al límit."
+"El nombre de dígits \"%(digits)s\" del camp \"%(field)s\" de \"%(value)s\" "
+"és superior al seu límit."
 
 msgctxt "error:domain_validation_record:"
 msgid ""
 "The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
 " to its domain."
-msgstr "El valor del camp \"%s\" de \"%s\" no és correcte segons aquest domini."
+msgstr ""
+"El valor del camp \"%(field)s\" de \"%(model)s\" no és correcte segons "
+"aquest domini."
 
 msgctxt "error:foreign_model_exist:"
 msgid ""
@@ -42,7 +44,7 @@ msgid ""
 " \"%(model)s\"."
 msgstr ""
 "No es poden eliminar els registres perquè es fan servir al camp "
-"\"%(field)s\" de \"%(models)\"."
+"\"%(field)s\" de \"%(model)s\"."
 
 msgctxt "error:foreign_model_missing:"
 msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
@@ -73,7 +75,7 @@ msgstr ""
 
 msgctxt "error:ir.action.report:"
 msgid "The internal name must be unique by module!"
-msgstr "El nom intern ha de ser únic del mòdul."
+msgstr "El nom intern ha de ser únic per mòdul."
 
 msgctxt "error:ir.attachment:"
 msgid "The  names of attachments must be unique by resource!"
@@ -237,7 +239,7 @@ msgstr "L'arrodoniment de data-hora ha de ser més gran que 0."
 
 msgctxt "error:ir.translation:"
 msgid "Translation must be unique"
-msgstr "La traducció ha de ser únic."
+msgstr "La traducció ha de ser única."
 
 msgctxt "error:ir.translation:"
 msgid ""
@@ -289,11 +291,11 @@ msgid ""
 " was configured as ancestor of itself."
 msgstr ""
 "Error de recursivitat: El registre \"%(rec_name)s\" amb el pare "
-"\"%(parent_rec_name)s\" es va configurar com pare de si mateix."
+"\"%(parent_rec_name)s\" s'ha configurat com a pare de si mateix."
 
 msgctxt "error:reference_syntax_error:"
 msgid "Syntax error for reference %r in %s"
-msgstr "Error de sintaxi en la referència %r a %s."
+msgstr "Error de sintaxi en la referència %r de %s."
 
 msgctxt "error:relation_not_found:"
 msgid "Relation not found: %r in %s"
@@ -301,11 +303,11 @@ msgstr "No s'ha trobat la relació: %r a %s."
 
 msgctxt "error:required_field:"
 msgid "The field \"%(field)s\" on \"%(model)s\" is required."
-msgstr "El camp \"%(field)s\" de \"%(model)s\" es obligatori."
+msgstr "El camp \"%(field)s\" de \"%(model)s\" és obligatori."
 
 msgctxt "error:required_validation_record:"
 msgid "The field \"%(field)s\" on \"%(model)s\" is required."
-msgstr "El camp \"%(field)s\" de \"%(model)s\" es obligatori."
+msgstr "El camp \"%(field)s\" de \"%(model)s\" és obligatori."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
@@ -359,7 +361,7 @@ msgstr "No podeu modificar aquest registre."
 
 msgctxt "error:xml_id_syntax_error:"
 msgid "Syntax error for XML id %r in %s"
-msgstr "Error de sintaxi XML id %r a %s."
+msgstr "Error de sintaxi en el XML id %r de %s."
 
 msgctxt "error:xml_record_desc:"
 msgid "This record is part of the base configuration."
@@ -581,6 +583,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Acció"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Actiu"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Data creació"
@@ -671,7 +677,7 @@ msgstr "Usuari creació"
 
 msgctxt "field:ir.action.report,direct_print:"
 msgid "Direct Print"
-msgstr "Direcció impressió"
+msgstr "Impressió directa"
 
 msgctxt "field:ir.action.report,email:"
 msgid "Email"
@@ -1259,7 +1265,7 @@ msgstr "Informació"
 
 msgctxt "field:ir.model,model:"
 msgid "Model Name"
-msgstr "Nom model"
+msgstr "Nom del model"
 
 msgctxt "field:ir.model,module:"
 msgid "Module"
@@ -1377,14 +1383,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Usuari creació"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Data d'inicialització"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Data actualització"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID recurs"
@@ -1393,6 +1391,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Identificador en el sistema de fitxers"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Valors en sistema de fitxers"
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1409,6 +1411,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "No actualitzat"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Sense sincronitzar"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Nom"
@@ -1891,7 +1897,7 @@ msgstr "Desfasament de data-hora"
 
 msgctxt "field:ir.sequence,timestamp_rounding:"
 msgid "Timestamp Rounding"
-msgstr "Arronodiment de data-hora"
+msgstr "Arrodoniment de data-hora"
 
 msgctxt "field:ir.sequence,type:"
 msgid "Type"
@@ -1967,7 +1973,7 @@ msgstr "Desfasament de data-hora"
 
 msgctxt "field:ir.sequence.strict,timestamp_rounding:"
 msgid "Timestamp Rounding"
-msgstr "Arronodiment de data-hora"
+msgstr "Arrodoniment de data-hora"
 
 msgctxt "field:ir.sequence.strict,type:"
 msgid "Type"
@@ -2099,7 +2105,7 @@ msgstr "Mòdul"
 
 msgctxt "field:ir.translation,name:"
 msgid "Field Name"
-msgstr "Nom"
+msgstr "Nom del camp"
 
 msgctxt "field:ir.translation,overriding_module:"
 msgid "Overriding Module"
@@ -2333,6 +2339,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Acció"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Accions de teclat"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Actiu"
@@ -2451,7 +2461,7 @@ msgstr "Domini"
 
 msgctxt "field:ir.ui.view,field_childs:"
 msgid "Children Field"
-msgstr "Camp fill"
+msgstr "Camp fills"
 
 msgctxt "field:ir.ui.view,id:"
 msgid "ID"
@@ -2635,7 +2645,7 @@ msgstr "Criteri de cerca per defecte en les vistes de llista."
 
 msgctxt "help:ir.action.act_window,window_name:"
 msgid "Use the action name as window name"
-msgstr "Utilitza el nom de l'acció pel nom de la finestra."
+msgstr "Utilitzeu el nom de l'acció pel nom de la finestra."
 
 msgctxt "help:ir.action.report,email:"
 msgid ""
@@ -2744,8 +2754,8 @@ msgid ""
 "Set a minimum delay in minutes between call to \"Action Function\" for the same record.\n"
 "0 for no delay."
 msgstr ""
-"Interval mínim en minuts entre les crides \"Acció de funció\" pel mateix registre.\n"
-"0 per no interval."
+"Marge mínim en minuts entre les crides \"Acció de funció\" pel mateix registre.\n"
+"0 per no deixar marge."
 
 msgctxt "help:ir.ui.view_search,domain:"
 msgid "The PYSON domain"
@@ -2815,6 +2825,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Botons"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Dades"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permisos dels camps"
@@ -2927,6 +2941,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Domini acció de finestra"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Tots"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Sense sincronitzar"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Vista acció de finestra"
@@ -2999,6 +3023,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Alemany"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Espanyol (Ecuador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Anglès"
@@ -3045,7 +3073,7 @@ msgstr "Botó model"
 
 msgctxt "model:ir.model.data,name:"
 msgid "Model data"
-msgstr "Data model"
+msgstr "Dades del model"
 
 msgctxt "model:ir.model.field,name:"
 msgid "Model field"
@@ -3243,6 +3271,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Botons"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Dades"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permisos camps"
@@ -3699,6 +3731,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Botons"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Dades del model"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Sincronitza"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Permisos dels camps"
diff --git a/trytond/ir/locale/cs_CZ.po b/trytond/ir/locale/cs_CZ.po
index 78ce3a0..a149887 100644
--- a/trytond/ir/locale/cs_CZ.po
+++ b/trytond/ir/locale/cs_CZ.po
@@ -21,7 +21,7 @@ msgstr ""
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 
 msgctxt "error:domain_validation_record:"
@@ -545,6 +545,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr ""
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr ""
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr ""
@@ -1341,14 +1345,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr ""
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr ""
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr ""
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr ""
@@ -1357,6 +1353,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr ""
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr ""
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr ""
@@ -1373,6 +1373,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr ""
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr ""
@@ -2297,6 +2301,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr ""
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr ""
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr ""
@@ -2765,6 +2773,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr ""
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr ""
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr ""
@@ -2877,6 +2889,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr ""
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr ""
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr ""
@@ -2949,6 +2971,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr ""
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr ""
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr ""
@@ -3193,6 +3219,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr ""
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr ""
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr ""
@@ -3649,6 +3679,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr ""
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr ""
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr ""
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr ""
diff --git a/trytond/ir/locale/de_DE.po b/trytond/ir/locale/de_DE.po
index 22986fe..7009184 100644
--- a/trytond/ir/locale/de_DE.po
+++ b/trytond/ir/locale/de_DE.po
@@ -25,7 +25,7 @@ msgstr "Keine Löschberechtigung für diesen Datensatz"
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 "Die Anzahl der Nachkommastellen \"%(digits)s\" in Feld \"%(field)s\" in "
 "\"%(value)s\" überschreitet die erlaubte Größe."
@@ -591,6 +591,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Aktion"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Aktiv"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Erstellungsdatum"
@@ -965,7 +969,7 @@ msgstr "Ressource"
 
 msgctxt "field:ir.attachment,summary:"
 msgid "Summary"
-msgstr "Zusammenfassung"
+msgstr "Beschreibung"
 
 msgctxt "field:ir.attachment,type:"
 msgid "Type"
@@ -1387,14 +1391,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Erstellt durch"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Initialdatum"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Aktualisierungsdatum"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID Ressource"
@@ -1403,6 +1399,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Identifikator im Dateisystem"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Werte im Dateisystem"
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1417,7 +1417,11 @@ msgstr "Modul"
 
 msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
-msgstr "Ohne Update"
+msgstr "Kein Update"
+
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Nicht synchronisiert"
 
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
@@ -2343,6 +2347,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Aktion"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Aktionsschlüsselwörter"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Aktiv"
@@ -2830,6 +2838,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Knöpfe"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Daten"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Zugriffsberechtigungen Felder"
@@ -2896,11 +2908,11 @@ msgstr "Übersetzungen"
 
 msgctxt "model:ir.action,name:act_translation_set"
 msgid "Set Report Translations"
-msgstr "Übersetzungen für Berichte aktualisieren"
+msgstr "Übersetzungen aktualisieren"
 
 msgctxt "model:ir.action,name:act_translation_update"
 msgid "Synchronize Translations"
-msgstr "Übersetzungen aktualisieren"
+msgstr "Übersetzungen eingeben"
 
 msgctxt "model:ir.action,name:act_trigger_form"
 msgid "Triggers"
@@ -2942,6 +2954,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Wertebereich Aktion Aktuelles Fenster"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Alle"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Nicht synchronisiert"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Aktion aktives Fenster Sicht"
@@ -3014,6 +3036,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Deutsch"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Spanisch (Ecuador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Englisch"
@@ -3258,6 +3284,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Knöpfe"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Daten"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Zugriffsberechtigungen Felder"
@@ -3324,11 +3354,11 @@ msgstr "Übersetzungen"
 
 msgctxt "model:ir.ui.menu,name:menu_translation_set"
 msgid "Set Translations"
-msgstr "Übersetzungen für Berichte aktualisieren"
+msgstr "Übersetzungen aktualisieren"
 
 msgctxt "model:ir.ui.menu,name:menu_translation_update"
 msgid "Synchronize Translations"
-msgstr "Übersetzungen aktualisieren"
+msgstr "Übersetzungen eingeben"
 
 msgctxt "model:ir.ui.menu,name:menu_trigger_form"
 msgid "Triggers"
@@ -3714,6 +3744,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Knöpfe"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Modelldaten"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Synchronisation"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Zugriffsberechtigung Felder"
@@ -3939,7 +3977,7 @@ msgstr "Übersetzungen bereinigen"
 
 msgctxt "view:ir.translation.clean.succeed:"
 msgid "Clean Translations Succeed!"
-msgstr "Bereinigung der Übersetzungen erfolgreich durchgeführt!"
+msgstr "Bereinigung der Übersetzungen erfolgreich durchgeführt"
 
 msgctxt "view:ir.translation.export.result:"
 msgid "Export Translation"
@@ -3951,7 +3989,7 @@ msgstr "Übersetzung exportieren"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Set Translations"
-msgstr "Übersetzungen für Berichte aktualisieren"
+msgstr "Übersetzungen aktualisieren"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Synchronize Translations?"
@@ -3959,15 +3997,15 @@ msgstr "Übersetzungen aktualisieren?"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Succeed!"
-msgstr "Aktualisierung erfolgreich."
+msgstr "Aktualisierung erfolgreich"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Translations"
-msgstr "Übersetzungen für Berichte aktualisieren"
+msgstr "Übersetzungen aktualisieren"
 
 msgctxt "view:ir.translation.update.start:"
 msgid "Synchronize Translations"
-msgstr "Übersetzungen aktualisieren"
+msgstr "Übersetzungen eingeben"
 
 msgctxt "view:ir.translation:"
 msgid "Translations"
@@ -4115,7 +4153,7 @@ msgstr "Abbrechen"
 
 msgctxt "wizard_button:ir.translation.update,start,update:"
 msgid "Update"
-msgstr "Aktualisieren"
+msgstr "Eingeben"
 
 msgctxt "wizard_button:ir.ui.view.show,start,end:"
 msgid "Close"
diff --git a/trytond/ir/locale/es_AR.po b/trytond/ir/locale/es_AR.po
index d727c5e..d59aedf 100644
--- a/trytond/ir/locale/es_AR.po
+++ b/trytond/ir/locale/es_AR.po
@@ -25,7 +25,7 @@ msgstr "No está autorizado a borrar este registro."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 "El número de decimales «%(digits)s» del campo «%(field)s» en «%(value)s» "
 "excede su límite."
@@ -254,7 +254,7 @@ msgstr ""
 
 msgctxt "error:ir.trigger:"
 msgid "\"On Time\" and others are mutually exclusive!"
-msgstr "¡\"Al Tiempo\" y otros son mutuamente excluyentes!"
+msgstr "¡«Al Tiempo» y otros son mutuamente excluyentes!"
 
 msgctxt "error:ir.trigger:"
 msgid ""
@@ -398,7 +398,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action,name:"
 msgid "Name"
@@ -474,7 +474,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.act_window,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action.act_window,limit:"
 msgid "Limit"
@@ -588,6 +588,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Activo"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Fecha creación"
@@ -642,7 +646,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.keyword,keyword:"
 msgid "Keyword"
-msgstr "Palabra clave"
+msgstr "Acción de teclado"
 
 msgctxt "field:ir.action.keyword,model:"
 msgid "Model"
@@ -702,7 +706,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.report,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action.report,model:"
 msgid "Model"
@@ -802,7 +806,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.url,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action.url,name:"
 msgid "Name"
@@ -866,7 +870,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.wizard,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action.wizard,model:"
 msgid "Model"
@@ -950,7 +954,7 @@ msgstr "Enlace"
 
 msgctxt "field:ir.attachment,name:"
 msgid "Name"
-msgstr "Nombre del adjunto"
+msgstr "Nombre"
 
 msgctxt "field:ir.attachment,rec_name:"
 msgid "Name"
@@ -1384,21 +1388,17 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Usuario creación"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Fecha de inicio"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Fecha de actualización"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID del recurso"
 
 msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
-msgstr "Identificador en el sistema de archivos"
+msgstr "Identificador en el Sistema de Archivos"
+
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Valores en el Sistema de Archivos"
 
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
@@ -1416,6 +1416,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "No Actualizar"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Sin sincronizar"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Nombre"
@@ -2340,6 +2344,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Acciones de teclado"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Activo"
@@ -2824,6 +2832,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Campos de Acceso"
@@ -2862,7 +2874,7 @@ msgstr "Propiedades predeterminadas"
 
 msgctxt "model:ir.action,name:act_rule_group_form"
 msgid "Record Rules"
-msgstr "Grabar reglas"
+msgstr "Reglas de registros"
 
 msgctxt "model:ir.action,name:act_sequence_form"
 msgid "Sequences"
@@ -2936,13 +2948,23 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Dominio de acción de ventana"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Todo"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Sin sincronizar"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Acción de vista de ventana"
 
 msgctxt "model:ir.action.keyword,name:"
 msgid "Action keyword"
-msgstr "Palabra clave de acción"
+msgstr "Acción de teclado"
 
 msgctxt "model:ir.action.report,name:"
 msgid "Action report"
@@ -3008,6 +3030,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Alemán"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Español (Ecuador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Inglés"
@@ -3066,7 +3092,7 @@ msgstr "Modelo de Acceso a Campo"
 
 msgctxt "model:ir.model.print_model_graph.start,name:"
 msgid "Print Model Graph"
-msgstr "Imprimir Gráfico del Modelo"
+msgstr "Imprimir gráfico de modelos"
 
 msgctxt "model:ir.module.module,name:"
 msgid "Module"
@@ -3253,9 +3279,13 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
-msgstr "Campos de Acceso"
+msgstr "Acceso a los campos"
 
 msgctxt "model:ir.ui.menu,name:menu_model_form"
 msgid "Models"
@@ -3287,7 +3317,7 @@ msgstr "Propiedades predeterminadas"
 
 msgctxt "model:ir.ui.menu,name:menu_rule_group_form"
 msgid "Record Rules"
-msgstr "Grabar reglas"
+msgstr "Reglas de registros"
 
 msgctxt "model:ir.ui.menu,name:menu_scheduler"
 msgid "Scheduler"
@@ -3379,11 +3409,11 @@ msgstr "Ancho vista de árbol"
 
 msgctxt "selection:ir.action.keyword,keyword:"
 msgid "Action form"
-msgstr "Formulario de acción"
+msgstr "Acción de formulario"
 
 msgctxt "selection:ir.action.keyword,keyword:"
 msgid "Action tree"
-msgstr "Árbol de acciones"
+msgstr "Acción de árbol"
 
 msgctxt "selection:ir.action.keyword,keyword:"
 msgid "Form relate"
@@ -3619,11 +3649,11 @@ msgstr "Abrir una ventana"
 
 msgctxt "view:ir.action.keyword:"
 msgid "Keyword"
-msgstr "Palabra clave"
+msgstr "Acción de teclado"
 
 msgctxt "view:ir.action.keyword:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Acciones de teclado"
 
 msgctxt "view:ir.action.report:"
 msgid "General"
@@ -3709,6 +3739,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Modelo de Datos"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Sincronizar"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Acceso al Campo"
@@ -3723,7 +3761,7 @@ msgstr "Campos"
 
 msgctxt "view:ir.model.print_model_graph.start:"
 msgid "Print Model Graph"
-msgstr "Imprimir Gráfico del Modelo"
+msgstr "Imprimir gráfico de modelos"
 
 msgctxt "view:ir.model:"
 msgid "Model Description"
@@ -3833,7 +3871,7 @@ msgstr ""
 
 msgctxt "view:ir.rule.group:"
 msgid "Record rules"
-msgstr "Grabar reglas"
+msgstr "Reglas de registros"
 
 msgctxt "view:ir.rule.group:"
 msgid "The rule is satisfied if at least one test is True"
diff --git a/trytond/ir/locale/es_CO.po b/trytond/ir/locale/es_CO.po
index 01c59ac..7b69425 100644
--- a/trytond/ir/locale/es_CO.po
+++ b/trytond/ir/locale/es_CO.po
@@ -23,7 +23,7 @@ msgstr "No está autorizado a borrar este registro."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 "El numero de decimales \"%(digits)s\" del campo \"%(field)s\" en el valor "
 "\"%(value)s\" excede el limite."
@@ -120,19 +120,19 @@ msgstr "decimal_point y «thousands_sep» deben ser distintos"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not create this kind of document! (%s)"
-msgstr "No puede crear este tipo de documento (%s)"
+msgstr "No puede crear este tipo de documento! (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not delete this document! (%s)"
-msgstr "No puede borrar este documento (%s)"
+msgstr "No puede borrar este documento! (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not read this document! (%s)"
-msgstr "No puede leer este documento (%s)"
+msgstr "No puede leer este documento! (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not write in this document! (%s)"
-msgstr "No puede escribir en este documento (%s)"
+msgstr "No puede escribir en este documento! (%s)"
 
 msgctxt "error:ir.model.button:"
 msgid "The button name in model must be unique!"
@@ -586,6 +586,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Activo"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Fecha de Creación"
@@ -1382,14 +1386,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Creado por Usuario"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Fecha Inicial"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Fecha de Actualización"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID del recurso"
@@ -1398,6 +1394,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Identificador en Sistema de Archivos"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Valores en el Sistema de Archivos"
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1414,6 +1414,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "No Actualizar"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Sin Sincronizar"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Nombre"
@@ -2338,6 +2342,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Teclas de Acción"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Activo"
@@ -2772,7 +2780,7 @@ msgstr "Acciones"
 
 msgctxt "model:ir.action,name:act_action_report_form"
 msgid "Reports"
-msgstr "Informes"
+msgstr "Reportes"
 
 msgctxt "model:ir.action,name:act_action_url_form"
 msgid "URLs"
@@ -2822,6 +2830,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permisos de Campos"
@@ -2934,6 +2946,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Accion en dominio de ventana de acción"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Todo"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Sin Sincronizar"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Acción en vista de ventana de acción"
@@ -3006,6 +3028,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Alemán"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Español (Ecuador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Inglés"
@@ -3048,7 +3074,7 @@ msgstr "Permisos de modelo"
 
 msgctxt "model:ir.model.button,name:"
 msgid "Model Button"
-msgstr "Modelo de Botón"
+msgstr "Modelo Botón"
 
 msgctxt "model:ir.model.data,name:"
 msgid "Model data"
@@ -3145,11 +3171,11 @@ msgstr "Iniciar limpieza de traducciones"
 
 msgctxt "model:ir.translation.export.result,name:"
 msgid "Export translation"
-msgstr "Exportar traducción - archivo"
+msgstr "Exportar traducción"
 
 msgctxt "model:ir.translation.export.start,name:"
 msgid "Export translation"
-msgstr "Exportar traducción - archivo"
+msgstr "Exportar traducción"
 
 msgctxt "model:ir.translation.set.start,name:"
 msgid "Set Translation"
@@ -3193,7 +3219,7 @@ msgstr "Ventana de Acciones"
 
 msgctxt "model:ir.ui.menu,name:menu_action_report_form"
 msgid "Reports"
-msgstr "Informes"
+msgstr "Reportes"
 
 msgctxt "model:ir.ui.menu,name:menu_action_url"
 msgid "URLs"
@@ -3251,6 +3277,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permisos de Campos"
@@ -3629,11 +3659,11 @@ msgstr "General"
 
 msgctxt "view:ir.action.report:"
 msgid "Report"
-msgstr "Informe"
+msgstr "Reporte"
 
 msgctxt "view:ir.action.report:"
 msgid "Report xml"
-msgstr "Informe xml"
+msgstr "Reporte xml"
 
 msgctxt "view:ir.action.url:"
 msgid "General"
@@ -3697,7 +3727,7 @@ msgstr "Formato de Números"
 
 msgctxt "view:ir.model.access:"
 msgid "Access controls"
-msgstr "Acceso a controles"
+msgstr "Controles de acceso"
 
 msgctxt "view:ir.model.button:"
 msgid "Button"
@@ -3707,6 +3737,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Modelo de Datos"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Syncronizar"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Permiso de Campo"
diff --git a/trytond/ir/locale/es_CO.po b/trytond/ir/locale/es_EC.po
similarity index 91%
copy from trytond/ir/locale/es_CO.po
copy to trytond/ir/locale/es_EC.po
index 01c59ac..de8595c 100644
--- a/trytond/ir/locale/es_CO.po
+++ b/trytond/ir/locale/es_EC.po
@@ -7,26 +7,28 @@ msgid ""
 "You try to bypass an access rule!\n"
 "(Document type: %s)"
 msgstr ""
-"Está tratando de saltarse una regla de acceso!\n"
+"¡Está intentando evitar una regla de acceso!\n"
 "(Tipo de documento: %s)"
 
 msgctxt "error:access_error:"
 msgid ""
 "You try to bypass an access rule.\n"
 "(Document type: %s)"
-msgstr "Esta intentando evitar una regla de acceso."
+msgstr ""
+"Esta intentando evitar una regla de acceso.\n"
+"(Tipo de documento: %s)"
 
 msgctxt "error:delete_xml_record:"
 msgid "You are not allowed to delete this record."
-msgstr "No está autorizado a borrar este registro."
+msgstr "No está autorizado para eliminar este registro."
 
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
-"El numero de decimales \"%(digits)s\" del campo \"%(field)s\" en el valor "
-"\"%(value)s\" excede el limite."
+"El número de decimales \"%(digits)s\" del campo \"%(field)s\" en el "
+"valor\"%(value)s\" excede su limite."
 
 msgctxt "error:domain_validation_record:"
 msgid ""
@@ -41,40 +43,38 @@ msgid ""
 "Could not delete the records because they are used on field \"%(field)s\" of"
 " \"%(model)s\"."
 msgstr ""
-"No se pudieron borrar los registros porque son usados en el campo "
+"No se pudieron eliminar los registros porque son utilizados en el campo "
 "\"%(field)s\" de \"%(model)s\"."
 
 msgctxt "error:foreign_model_missing:"
 msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
-msgstr ""
-"El valor \"%(value)s\" en el campo \"%(field)s\" en el modelo \"%(model)s\" "
-"no existe."
+msgstr "El valor \"%(value)s\" del campo \"%(field)s\" en \"%(model)s\" no existe."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
-msgstr "Contexto inválido \"%(context)s\" en action \"%(action)s\"."
+msgstr "El contexto \"%(context)s\" en la acción \"%(action)s\" no es válido."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid domain or search criteria \"%(domain)s\" on action \"%(action)s\"."
 msgstr ""
-"Dominio inválido o criterio de busqueda \"%(domain)s\" en acción "
-"\"%(action)s\"."
+"El dominio o el criterio de búsqueda \"%(domain)s\" en la acción "
+"\"%(action)s\" no es válido."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid view \"%(view)s\" for action \"%(action)s\"."
-msgstr "Vista \"%(view)s\" inválida para la acción \"%(action)s\"."
+msgstr "La vista \"%(view)s\" para la acción \"%(action)s\" no es válida."
 
 msgctxt "error:ir.action.keyword:"
 msgid "Wrong wizard model in keyword action \"%s\"."
-msgstr "Errado modelo asistente para acción la \"%s\"."
+msgstr "Modelo de asistente incorrecto en la acción de teclado \"%s\"."
 
 msgctxt "error:ir.action.report:"
 msgid "Invalid email definition on report \"%s\"."
-msgstr "El correo electronico definido en el informe \"%s\" es inválido."
+msgstr "La definición de correo electrónico sobre el informe \"%s\" no es válido."
 
 msgctxt "error:ir.action.report:"
 msgid "The internal name must be unique by module!"
-msgstr "¡El nombre interno de los módulos debe ser único!"
+msgstr "¡El nombre interno debe ser único por módulo!"
 
 msgctxt "error:ir.attachment:"
 msgid "The  names of attachments must be unique by resource!"
@@ -92,23 +92,25 @@ msgid ""
 "\n"
 "%s\n"
 msgstr ""
-"La siguiente acción falló al ejecutar apropiadamente: \"%s\"\n"
+"La siguiente acción falló cuando se ejecutaba: \"%s\"\n"
 "%s\n"
-" Traceback:\n"
+" Traza del programa:\n"
 "\n"
-"%s"
+"%s\n"
 
 msgctxt "error:ir.lang:"
 msgid "Default language can not be deleted."
-msgstr "Los lenguajes por defecto no pueden ser borrados."
+msgstr "El idioma por defecto no puede ser eliminado."
 
 msgctxt "error:ir.lang:"
 msgid "Invalid date format \"%(format)s\" on \"%(language)s\" language."
-msgstr "Formato de fecha inválido \"%(format)s\" para el lenguaje \"%(languages)s\"."
+msgstr ""
+"El formato de la fecha \"%(format)s\" para el idioma \"%(languages)s\" no es"
+" válido."
 
 msgctxt "error:ir.lang:"
 msgid "Invalid grouping \"%(grouping)s\" on \"%(language)s\" language."
-msgstr "Agrupamiento inválido \"%(grouping)s\" en lenguaje \"%(languages)s\"."
+msgstr "Agrupamiento no válido \"%(grouping)s\" en idioma \"%(languages)s\"."
 
 msgctxt "error:ir.lang:"
 msgid "The default language must be translatable."
@@ -116,7 +118,7 @@ msgstr "El idioma por defecto debe ser traducible."
 
 msgctxt "error:ir.lang:"
 msgid "decimal_point and thousands_sep must be different!"
-msgstr "decimal_point y «thousands_sep» deben ser distintos"
+msgstr "¡el punto decimal y el separador de miles deben ser distintos!"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not create this kind of document! (%s)"
@@ -124,7 +126,7 @@ msgstr "No puede crear este tipo de documento (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not delete this document! (%s)"
-msgstr "No puede borrar este documento (%s)"
+msgstr "No puede eliminar este documento (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not read this document! (%s)"
@@ -136,7 +138,7 @@ msgstr "No puede escribir en este documento (%s)"
 
 msgctxt "error:ir.model.button:"
 msgid "The button name in model must be unique!"
-msgstr "El nombre del botón en el modelo debe ser único!"
+msgstr "¡El nombre del botón en el modelo debe ser único!"
 
 msgctxt "error:ir.model.data:"
 msgid "The triple (fs_id, module, model) must be unique!"
@@ -152,7 +154,7 @@ msgstr "¡No puede escribir en el campo! (%s.%s)"
 
 msgctxt "error:ir.model.field:"
 msgid "Model Field name \"%s\" is not a valid python identifier."
-msgstr "El nombre del Modelo Campo \"%s\" no es un identificador python válido."
+msgstr "El nombre del Campo Modelo \"%s\" no es un identificador python válido."
 
 msgctxt "error:ir.model.field:"
 msgid "The field name in model must be unique!"
@@ -194,21 +196,19 @@ msgstr "Global y Predeterminado son mutuamente excluyentes"
 
 msgctxt "error:ir.rule:"
 msgid "Invalid domain in rule \"%s\"."
-msgstr "Dominio inválido en regla \"%s\"."
+msgstr "Dominio no válido en la regla \"%s\"."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Invalid prefix \"%(prefix)s\" on sequence \"%(sequence)s\"."
-msgstr "Prefijo inválido \"%(prefix)s\" en secuencia \"%(sequence)s\"."
+msgstr "El prefijo \"%(prefix)s\" en la secuencia \"%(sequence)s\" no es válido."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Invalid suffix \"%(suffix)s\" on sequence \"%(sequence)s\"."
-msgstr "Sufijo inválido \"%(suffix)s\" en secuencia \"%(sequence)s\"."
+msgstr "El sufijo \"%(sufix)s\" de la secuencia \"%(sequence)s\" no es válido."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
-msgstr ""
-"La ultima marca de tiempo no puede estar en el futuro para la secuencia "
-"\"%s\"."
+msgstr "La ultima fecha-hora no puede ser del futuro para la secuencia \"%s\"."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Missing sequence."
@@ -216,21 +216,20 @@ msgstr "Falta la secuencia."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Timestamp rounding should be greater than 0"
-msgstr "El redondeo de la marca de tiempo debe ser mayor que 0"
+msgstr "El redondeo de la fecha-hora debe ser mayor que 0"
 
 msgctxt "error:ir.sequence:"
 msgid "Invalid prefix \"%(prefix)s\" on sequence \"%(sequence)s\"."
-msgstr "Prefijo inválido \"%(prefix)s\" en secuencia \"%(sequence)s\"."
+msgstr "El prefijo \"%(prefix)s\" en la secuencia \"%(sequence)s\" no es válido."
 
 msgctxt "error:ir.sequence:"
 msgid "Invalid suffix \"%(suffix)s\" on sequence \"%(sequence)s\"."
-msgstr "Sufijo inválido \"%(suffix)s\" en secuencia \"%(sequence)s\"."
+msgstr "El sufijo \"%(sufix)s\" de la secuencia \"%(sequence)s\" no es válido."
 
 msgctxt "error:ir.sequence:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
 msgstr ""
-"La ultima marca de tiempo no puede estar en el futuro para la secuencia "
-"\"%s\"."
+"La ultima fecha-hora no puede estar en el futuro para la secuencia \"%s\"."
 
 msgctxt "error:ir.sequence:"
 msgid "Missing sequence."
@@ -238,7 +237,7 @@ msgstr "Falta la secuencia."
 
 msgctxt "error:ir.sequence:"
 msgid "Timestamp rounding should be greater than 0"
-msgstr "El redondeo de la marca de tiempo debe ser mayor que 0"
+msgstr "El redondeo de la fecha-hora debe ser mayor que 0"
 
 msgctxt "error:ir.translation:"
 msgid "Translation must be unique"
@@ -249,19 +248,19 @@ msgid ""
 "You can not export translation %(name)s because it is an overridden "
 "translation by module %(overriding_module)s"
 msgstr ""
-"No puede exportar la traduccion %(name)s porque esta es una traducción "
+"No puede exportar la traduccion %(name)s porque es una traducción "
 "sobreescrita por el módulo %(overrinding_module)s"
 
 msgctxt "error:ir.trigger:"
 msgid "\"On Time\" and others are mutually exclusive!"
-msgstr "\"Al Tiempo\" y otros son mutuamente excluyentes!"
+msgstr "¡\"A Tiempo\" y otros son mutuamente excluyentes!"
 
 msgctxt "error:ir.trigger:"
 msgid ""
 "Condition \"%(condition)s\" is not a valid python expression on trigger "
 "\"%(trigger)s\"."
 msgstr ""
-"Condición \"%(condition)s\" no es una expressión python válida en el "
+"La condición \"%(condition)s\" no es una expressión python válida en el "
 "disparador \"%s(trigger)s\"."
 
 msgctxt "error:ir.ui.menu:"
@@ -270,21 +269,23 @@ msgstr "\"%s\" no es un nombre válido de menú porque no esta permitido contene
 
 msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
-msgstr "XML inválido para la vista \"%s\"."
+msgstr "XML no válido para la vista \"%s\"."
 
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
 "(Document type: %s)"
 msgstr ""
-"Está intentando leer registros que ya no existen\n"
+"¡Está intentando leer registros que ya no existen!\n"
 "(Tipo de documento: %s)"
 
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore.\n"
 "(Document type: %s)"
-msgstr "Esta intentando leer registros que ya no existen."
+msgstr ""
+"Está intentando leer registros que ya no existen.\n"
+"(Tipo de documento: %s)"
 
 msgctxt "error:recursion_error:"
 msgid ""
@@ -292,7 +293,7 @@ msgid ""
 " was configured as ancestor of itself."
 msgstr ""
 "Error de recursión: El registro \"%(rec_name)s\" con padre \"%(rec_name)s\" "
-"fue configurado como antecesor del mismo."
+"fue configurado como antecesor de si mismo."
 
 msgctxt "error:reference_syntax_error:"
 msgid "Syntax error for reference %r in %s"
@@ -304,33 +305,33 @@ msgstr "Relación no encontrada: %r en %s"
 
 msgctxt "error:required_field:"
 msgid "The field \"%(field)s\" on \"%(model)s\" is required."
-msgstr "El campo \"%(field)s\" en el modelo \"%(model)s\" es obligatorio."
+msgstr "El campo \"%(field)s\" en \"%(model)s\" es requerido."
 
 msgctxt "error:required_validation_record:"
 msgid "The field \"%(field)s\" on \"%(model)s\" is required."
-msgstr "El campo \"%(field)s\" en el modelo \"%(model)s\" es obligatorio."
+msgstr "El campo \"%(field)s\" en \"%(model)s\" es requerido."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
-msgstr "Falta la función en el campo \"%s\"."
+msgstr "Falta la función de búsqueda en el campo \"%s\"."
 
 msgctxt "error:selection_validation_record:"
 msgid ""
 "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
 "the selection."
 msgstr ""
-"El valor \"%(value)s\" en el campo \"%(field)s\" en el modelo \"%(model)s\" "
-"no esta en la selección."
+"El valor \"%(value)s\" del campo \"%(field)s\" en el modelo \"%(model)s\" no"
+" esta en la selección."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
-msgstr "El valor no esta en la selección para el campo \"%s\"."
+msgstr "El valor no se encuentra en la selección para el campo \"%s\"."
 
 msgctxt "error:size_validation_record:"
 msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
 msgstr ""
-"El tamaño \"%(size)s\" del campo \"%(field)s\" en el modelo \"%(model)s\" is"
-" demasiado largo."
+"El tamaño \"%(size)s\" del campo \"%(field)s\" en \"%(model)s\" is demasiado"
+" largo."
 
 msgctxt "error:time_format_validation_record:"
 msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
@@ -347,7 +348,7 @@ msgid ""
 "You try to write on records that don't exist anymore!\n"
 "(Document type: %s)"
 msgstr ""
-"Está tratando de escribir en registros que ya no existen\n"
+"¡Está tratando de escribir en registros que ya no existen!\n"
 "(Tipo de documento: %s)"
 
 msgctxt "error:write_error:"
@@ -364,7 +365,7 @@ msgstr "No está autorizado para modificar este registro."
 
 msgctxt "error:xml_id_syntax_error:"
 msgid "Syntax error for XML id %r in %s"
-msgstr "Error de sintaxis para id XML %r en %s"
+msgstr "Error de sintaxis para XML id %r en %s"
 
 msgctxt "error:xml_record_desc:"
 msgid "This record is part of the base configuration."
@@ -396,7 +397,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action,keywords:"
 msgid "Keywords"
-msgstr "Teclas"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action,name:"
 msgid "Name"
@@ -472,7 +473,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.act_window,keywords:"
 msgid "Keywords"
-msgstr "Teclas"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action.act_window,limit:"
 msgid "Limit"
@@ -586,6 +587,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Activo"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Fecha de Creación"
@@ -700,7 +705,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.report,keywords:"
 msgid "Keywords"
-msgstr "Teclas"
+msgstr "Acciones de teclado"
 
 msgctxt "field:ir.action.report,model:"
 msgid "Model"
@@ -752,7 +757,7 @@ msgstr "Estilo"
 
 msgctxt "field:ir.action.report,template_extension:"
 msgid "Template Extension"
-msgstr "Plantilla de Extensión"
+msgstr "Extensión de Plantilla"
 
 msgctxt "field:ir.action.report,type:"
 msgid "Type"
@@ -1060,11 +1065,11 @@ msgstr "ID"
 
 msgctxt "field:ir.cron,interval_number:"
 msgid "Interval Number"
-msgstr "Número de Intervalo"
+msgstr "Número de Intervalos"
 
 msgctxt "field:ir.cron,interval_type:"
 msgid "Interval Unit"
-msgstr "Unidad Intervalo"
+msgstr "Unidad de Intervalo"
 
 msgctxt "field:ir.cron,model:"
 msgid "Model"
@@ -1092,7 +1097,7 @@ msgstr "Repetir Fallidos"
 
 msgctxt "field:ir.cron,request_user:"
 msgid "Request User"
-msgstr "Petición de Usuario"
+msgstr "Solicitud de Usuario"
 
 msgctxt "field:ir.cron,user:"
 msgid "Execution User"
@@ -1382,21 +1387,17 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Creado por Usuario"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Fecha Inicial"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Fecha de Actualización"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID del recurso"
 
 msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
-msgstr "Identificador en Sistema de Archivos"
+msgstr "Identificador en el Sistema de Archivos"
+
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Valores del Sistema de Archivos"
 
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
@@ -1414,6 +1415,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "No Actualizar"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Fuera de Sincronización"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Nombre"
@@ -1472,7 +1477,7 @@ msgstr "Nombre"
 
 msgctxt "field:ir.model.field,relation:"
 msgid "Model Relation"
-msgstr "Modelo Relación"
+msgstr "Relación del Modelo"
 
 msgctxt "field:ir.model.field,ttype:"
 msgid "Field Type"
@@ -1516,7 +1521,7 @@ msgstr "Permiso para Crear"
 
 msgctxt "field:ir.model.field.access,perm_delete:"
 msgid "Delete Access"
-msgstr "Permiso para Borrar"
+msgstr "Permiso para Eliminar"
 
 msgctxt "field:ir.model.field.access,perm_read:"
 msgid "Read Access"
@@ -1804,7 +1809,7 @@ msgstr "Permiso para Crear"
 
 msgctxt "field:ir.rule.group,perm_delete:"
 msgid "Delete Access"
-msgstr "Permiso para Borrar"
+msgstr "Permiso para Eliminar"
 
 msgctxt "field:ir.rule.group,perm_read:"
 msgid "Read Access"
@@ -1856,7 +1861,7 @@ msgstr "ID"
 
 msgctxt "field:ir.sequence,last_timestamp:"
 msgid "Last Timestamp"
-msgstr "Última Marca de Tiempo"
+msgstr "Última Fecha-hora"
 
 msgctxt "field:ir.sequence,name:"
 msgid "Sequence Name"
@@ -1868,11 +1873,11 @@ msgstr "Número de Incremento"
 
 msgctxt "field:ir.sequence,number_next:"
 msgid "Next Number"
-msgstr "Número Siguiente"
+msgstr "Siguiente Número"
 
 msgctxt "field:ir.sequence,number_next_internal:"
 msgid "Next Number"
-msgstr "Número Siguiente"
+msgstr "Siguiente Número"
 
 msgctxt "field:ir.sequence,padding:"
 msgid "Number padding"
@@ -1892,11 +1897,11 @@ msgstr "Sufijo"
 
 msgctxt "field:ir.sequence,timestamp_offset:"
 msgid "Timestamp Offset"
-msgstr "Compensación de Marca de Tiempo"
+msgstr "Desfase de Fecha-hora"
 
 msgctxt "field:ir.sequence,timestamp_rounding:"
 msgid "Timestamp Rounding"
-msgstr "Redondeo de Marca de Tiempo"
+msgstr "Redondeo de Fecha-hora"
 
 msgctxt "field:ir.sequence,type:"
 msgid "Type"
@@ -1932,7 +1937,7 @@ msgstr "ID"
 
 msgctxt "field:ir.sequence.strict,last_timestamp:"
 msgid "Last Timestamp"
-msgstr "Última Marca de Tiempo"
+msgstr "Última Fecha-hora"
 
 msgctxt "field:ir.sequence.strict,name:"
 msgid "Sequence Name"
@@ -1944,11 +1949,11 @@ msgstr "Número de Incremento"
 
 msgctxt "field:ir.sequence.strict,number_next:"
 msgid "Next Number"
-msgstr "Número Siguiente"
+msgstr "Siguiente Número"
 
 msgctxt "field:ir.sequence.strict,number_next_internal:"
 msgid "Next Number"
-msgstr "Número Siguiente"
+msgstr "Siguiente Número"
 
 msgctxt "field:ir.sequence.strict,padding:"
 msgid "Number padding"
@@ -1968,11 +1973,11 @@ msgstr "Sufijo"
 
 msgctxt "field:ir.sequence.strict,timestamp_offset:"
 msgid "Timestamp Offset"
-msgstr "Compensación de Marca de Tiempo"
+msgstr "Desfase de Fecha-hora"
 
 msgctxt "field:ir.sequence.strict,timestamp_rounding:"
 msgid "Timestamp Rounding"
-msgstr "Redondeo de Marca de Tiempo"
+msgstr "Redondeo de Fecha-hora"
 
 msgctxt "field:ir.sequence.strict,type:"
 msgid "Type"
@@ -2084,7 +2089,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.translation,fuzzy:"
 msgid "Fuzzy"
-msgstr "Vago"
+msgstr "Confuso"
 
 msgctxt "field:ir.translation,id:"
 msgid "ID"
@@ -2188,11 +2193,11 @@ msgstr "Idioma"
 
 msgctxt "field:ir.trigger,action_function:"
 msgid "Action Function"
-msgstr "Acción Función"
+msgstr "Función de la Acción"
 
 msgctxt "field:ir.trigger,action_model:"
 msgid "Action Model"
-msgstr "Acción en Modelo"
+msgstr "Acción de Modelo"
 
 msgctxt "field:ir.trigger,active:"
 msgid "Active"
@@ -2338,6 +2343,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Teclas de Acción"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Activo"
@@ -2436,7 +2445,7 @@ msgstr "Modificado por Usuario"
 
 msgctxt "field:ir.ui.view,arch:"
 msgid "View Architecture"
-msgstr "Ver arquitectura"
+msgstr "Ver Arquitectura"
 
 msgctxt "field:ir.ui.view,create_date:"
 msgid "Create Date"
@@ -2632,11 +2641,11 @@ msgstr "Modificado por Usuario"
 
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
-msgstr "Límite predeterminado para la vista en lista"
+msgstr "Límite predeterminado para la vista de lista"
 
 msgctxt "help:ir.action.act_window,search_value:"
 msgid "Default search criteria for the list view"
-msgstr "Criterio de búsqueda por defecto para la vista en lista"
+msgstr "Criterio de búsqueda por defecto para la vista de lista"
 
 msgctxt "help:ir.action.act_window,window_name:"
 msgid "Use the action name as window name"
@@ -2648,7 +2657,7 @@ msgid ""
 "Example: {'to': 'test at example.com', 'cc': 'user at example.com'}"
 msgstr ""
 "Diccionario de Python donde las claves definen \"to\" \"cc\" \"subject\"\n"
-"Ejemplo: {'to': 'test at example.com', 'cc': 'user at example.com'}"
+"Ejemplo: {'to': 'test at ejemplo.com', 'cc': 'usuario at ejemplo.com'}"
 
 msgctxt "help:ir.action.report,extension:"
 msgid ""
@@ -2664,7 +2673,7 @@ msgstr "Define el estilo a aplicar en el informe."
 
 msgctxt "help:ir.action.wizard,window:"
 msgid "Run wizard in a new window"
-msgstr "Ejecutar asistente en una nueva ventana"
+msgstr "Ejecutar el asistente en una nueva ventana"
 
 msgctxt "help:ir.cron,number_calls:"
 msgid ""
@@ -2684,7 +2693,7 @@ msgstr "El usuario que se utilizará para ejecutar esta acción"
 
 msgctxt "help:ir.lang,code:"
 msgid "RFC 4646 tag: http://tools.ietf.org/html/rfc4646"
-msgstr "RFC 4646 tag: http://tools.ietf.org/html/rfc4646"
+msgstr "Etiqueta RFC 4646: http://tools.ietf.org/html/rfc4646"
 
 msgctxt "help:ir.model,module:"
 msgid "Module in which this model is defined"
@@ -2707,8 +2716,8 @@ msgid ""
 "Entering a Python Regular Expression will exclude matching models from the "
 "graph."
 msgstr ""
-"Ingresando una Expresión Regular de Python se excluirán modelos que "
-"concuerden con el gráfico."
+"Ingresando una Expresión Regular de Python se excluirán del gráfico modelos "
+"que concuerden."
 
 msgctxt "help:ir.rule,domain:"
 msgid "Domain is evaluated with \"user\" as the current user"
@@ -2716,7 +2725,7 @@ msgstr "El dominio es evaluado con el \"usuario\" como el usuario actual"
 
 msgctxt "help:ir.rule.group,default_p:"
 msgid "Add this rule to all users by default"
-msgstr "Añadir la regla a todos los usuarios por defecto"
+msgstr "Añadir esta regla a todos los usuarios por defecto"
 
 msgctxt "help:ir.rule.group,global_p:"
 msgid ""
@@ -2728,7 +2737,7 @@ msgstr ""
 
 msgctxt "help:ir.rule.group,rules:"
 msgid "The rule is satisfied if at least one test is True"
-msgstr "La regla se satisface si al menos una condición es cierta"
+msgstr "La regla se satisface si al menos una condición es verdadera"
 
 msgctxt "help:ir.trigger,condition:"
 msgid ""
@@ -2788,7 +2797,7 @@ msgstr "Adjuntos"
 
 msgctxt "model:ir.action,name:act_config_wizard_item_form"
 msgid "Config Wizard Items"
-msgstr "Asistentes de Configuración"
+msgstr "Elementos del Asistente de Configuración"
 
 msgctxt "model:ir.action,name:act_cron_form"
 msgid "Scheduled Actions"
@@ -2822,9 +2831,14 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+#, fuzzy
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
-msgstr "Permisos de Campos"
+msgstr "Permisos de los Campos"
 
 msgctxt "model:ir.action,name:act_model_fields_form"
 msgid "Fields"
@@ -2836,7 +2850,7 @@ msgstr "Modelos"
 
 msgctxt "model:ir.action,name:act_module_config"
 msgid "Configure Modules"
-msgstr "Configuración de Modulos"
+msgstr "Configuración de Módulos"
 
 msgctxt "model:ir.action,name:act_module_config_wizard"
 msgid "Module Configuration"
@@ -2916,7 +2930,7 @@ msgstr "Estado de Arbol"
 
 msgctxt "model:ir.action,name:act_view_tree_width_form"
 msgid "View Tree Width"
-msgstr "Ancho de Columna"
+msgstr "Ancho de la Vista de Arbol"
 
 msgctxt "model:ir.action,name:print_model_graph"
 msgid "Graph"
@@ -2934,13 +2948,24 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Accion en dominio de ventana de acción"
 
+#, fuzzy
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Todo"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Fuera de Sincronización"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Acción en vista de ventana de acción"
 
 msgctxt "model:ir.action.keyword,name:"
 msgid "Action keyword"
-msgstr "Teclas de acción "
+msgstr "Tecla de acción "
 
 msgctxt "model:ir.action.report,name:"
 msgid "Action report"
@@ -2952,7 +2977,7 @@ msgstr "URL de la acción"
 
 msgctxt "model:ir.action.wizard,name:"
 msgid "Action wizard"
-msgstr "Acción del Asistente"
+msgstr "Asistente de la acción"
 
 msgctxt "model:ir.attachment,name:"
 msgid "Attachment"
@@ -3006,6 +3031,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Alemán"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Español (Ecuador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Inglés"
@@ -3048,19 +3077,19 @@ msgstr "Permisos de modelo"
 
 msgctxt "model:ir.model.button,name:"
 msgid "Model Button"
-msgstr "Modelo de Botón"
+msgstr "Botón de Modelo"
 
 msgctxt "model:ir.model.data,name:"
 msgid "Model data"
-msgstr "Modelo de datos"
+msgstr "Datos de modelo"
 
 msgctxt "model:ir.model.field,name:"
 msgid "Model field"
-msgstr "Modelo de campo"
+msgstr "Campo de modelo"
 
 msgctxt "model:ir.model.field.access,name:"
 msgid "Model Field Access"
-msgstr "Permiso de Modelo Campo"
+msgstr "Permiso Campo de Modelo"
 
 msgctxt "model:ir.model.print_model_graph.start,name:"
 msgid "Print Model Graph"
@@ -3072,20 +3101,19 @@ msgstr "Módulo"
 
 msgctxt "model:ir.module.module.config_wizard.done,name:"
 msgid "Module Config Wizard Done"
-msgstr "Configuración del Módulo Terminada"
+msgstr "Asitente de Configuración del Módulo - Realizado"
 
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
-msgstr "Primer Asistente de Configuración de Módulo"
+msgstr "Asistente de Configuración del Módulo - Primero"
 
 msgctxt "model:ir.module.module.config_wizard.item,name:"
 msgid "Config wizard to run after installing module"
-msgstr ""
-"Asistente de configuración a ejecutar después de la instalaciń del módulo"
+msgstr "Asistente de configuración a ejecutar después de instalar un módulo"
 
 msgctxt "model:ir.module.module.config_wizard.other,name:"
 msgid "Module Config Wizard Other"
-msgstr "Siguiente Asistente de Configuración de Módulo"
+msgstr "Asistente de Configuración del Módulo - Otro"
 
 msgctxt "model:ir.module.module.dependency,name:"
 msgid "Module dependency"
@@ -3093,11 +3121,11 @@ msgstr "Dependencias del módulo"
 
 msgctxt "model:ir.module.module.install_upgrade.done,name:"
 msgid "Module Install Upgrade Done"
-msgstr "Actualización del Módulo Terminada"
+msgstr "Instalación / Actualización del Módulo - Realizada"
 
 msgctxt "model:ir.module.module.install_upgrade.start,name:"
 msgid "Module Install Upgrade Start"
-msgstr "Inicio de Asistente de Instalación del Módulo"
+msgstr "Instalación / Actualización del Módulo - Iniciar"
 
 msgctxt "model:ir.property,name:"
 msgid "Property"
@@ -3109,7 +3137,7 @@ msgstr "Regla"
 
 msgctxt "model:ir.rule.group,name:"
 msgid "Rule group"
-msgstr "Regla de grupo"
+msgstr "Grupo de reglas"
 
 msgctxt "model:ir.sequence,name:"
 msgid "Sequence"
@@ -3213,7 +3241,7 @@ msgstr "Adjuntos"
 
 msgctxt "model:ir.ui.menu,name:menu_config_wizard_item_form"
 msgid "Config Wizard Items"
-msgstr "Asistentes de Configuración"
+msgstr "Elementos del Asistente de Configuración"
 
 msgctxt "model:ir.ui.menu,name:menu_cron_form"
 msgid "Scheduled Actions"
@@ -3251,6 +3279,11 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+#, fuzzy
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permisos de Campos"
@@ -3317,7 +3350,7 @@ msgstr "Traducciones"
 
 msgctxt "model:ir.ui.menu,name:menu_translation_set"
 msgid "Set Translations"
-msgstr "Establecer Traducción"
+msgstr "Establecer Traducciones"
 
 msgctxt "model:ir.ui.menu,name:menu_translation_update"
 msgid "Synchronize Translations"
@@ -3345,7 +3378,7 @@ msgstr "Estado de Arbol"
 
 msgctxt "model:ir.ui.menu,name:menu_view_tree_width"
 msgid "View Tree Width"
-msgstr "Ancho de Columna"
+msgstr "Ancho de la Vista de Arbol"
 
 msgctxt "model:ir.ui.menu,name:model_model_fields_form"
 msgid "Fields"
@@ -3373,7 +3406,7 @@ msgstr "Estado de Vista de Arbol"
 
 msgctxt "model:ir.ui.view_tree_width,name:"
 msgid "View Tree Width"
-msgstr "Ancho de Columna"
+msgstr "Ancho de la Vista de Arbol"
 
 msgctxt "selection:ir.action.keyword,keyword:"
 msgid "Action form"
@@ -3433,7 +3466,7 @@ msgstr "De izquierda a derecha"
 
 msgctxt "selection:ir.lang,direction:"
 msgid "Right-to-left"
-msgstr "Derecha-a-Izquierda"
+msgstr "De derecha a Izquierda"
 
 msgctxt "selection:ir.module.module,state:"
 msgid "Installed"
@@ -3445,15 +3478,15 @@ msgstr "No instalado"
 
 msgctxt "selection:ir.module.module,state:"
 msgid "To be installed"
-msgstr "Pendiente de ser instalado"
+msgstr "Para ser instalado"
 
 msgctxt "selection:ir.module.module,state:"
 msgid "To be removed"
-msgstr "Pendiente de ser eliminado"
+msgstr "Para ser eliminado"
 
 msgctxt "selection:ir.module.module,state:"
 msgid "To be upgraded"
-msgstr "Pendiente de ser actualizado"
+msgstr "Para ser actualizado"
 
 msgctxt "selection:ir.module.module.config_wizard.item,state:"
 msgid "Done"
@@ -3473,15 +3506,15 @@ msgstr "No instalado"
 
 msgctxt "selection:ir.module.module.dependency,state:"
 msgid "To be installed"
-msgstr "Pendiente de instalar"
+msgstr "Para ser instalado"
 
 msgctxt "selection:ir.module.module.dependency,state:"
 msgid "To be removed"
-msgstr "Pendiente de ser eliminado"
+msgstr "Para ser eliminado"
 
 msgctxt "selection:ir.module.module.dependency,state:"
 msgid "To be upgraded"
-msgstr "Pendiente de ser actualizado"
+msgstr "Para ser actualizado"
 
 msgctxt "selection:ir.module.module.dependency,state:"
 msgid "Unknown"
@@ -3489,11 +3522,11 @@ msgstr "Desconocido"
 
 msgctxt "selection:ir.sequence,type:"
 msgid "Decimal Timestamp"
-msgstr "Marca de Tiempo Decimal"
+msgstr "Fecha-hora Decimal"
 
 msgctxt "selection:ir.sequence,type:"
 msgid "Hexadecimal Timestamp"
-msgstr "Marca de Tiempo Hexadecimal"
+msgstr "Fecha-hora Hexadecimal"
 
 msgctxt "selection:ir.sequence,type:"
 msgid "Incremental"
@@ -3501,11 +3534,11 @@ msgstr "Incremental"
 
 msgctxt "selection:ir.sequence.strict,type:"
 msgid "Decimal Timestamp"
-msgstr "Marca de Tiempo Decimal"
+msgstr "Fecha-hora Decimal"
 
 msgctxt "selection:ir.sequence.strict,type:"
 msgid "Hexadecimal Timestamp"
-msgstr "Marca de Tiempo Hexadecimal"
+msgstr "Fecha-hora Hexadecimal"
 
 msgctxt "selection:ir.sequence.strict,type:"
 msgid "Incremental"
@@ -3617,11 +3650,11 @@ msgstr "Abrir una Ventana"
 
 msgctxt "view:ir.action.keyword:"
 msgid "Keyword"
-msgstr "Tecla"
+msgstr "Acción de teclado"
 
 msgctxt "view:ir.action.keyword:"
 msgid "Keywords"
-msgstr "Teclas"
+msgstr "Acciones de teclado"
 
 msgctxt "view:ir.action.report:"
 msgid "General"
@@ -3633,7 +3666,7 @@ msgstr "Informe"
 
 msgctxt "view:ir.action.report:"
 msgid "Report xml"
-msgstr "Informe xml"
+msgstr "Informe XML"
 
 msgctxt "view:ir.action.url:"
 msgid "General"
@@ -3697,7 +3730,7 @@ msgstr "Formato de Números"
 
 msgctxt "view:ir.model.access:"
 msgid "Access controls"
-msgstr "Acceso a controles"
+msgstr "Controles de acceso"
 
 msgctxt "view:ir.model.button:"
 msgid "Button"
@@ -3707,6 +3740,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Datos de Modelo"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Sincronizar"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Permiso de Campo"
@@ -3733,7 +3774,7 @@ msgstr "Configuración del módulo"
 
 msgctxt "view:ir.module.module.config_wizard.done:"
 msgid "The configuration is done."
-msgstr "La configuracion ha terminado."
+msgstr "La configuracion se ha realizado."
 
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
@@ -3744,12 +3785,12 @@ msgid ""
 "You will be able to configure your installation depending on the modules you"
 " have installed."
 msgstr ""
-"Usted será capaz de configurar la instalación en función de los módulos que "
-"ha instalado."
+"Usted será capaz de configurar su instalación dependiendo de los módulos que"
+" ha instalado."
 
 msgctxt "view:ir.module.module.config_wizard.item:"
 msgid "Config Wizard Items"
-msgstr "Asistentes de Configuración"
+msgstr "Elementos del Asistente de Configuración"
 
 msgctxt "view:ir.module.module.config_wizard.other:"
 msgid "Configuration Wizard Next Step!"
@@ -3765,15 +3806,15 @@ msgstr "Dependencia"
 
 msgctxt "view:ir.module.module.install_upgrade.done:"
 msgid "System Upgrade Done"
-msgstr "Actualización del Sistema Finalizada"
+msgstr "Actualización del Sistema Realizada"
 
 msgctxt "view:ir.module.module.install_upgrade.done:"
 msgid "The modules have been upgraded / installed !"
-msgstr "Los módulos se han actualizado / instalado"
+msgstr "¡Los módulos se han actualizado / instalado!"
 
 msgctxt "view:ir.module.module.install_upgrade.start:"
 msgid "Note that this operation may take a few minutes."
-msgstr "Esta operación puede demorar varios minutos."
+msgstr "Tenga en cuenta que esta operación puede demorar varios minutos."
 
 msgctxt "view:ir.module.module.install_upgrade.start:"
 msgid "System Upgrade"
@@ -3836,7 +3877,7 @@ msgstr "Reglas de Registros"
 
 msgctxt "view:ir.rule.group:"
 msgid "The rule is satisfied if at least one test is True"
-msgstr "La regla se satisface si por lo menos una condición es verdadera"
+msgstr "La regla se satisface si al menos una condición es verdadera"
 
 msgctxt "view:ir.rule:"
 msgid "Test"
@@ -3860,7 +3901,7 @@ msgstr "Día:"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Legend (for prefix, suffix)"
-msgstr "Leyenda (variables para usar en prefijo y/o sufijo)"
+msgstr "Leyenda (para prefijo y/o sufijo)"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Month:"
@@ -3900,7 +3941,7 @@ msgstr "Incremental"
 
 msgctxt "view:ir.sequence:"
 msgid "Legend (Placeholders for prefix, suffix)"
-msgstr "Leyenda (variables para usar en prefijo y/o sufijo)"
+msgstr "Leyenda (variables para utilizar en prefijo y/o sufijo)"
 
 msgctxt "view:ir.sequence:"
 msgid "Month:"
@@ -3912,7 +3953,7 @@ msgstr "Secuencias"
 
 msgctxt "view:ir.sequence:"
 msgid "Timestamp"
-msgstr "Marca de tiempo"
+msgstr "Fecha-hora"
 
 msgctxt "view:ir.sequence:"
 msgid "Year:"
@@ -3924,7 +3965,7 @@ msgstr "Limpiar Traducciones"
 
 msgctxt "view:ir.translation.clean.start:"
 msgid "Clean Translations?"
-msgstr "Limpiar Traducciones?"
+msgstr "¿Limpiar las Traducciones?"
 
 msgctxt "view:ir.translation.clean.succeed:"
 msgid "Clean Translations"
@@ -3944,19 +3985,19 @@ msgstr "Exportar Traducción"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Set Translations"
-msgstr "Establecer Traducción"
+msgstr "Establecer Traducciones"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Synchronize Translations?"
-msgstr "Sincronizar Traducciones?"
+msgstr "¿Sincronizar las Traducciones?"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Succeed!"
-msgstr "Realizado Exitosamente!"
+msgstr "¡Realizado Exitosamente!"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Translations"
-msgstr "Establecer Traducción"
+msgstr "Establecer Traducciones"
 
 msgctxt "view:ir.translation.update.start:"
 msgid "Synchronize Translations"
@@ -4016,15 +4057,15 @@ msgstr "Estado de Vista de Arbol"
 
 msgctxt "view:ir.ui.view_tree_state:"
 msgid "Views Tree State"
-msgstr "Estado de Vista de Arbol"
+msgstr "Estado de la Vista de Arbol"
 
 msgctxt "view:ir.ui.view_tree_width:"
 msgid "View Tree Width"
-msgstr "Ancho de Columna"
+msgstr "Ancho de la Vista de Arbol"
 
 msgctxt "view:ir.ui.view_tree_width:"
 msgid "Views Tree Width"
-msgstr "Anchos de Columna"
+msgstr "Ancho de la Vista de Arbol"
 
 msgctxt "wizard_button:ir.model.print_model_graph,start,end:"
 msgid "Cancel"
@@ -4064,7 +4105,7 @@ msgstr "Cancelar"
 
 msgctxt "wizard_button:ir.module.module.install_upgrade,start,upgrade:"
 msgid "Start Upgrade"
-msgstr "Iniciar Mejora"
+msgstr "Iniciar Actualización"
 
 msgctxt "wizard_button:ir.translation.clean,start,clean:"
 msgid "Clean"
diff --git a/trytond/ir/locale/es_ES.po b/trytond/ir/locale/es_ES.po
index bdb021d..987d042 100644
--- a/trytond/ir/locale/es_ES.po
+++ b/trytond/ir/locale/es_ES.po
@@ -25,7 +25,7 @@ msgstr "No está autorizado para eliminar este registro."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 "El número de dígitos \"%(digits)s\" del campo \"%(field)s\" de \"%(value)s\""
 " es superior a su límite."
@@ -43,8 +43,8 @@ msgid ""
 "Could not delete the records because they are used on field \"%(field)s\" of"
 " \"%(model)s\"."
 msgstr ""
-"No se pueden eliminar los registros porqué se utilizan en el campo "
-"\"%(field)s\" del \"%(model)s\". "
+"No se pueden eliminar los registros porque se utilizan en el campo "
+"\"%(field)s\" de \"%(model)s\". "
 
 msgctxt "error:foreign_model_missing:"
 msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
@@ -291,11 +291,11 @@ msgid ""
 " was configured as ancestor of itself."
 msgstr ""
 "Error de recursividad: El registro \"%(rec_name)s\" con el padre "
-"\"%(parent_rec_name)s\" se configuró como padre de si mismo."
+"\"%(parent_rec_name)s\" se ha configurado como padre de si mismo."
 
 msgctxt "error:reference_syntax_error:"
 msgid "Syntax error for reference %r in %s"
-msgstr "Error de sintaxis para la referencia %r en %s."
+msgstr "Error de sintaxis para la referencia %r de %s."
 
 msgctxt "error:relation_not_found:"
 msgid "Relation not found: %r in %s"
@@ -363,7 +363,7 @@ msgstr "No está autorizado para modificar este registro."
 
 msgctxt "error:xml_id_syntax_error:"
 msgid "Syntax error for XML id %r in %s"
-msgstr "Error de sintaxis para el XML id %r en %s."
+msgstr "Error de sintaxis para el XML id %r de %s."
 
 msgctxt "error:xml_record_desc:"
 msgid "This record is part of the base configuration."
@@ -585,6 +585,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Activo"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Fecha creación"
@@ -891,7 +895,7 @@ msgstr "Ventana"
 
 msgctxt "field:ir.action.wizard,wiz_name:"
 msgid "Wizard name"
-msgstr "Nombre asistente"
+msgstr "Nombre del asistente"
 
 msgctxt "field:ir.action.wizard,write_date:"
 msgid "Write Date"
@@ -1263,7 +1267,7 @@ msgstr "Información"
 
 msgctxt "field:ir.model,model:"
 msgid "Model Name"
-msgstr "Nombre modelo"
+msgstr "Nombre del modelo"
 
 msgctxt "field:ir.model,module:"
 msgid "Module"
@@ -1381,14 +1385,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Usuario creación"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Fecha de inicio"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Fecha de actualización"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID del recurso"
@@ -1397,6 +1393,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Identificador en el sistema de archivos"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Valores en sistema de ficheros"
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1413,6 +1413,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "No actualizar"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Sin sincronizar"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Nombre"
@@ -2103,7 +2107,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.translation,name:"
 msgid "Field Name"
-msgstr "Nombre campo"
+msgstr "Nombre del campo"
 
 msgctxt "field:ir.translation,overriding_module:"
 msgid "Overriding Module"
@@ -2337,6 +2341,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Acción"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Acciones de teclado"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Activo"
@@ -2455,7 +2463,7 @@ msgstr "Dominio"
 
 msgctxt "field:ir.ui.view,field_childs:"
 msgid "Children Field"
-msgstr "Campo de los hijos"
+msgstr "Campo hijos"
 
 msgctxt "field:ir.ui.view,id:"
 msgid "ID"
@@ -2543,7 +2551,7 @@ msgstr "Usuario modificación"
 
 msgctxt "field:ir.ui.view_tree_state,child_name:"
 msgid "Child Name"
-msgstr "Nombre hijo"
+msgstr "Nombre del hijo"
 
 msgctxt "field:ir.ui.view_tree_state,create_date:"
 msgid "Create Date"
@@ -2821,6 +2829,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permiso de los campos"
@@ -2933,6 +2945,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Dominio acción de ventana"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Todos"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Sin sincronizar"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Acción vista de ventana"
@@ -3005,6 +3027,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Alemán"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Español (Ecuador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Inglés"
@@ -3051,7 +3077,7 @@ msgstr "Botón modelo"
 
 msgctxt "model:ir.model.data,name:"
 msgid "Model data"
-msgstr "Datos modelo"
+msgstr "Datos del modelo"
 
 msgctxt "model:ir.model.field,name:"
 msgid "Model field"
@@ -3249,6 +3275,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Datos"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Permisos campos"
@@ -3705,6 +3735,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Botones"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Datos del modelo"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Sincroniza"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Permiso del campo"
diff --git a/trytond/ir/locale/fr_FR.po b/trytond/ir/locale/fr_FR.po
index 8cc677d..44e6984 100644
--- a/trytond/ir/locale/fr_FR.po
+++ b/trytond/ir/locale/fr_FR.po
@@ -25,9 +25,9 @@ msgstr "Vous n'êtes pas autorisé à supprimer cet enregistrement"
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
-"Le nombre de décimales \"%(digits)s\" du champ \"%(field)s\" dépasse sa "
+"Le nombre de décimales « %(digits)s » du champ « %(field)s » dépasse sa "
 "limite."
 
 msgctxt "error:domain_validation_record:"
@@ -35,7 +35,7 @@ msgid ""
 "The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
 " to its domain."
 msgstr ""
-"La valeur du champ \"%(field)s\" sur \"%(model)s\" n'est pas valide selon "
+"La valeur du champ « %(field)s » sur « %(model)s » n'est pas valide selon "
 "son domaine."
 
 msgctxt "error:foreign_model_exist:"
@@ -44,42 +44,44 @@ msgid ""
 " \"%(model)s\"."
 msgstr ""
 "Impossible de supprimer les enregistrements car ils sont utilisés dans le "
-"champ \"%(field)s\" de \"%(model)s\"."
+"champ « %(field)s » de « %(model)s »."
 
 msgctxt "error:foreign_model_missing:"
 msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
-msgstr "La valeur \"%(value)s\" du champ \"%(field)s\" sur \"%(model)s\" n'existe pas."
+msgstr ""
+"La valeur « %(value)s » du champ « %(field)s » sur « %(model)s » n'existe "
+"pas."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
-msgstr "Le contexte \"%(context)s\" de l'action \"%(action)s\" est invalide."
+msgstr "Le contexte « %(context)s » de l'action « %(action)s » est invalide."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid domain or search criteria \"%(domain)s\" on action \"%(action)s\"."
 msgstr ""
-"Le critère de recherche ou le domaine \"%(domain)s\" de l'action "
-"\"%(action)s\" est invalide."
+"Le critère de recherche ou le domaine « %(domain)s » de l'action « "
+"%(action)s » est invalide."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid view \"%(view)s\" for action \"%(action)s\"."
-msgstr "La vue \"%(view)s\" de l'action \"%(action)s\" est invalide."
+msgstr "La vue « %(view)s » de l'action « %(action)s » est invalide."
 
 msgctxt "error:ir.action.keyword:"
 msgid "Wrong wizard model in keyword action \"%s\"."
-msgstr "Modèle d'assistant incorrect pour le mot-clé d'action \"%s\"."
+msgstr "Modèle d'assistant incorrect pour le mot-clé d'action « %s »."
 
 msgctxt "error:ir.action.report:"
 msgid "Invalid email definition on report \"%s\"."
-msgstr "Définition de mail incorrecte sur le rapport \"%s\"."
+msgstr "Définition de mail incorrecte sur le rapport « %s »."
 
 msgctxt "error:ir.action.report:"
 msgid "The internal name must be unique by module!"
-msgstr "Le nom interne doit être unique par module !"
+msgstr "Le nom interne doit être unique par module !"
 
 msgctxt "error:ir.attachment:"
 msgid "The  names of attachments must be unique by resource!"
 msgstr ""
-"Le nom des pièces jointes doivent être unique pour une même ressource !"
+"Le nom des pièces jointes doivent être unique pour une même ressource !"
 
 msgctxt "error:ir.cron:"
 msgid "Scheduled action failed"
@@ -93,7 +95,7 @@ msgid ""
 "\n"
 "%s\n"
 msgstr ""
-"L'action suivante n'a pas pu s'exécuter correctement: \"%s\"\n"
+"L'action suivante n'a pas pu s'exécuter correctement: « %s »\n"
 "%s\n"
 " Retraçage:\n"
 "\n"
@@ -105,11 +107,13 @@ msgstr "Le langage par défaut ne peut pas être supprimé"
 
 msgctxt "error:ir.lang:"
 msgid "Invalid date format \"%(format)s\" on \"%(language)s\" language."
-msgstr "Le format de date \"%(format)s\" du langage \"%(language)s\" est invalide."
+msgstr ""
+"Le format de date « %(format)s » du langage « %(language)s » est invalide."
 
 msgctxt "error:ir.lang:"
 msgid "Invalid grouping \"%(grouping)s\" on \"%(language)s\" language."
-msgstr "Le groupement \"%(grouping)s\" de la langye \"%(language)s\" est invalide."
+msgstr ""
+"Le groupement « %(grouping)s » de la langye « %(language)s » est invalide."
 
 msgctxt "error:ir.lang:"
 msgid "The default language must be translatable."
@@ -119,23 +123,23 @@ msgctxt "error:ir.lang:"
 msgid "decimal_point and thousands_sep must be different!"
 msgstr ""
 "Le séparateur de décimale et le séparateur des centaines doivent être "
-"différents !"
+"différents !"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not create this kind of document! (%s)"
-msgstr "Vous ne pouvez pas créer ce type de document ! (%s)"
+msgstr "Vous ne pouvez pas créer ce type de document ! (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not delete this document! (%s)"
-msgstr "Vous ne pouvez pas supprimer ce document ! (%s)"
+msgstr "Vous ne pouvez pas supprimer ce document ! (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not read this document! (%s)"
-msgstr "Vous ne pouvez pas lire ce document ! (%s)"
+msgstr "Vous ne pouvez pas lire ce document ! (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not write in this document! (%s)"
-msgstr "Vous ne pouvez pas écrire dans ce document ! (%s)"
+msgstr "Vous ne pouvez pas écrire dans ce document ! (%s)"
 
 msgctxt "error:ir.model.button:"
 msgid "The button name in model must be unique!"
@@ -143,45 +147,45 @@ msgstr "Le nom du bouton dans le modèle doit être unique"
 
 msgctxt "error:ir.model.data:"
 msgid "The triple (fs_id, module, model) must be unique!"
-msgstr "Le triplet (fs_id, module, model) doit être unique !"
+msgstr "Le triplet (fs_id, module, model) doit être unique !"
 
 msgctxt "error:ir.model.field.access:"
 msgid "You can not read the field! (%s.%s)"
-msgstr "Vous ne pouvez lire le champs (%s.%s) !"
+msgstr "Vous ne pouvez lire le champs ! (%s.%s)"
 
 msgctxt "error:ir.model.field.access:"
 msgid "You can not write on the field! (%s.%s)"
-msgstr "Vous ne pouvez écrire sur le champs (%s.%s) !"
+msgstr "Vous ne pouvez écrire sur le champs ! (%s.%s)"
 
 msgctxt "error:ir.model.field:"
 msgid "Model Field name \"%s\" is not a valid python identifier."
-msgstr "Le nom de champ modèle \"%s\" n'est pas un identifiant Python valide."
+msgstr "Le nom de champ modèle « %s » n'est pas un identifiant Python valide."
 
 msgctxt "error:ir.model.field:"
 msgid "The field name in model must be unique!"
-msgstr "Le nom du champ doit être unique sur le modèle !"
+msgstr "Le nom du champ doit être unique sur le modèle !"
 
 msgctxt "error:ir.model:"
 msgid "Module name \"%s\" is not a valid python identifier."
-msgstr "Le nom de module \"%s\" n'est pas un identifiant Python valide."
+msgstr "Le nom de module « %s » n'est pas un identifiant Python valide."
 
 msgctxt "error:ir.model:"
 msgid "The model must be unique!"
-msgstr "Le modèle doit être unique !"
+msgstr "Le modèle doit être unique !"
 
 msgctxt "error:ir.module.module.dependency:"
 msgid "Dependency must be unique by module!"
-msgstr "Une dépendance doit être unique par module !"
+msgstr "Une dépendance doit être unique par module !"
 
 msgctxt "error:ir.module.module:"
 msgid "Missing dependencies %s for module \"%s\""
-msgstr "Dépendences %s manquante pour le module \"%s\""
+msgstr "Dépendences %s manquante pour le module « %s »"
 
 msgctxt "error:ir.module.module:"
 msgid "The modules you are trying to uninstall depends on installed modules:"
 msgstr ""
 "Les modules que vous essayez de dés-installer dépendent de modules installés"
-" :"
+" :"
 
 msgctxt "error:ir.module.module:"
 msgid "The name of the module must be unique!"
@@ -194,25 +198,27 @@ msgstr ""
 
 msgctxt "error:ir.rule.group:"
 msgid "Global and Default are mutually exclusive!"
-msgstr "Global et Défaut sont exclusifs !"
+msgstr "Global et Défaut sont exclusifs !"
 
 msgctxt "error:ir.rule:"
 msgid "Invalid domain in rule \"%s\"."
-msgstr "Domaine invalide sur la règle \"%s\"."
+msgstr "Domaine invalide sur la règle « %s »."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Invalid prefix \"%(prefix)s\" on sequence \"%(sequence)s\"."
-msgstr "Le préfixe \"%(prefix)s\" de la séquence \"%(sequence)s\" est invalide"
+msgstr ""
+"Le préfixe « %(prefix)s » de la séquence « %(sequence)s » est invalide"
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Invalid suffix \"%(suffix)s\" on sequence \"%(sequence)s\"."
-msgstr "Le suffixe \"%(suffix)s\" de la séquence \"%(sequence)s\" est invalide"
+msgstr ""
+"Le suffixe « %(suffix)s » de la séquence « %(sequence)s » est invalide"
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
 msgstr ""
-"La dernière estampille ne peut pas être dans le futur pour la séquence "
-"\"%s\""
+"La dernière estampille ne peut pas être dans le futur pour la séquence « %s "
+"»."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Missing sequence."
@@ -224,17 +230,19 @@ msgstr "L'estampille doit être plus grande que 0"
 
 msgctxt "error:ir.sequence:"
 msgid "Invalid prefix \"%(prefix)s\" on sequence \"%(sequence)s\"."
-msgstr "Le préfixe \"%(prefix)s\" de la séquence \"%(sequence)s\" est invalide"
+msgstr ""
+"Le préfixe « %(prefix)s » de la séquence « %(sequence)s » est invalide."
 
 msgctxt "error:ir.sequence:"
 msgid "Invalid suffix \"%(suffix)s\" on sequence \"%(sequence)s\"."
-msgstr "Le suffixe \"%(suffix)s\" de la séquence \"%(sequence)s\" est invalide"
+msgstr ""
+"Le suffixe « %(suffix)s » de la séquence « %(sequence)s » est invalide."
 
 msgctxt "error:ir.sequence:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
 msgstr ""
-"La dernière estampille ne peut pas être dans le futur pour la séquence "
-"\"%s\""
+"La dernière estampille ne peut pas être dans le futur pour la séquence « %s "
+"»."
 
 msgctxt "error:ir.sequence:"
 msgid "Missing sequence."
@@ -258,30 +266,30 @@ msgstr ""
 
 msgctxt "error:ir.trigger:"
 msgid "\"On Time\" and others are mutually exclusive!"
-msgstr "\"À temps\" exclus les autres type de déclencheurs !"
+msgstr "« À temps » exclus les autres type de déclencheurs !"
 
 msgctxt "error:ir.trigger:"
 msgid ""
 "Condition \"%(condition)s\" is not a valid python expression on trigger "
 "\"%(trigger)s\"."
 msgstr ""
-"la condition \"%(condition)s\" du déclencheur \"%(trigger)s\" n'est pas une "
+"la condition « %(condition)s » du déclencheur « %(trigger)s » n'est pas une "
 "expression python valide."
 
 msgctxt "error:ir.ui.menu:"
 msgid "\"%s\" is not a valid menu name because it is not allowed to contain \" / \"."
-msgstr "\"%s\" n'est pas un nom de menu valide car il contient \"/\"."
+msgstr "« %s » n'est pas un nom de menu valide car il contient « / »."
 
 msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
-msgstr "XML invalide sur la vue \"%s\"."
+msgstr "XML invalide sur la vue « %s »."
 
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
 "(Document type: %s)"
 msgstr ""
-"Vous essayez de lire un enregistrement qui n'existe plus !\n"
+"Vous essayez de lire un enregistrement qui n'existe plus !\n"
 "(Type du document: %s)"
 
 msgctxt "error:read_error:"
@@ -289,7 +297,7 @@ msgid ""
 "You try to read records that don't exist anymore.\n"
 "(Document type: %s)"
 msgstr ""
-"Vous essayez de lire des enregistrements qui n'existent plus !\n"
+"Vous essayez de lire des enregistrements qui n'existent plus.\n"
 "(Type du document: %s)"
 
 msgctxt "error:recursion_error:"
@@ -297,8 +305,8 @@ msgid ""
 "Recursion error: Record \"%(rec_name)s\" with parent \"%(parent_rec_name)s\""
 " was configured as ancestor of itself."
 msgstr ""
-"Erreur de récursion: L'enregistrement \"%(rec_name)s\" avec le parent "
-"\"%(parent_rec_name)s\" a été configuré comme ancêtre de lui même."
+"Erreur de récursion: L'enregistrement « %(rec_name)s » avec le parent « "
+"%(parent_rec_name)s » a été configuré comme ancêtre de lui même."
 
 msgctxt "error:reference_syntax_error:"
 msgid "Syntax error for reference %r in %s"
@@ -306,50 +314,54 @@ msgstr "Erreur de syntaxe pour la référence %r de %r"
 
 msgctxt "error:relation_not_found:"
 msgid "Relation not found: %r in %s"
-msgstr "Relation non trouvée : %r dans %r"
+msgstr "Relation non trouvée : %r dans %r"
 
 msgctxt "error:required_field:"
 msgid "The field \"%(field)s\" on \"%(model)s\" is required."
-msgstr "Le champ \"%(field)s\" sur \"%(model)s\" est requis."
+msgstr "Le champ « %(field)s » sur « %(model)s » est requis."
 
 msgctxt "error:required_validation_record:"
 msgid "The field \"%(field)s\" on \"%(model)s\" is required."
-msgstr "Le champ \"%(field)s\" sur \"%(model)s\" est requis."
+msgstr "Le champ « %(field)s » sur « %(model)s » est requis."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
-msgstr "Fonction de recherche absente pour le champ \"%s\"."
+msgstr "Fonction de recherche absente pour le champ « %s »."
 
 msgctxt "error:selection_validation_record:"
 msgid ""
 "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
 "the selection."
 msgstr ""
-"La valeur \"%(value)s\" du champ \"%(field)s\" sur \"%(model)s\" n'est pas "
+"La valeur « %(value)s » du champ « %(field)s » sur « %(model)s » n'est pas "
 "dans la sélection."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
-msgstr "Valeur absente de la sélection pour le champ \"%s\"."
+msgstr "Valeur absente de la sélection pour le champ « %s »."
 
 msgctxt "error:size_validation_record:"
 msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
-msgstr "La taille \"%(size)s\" du champ \"%(field)s\" sur \"%(model)s\" est trop longue."
+msgstr ""
+"La taille « %(size)s » du champ « %(field)s » sur « %(model)s » est trop "
+"longue."
 
 msgctxt "error:time_format_validation_record:"
 msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
-msgstr "L'heure \"%(value)s\" du champ \"%(field)s\" sur \"%(model)s\" n'est pas valide."
+msgstr ""
+"L'heure « %(value)s » du champ « %(field)s » sur « %(model)s » n'est pas "
+"valide."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
-msgstr "Trop de relations pour : %r dans %s"
+msgstr "Trop de relations pour : %r dans %s"
 
 msgctxt "error:write_error:"
 msgid ""
 "You try to write on records that don't exist anymore!\n"
 "(Document type: %s)"
 msgstr ""
-"Vous essayez d'écrire sur un enregistrement qui n'existe plus !\n"
+"Vous essayez d'écrire sur un enregistrement qui n'existe plus !\n"
 "(Type du document: %s)"
 
 msgctxt "error:write_error:"
@@ -588,6 +600,10 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Action"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Actif"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Date de création"
@@ -682,7 +698,7 @@ msgstr "Imprimer directement"
 
 msgctxt "field:ir.action.report,email:"
 msgid "Email"
-msgstr "E-mail"
+msgstr "Email"
 
 msgctxt "field:ir.action.report,extension:"
 msgid "Extension"
@@ -850,7 +866,7 @@ msgstr "Créé par"
 
 msgctxt "field:ir.action.wizard,email:"
 msgid "Email"
-msgstr "E-mail"
+msgstr "Email"
 
 msgctxt "field:ir.action.wizard,groups:"
 msgid "Groups"
@@ -1384,14 +1400,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Créé par"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Date d'initialisation"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Date de mis à jour"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "ID de la ressource"
@@ -1400,6 +1408,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Identifiant sur le système de fichier"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Valeurs sur le système de fichier"
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1416,6 +1428,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "Pas de mise à jour"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Désynchronisé"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Nom"
@@ -2340,6 +2356,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Action"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Mots-clés d'action"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Actif"
@@ -2649,7 +2669,7 @@ msgid ""
 "Python dictonary where keys define \"to\" \"cc\" \"subject\"\n"
 "Example: {'to': 'test at example.com', 'cc': 'user at example.com'}"
 msgstr ""
-"Dictionnaire Python définissant \"to\", \"cc\" et \"subject\".\n"
+"Dictionnaire Python définissant « to », « cc » et « subject ».\n"
 " Exemple: {'to': 'test at example.com', 'cc': 'user at example.com'}"
 
 msgctxt "help:ir.action.report,extension:"
@@ -2714,7 +2734,7 @@ msgstr ""
 
 msgctxt "help:ir.rule,domain:"
 msgid "Domain is evaluated with \"user\" as the current user"
-msgstr "Le domaine est évalué avec \"user\" comme utilisateur courant"
+msgstr "Le domaine est évalué avec « user » comme utilisateur courant"
 
 msgctxt "help:ir.rule.group,default_p:"
 msgid "Add this rule to all users by default"
@@ -2736,7 +2756,7 @@ msgid ""
 "A Python statement evaluated with record represented by \"self\"\n"
 "It triggers the action if true."
 msgstr ""
-"Une expression Python ou l'enregistrement est représenté par \"self\".\n"
+"Une expression Python ou l'enregistrement est représenté par « self ».\n"
 "Déclenche une action si vrai."
 
 msgctxt "help:ir.trigger,limit_number:"
@@ -2744,7 +2764,7 @@ msgid ""
 "Limit the number of call to \"Action Function\" by records.\n"
 "0 for no limit."
 msgstr ""
-"Limite le nombre d'appel aux \"Fonction action\" par enregistrement.\n"
+"Limite le nombre d'appel aux « Fonction action » par enregistrement.\n"
 "0 signifie pas de limite."
 
 msgctxt "help:ir.trigger,minimum_delay:"
@@ -2752,7 +2772,7 @@ msgid ""
 "Set a minimum delay in minutes between call to \"Action Function\" for the same record.\n"
 "0 for no delay."
 msgstr ""
-"Défini un délais minimum en minutes entre les appels aux \"Fonctions actions\" sur un même modèle.\n"
+"Défini un délais minimum en minutes entre les appels aux « Fonctions actions » sur un même modèle.\n"
 "0 signifie pas de délais."
 
 msgctxt "help:ir.ui.view_search,domain:"
@@ -2823,6 +2843,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Boutons"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Données"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Droits d'accès"
@@ -2935,6 +2959,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Action act window domain"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Toutes"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Désynchronisé"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Action ouvrir fenêtre vue"
@@ -3007,6 +3041,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Allemand"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Espagnol (Équateur)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Anglais"
@@ -3251,6 +3289,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Boutons"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Données"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Droits d'accès au champs"
@@ -3707,6 +3749,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Boutons"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Données de modèle"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Synchroniser"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Droits d'accès au champs"
@@ -3737,7 +3787,7 @@ msgstr "La configuration est faite."
 
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
-msgstr "Bienvenu dans l'assistant de configuration de module !"
+msgstr "Bienvenu dans l'assistant de configuration de module !"
 
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid ""
@@ -3753,7 +3803,7 @@ msgstr "Élements de l'assistant de configuration"
 
 msgctxt "view:ir.module.module.config_wizard.other:"
 msgid "Configuration Wizard Next Step!"
-msgstr "Prochaine étape du wizard de configuration !"
+msgstr "Prochaine étape du wizard de configuration !"
 
 msgctxt "view:ir.module.module.dependency:"
 msgid "Dependencies"
@@ -3769,7 +3819,7 @@ msgstr "Mise à jour du système terminée"
 
 msgctxt "view:ir.module.module.install_upgrade.done:"
 msgid "The modules have been upgraded / installed !"
-msgstr "Les modules ont été mis à jour / installés !"
+msgstr "Les modules ont été mis à jour / installés !"
 
 msgctxt "view:ir.module.module.install_upgrade.start:"
 msgid "Note that this operation may take a few minutes."
@@ -3864,7 +3914,7 @@ msgstr "Légende (préfixe et suffixe)"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Month:"
-msgstr "Mois :"
+msgstr "Mois :"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Sequences Strict"
@@ -3872,7 +3922,7 @@ msgstr "Séquence stricte"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Year:"
-msgstr "Année :"
+msgstr "Année :"
 
 msgctxt "view:ir.sequence.type:"
 msgid "Sequence Type"
@@ -3904,7 +3954,7 @@ msgstr "Légende (charactère pour le préfixe, le suffixe)"
 
 msgctxt "view:ir.sequence:"
 msgid "Month:"
-msgstr "Mois :"
+msgstr "Mois :"
 
 msgctxt "view:ir.sequence:"
 msgid "Sequences"
@@ -3916,7 +3966,7 @@ msgstr "Estampille"
 
 msgctxt "view:ir.sequence:"
 msgid "Year:"
-msgstr "Année :"
+msgstr "Année :"
 
 msgctxt "view:ir.translation.clean.start:"
 msgid "Clean Translations"
@@ -3924,7 +3974,7 @@ msgstr "Nettoyer les traductions"
 
 msgctxt "view:ir.translation.clean.start:"
 msgid "Clean Translations?"
-msgstr "Nettoyer les traductions ?"
+msgstr "Nettoyer les traductions ?"
 
 msgctxt "view:ir.translation.clean.succeed:"
 msgid "Clean Translations"
@@ -3932,7 +3982,7 @@ msgstr "Nettoyer les traductions"
 
 msgctxt "view:ir.translation.clean.succeed:"
 msgid "Clean Translations Succeed!"
-msgstr "Nettoyage des traductions réussi !"
+msgstr "Nettoyage des traductions réussi !"
 
 msgctxt "view:ir.translation.export.result:"
 msgid "Export Translation"
@@ -3948,11 +3998,11 @@ msgstr "Définir les traductions"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Synchronize Translations?"
-msgstr "Synchroniser les traductions ?"
+msgstr "Synchroniser les traductions ?"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Succeed!"
-msgstr "Mise à jour réussie !"
+msgstr "Mise à jour réussie !"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Translations"
diff --git a/trytond/ir/locale/nl_NL.po b/trytond/ir/locale/nl_NL.po
index daa70aa..a34a383 100644
--- a/trytond/ir/locale/nl_NL.po
+++ b/trytond/ir/locale/nl_NL.po
@@ -23,7 +23,7 @@ msgstr "Het is u niet toegestaan dit item te verwijderen."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 
 msgctxt "error:domain_validation_record:"
@@ -566,6 +566,11 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Actie"
 
+#, fuzzy
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Actief"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr ""
@@ -1392,14 +1397,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr ""
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Datum aanmaken"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Datum bijgewerkt"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "Middel ID"
@@ -1408,6 +1405,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Kenmerk voor bestandssysteem"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr ""
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr ""
@@ -1424,6 +1425,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "Niet bij te werken"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Naam"
@@ -2371,6 +2376,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Actie"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr ""
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Actief"
@@ -2861,6 +2870,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr ""
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr ""
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr ""
@@ -2973,6 +2986,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr ""
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr ""
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Actie uitvoerend schermaanzicht"
@@ -3046,6 +3069,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Duits"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr ""
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Engels"
@@ -3296,6 +3323,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr ""
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr ""
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr ""
@@ -3758,6 +3789,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr ""
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr ""
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr ""
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr ""
diff --git a/trytond/ir/locale/ru_RU.po b/trytond/ir/locale/ru_RU.po
index 6af0951..687f877 100644
--- a/trytond/ir/locale/ru_RU.po
+++ b/trytond/ir/locale/ru_RU.po
@@ -25,7 +25,7 @@ msgstr "Вы не можете удалить эту запись."
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 
 msgctxt "error:domain_validation_record:"
@@ -562,6 +562,11 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Действие"
 
+#, fuzzy
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Действующий"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
 msgstr "Дата создания"
@@ -1358,14 +1363,6 @@ msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
 msgstr "Создано пользователем"
 
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Дата уставки"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Дата обновления"
-
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
 msgstr "Ресурс ID"
@@ -1374,6 +1371,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Идентификатор в файловой системе"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr ""
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1390,6 +1391,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "Нет обновлений"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Наименование"
@@ -2315,6 +2320,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Действие"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr ""
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Действующий"
@@ -2808,6 +2817,11 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Кнопки"
 
+#, fuzzy
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Данные"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Доступ к полям"
@@ -2920,6 +2934,17 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Действие окна домен"
 
+#, fuzzy
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Все"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr ""
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Действие окна вида"
@@ -2992,6 +3017,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Немецкий"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr ""
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Английский"
@@ -3236,6 +3265,11 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Кнопки"
 
+#, fuzzy
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Данные"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Доступ к полям"
@@ -3693,6 +3727,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Кнопки"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr ""
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr ""
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Доступ к полю"
diff --git a/trytond/ir/locale/sl_SI.po b/trytond/ir/locale/sl_SI.po
index 59694de..8b73500 100644
--- a/trytond/ir/locale/sl_SI.po
+++ b/trytond/ir/locale/sl_SI.po
@@ -25,7 +25,7 @@ msgstr "Nimate dovoljenja za brisanje tega zapisa"
 msgctxt "error:digits_validation_record:"
 msgid ""
 "The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
-"exceeds it's limit."
+"exceeds its limit."
 msgstr ""
 "Število decimalk \"%(digits)s\" polja \"%(field)s\" pri \"%(model)s\" je "
 "preseženo."
@@ -120,7 +120,7 @@ msgstr "decimal_point in thousands_sep morata biti različna."
 
 msgctxt "error:ir.model.access:"
 msgid "You can not create this kind of document! (%s)"
-msgstr "Te vrste dokumenta ni možno ustvariti. (%s)"
+msgstr "Te vrste dokumenta ni možno izdelati. (%s)"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not delete this document! (%s)"
@@ -369,11 +369,11 @@ msgstr "Aktivno"
 
 msgctxt "field:ir.action,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action,groups:"
 msgid "Groups"
@@ -437,11 +437,11 @@ msgstr "Vrednost konteksta"
 
 msgctxt "field:ir.action.act_window,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.act_window,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.act_window,domain:"
 msgid "Domain Value"
@@ -541,11 +541,11 @@ msgstr "Aktivno"
 
 msgctxt "field:ir.action.act_window.domain,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.act_window.domain,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.act_window.domain,domain:"
 msgid "Domain"
@@ -579,13 +579,17 @@ msgctxt "field:ir.action.act_window.view,act_window:"
 msgid "Action"
 msgstr "Ukrep"
 
+msgctxt "field:ir.action.act_window.view,active:"
+msgid "Active"
+msgstr "Aktivno"
+
 msgctxt "field:ir.action.act_window.view,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.act_window.view,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.act_window.view,id:"
 msgid "ID"
@@ -617,11 +621,11 @@ msgstr "Ukrep"
 
 msgctxt "field:ir.action.keyword,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.keyword,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.keyword,groups:"
 msgid "Groups"
@@ -661,11 +665,11 @@ msgstr "Aktivno"
 
 msgctxt "field:ir.action.report,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.report,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.report,direct_print:"
 msgid "Direct Print"
@@ -773,11 +777,11 @@ msgstr "Aktivno"
 
 msgctxt "field:ir.action.url,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.url,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.url,groups:"
 msgid "Groups"
@@ -833,11 +837,11 @@ msgstr "Aktivno"
 
 msgctxt "field:ir.action.wizard,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action.wizard,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action.wizard,email:"
 msgid "Email"
@@ -901,11 +905,11 @@ msgstr "Prekrivanje"
 
 msgctxt "field:ir.attachment,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.attachment,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.attachment,data:"
 msgid "Data"
@@ -969,11 +973,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.cache,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.cache,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.cache,id:"
 msgid "ID"
@@ -1001,11 +1005,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.configuration,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.configuration,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.configuration,id:"
 msgid "ID"
@@ -1037,11 +1041,11 @@ msgstr "Argumenti"
 
 msgctxt "field:ir.cron,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.cron,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.cron,function:"
 msgid "Function"
@@ -1105,11 +1109,11 @@ msgstr "ID"
 
 msgctxt "field:ir.export,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.export,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.export,export_fields:"
 msgid "Fields"
@@ -1141,11 +1145,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.export.line,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.export.line,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.export.line,export:"
 msgid "Export"
@@ -1181,11 +1185,11 @@ msgstr "Šifra"
 
 msgctxt "field:ir.lang,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.lang,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.lang,date:"
 msgid "Date"
@@ -1233,11 +1237,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model,fields:"
 msgid "Fields"
@@ -1281,11 +1285,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model.access,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.access,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.access,description:"
 msgid "Description"
@@ -1305,7 +1309,7 @@ msgstr "Model"
 
 msgctxt "field:ir.model.access,perm_create:"
 msgid "Create Access"
-msgstr "Ustvarjanje"
+msgstr "Izdelava"
 
 msgctxt "field:ir.model.access,perm_delete:"
 msgid "Delete Access"
@@ -1333,11 +1337,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model.button,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.button,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.button,groups:"
 msgid "Groups"
@@ -1369,19 +1373,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model.data,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.data,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
-
-msgctxt "field:ir.model.data,date_init:"
-msgid "Init Date"
-msgstr "Začetni datum"
-
-msgctxt "field:ir.model.data,date_update:"
-msgid "Update Date"
-msgstr "Datum posodobitve"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
@@ -1391,6 +1387,10 @@ msgctxt "field:ir.model.data,fs_id:"
 msgid "Identifier on File System"
 msgstr "Identifikator v datotečnem sistemu"
 
+msgctxt "field:ir.model.data,fs_values:"
+msgid "Values on File System"
+msgstr "Vrednosti v datotečnem sistemu"
+
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
 msgstr "ID"
@@ -1407,6 +1407,10 @@ msgctxt "field:ir.model.data,noupdate:"
 msgid "No Update"
 msgstr "Brez posodabljanja"
 
+msgctxt "field:ir.model.data,out_of_sync:"
+msgid "Out of Sync"
+msgstr "Izven sinhronizacije"
+
 msgctxt "field:ir.model.data,rec_name:"
 msgid "Name"
 msgstr "Ime"
@@ -1425,11 +1429,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model.field,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.field,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.field,field_description:"
 msgid "Field Description"
@@ -1481,11 +1485,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model.field.access,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.field.access,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.field.access,description:"
 msgid "Description"
@@ -1505,7 +1509,7 @@ msgstr "ID"
 
 msgctxt "field:ir.model.field.access,perm_create:"
 msgid "Create Access"
-msgstr "Ustvarjanje"
+msgstr "Izdelava"
 
 msgctxt "field:ir.model.field.access,perm_delete:"
 msgid "Delete Access"
@@ -1549,11 +1553,11 @@ msgstr "Podmoduli"
 
 msgctxt "field:ir.module.module,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.module.module,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.module.module,dependencies:"
 msgid "Dependencies"
@@ -1605,11 +1609,11 @@ msgstr "Ukrep"
 
 msgctxt "field:ir.module.module.config_wizard.item,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.module.module.config_wizard.item,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.module.module.config_wizard.item,id:"
 msgid "ID"
@@ -1645,11 +1649,11 @@ msgstr "Odstotek"
 
 msgctxt "field:ir.module.module.dependency,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.module.module.dependency,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.module.module.dependency,id:"
 msgid "ID"
@@ -1693,11 +1697,11 @@ msgstr "Moduli za posodobitev"
 
 msgctxt "field:ir.property,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.property,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.property,field:"
 msgid "Field"
@@ -1729,11 +1733,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.rule,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.rule,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.rule,domain:"
 msgid "Domain"
@@ -1761,11 +1765,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.rule.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.rule.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.rule.group,default_p:"
 msgid "Default"
@@ -1793,7 +1797,7 @@ msgstr "Naziv"
 
 msgctxt "field:ir.rule.group,perm_create:"
 msgid "Create Access"
-msgstr "Ustvarjanje"
+msgstr "Izdelava"
 
 msgctxt "field:ir.rule.group,perm_delete:"
 msgid "Delete Access"
@@ -1837,11 +1841,11 @@ msgstr "Šifra"
 
 msgctxt "field:ir.sequence,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.sequence,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.sequence,id:"
 msgid "ID"
@@ -1913,11 +1917,11 @@ msgstr "Šifra"
 
 msgctxt "field:ir.sequence.strict,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.sequence.strict,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.sequence.strict,id:"
 msgid "ID"
@@ -1985,11 +1989,11 @@ msgstr "Šifra"
 
 msgctxt "field:ir.sequence.type,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.sequence.type,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.sequence.type,id:"
 msgid "ID"
@@ -2013,11 +2017,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.session,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.session,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.session,id:"
 msgid "ID"
@@ -2041,11 +2045,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.session.wizard,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.session.wizard,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.session.wizard,data:"
 msgid "Data"
@@ -2069,11 +2073,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.translation,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.translation,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.translation,fuzzy:"
 msgid "Fuzzy"
@@ -2197,11 +2201,11 @@ msgstr "Pogoj"
 
 msgctxt "field:ir.trigger,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.trigger,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.trigger,id:"
 msgid "ID"
@@ -2225,7 +2229,7 @@ msgstr "Naziv"
 
 msgctxt "field:ir.trigger,on_create:"
 msgid "On Create"
-msgstr "Ob ustvarjanju"
+msgstr "Ob izdelavi"
 
 msgctxt "field:ir.trigger,on_delete:"
 msgid "On Delete"
@@ -2253,11 +2257,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.trigger.log,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.trigger.log,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.trigger.log,id:"
 msgid "ID"
@@ -2285,11 +2289,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.ui.icon,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.icon,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.icon,icon:"
 msgid "Icon"
@@ -2331,6 +2335,10 @@ msgctxt "field:ir.ui.menu,action:"
 msgid "Action"
 msgstr "Ukrep"
 
+msgctxt "field:ir.ui.menu,action_keywords:"
+msgid "Action Keywords"
+msgstr "Ključne besede ukrepov"
+
 msgctxt "field:ir.ui.menu,active:"
 msgid "Active"
 msgstr "Aktivno"
@@ -2345,11 +2353,11 @@ msgstr "Polno ime"
 
 msgctxt "field:ir.ui.menu,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.menu,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.menu,favorite:"
 msgid "Favorite"
@@ -2393,11 +2401,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.ui.menu.favorite,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.menu.favorite,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.menu.favorite,id:"
 msgid "ID"
@@ -2433,11 +2441,11 @@ msgstr "Zgradba"
 
 msgctxt "field:ir.ui.view,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.view,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.view,data:"
 msgid "Data"
@@ -2497,11 +2505,11 @@ msgstr "ID"
 
 msgctxt "field:ir.ui.view_search,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.view_search,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.view_search,domain:"
 msgid "Domain"
@@ -2541,11 +2549,11 @@ msgstr "Ime poddrevesa"
 
 msgctxt "field:ir.ui.view_tree_state,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.view_tree_state,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.view_tree_state,domain:"
 msgid "Domain"
@@ -2585,11 +2593,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.ui.view_tree_width,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.view_tree_width,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.view_tree_width,field:"
 msgid "Field"
@@ -2813,6 +2821,10 @@ msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
 msgstr "Gumbi"
 
+msgctxt "model:ir.action,name:act_model_data_form"
+msgid "Data"
+msgstr "Podatki"
+
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
 msgstr "Dostop do polj"
@@ -2925,6 +2937,16 @@ msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
 msgstr "Domena okna za ukrepe"
 
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_all"
+msgid "All"
+msgstr "Vse"
+
+msgctxt ""
+"model:ir.action.act_window.domain,name:act_model_data_form_domain_out_of_sync"
+msgid "Out of Sync"
+msgstr "Nesinhronizirano"
+
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
 msgstr "Pogled ukrepa za okna"
@@ -2997,6 +3019,10 @@ msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
 msgstr "Nemščina"
 
+msgctxt "model:ir.lang,name:lang_ec"
+msgid "Spanish (Ecuador)"
+msgstr "Španščina (Ekvador)"
+
 msgctxt "model:ir.lang,name:lang_en"
 msgid "English"
 msgstr "Angleščina"
@@ -3241,6 +3267,10 @@ msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
 msgstr "Gumbi"
 
+msgctxt "model:ir.ui.menu,name:menu_model_data_form"
+msgid "Data"
+msgstr "Podatki"
+
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
 msgstr "Dostop do polj"
@@ -3697,6 +3727,14 @@ msgctxt "view:ir.model.button:"
 msgid "Buttons"
 msgstr "Gumbi"
 
+msgctxt "view:ir.model.data:"
+msgid "Model Data"
+msgstr "Podatki modela"
+
+msgctxt "view:ir.model.data:"
+msgid "Sync"
+msgstr "Sinhronizacija"
+
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
 msgstr "Dostop do polj"
diff --git a/trytond/ir/model.py b/trytond/ir/model.py
index cb63303..53855a1 100644
--- a/trytond/ir/model.py
+++ b/trytond/ir/model.py
@@ -1,11 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import datetime
 import re
 import heapq
 from sql.aggregate import Max
 from sql.conditionals import Case
 from collections import defaultdict
+try:
+    import simplejson as json
+except ImportError:
+    import json
 
 from ..model import ModelView, ModelSQL, fields
 from ..report import Report
@@ -16,6 +19,7 @@ from ..pool import Pool
 from ..pyson import Bool, Eval
 from ..rpc import RPC
 from .. import backend
+from ..protocols.jsonrpc import JSONDecoder, JSONEncoder
 try:
     from ..tools.StringMatcher import StringMatcher
 except ImportError:
@@ -122,13 +126,12 @@ class Model(ModelSQL, ModelView):
     @classmethod
     def list_models(cls):
         'Return a list of all models names'
-        with Transaction().set_user(0):
-            models = cls.search([], order=[
-                    ('module', 'ASC'),  # Optimization assumption
-                    ('model', 'ASC'),
-                    ('id', 'ASC'),
-                    ])
-            return [m.model for m in models]
+        models = cls.search([], order=[
+                ('module', 'ASC'),  # Optimization assumption
+                ('model', 'ASC'),
+                ('id', 'ASC'),
+                ])
+        return [m.model for m in models]
 
     @classmethod
     def list_history(cls):
@@ -522,7 +525,9 @@ class ModelAccess(ModelSQL, ModelView):
         'Check access for model_name and mode'
         assert mode in ['read', 'write', 'create', 'delete'], \
             'Invalid access mode for security'
-        if Transaction().user == 0:
+        if ((Transaction().user == 0)
+                or (raise_exception
+                    and not Transaction().context.get('_check_access'))):
             return True
 
         access = cls.get_access([model_name])[model_name][mode]
@@ -600,7 +605,7 @@ class ModelFieldAccess(ModelSQL, ModelView):
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
     description = fields.Text('Description')
-    _get_access_cache = Cache('ir_model_field_access.check')
+    _get_access_cache = Cache('ir_model_field_access.check', context=False)
 
     @classmethod
     def __setup__(cls):
@@ -702,7 +707,9 @@ class ModelFieldAccess(ModelSQL, ModelView):
         '''
         assert mode in ('read', 'write', 'create', 'delete'), \
             'Invalid access mode'
-        if Transaction().user == 0:
+        if ((Transaction().user == 0)
+                or (raise_exception
+                    and not Transaction().context.get('_check_access'))):
             if access:
                 return dict((x, True) for x in fields)
             return True
@@ -813,10 +820,11 @@ class ModelData(ModelSQL, ModelView):
     db_id = fields.Integer('Resource ID',
         help="The id of the record in the database.", select=True,
         required=True)
-    date_update = fields.DateTime('Update Date')
-    date_init = fields.DateTime('Init Date')
     values = fields.Text('Values')
+    fs_values = fields.Text('Values on File System')
     noupdate = fields.Boolean('No Update')
+    out_of_sync = fields.Function(fields.Boolean('Out of Sync'),
+        'get_out_of_sync', searcher='search_out_of_sync')
     _get_id_cache = Cache('ir_model_data.get_id', context=False)
 
     @classmethod
@@ -826,6 +834,11 @@ class ModelData(ModelSQL, ModelView):
             ('fs_id_module_model_uniq', 'UNIQUE("fs_id", "module", "model")',
                 'The triple (fs_id, module, model) must be unique!'),
         ]
+        cls._buttons.update({
+                'sync': {
+                    'invisible': ~Eval('out_of_sync'),
+                    },
+                })
 
     @classmethod
     def __register__(cls, module_name):
@@ -844,13 +857,23 @@ class ModelData(ModelSQL, ModelView):
             table.drop_column('inherit', True)
 
     @staticmethod
-    def default_date_init():
-        return datetime.datetime.now()
-
-    @staticmethod
     def default_noupdate():
         return False
 
+    def get_out_of_sync(self, name):
+        return self.values != self.fs_values and self.fs_values is not None
+
+    @classmethod
+    def search_out_of_sync(cls, name, clause):
+        table = cls.__table__()
+        name, operator, value = clause
+        Operator = fields.SQL_OPERATORS[operator]
+        query = table.select(table.id,
+            where=Operator(
+                (table.fs_values != table.values) & (table.fs_values != None),
+                value))
+        return [('id', 'in', query)]
+
     @classmethod
     def write(cls, data, values, *args):
         super(ModelData, cls).write(data, values, *args)
@@ -877,6 +900,45 @@ class ModelData(ModelSQL, ModelView):
         cls._get_id_cache.set(key, id_)
         return id_
 
+    @classmethod
+    def dump_values(cls, values):
+        return json.dumps(sorted(values.iteritems()), cls=JSONEncoder)
+
+    @classmethod
+    def load_values(cls, values):
+        try:
+            return dict(json.loads(values, object_hook=JSONDecoder()))
+        except ValueError:
+            # Migration from 3.2
+            from ..tools import safe_eval
+            from decimal import Decimal
+            import datetime
+            return safe_eval(values, {
+                    'Decimal': Decimal,
+                    'datetime': datetime,
+                    })
+
+    @classmethod
+    @ModelView.button
+    def sync(cls, records):
+        pool = Pool()
+        to_write = []
+        for data in records:
+            Model = pool.get(data.model)
+            values = cls.load_values(data.values)
+            fs_values = cls.load_values(data.fs_values)
+            # values could be the same once loaded
+            # if they come from version < 3.2
+            if values != fs_values:
+                record = Model(data.db_id)
+                Model.write([record], fs_values)
+                values.update(fs_values)
+            to_write.extend([[data], {
+                        'values': cls.dump_values(values),
+                        }])
+        if to_write:
+            cls.write(*to_write)
+
 
 class PrintModelGraphStart(ModelView):
     'Print Model Graph'
diff --git a/trytond/ir/model.xml b/trytond/ir/model.xml
index d2806ac..9c5641a 100644
--- a/trytond/ir/model.xml
+++ b/trytond/ir/model.xml
@@ -173,5 +173,49 @@ this repository contains the full copyright notices and license terms. -->
         <menuitem parent="menu_model_access_form"
             action="act_model_button_form" id="menu_model_button_form"/>
 
+        <record model="ir.ui.view" id="model_data_view_list">
+            <field name="model">ir.model.data</field>
+            <field name="type">tree</field>
+            <field name="name">model_data_list</field>
+        </record>
+        <record model="ir.ui.view" id="model_data_view_form">
+            <field name="model">ir.model.data</field>
+            <field name="type">form</field>
+            <field name="name">model_data_form</field>
+        </record>
+
+        <record model="ir.action.act_window" id="act_model_data_form">
+            <field name="name">Data</field>
+            <field name="res_model">ir.model.data</field>
+        </record>
+        <record model="ir.action.act_window.view"
+            id="act_model_data_form_view1">
+            <field name="sequence" eval="10"/>
+            <field name="view" ref="model_data_view_list"/>
+            <field name="act_window" ref="act_model_data_form"/>
+        </record>
+        <record model="ir.action.act_window.view"
+            id="act_model_data_form_view2">
+            <field name="sequence" eval="20"/>
+            <field name="view" ref="model_data_view_form"/>
+            <field name="act_window" ref="act_model_data_form"/>
+        </record>
+        <record model="ir.action.act_window.domain"
+            id="act_model_data_form_domain_out_of_sync">
+            <field name="name">Out of Sync</field>
+            <field name="sequence" eval="10"/>
+            <field name="domain">[('out_of_sync', '=', True)]</field>
+            <field name="act_window" ref="act_model_data_form"/>
+        </record>
+        <record model="ir.action.act_window.domain"
+            id="act_model_data_form_domain_all">
+            <field name="name">All</field>
+            <field name="sequence" eval="9999"/>
+            <field name="domain"></field>
+            <field name="act_window" ref="act_model_data_form"/>
+        </record>
+        <menuitem parent="menu_model_form" action="act_model_data_form"
+            id="menu_model_data_form"/>
+
     </data>
 </tryton>
diff --git a/trytond/ir/module/module.py b/trytond/ir/module/module.py
index 605a614..8153bde 100644
--- a/trytond/ir/module/module.py
+++ b/trytond/ir/module/module.py
@@ -483,6 +483,12 @@ class ModuleInstallUpgrade(Wizard):
             ])
     config = StateAction('ir.act_module_config_wizard')
 
+    @classmethod
+    def check_access(cls):
+        # Use new cursor to prevent lock when installing modules
+        with Transaction().new_cursor():
+            super(ModuleInstallUpgrade, cls).check_access()
+
     @staticmethod
     def default_start(fields):
         pool = Pool()
@@ -509,13 +515,14 @@ class ModuleInstallUpgrade(Wizard):
             modules = Module.search([
                 ('state', 'in', ['to upgrade', 'to remove', 'to install']),
                 ])
+            update = [m.name for m in modules]
             langs = Lang.search([
                 ('translatable', '=', True),
                 ])
             lang = [x.code for x in langs]
             transaction.cursor.commit()
-        if modules:
-            pool.init(update=True, lang=lang)
+        if update:
+            pool.init(update=update, lang=lang)
         return 'done'
 
 
diff --git a/trytond/ir/property.py b/trytond/ir/property.py
index 9567c45..f5c79ff 100644
--- a/trytond/ir/property.py
+++ b/trytond/ir/property.py
@@ -135,8 +135,7 @@ class Property(ModelSQL, ModelView):
             ('field', '=', model_field.id),
             ('res', 'in', [model + ',' + str(res_id) for res_id in ids]),
             ], order=[])
-        with Transaction().set_user(0, set_context=True):
-            cls.delete(properties)
+        cls.delete(properties)
 
         defaults = cls.search([
             ('field', '=', model_field.id),
@@ -160,5 +159,4 @@ class Property(ModelSQL, ModelView):
         if (val != default_val):
             for res_id in ids:
                 vals = cls._set_values(model, res_id, val, model_field.id)
-                with Transaction().set_user(0, set_context=True):
-                    cls.create([vals])
+                cls.create([vals])
diff --git a/trytond/ir/rule.py b/trytond/ir/rule.py
index 479f162..8491514 100644
--- a/trytond/ir/rule.py
+++ b/trytond/ir/rule.py
@@ -1,8 +1,5 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import time
-import datetime
-
 from ..model import ModelView, ModelSQL, fields
 from ..tools import safe_eval
 from ..transaction import Transaction
@@ -29,6 +26,7 @@ class RuleGroup(ModelSQL, ModelView):
         help="The rule is satisfied if at least one test is True")
     groups = fields.Many2Many('ir.rule.group-res.group',
         'rule_group', 'group', 'Groups')
+    # TODO remove to only use groups
     users = fields.Many2Many('ir.rule.group-res.user',
         'rule_group', 'user', 'Users')
     perm_read = fields.Boolean('Read Access')
@@ -99,7 +97,7 @@ class Rule(ModelSQL, ModelView):
        required=True, ondelete="CASCADE")
     domain = fields.Char('Domain', required=True,
         help='Domain is evaluated with "user" as the current user')
-    _domain_get_cache = Cache('ir_rule.domain_get')
+    _domain_get_cache = Cache('ir_rule.domain_get', context=False)
 
     @classmethod
     def __setup__(cls):
@@ -144,15 +142,16 @@ class Rule(ModelSQL, ModelView):
     def _get_context():
         User = Pool().get('res.user')
         user_id = Transaction().user
-        with Transaction().set_user(0, set_context=True):
+        with Transaction().set_context(_check_access=False):
             user = User(user_id)
         return {
             'user': user,
-            'current_date': datetime.datetime.today(),
-            'time': time,
-            'context': Transaction().context,
             }
 
+    @staticmethod
+    def _get_cache_key():
+        return (Transaction().user,)
+
     @classmethod
     def domain_get(cls, model_name, mode='read'):
         assert mode in ['read', 'write', 'create', 'delete'], \
@@ -165,7 +164,7 @@ class Rule(ModelSQL, ModelView):
             with Transaction().set_user(Transaction().context['user']):
                 return cls.domain_get(model_name, mode=mode)
 
-        key = (model_name, mode)
+        key = (model_name, mode) + cls._get_cache_key()
         domain = cls._domain_get_cache.get(key, False)
         if domain is not False:
             return domain
diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py
index d1b3c73..93bae63 100644
--- a/trytond/ir/sequence.py
+++ b/trytond/ir/sequence.py
@@ -10,14 +10,13 @@ from ..tools import datetime_strftime
 from ..pyson import Eval, And
 from ..transaction import Transaction
 from ..pool import Pool
-from ..config import CONFIG
 from .. import backend
 
 __all__ = [
     'SequenceType', 'Sequence', 'SequenceStrict',
     ]
 
-sql_sequence = CONFIG.options['db_type'] == 'postgresql'
+sql_sequence = backend.name() == 'postgresql'
 
 
 class SequenceType(ModelSQL, ModelView):
@@ -314,20 +313,18 @@ class Sequence(ModelSQL, ModelView):
                 #Pre-fetch number_next
                 number_next = sequence.number_next_internal
 
-                with Transaction().set_user(0):
-                    cls.write([sequence], {
-                            'number_next_internal': (number_next
-                                + sequence.number_increment),
-                            })
+                cls.write([sequence], {
+                        'number_next_internal': (number_next
+                            + sequence.number_increment),
+                        })
             return '%%0%sd' % sequence.padding % number_next
         elif sequence.type in ('decimal timestamp', 'hexadecimal timestamp'):
             timestamp = sequence.last_timestamp
             while timestamp == sequence.last_timestamp:
                 timestamp = cls._timestamp(sequence)
-            with Transaction().set_user(0):
-                cls.write([sequence], {
-                    'last_timestamp': timestamp,
-                    })
+            cls.write([sequence], {
+                'last_timestamp': timestamp,
+                })
             if sequence.type == 'decimal timestamp':
                 return '%d' % timestamp
             else:
@@ -345,18 +342,18 @@ class Sequence(ModelSQL, ModelView):
             domain = [('id', '=', domain)]
 
         # bypass rules on sequences
-        with Transaction().set_context(user=False):
+        with Transaction().set_context(user=False, _check_access=False):
             with Transaction().set_user(0):
                 try:
                     sequence, = cls.search(domain, limit=1)
                 except TypeError:
                     cls.raise_user_error('missing')
-            date = Transaction().context.get('date')
-            return '%s%s%s' % (
-                cls._process(sequence.prefix, date=date),
-                cls._get_sequence(sequence),
-                cls._process(sequence.suffix, date=date),
-                )
+                date = Transaction().context.get('date')
+                return '%s%s%s' % (
+                    cls._process(sequence.prefix, date=date),
+                    cls._get_sequence(sequence),
+                    cls._process(sequence.suffix, date=date),
+                    )
 
     @classmethod
     def get(cls, code):
diff --git a/trytond/ir/session.py b/trytond/ir/session.py
index e6512ae..d9beae9 100644
--- a/trytond/ir/session.py
+++ b/trytond/ir/session.py
@@ -8,7 +8,7 @@ import uuid
 import datetime
 
 from trytond.model import ModelSQL, fields
-from trytond.config import CONFIG
+from trytond.config import config
 from .. import backend
 from ..transaction import Transaction
 
@@ -45,7 +45,8 @@ class Session(ModelSQL):
     def check(cls, user, key):
         "Check user key and delete old one"
         now = datetime.datetime.now()
-        timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
+        timeout = datetime.timedelta(
+            seconds=config.getint('session', 'timeout'))
         sessions = cls.search([
                 ('create_uid', '=', user),
                 ])
diff --git a/trytond/ir/time_locale.py b/trytond/ir/time_locale.py
index e977b76..1371e77 100644
--- a/trytond/ir/time_locale.py
+++ b/trytond/ir/time_locale.py
@@ -265,6 +265,47 @@ TIME_LOCALE = \
                   u'nov',
                   u'dic'],
            '%p': [u'AM', u'PM']},
+ 'es_EC': {'%A': [u'lunes',
+                  u'martes',
+                  u'mi\xe9rcoles',
+                  u'jueves',
+                  u'viernes',
+                  u's\xe1bado',
+                  u'domingo'],
+           '%B': [None,
+                  u'enero',
+                  u'febrero',
+                  u'marzo',
+                  u'abril',
+                  u'mayo',
+                  u'junio',
+                  u'julio',
+                  u'agosto',
+                  u'septiembre',
+                  u'octubre',
+                  u'noviembre',
+                  u'diciembre'],
+           '%a': [u'lun',
+                  u'mar',
+                  u'mi\xe9',
+                  u'jue',
+                  u'vie',
+                  u's\xe1b',
+                  u'dom'],
+           '%b': [None,
+                  u'ene',
+                  u'feb',
+                  u'mar',
+                  u'abr',
+                  u'may',
+                  u'jun',
+                  u'jul',
+                  u'ago',
+                  u'sep',
+                  u'oct',
+                  u'nov',
+                  u'dic'],
+           '%p': [u'AM', u'PM']},
  'es_ES': {'%A': [u'lunes',
                   u'martes',
                   u'mi\xe9rcoles',
diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py
index 17e7079..b0c8a54 100644
--- a/trytond/ir/translation.py
+++ b/trytond/ir/translation.py
@@ -21,7 +21,7 @@ from sql.aggregate import Max
 from ..model import ModelView, ModelSQL, fields
 from ..wizard import Wizard, StateView, StateTransition, StateAction, \
     Button
-from ..tools import file_open, reduce_ids
+from ..tools import file_open, reduce_ids, grouped_slice
 from .. import backend
 from ..pyson import PYSONEncoder
 from ..transaction import Transaction
@@ -124,13 +124,10 @@ class Translation(ModelSQL, ModelView):
             table = TableHandler(cursor, cls, module_name)
             table.not_null_action('src_md5', action='add')
 
-        # Migration from 2.2
+        # Migration from 2.2 and 2.8
         cursor.execute(*ir_translation.update([ir_translation.res_id],
-                [None], where=ir_translation.res_id == 0))
-
-        # Migration from 2.8
-        cursor.execute(*ir_translation.update([ir_translation.res_id],
-                [-1], where=ir_translation.res_id == None))
+                [-1], where=(ir_translation.res_id == None)
+                | (ir_translation.res_id == 0)))
 
         table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['lang', 'type', 'name'], 'add')
@@ -387,7 +384,7 @@ class Translation(ModelSQL, ModelView):
         lang = unicode(lang)
         if name.split(',')[0] in ('ir.model.field', 'ir.model'):
             field_name = name.split(',')[1]
-            with Transaction().set_user(0):
+            with Transaction().set_context(_check_access=False):
                 if name.split(',')[0] == 'ir.model.field':
                     if field_name == 'field_description':
                         ttype = u'field'
@@ -433,8 +430,7 @@ class Translation(ModelSQL, ModelView):
             if Transaction().context.get('fuzzy_translation', False):
                 fuzzy_sql = None
             in_max = cursor.IN_MAX / 7
-            for i in range(0, len(to_fetch), in_max):
-                sub_to_fetch = to_fetch[i:i + in_max]
+            for sub_to_fetch in grouped_slice(to_fetch, in_max):
                 red_sql = reduce_ids(table.res_id, sub_to_fetch)
                 where = And(((table.lang == lang),
                         (table.type == ttype),
@@ -518,14 +514,14 @@ class Translation(ModelSQL, ModelView):
                             'fuzzy': False,
                             })
                 else:
-                    with Transaction().set_user(0):
+                    with Transaction().set_context(_check_access=False):
                         cls.write([translation], {
                             'src': getattr(record, field_name),
                             'value': value,
                             'fuzzy': False,
                             })
             if to_create:
-                with Transaction().set_user(0):
+                with Transaction().set_context(_check_access=False):
                     cls.create(to_create)
             return
 
@@ -568,7 +564,7 @@ class Translation(ModelSQL, ModelView):
                         'fuzzy': False,
                         })
             else:
-                with Transaction().set_user(0):
+                with Transaction().set_context(_check_access=False):
                     cls.write([translation], {
                         'value': value,
                         'src': getattr(record, field_name),
@@ -581,22 +577,20 @@ class Translation(ModelSQL, ModelView):
                                 'fuzzy': True,
                                 })
         if to_create:
-            with Transaction().set_user(0):
+            with Transaction().set_context(_check_access=False):
                 cls.create(to_create)
 
     @classmethod
     def delete_ids(cls, model, ttype, ids):
         "Delete translation for each id"
-        cursor = Transaction().cursor
         translations = []
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
+        for sub_ids in grouped_slice(ids):
             translations += cls.search([
                     ('type', '=', ttype),
                     ('name', 'like', model + ',%'),
-                    ('res_id', 'in', sub_ids),
+                    ('res_id', 'in', list(sub_ids)),
                     ])
-        with Transaction().set_user(0):
+        with Transaction().set_context(_check_access=False):
             cls.delete(translations)
 
     @classmethod
@@ -643,9 +637,8 @@ class Translation(ModelSQL, ModelView):
         cursor = Transaction().cursor
         table = cls.__table__()
         if len(args) > cursor.IN_MAX:
-            for i in range(0, len(args), cursor.IN_MAX):
-                sub_args = args[i:i + cursor.IN_MAX]
-                res.update(cls.get_sources(sub_args))
+            for sub_args in grouped_slice(args):
+                res.update(cls.get_sources(list(sub_args)))
             return res
         for name, ttype, lang, source in args:
             name = unicode(name)
@@ -672,11 +665,11 @@ class Translation(ModelSQL, ModelView):
                 clause.append(where)
         if clause:
             in_max = cursor.IN_MAX / 7
-            for i in range(0, len(clause), in_max):
+            for sub_clause in grouped_slice(clause, in_max):
                 cursor.execute(*table.select(
                         table.lang, table.type, table.name, table.src,
                         table.value,
-                        where=Or(clause[i:i + in_max])))
+                        where=Or(list(sub_clause))))
                 for lang, ttype, name, source, value in cursor.fetchall():
                     if (name, ttype, lang, source) not in args:
                         source = None
@@ -815,8 +808,7 @@ class Translation(ModelSQL, ModelView):
                 res_id = model_data.db_id
             else:
                 res_id = -1
-            with Transaction().set_user(0), \
-                    Transaction().set_context(module=res_id_module):
+            with Transaction().set_context(module=res_id_module):
                 domain = [
                     ('name', '=', new_translation.name),
                     ('res_id', '=', res_id),
@@ -889,8 +881,7 @@ class Translation(ModelSQL, ModelView):
                                 or old_translation.fuzzy !=
                                 translation.fuzzy):
                             to_write.append(old_translation)
-                    with Transaction().set_user(0), \
-                            Transaction().set_context(module=module):
+                    with Transaction().set_context(module=module):
                         if to_write and not noupdate:
                             cls.write(to_write, {
                                     'value': translation.value,
@@ -899,8 +890,7 @@ class Translation(ModelSQL, ModelView):
                         translations |= set(cls.browse(ids))
 
         if to_create:
-            with Transaction().set_user(0), \
-                    Transaction().set_context(module=module):
+            with Transaction().set_context(module=module):
                 translations |= set(cls.create(to_create))
 
         if translations:
@@ -1493,8 +1483,7 @@ class TranslationUpdate(Wizard):
                 'module': row['module'],
                 })
         if to_create:
-            with Transaction().set_user(0):
-                Translation.create(to_create)
+            Translation.create(to_create)
         columns = [translation.name.as_('name'),
             translation.res_id.as_('res_id'), translation.type.as_('type'),
             translation.module.as_('module')]
@@ -1514,8 +1503,7 @@ class TranslationUpdate(Wizard):
                 'module': row['module'],
                 })
         if to_create:
-            with Transaction().set_user(0):
-                Translation.create(to_create)
+            Translation.create(to_create)
         columns = [translation.name.as_('name'),
             translation.res_id.as_('res_id'), translation.type.as_('type'),
             translation.src.as_('src'), translation.module.as_('module')]
diff --git a/trytond/ir/translation.xml b/trytond/ir/translation.xml
index d6de503..e93b30b 100644
--- a/trytond/ir/translation.xml
+++ b/trytond/ir/translation.xml
@@ -127,8 +127,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">digits_validation_record</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The number of digits "%(digits)s" of field "%(field)s" on "%(value)s" exceeds it's limit.</field>
-            <field name="value">The number of digits "%(digits)s" of field "%(field)s" on "%(value)s" exceeds it's limit.</field>
+            <field name="src">The number of digits "%(digits)s" of field "%(field)s" on "%(value)s" exceeds its limit.</field>
+            <field name="value">The number of digits "%(digits)s" of field "%(field)s" on "%(value)s" exceeds its limit.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
diff --git a/trytond/ir/trigger.py b/trytond/ir/trigger.py
index 56dbddd..9b88d2f 100644
--- a/trytond/ir/trigger.py
+++ b/trytond/ir/trigger.py
@@ -7,7 +7,7 @@ from sql.aggregate import Count, Max
 
 from ..model import ModelView, ModelSQL, fields
 from ..pyson import Eval
-from ..tools import safe_eval
+from ..tools import safe_eval, grouped_slice
 from .. import backend
 from ..tools import reduce_ids
 from ..transaction import Transaction
@@ -172,15 +172,14 @@ class Trigger(ModelSQL, ModelView):
         Model = pool.get(trigger.model.model)
         ActionModel = pool.get(trigger.action_model.model)
         cursor = Transaction().cursor
-        in_max = cursor.IN_MAX
         trigger_log = TriggerLog.__table__()
         ids = map(int, records)
 
         # Filter on limit_number
         if trigger.limit_number:
             new_ids = []
-            for i in range(0, len(ids), in_max):
-                sub_ids = ids[i:i + in_max]
+            for sub_ids in grouped_slice(ids):
+                sub_ids = list(sub_ids)
                 red_sql = reduce_ids(trigger_log.record_id, sub_ids)
                 cursor.execute(*trigger_log.select(
                         trigger_log.record_id, Count(Literal(1)),
@@ -198,8 +197,8 @@ class Trigger(ModelSQL, ModelView):
         # Filter on minimum_delay
         if trigger.minimum_delay:
             new_ids = []
-            for i in range(0, len(ids), in_max):
-                sub_ids = ids[i:i + in_max]
+            for sub_ids in grouped_slice(ids):
+                sub_ids = list(sub_ids)
                 red_sql = reduce_ids(trigger_log.record_id, sub_ids)
                 cursor.execute(*trigger_log.select(
                         trigger_log.record_id, Max(trigger_log.create_date),
diff --git a/trytond/ir/ui/icons/tryton-tree.svg b/trytond/ir/ui/icons/tryton-tree.svg
index 0f9b8a2..3e26f16 100644
--- a/trytond/ir/ui/icons/tryton-tree.svg
+++ b/trytond/ir/ui/icons/tryton-tree.svg
@@ -14,7 +14,7 @@
    height="48px"
    id="svg4198"
    sodipodi:version="0.32"
-   inkscape:version="0.48.0 r9654"
+   inkscape:version="0.48.4 r9939"
    sodipodi:docname="tryton-tree.svg"
    inkscape:output_extension="org.inkscape.output.svg.inkscape"
    version="1.1">
@@ -207,15 +207,15 @@
      borderopacity="1.0000000"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="16.5"
-     inkscape:cx="24"
-     inkscape:cy="24"
+     inkscape:zoom="6.51"
+     inkscape:cx="22.261972"
+     inkscape:cy="12.80998"
      inkscape:current-layer="layer1"
-     showgrid="false"
+     showgrid="true"
      inkscape:grid-bbox="true"
      inkscape:document-units="px"
-     inkscape:window-width="1278"
-     inkscape:window-height="1007"
+     inkscape:window-width="1364"
+     inkscape:window-height="751"
      inkscape:window-x="0"
      inkscape:window-y="15"
      inkscape:showpageshadow="false"
@@ -244,11 +244,6 @@
         </dc:subject>
         <cc:license
            rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
-        <dc:contributor>
-          <cc:Agent>
-            <dc:title>Cédric Krier</dc:title>
-          </cc:Agent>
-        </dc:contributor>
       </cc:Work>
       <cc:License
          rdf:about="http://creativecommons.org/licenses/publicdomain/">
@@ -297,43 +292,43 @@
        rx="0.56650788"
        ry="0.56650823" />
     <rect
-       style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+       style="opacity:1;color:#000000;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
        id="rect4248"
-       width="26"
+       width="30"
        height="2"
-       x="-34"
+       x="-39"
        y="10"
-       transform="scale(-1,1)" />
+       transform="scale(-1.000000,1.000000)" />
     <rect
        y="16"
-       x="-31"
+       x="-39"
        height="2"
-       width="23"
+       width="25.5"
        id="rect4250"
        style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
        transform="scale(-1,1)" />
     <rect
        style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
        id="rect4252"
-       width="21"
+       width="19"
        height="2"
-       x="-29"
+       x="-39"
        y="22"
        transform="scale(-1,1)" />
     <rect
        y="28"
-       x="-34"
+       x="-39"
        height="2"
-       width="26"
+       width="12.5"
        id="rect4254"
        style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
        transform="scale(-1,1)" />
     <rect
        style="color:#000000;fill:#999999;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
        id="rect4256"
-       width="17"
+       width="6"
        height="2"
-       x="-25"
+       x="-38.5"
        y="34"
        transform="scale(-1,1)" />
     <rect
diff --git a/trytond/ir/ui/menu.py b/trytond/ir/ui/menu.py
index 2d3b45e..94af4d8 100644
--- a/trytond/ir/ui/menu.py
+++ b/trytond/ir/ui/menu.py
@@ -1,5 +1,7 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from itertools import groupby
+
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.transaction import Transaction
 from trytond.pool import Pool
@@ -89,6 +91,8 @@ class UIMenu(ModelSQL, ModelView):
                 ('ir.action.wizard', 'ir.action.wizard'),
                 ('ir.action.url', 'ir.action.url'),
                 ]), 'get_action', setter='set_action')
+    action_keywords = fields.One2Many('ir.action.keyword', 'model',
+        'Action Keywords')
     active = fields.Boolean('Active')
     favorite = fields.Function(fields.Boolean('Favorite'), 'get_favorite')
 
@@ -190,25 +194,27 @@ class UIMenu(ModelSQL, ModelView):
     @classmethod
     def get_action(cls, menus, name):
         pool = Pool()
-        ActionKeyword = pool.get('ir.action.keyword')
         actions = dict((m.id, None) for m in menus)
         with Transaction().set_context(active_test=False):
-            action_keywords = ActionKeyword.search([
-                    ('keyword', '=', 'tree_open'),
-                    ('model', 'in', [str(m) for m in menus]),
-                    ])
-        for action_keyword in action_keywords:
-            model = action_keyword.model
-            Action = pool.get(action_keyword.action.type)
+            menus = cls.browse(menus)
+        action_keywords = sum((list(m.action_keywords) for m in menus), [])
+        key = lambda k: k.action.type
+        action_keywords.sort(key=key)
+        for type, action_keywords in groupby(action_keywords, key=key):
+            action_keywords = list(action_keywords)
+            for action_keyword in action_keywords:
+                model = action_keyword.model
+                actions[model.id] = '%s,-1' % type
+
+            Action = pool.get(type)
+            action2keyword = {k.action.id: k for k in action_keywords}
             with Transaction().set_context(active_test=False):
                 factions = Action.search([
-                        ('action', '=', action_keyword.action.id),
-                        ], limit=1)
-            if factions:
-                action, = factions
-            else:
-                action = '%s,0' % action_keyword.action.type
-            actions[model.id] = str(action)
+                        ('action', 'in', action2keyword.keys()),
+                        ])
+            for action in factions:
+                model = action2keyword[action.id].model
+                actions[model.id] = str(action)
         return actions
 
     @classmethod
diff --git a/trytond/ir/ui/tree.rnc b/trytond/ir/ui/tree.rnc
index 3500bc8..925331e 100644
--- a/trytond/ir/ui/tree.rnc
+++ b/trytond/ir/ui/tree.rnc
@@ -12,6 +12,8 @@ attlist.tree &= attribute sequence { text }?
 attlist.tree &= attribute colors { text }?
 attlist.tree &=
   [ a:defaultValue = "0" ] attribute keyword_open { "0" | "1" }?
+attlist.tree &=
+  [ a:defaultValue = "0" ] attribute tree_state { "0" | "1" }?
 field = element field { attlist.field, (prefix | suffix)* }
 attlist.field &= attribute name { text }
 attlist.field &= attribute readonly { "0" | "1" }?
@@ -39,6 +41,7 @@ attlist.field &=
     | "reference"
     | "one2one"
     | "binary"
+    | "image"
   }?
 attlist.field &=
   [ a:defaultValue = "0" ] attribute tree_invisible { "0" | "1" }?
@@ -47,6 +50,7 @@ attlist.field &=
 attlist.field &= attribute icon { text }?
 attlist.field &= attribute sum { text }?
 attlist.field &= attribute width { text }?
+attlist.field &= attribute height { text }?
 attlist.field &=
   [ a:defaultValue = "left_to_right" ] attribute orientation {
     "left_to_right"
diff --git a/trytond/ir/ui/tree.rng b/trytond/ir/ui/tree.rng
index 85ecc64..0508c44 100644
--- a/trytond/ir/ui/tree.rng
+++ b/trytond/ir/ui/tree.rng
@@ -51,6 +51,16 @@
       </attribute>
     </optional>
   </define>
+  <define name="attlist.tree" combine="interleave">
+    <optional>
+      <attribute name="tree_state" a:defaultValue="0">
+        <choice>
+          <value>0</value>
+          <value>1</value>
+        </choice>
+      </attribute>
+    </optional>
+  </define>
   <define name="field">
     <element name="field">
       <ref name="attlist.field"/>
@@ -101,6 +111,7 @@
           <value>reference</value>
           <value>one2one</value>
           <value>binary</value>
+          <value>image</value>
         </choice>
       </attribute>
     </optional>
@@ -142,6 +153,11 @@
   </define>
   <define name="attlist.field" combine="interleave">
     <optional>
+      <attribute name="height"/>
+    </optional>
+  </define>
+  <define name="attlist.field" combine="interleave">
+    <optional>
       <attribute name="orientation" a:defaultValue="left_to_right">
         <choice>
           <value>left_to_right</value>
diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py
index 5e16398..2c3fb90 100644
--- a/trytond/ir/ui/view.py
+++ b/trytond/ir/ui/view.py
@@ -3,6 +3,11 @@
 import os
 import sys
 import logging
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
 from lxml import etree
 from trytond.model import ModelView, ModelSQL, fields
 from trytond import backend
@@ -305,8 +310,8 @@ class ViewTreeState(ModelSQL, ModelView):
     def __setup__(cls):
         super(ViewTreeState, cls).__setup__()
         cls.__rpc__.update({
-                'set': RPC(readonly=False),
-                'get': RPC(),
+                'set': RPC(readonly=False, check_access=False),
+                'get': RPC(check_access=False),
                 })
 
     @classmethod
@@ -335,40 +340,40 @@ class ViewTreeState(ModelSQL, ModelView):
     @classmethod
     def set(cls, model, domain, child_name, nodes, selected_nodes):
         current_user = Transaction().user
-        with Transaction().set_user(0):
-            records = cls.search([
-                    ('user', '=', current_user),
-                    ('model', '=', model),
-                    ('domain', '=', domain),
-                    ('child_name', '=', child_name),
-                    ])
-            cls.delete(records)
-            cls.create([{
-                        'user': current_user,
-                        'model': model,
-                        'domain': domain,
-                        'child_name': child_name,
-                        'nodes': nodes,
-                        'selected_nodes': selected_nodes,
-                        }])
+        records = cls.search([
+                ('user', '=', current_user),
+                ('model', '=', model),
+                ('domain', '=', domain),
+                ('child_name', '=', child_name),
+                ])
+        cls.delete(records)
+        cls.create([{
+                    'user': current_user,
+                    'model': model,
+                    'domain': domain,
+                    'child_name': child_name,
+                    'nodes': nodes,
+                    'selected_nodes': selected_nodes,
+                    }])
 
     @classmethod
     def get(cls, model, domain, child_name):
+        # Normalize the json domain
+        domain = json.dumps(json.loads(domain))
         current_user = Transaction().user
-        with Transaction().set_user(0):
-            try:
-                expanded_info, = cls.search([
-                        ('user', '=', current_user),
-                        ('model', '=', model),
-                        ('domain', '=', domain),
-                        ('child_name', '=', child_name),
-                        ],
-                    limit=1)
-            except ValueError:
-                return (cls.default_nodes(), cls.default_selected_nodes())
-            state = cls(expanded_info)
-            return (state.nodes or cls.default_nodes(),
-                state.selected_nodes or cls.default_selected_nodes())
+        try:
+            expanded_info, = cls.search([
+                    ('user', '=', current_user),
+                    ('model', '=', model),
+                    ('domain', '=', domain),
+                    ('child_name', '=', child_name),
+                    ],
+                limit=1)
+        except ValueError:
+            return (cls.default_nodes(), cls.default_selected_nodes())
+        state = cls(expanded_info)
+        return (state.nodes or cls.default_nodes(),
+            state.selected_nodes or cls.default_selected_nodes())
 
 
 class ViewSearch(ModelSQL, ModelView):
diff --git a/trytond/ir/view/action_act_window_view_form.xml b/trytond/ir/view/action_act_window_view_form.xml
index d282a67..689eac0 100644
--- a/trytond/ir/view/action_act_window_view_form.xml
+++ b/trytond/ir/view/action_act_window_view_form.xml
@@ -1,11 +1,13 @@
 <?xml version="1.0"?>
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
-<form string="View" col="2">
+<form string="View">
     <label name="act_window"/>
-    <field name="act_window"/>
+    <field name="act_window" colspan="3"/>
     <label name="sequence"/>
     <field name="sequence"/>
+    <label name="active"/>
+    <field name="active"/>
     <label name="view"/>
     <field name="view"/>
 </form>
diff --git a/trytond/ir/view/action_act_window_view_list.xml b/trytond/ir/view/action_act_window_view_list.xml
index 5868cf7..4f8c136 100644
--- a/trytond/ir/view/action_act_window_view_list.xml
+++ b/trytond/ir/view/action_act_window_view_list.xml
@@ -5,4 +5,5 @@ this repository contains the full copyright notices and license terms. -->
     <field name="act_window"/>
     <field name="sequence"/>
     <field name="view"/>
+    <field name="active" tree_invisible="1"/>
 </tree>
diff --git a/trytond/ir/view/model_data_form.xml b/trytond/ir/view/model_data_form.xml
new file mode 100644
index 0000000..9ba13fa
--- /dev/null
+++ b/trytond/ir/view/model_data_form.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<form string="Model Data">
+    <label name="module"/>
+    <field name="module"/>
+    <label name="model"/>
+    <field name="model"/>
+    <label name="fs_id"/>
+    <field name="fs_id"/>
+    <label name="db_id"/>
+    <field name="db_id"/>
+    <label name="noupdate"/>
+    <field name="noupdate"/>
+    <newline/>
+    <label name="out_of_sync"/>
+    <field name="out_of_sync"/>
+    <button string="Sync" name="sync" colspan="2"/>
+    <group id="values" colspan="2" yexpand="1" yfill="1" col="1">
+        <separator name="values"/>
+        <field name="values"/>
+    </group>
+    <group id="fs_values" colspan="2" yexpand="1" yfill="1" col="1">
+        <separator name="fs_values"/>
+        <field name="fs_values"/>
+    </group>
+</form>
diff --git a/trytond/ir/view/model_data_list.xml b/trytond/ir/view/model_data_list.xml
new file mode 100644
index 0000000..ef361cc
--- /dev/null
+++ b/trytond/ir/view/model_data_list.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<tree string="Model Data">
+    <field name="module"/>
+    <field name="model"/>
+    <field name="fs_id"/>
+    <field name="db_id"/>
+    <field name="noupdate"/>
+    <field name="out_of_sync"/>
+    <button string="Sync" name="sync"/>
+</tree>
+
diff --git a/trytond/ir/view/ui_menu_tree.xml b/trytond/ir/view/ui_menu_tree.xml
index 36562f9..995c3cd 100644
--- a/trytond/ir/view/ui_menu_tree.xml
+++ b/trytond/ir/view/ui_menu_tree.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
-<tree string="Menu" keyword_open="1">
+<tree string="Menu" keyword_open="1" tree_state="1">
     <field name="name" icon="icon" expand="1"/>
     <field name="favorite" tree_invisible="1"/>
 </tree>
diff --git a/trytond/model/__init__.py b/trytond/model/__init__.py
index b63e22c..dae657d 100644
--- a/trytond/model/__init__.py
+++ b/trytond/model/__init__.py
@@ -7,6 +7,8 @@ from .modelsingleton import ModelSingleton
 from .modelsql import ModelSQL
 from .workflow import Workflow
 from .dictschema import DictSchemaMixin
+from .match import MatchMixin
+from .union import UnionMixin
 
 __all__ = ['Model', 'ModelView', 'ModelStorage', 'ModelSingleton', 'ModelSQL',
-    'Workflow', 'DictSchemaMixin']
+    'Workflow', 'DictSchemaMixin', 'MatchMixin', 'UnionMixin']
diff --git a/trytond/model/dictschema.py b/trytond/model/dictschema.py
index d6f1b33..34363a8 100644
--- a/trytond/model/dictschema.py
+++ b/trytond/model/dictschema.py
@@ -1,5 +1,6 @@
 # This file is part of Tryton.  The COPYRIGHT file at the toplevel of this
 # repository contains the full copyright notices and license terms.
+from collections import OrderedDict
 try:
     import simplejson as json
 except ImportError:
@@ -33,6 +34,10 @@ class DictSchemaMixin(object):
             'invisible': Eval('type_') != 'selection',
             }, translate=True, depends=['type_'],
         help='A couple of key and label separated by ":" per line')
+    selection_sorted = fields.Boolean('Selection Sorted', states={
+            'invisible': Eval('type_') != 'selection',
+            }, depends=['type_'],
+        help='If the selection must be sorted on label')
     selection_json = fields.Function(fields.Char('Selection JSON',
             states={
                 'invisible': Eval('type_') != 'selection',
@@ -50,6 +55,10 @@ class DictSchemaMixin(object):
     def default_digits():
         return 2
 
+    @staticmethod
+    def default_selection_sorted():
+        return True
+
     def get_selection_json(self, name):
         db_selection = self.selection or ''
         selection = [[w.strip() for w in v.split(':', 1)]
@@ -71,9 +80,11 @@ class DictSchemaMixin(object):
             if record.type_ == 'selection':
                 with Transaction().set_context(language=Config.get_language()):
                     english_key = cls(record.id)
-                    selection = dict(json.loads(english_key.selection_json))
+                    selection = OrderedDict(json.loads(
+                            english_key.selection_json))
                 selection.update(dict(json.loads(record.selection_json)))
                 new_key['selection'] = selection.items()
+                new_key['sorted'] = record.selection_sorted
             elif record.type_ in ('float', 'numeric'):
                 new_key['digits'] = (16, record.digits)
             keys.append(new_key)
diff --git a/trytond/model/fields/binary.py b/trytond/model/fields/binary.py
index 94b278e..42faa79 100644
--- a/trytond/model/fields/binary.py
+++ b/trytond/model/fields/binary.py
@@ -4,7 +4,7 @@ from sql import Query, Expression
 
 from .field import Field, SQLType
 from ...transaction import Transaction
-from ...config import CONFIG
+from ... import backend
 
 
 class Binary(Field):
@@ -59,14 +59,14 @@ class Binary(Field):
     def sql_format(value):
         if isinstance(value, (Query, Expression)):
             return value
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'postgresql' and value is not None:
             import psycopg2
             return psycopg2.Binary(value)
         return value
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'postgresql':
             return SQLType('BYTEA', 'BYTEA')
         elif db_type == 'mysql':
diff --git a/trytond/model/fields/char.py b/trytond/model/fields/char.py
index 1168cac..6254077 100644
--- a/trytond/model/fields/char.py
+++ b/trytond/model/fields/char.py
@@ -4,7 +4,7 @@ import warnings
 
 from sql import Query, Expression
 
-from ...config import CONFIG
+from ... import backend
 from .field import Field, FieldTranslate, size_validate, SQLType
 
 
@@ -59,7 +59,7 @@ class Char(FieldTranslate):
         return value
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if self.size and db_type != 'sqlite':
             return SQLType('VARCHAR', 'VARCHAR(%s)' % self.size)
         elif db_type == 'mysql':
diff --git a/trytond/model/fields/date.py b/trytond/model/fields/date.py
index f769e9d..428f6c4 100644
--- a/trytond/model/fields/date.py
+++ b/trytond/model/fields/date.py
@@ -3,7 +3,7 @@
 import datetime
 from sql import Query, Expression
 
-from ...config import CONFIG
+from ... import backend
 from .field import Field, SQLType
 
 
@@ -71,7 +71,7 @@ class DateTime(Field):
         return value.replace(microsecond=0)
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'sqlite':
             return SQLType('TIMESTAMP', 'TIMESTAMP')
         elif db_type == 'mysql':
@@ -106,7 +106,7 @@ class Timestamp(Field):
         return value
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'sqlite':
             return SQLType('TIMESTAMP', 'TIMESTAMP')
         elif db_type == 'mysql':
diff --git a/trytond/model/fields/dict.py b/trytond/model/fields/dict.py
index a775916..ef63d52 100644
--- a/trytond/model/fields/dict.py
+++ b/trytond/model/fields/dict.py
@@ -7,7 +7,7 @@ except ImportError:
 from sql import Query, Expression
 
 from .field import Field, SQLType
-from ...protocols.jsonrpc import object_hook, JSONEncoder
+from ...protocols.jsonrpc import JSONDecoder, JSONEncoder
 
 
 class Dict(Field):
@@ -28,7 +28,7 @@ class Dict(Field):
         for value in values or []:
             if value[name]:
                 dicts[value['id']] = json.loads(value[name],
-                    object_hook=object_hook)
+                    object_hook=JSONDecoder())
         return dicts
 
     @staticmethod
diff --git a/trytond/model/fields/field.py b/trytond/model/fields/field.py
index 1404b80..3eb95f2 100644
--- a/trytond/model/fields/field.py
+++ b/trytond/model/fields/field.py
@@ -81,6 +81,9 @@ def depends(*fields, **kwargs):
         @wraps(func)
         def wrapper(self, *args, **kwargs):
             for field in fields:
+                field = field.split('.')[0]
+                if field.startswith('_parent_'):
+                    field = field[8:]  # Strip '_parent_'
                 if not hasattr(self, field):
                     setattr(self, field, None)
             return func(self, *args, **kwargs)
@@ -248,6 +251,9 @@ class Field(object):
         table, _ = tables[None]
         name, operator, value = domain
         assert name == self.name
+        method = getattr(Model, 'domain_%s' % name, None)
+        if method:
+            return method(domain, tables)
         Operator = SQL_OPERATORS[operator]
         column = self.sql_column(table)
         expression = Operator(column, self._domain_value(operator, value))
diff --git a/trytond/model/fields/float.py b/trytond/model/fields/float.py
index 1041f0a..42a8db5 100644
--- a/trytond/model/fields/float.py
+++ b/trytond/model/fields/float.py
@@ -2,7 +2,7 @@
 #this repository contains the full copyright notices and license terms.
 from sql import Query, Expression
 
-from ...config import CONFIG
+from ... import backend
 from .field import Field, SQLType
 from ...pyson import PYSON
 
@@ -59,7 +59,7 @@ class Float(Field):
         return float(value)
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'postgresql':
             return SQLType('FLOAT8', 'FLOAT8')
         elif db_type == 'mysql':
diff --git a/trytond/model/fields/function.py b/trytond/model/fields/function.py
index d5489b7..8a72955 100644
--- a/trytond/model/fields/function.py
+++ b/trytond/model/fields/function.py
@@ -4,6 +4,7 @@
 import inspect
 import copy
 from trytond.model.fields.field import Field
+from trytond.transaction import Transaction
 
 
 class Function(Field):
@@ -37,14 +38,11 @@ class Function(Field):
 
     def __copy__(self):
         return Function(copy.copy(self._field), self.getter,
-                setter=self.setter, searcher=self.searcher)
+            setter=self.setter, searcher=self.searcher, loading=self.loading)
 
     def __deepcopy__(self, memo):
-        return Function(copy.deepcopy(self._field, memo),
-            copy.deepcopy(self.getter, memo),
-            setter=copy.deepcopy(self.setter, memo),
-            searcher=copy.deepcopy(self.searcher, memo),
-            loading=copy.deepcopy(self.loading, memo))
+        return Function(copy.deepcopy(self._field, memo), self.getter,
+            setter=self.setter, searcher=self.searcher, loading=self.loading)
 
     def __getattr__(self, name):
         return getattr(self._field, name)
@@ -71,36 +69,38 @@ class Function(Field):
         If the function has ``names`` in the function definition then
         it will call it with a list of name.
         '''
-        method = getattr(Model, self.getter)
+        with Transaction().set_context(_check_access=False):
+            method = getattr(Model, self.getter)
 
-        def call(name):
-            records = Model.browse(ids)
-            if not hasattr(method, 'im_self') or method.im_self:
-                return method(records, name)
+            def call(name):
+                records = Model.browse(ids)
+                if not hasattr(method, 'im_self') or method.im_self:
+                    return method(records, name)
+                else:
+                    return dict((r.id, method(r, name)) for r in records)
+            if isinstance(name, list):
+                names = name
+                # Test is the function works with a list of names
+                if 'names' in inspect.getargspec(method)[0]:
+                    return call(names)
+                return dict((name, call(name)) for name in names)
             else:
-                return dict((r.id, method(r, name)) for r in records)
-        if isinstance(name, list):
-            names = name
-            # Test is the function works with a list of names
-            if 'names' in inspect.getargspec(method)[0]:
-                return call(names)
-            return dict((name, call(name)) for name in names)
-        else:
-            # Test is the function works with a list of names
-            if 'names' in inspect.getargspec(method)[0]:
-                name = [name]
-            return call(name)
+                # Test is the function works with a list of names
+                if 'names' in inspect.getargspec(method)[0]:
+                    name = [name]
+                return call(name)
 
     def set(self, Model, name, ids, value, *args):
         '''
         Call the setter.
         '''
-        if self.setter:
-            # TODO change setter API to use sequence of records, value
-            setter = getattr(Model, self.setter)
-            args = iter((ids, value) + args)
-            for ids, value in zip(args, args):
-                setter(Model.browse(ids), name, value)
+        with Transaction().set_context(_check_access=False):
+            if self.setter:
+                # TODO change setter API to use sequence of records, value
+                setter = getattr(Model, self.setter)
+                args = iter((ids, value) + args)
+                for ids, value in zip(args, args):
+                    setter(Model.browse(ids), name, value)
 
     def __set__(self, inst, value):
         self._field.__set__(inst, value)
diff --git a/trytond/model/fields/integer.py b/trytond/model/fields/integer.py
index 2ba0694..64f51bf 100644
--- a/trytond/model/fields/integer.py
+++ b/trytond/model/fields/integer.py
@@ -2,7 +2,7 @@
 #this repository contains the full copyright notices and license terms.
 from sql import Query, Expression
 
-from ...config import CONFIG
+from ... import backend
 from .field import Field, SQLType
 
 
@@ -13,7 +13,7 @@ class Integer(Field):
     _type = 'integer'
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'postgresql':
             return SQLType('INT4', 'INT4')
         elif db_type == 'mysql':
@@ -22,7 +22,7 @@ class Integer(Field):
             return SQLType('INTEGER', 'INTEGER')
 
     def sql_format(self, value):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if (db_type == 'sqlite'
                 and value is not None
                 and not isinstance(value, (Query, Expression))):
@@ -37,7 +37,7 @@ class BigInteger(Integer):
     _type = 'biginteger'
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'postgresql':
             return SQLType('INT8', 'INT8')
         return super(BigInteger, self).sql_type()
diff --git a/trytond/model/fields/many2many.py b/trytond/model/fields/many2many.py
index f037eaa..46f4da5 100644
--- a/trytond/model/fields/many2many.py
+++ b/trytond/model/fields/many2many.py
@@ -5,8 +5,8 @@ from sql import Cast, Literal
 from sql.functions import Substring, Position
 
 from .field import Field, size_validate
-from ...transaction import Transaction
 from ...pool import Pool
+from ...tools import grouped_slice
 
 
 class Many2Many(Field):
@@ -58,6 +58,10 @@ class Many2Many(Field):
 
     size = property(_get_size, _set_size)
 
+    @property
+    def add_remove(self):
+        return self.domain
+
     def get(self, ids, model, name, values=None):
         '''
         Return target records ordered.
@@ -79,13 +83,12 @@ class Many2Many(Field):
         origin_field = Relation._fields[self.origin]
 
         relations = []
-        for i in range(0, len(ids), Transaction().cursor.IN_MAX):
-            sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
+        for sub_ids in grouped_slice(ids):
             if origin_field._type == 'reference':
                 references = ['%s,%s' % (model.__name__, x) for x in sub_ids]
                 clause = [(self.origin, 'in', references)]
             else:
-                clause = [(self.origin, 'in', sub_ids)]
+                clause = [(self.origin, 'in', list(sub_ids))]
             clause += [(self.target + '.id', '!=', None)]
             relations.append(Relation.search(clause, order=order))
         relations = list(chain(*relations))
@@ -150,12 +153,10 @@ class Many2Many(Field):
             if not target_ids:
                 return
             existing_ids = set()
-            in_max = Transaction().cursor.IN_MAX
-            for i in range(0, len(target_ids), in_max):
-                sub_ids = target_ids[i:i + in_max]
+            for sub_ids in grouped_slice(target_ids):
                 relations = Relation.search([
                         search_clause(ids),
-                        (self.target, 'in', sub_ids),
+                        (self.target, 'in', list(sub_ids)),
                         ])
                 for relation in relations:
                     existing_ids.add(getattr(relation, self.target).id)
@@ -172,12 +173,10 @@ class Many2Many(Field):
             target_ids = map(int, target_ids)
             if not target_ids:
                 return
-            in_max = Transaction().cursor.IN_MAX
-            for i in range(0, len(target_ids), in_max):
-                sub_ids = target_ids[i:i + in_max]
+            for sub_ids in grouped_slice(target_ids):
                 relation_to_delete.extend(Relation.search([
                             search_clause(ids),
-                            (self.target, 'in', sub_ids),
+                            (self.target, 'in', list(sub_ids)),
                             ]))
 
         def copy(ids, copy_ids, default=None):
diff --git a/trytond/model/fields/many2one.py b/trytond/model/fields/many2one.py
index ef109d0..e6aa9ba 100644
--- a/trytond/model/fields/many2one.py
+++ b/trytond/model/fields/many2one.py
@@ -6,7 +6,7 @@ from sql.operators import Or
 
 from .field import Field, SQLType
 from ...pool import Pool
-from ...config import CONFIG
+from ... import backend
 from ...tools import reduce_ids
 from ...transaction import Transaction
 
@@ -86,7 +86,7 @@ class Many2One(Field):
         return int(value)
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'postgresql':
             return SQLType('INT4', 'INT4')
         elif db_type == 'mysql':
diff --git a/trytond/model/fields/numeric.py b/trytond/model/fields/numeric.py
index 2a8dda3..3580bb7 100644
--- a/trytond/model/fields/numeric.py
+++ b/trytond/model/fields/numeric.py
@@ -3,7 +3,7 @@
 from decimal import Decimal
 from sql import Query, Expression, Cast, Literal, Select, CombiningQuery
 
-from ...config import CONFIG
+from ... import backend
 from .field import SQLType
 from .float import Float
 
@@ -26,14 +26,14 @@ class Numeric(Float):
         return value
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'mysql':
             return SQLType('DECIMAL', 'DECIMAL(65, 30)')
         return SQLType('NUMERIC', 'NUMERIC')
 
     def sql_column(self, table):
         column = super(Numeric, self).sql_column(table)
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'sqlite':
             # Must be casted as Decimal is stored as bytes
             column = Cast(column, self.sql_type().base)
@@ -41,7 +41,7 @@ class Numeric(Float):
 
     def _domain_value(self, operator, value):
         value = super(Numeric, self)._domain_value(operator, value)
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'sqlite':
             if isinstance(value, (Select, CombiningQuery)):
                 return value
diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py
index f9c4f2a..5387ab6 100644
--- a/trytond/model/fields/one2many.py
+++ b/trytond/model/fields/one2many.py
@@ -5,8 +5,8 @@ from sql import Cast, Literal
 from sql.functions import Substring, Position
 
 from .field import Field, size_validate
-from ...transaction import Transaction
 from ...pool import Pool
+from ...tools import grouped_slice
 
 
 def add_remove_validate(value):
@@ -87,13 +87,12 @@ class One2Many(Field):
             res[i] = []
 
         targets = []
-        for i in range(0, len(ids), Transaction().cursor.IN_MAX):
-            sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
+        for sub_ids in grouped_slice(ids):
             if field._type == 'reference':
                 references = ['%s,%s' % (model.__name__, x) for x in sub_ids]
                 clause = [(self.field, 'in', references)]
             else:
-                clause = [(self.field, 'in', sub_ids)]
+                clause = [(self.field, 'in', list(sub_ids))]
             targets.append(Relation.search(clause, order=self.order))
         targets = list(chain(*targets))
 
@@ -162,12 +161,10 @@ class One2Many(Field):
             target_ids = map(int, target_ids)
             if not target_ids:
                 return
-            in_max = Transaction().cursor.IN_MAX
-            for i in range(0, len(target_ids), in_max):
-                sub_ids = target_ids[i:i + in_max]
+            for sub_ids in grouped_slice(target_ids):
                 targets = Target.search([
                         search_clause(ids),
-                        ('id', 'in', sub_ids),
+                        ('id', 'in', list(sub_ids)),
                         ])
                 to_write.extend((targets, {
                             self.field: None,
diff --git a/trytond/model/fields/property.py b/trytond/model/fields/property.py
index 13737dd..94a77a4 100644
--- a/trytond/model/fields/property.py
+++ b/trytond/model/fields/property.py
@@ -42,25 +42,27 @@ class Property(Function):
         :param values:
         :return: a dictionary with ids as key and values as value
         '''
-        pool = Pool()
-        Property = pool.get('ir.property')
-        return Property.get(name, model.__name__, ids)
+        with Transaction().set_context(_check_access=False):
+            pool = Pool()
+            Property = pool.get('ir.property')
+            return Property.get(name, model.__name__, ids)
 
     def set(self, Model, name, ids, value, *args):
         '''
         Set the property.
         '''
-        pool = Pool()
-        Property = pool.get('ir.property')
-        args = iter((ids, value) + args)
-        for ids, value in zip(args, args):
-            if value is not None:
-                prop_value = '%s,%s' % (getattr(self, 'model_name', ''),
-                    str(value))
-            else:
-                prop_value = None
-            # TODO change set API to use sequence of records, value
-            Property.set(name, Model.__name__, ids, prop_value)
+        with Transaction().set_context(_check_access=False):
+            pool = Pool()
+            Property = pool.get('ir.property')
+            args = iter((ids, value) + args)
+            for ids, value in zip(args, args):
+                if value is not None:
+                    prop_value = '%s,%s' % (getattr(self, 'model_name', ''),
+                        str(value))
+                else:
+                    prop_value = None
+                # TODO change set API to use sequence of records, value
+                Property.set(name, Model.__name__, ids, prop_value)
 
     def convert_domain(self, domain, tables, Model):
         pool = Pool()
diff --git a/trytond/model/fields/reference.py b/trytond/model/fields/reference.py
index 1e3b378..e4a4079 100644
--- a/trytond/model/fields/reference.py
+++ b/trytond/model/fields/reference.py
@@ -8,7 +8,7 @@ from .field import Field, SQLType
 from .char import Char
 from ...transaction import Transaction
 from ...pool import Pool
-from ...config import CONFIG
+from ... import backend
 
 
 class Reference(Field):
@@ -68,7 +68,7 @@ class Reference(Field):
 
         # Check if reference ids still exist
         with Transaction().set_context(active_test=False), \
-                Transaction().set_user(0):
+                Transaction().set_context(_check_access=False):
             for ref_model, (ref_ids, ids) in ref_to_check.iteritems():
                 try:
                     pool.get(ref_model)
@@ -89,11 +89,14 @@ class Reference(Field):
         from ..model import Model
         if not isinstance(value, (Model, NoneType)):
             if isinstance(value, basestring):
-                target, id_ = value.split(',')
+                target, value = value.split(',')
             else:
-                target, id_ = value
+                target, value = value
             Target = Pool().get(target)
-            value = Target(id_)
+            if isinstance(value, dict):
+                value = Target(**value)
+            else:
+                value = Target(value)
         super(Reference, self).__set__(inst, value)
 
     @staticmethod
@@ -106,7 +109,7 @@ class Reference(Field):
         return Char.sql_format(value)
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'mysql':
             return SQLType('CHAR', 'VARCHAR(255)')
         return SQLType('VARCHAR', 'VARCHAR')
diff --git a/trytond/model/fields/selection.py b/trytond/model/fields/selection.py
index 230e153..1f75d27 100644
--- a/trytond/model/fields/selection.py
+++ b/trytond/model/fields/selection.py
@@ -4,7 +4,7 @@ import warnings
 
 from sql.conditionals import Case
 
-from ...config import CONFIG
+from ... import backend
 from .field import Field, SQLType
 
 
@@ -44,7 +44,7 @@ class Selection(Field):
     __init__.__doc__ += Field.__init__.__doc__
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'mysql':
             return SQLType('CHAR', 'VARCHAR(255)')
         return SQLType('VARCHAR', 'VARCHAR')
@@ -63,3 +63,29 @@ class Selection(Field):
         for key, value in selections:
             whens.append((column == key, value))
         return [Case(*whens, else_=column)]
+
+    def translated(self, name=None):
+        "Return a descriptor for the translated value of the field"
+        if name is None:
+            name = self.name
+        if name is None:
+            raise ValueError('Missing name argument')
+        return TranslatedSelection(name)
+
+
+class TranslatedSelection(object):
+    'A descriptor for translated value of Selection field'
+
+    def __init__(self, name):
+        self.name = name
+
+    def __get__(self, inst, cls):
+        if inst is None:
+            return self
+        selection = dict(cls.fields_get([self.name])[self.name]['selection'])
+        value = getattr(inst, self.name)
+        # None and '' are equivalent
+        if value is None or value == '':
+            if value not in selection:
+                value = {None: '', '': None}[value]
+        return selection[value]
diff --git a/trytond/model/fields/sha.py b/trytond/model/fields/sha.py
index 0209337..222aca8 100644
--- a/trytond/model/fields/sha.py
+++ b/trytond/model/fields/sha.py
@@ -3,7 +3,7 @@
 import hashlib
 from sql import Query, Expression
 
-from ...config import CONFIG
+from ... import backend
 from .field import SQLType
 from .char import Char
 
@@ -22,7 +22,7 @@ class Sha(Char):
         return super(Sha, self).sql_format(value)
 
     def sql_type(self):
-        db_type = CONFIG['db_type']
+        db_type = backend.name()
         if db_type == 'mysql':
             return SQLType('CHAR', 'VARCHAR(40)')
         return SQLType('VARCHAR', 'VARCHAR(40)')
diff --git a/trytond/model/match.py b/trytond/model/match.py
new file mode 100644
index 0000000..cb82f19
--- /dev/null
+++ b/trytond/model/match.py
@@ -0,0 +1,19 @@
+# This file is part of Tryton.  The COPYRIGHT file at the toplevel of this
+# repository contains the full copyright notices and license terms.
+
+
+class MatchMixin(object):
+
+    def match(self, pattern):
+        '''Match on pattern
+        pattern is a dictionary with model field as key
+        and matching value as value'''
+        for field, pattern_value in pattern.iteritems():
+            value = getattr(self, field)
+            if value is None:
+                continue
+            if self._fields[field]._type == 'many2one':
+                value = value.id
+            if value != pattern_value:
+                return False
+        return True
diff --git a/trytond/model/model.py b/trytond/model/model.py
index 8458697..13e8554 100644
--- a/trytond/model/model.py
+++ b/trytond/model/model.py
@@ -4,6 +4,7 @@
 import copy
 import collections
 import warnings
+from functools import total_ordering
 
 from trytond.model import fields
 from trytond.error import WarningErrorMixin
@@ -16,6 +17,7 @@ from trytond.rpc import RPC
 __all__ = ['Model']
 
 
+ at total_ordering
 class Model(WarningErrorMixin, URLMixin, PoolBase):
     """
     Define a model in Tryton.
@@ -30,6 +32,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
         cls.__rpc__ = {
             'default_get': RPC(),
             'fields_get': RPC(),
+            'on_change': RPC(instantiate=0),
             'on_change_with': RPC(instantiate=0),
             'pre_validate': RPC(instantiate=0),
             }
@@ -47,7 +50,14 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
             if not isinstance(getattr(cls, attr), fields.Field):
                 continue
             field_name = attr
-            field = copy.deepcopy(getattr(cls, field_name))
+            field = getattr(cls, field_name)
+            # Copy the original field definition to prevent side-effect with
+            # the mutable attributes
+            for parent_cls in cls.__mro__:
+                parent_field = getattr(parent_cls, field_name, None)
+                if isinstance(parent_field, fields.Field):
+                    field = parent_field
+            field = copy.deepcopy(field)
             setattr(cls, field_name, field)
 
             for attribute in ('on_change', 'on_change_with', 'autocomplete',
@@ -60,8 +70,14 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                         continue
                 else:
                     function_name = '%s_%s' % (attribute, field_name)
-                function = getattr(cls, function_name, None)
-                if function:
+                if not getattr(cls, function_name, None):
+                    continue
+                # Search depends on all parent class because field has been
+                # copied with the original definition
+                for parent_cls in cls.__mro__:
+                    function = getattr(parent_cls, function_name, None)
+                    if not function:
+                        continue
                     if getattr(function, 'depends', None):
                         setattr(field, attribute,
                             getattr(field, attribute) | function.depends)
@@ -157,12 +173,10 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
         Translation.register_error_messages(cls, module_name)
 
     @classmethod
-    def default_get(cls, fields_names, with_rec_name=True,
-            with_on_change=True):
+    def default_get(cls, fields_names, with_rec_name=True):
         '''
         Return a dict with the default values for each field in fields_names.
         If with_rec_name is True, rec_name will be added.
-        If with_on_change is True, on_change will be added.
         '''
         pool = Pool()
         Property = pool.get('ir.property')
@@ -186,8 +200,6 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                     value[field_name + '.rec_name'] = Target(
                         value[field_name]).rec_name
 
-        if with_on_change:
-            value = cls._default_on_change(value)
         if not with_rec_name:
             for field in value.keys():
                 if field.endswith('.rec_name'):
@@ -195,29 +207,6 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
         return value
 
     @classmethod
-    def _default_on_change(cls, value):
-        """
-        Call on_change function for the default value
-        and return new default value
-        """
-        pool = Pool()
-        res = value.copy()
-        val = {}
-        for field in value.keys():
-            if field in cls._fields:
-                if cls._fields[field].on_change:
-                    inst = cls()
-                    for fname in cls._fields[field].on_change:
-                        setattr(inst, fname, value.get(fname))
-                    val.update(getattr(inst, 'on_change_' + field)())
-                if cls._fields[field]._type in ('one2many',):
-                    Target = pool.get(cls._fields[field].model_name)
-                    for val2 in res[field]:
-                        val2.update(Target._default_on_change(val2))
-        res.update(val)
-        return res
-
-    @classmethod
     def fields_get(cls, fields_names=None):
         """
         Return the definition of each field on the model.
@@ -365,12 +354,15 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                         cls._fields[field].field)
             if res[field]['type'] == 'many2one':
                 target = cls._fields[field].get_target()
+                relation_fields = []
                 for target_name, target_field in target._fields.iteritems():
                     if (target_field._type == 'one2many'
                             and target_field.model_name == cls.__name__
                             and target_field.field == field):
-                        res[field]['relation_field'] = target_name
-                        break
+                        relation_fields.append(target_name)
+                # Set relation_field only if there is no ambiguity
+                if len(relation_fields) == 1:
+                    res[field]['relation_field'], = relation_fields
             if res[field]['type'] in ('datetime', 'time'):
                 res[field]['format'] = copy.copy(cls._fields[field].format)
             if res[field]['type'] == 'selection':
@@ -399,6 +391,14 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                 del res[i]
         return res
 
+    def on_change(self, fieldnames):
+        changes = []
+        for fieldname in sorted(fieldnames):
+            method = getattr(self, 'on_change_%s' % fieldname, None)
+            if method:
+                changes.append(method())
+        return changes
+
     def on_change_with(self, fieldnames):
         changes = {}
         for fieldname in fieldnames:
@@ -480,11 +480,6 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
             return NotImplemented
         return self.id < other.id
 
-    # TODO: replace by total_ordering when 2.6 will be dropped
-    __gt__ = lambda self, other: not (self < other or self == other)
-    __le__ = lambda self, other: self < other or self == other
-    __ge__ = lambda self, other: not self < other
-
     def __ne__(self, other):
         if not isinstance(other, Model):
             return NotImplemented
diff --git a/trytond/model/modelsingleton.py b/trytond/model/modelsingleton.py
index f9ff034..e1347f6 100644
--- a/trytond/model/modelsingleton.py
+++ b/trytond/model/modelsingleton.py
@@ -83,13 +83,12 @@ class ModelSingleton(ModelStorage):
         return res
 
     @classmethod
-    def default_get(cls, fields_names, with_rec_name=True,
-            with_on_change=True):
+    def default_get(cls, fields_names, with_rec_name=True):
         if '_timestamp' in fields_names:
             fields_names = list(fields_names)
             fields_names.remove('_timestamp')
         default = super(ModelSingleton, cls).default_get(fields_names,
-                with_rec_name=with_rec_name, with_on_change=with_on_change)
+                with_rec_name=with_rec_name)
         singleton = cls.get_singleton()
         if singleton:
             if with_rec_name:
diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py
index 58b7dc7..4f3cf43 100644
--- a/trytond/model/modelsql.py
+++ b/trytond/model/modelsql.py
@@ -3,7 +3,7 @@
 import re
 import datetime
 from functools import reduce
-from itertools import islice, izip, chain
+from itertools import islice, izip, chain, ifilter
 
 from sql import Table, Column, Literal, Desc, Asc, Expression, Flavor
 from sql.functions import Now, Extract
@@ -14,7 +14,7 @@ from sql.aggregate import Count, Max
 from trytond.model import ModelStorage, ModelView
 from trytond.model import fields
 from trytond import backend
-from trytond.tools import reduce_ids
+from trytond.tools import reduce_ids, grouped_slice
 from trytond.const import OPERATORS, RECORD_CACHE_SIZE
 from trytond.transaction import Transaction
 from trytond.pool import Pool
@@ -139,8 +139,7 @@ class ModelSQL(ModelStorage):
             if isinstance(field, fields.Many2One) \
                     and field.model_name == cls.__name__ \
                     and field.left and field.right:
-                with Transaction().set_user(0):
-                    cls._rebuild_tree(field_name, None, 0)
+                cls._rebuild_tree(field_name, None, 0)
 
         for ident, constraint, _ in cls._sql_constraints:
             table.add_constraint(ident, constraint)
@@ -189,10 +188,6 @@ class ModelSQL(ModelStorage):
 
     @staticmethod
     def table_query():
-        '''
-        Return None if the model is a real table in the database
-        or return a tuple with the SQL query and the arguments.
-        '''
         return None
 
     @classmethod
@@ -246,9 +241,7 @@ class ModelSQL(ModelStorage):
         table = cls.__table_history__()
         user = User.__table__()
         revisions = []
-        in_max = cursor.IN_MAX
-        for i in range(0, len(ids), in_max):
-            sub_ids = ids[i:i + in_max]
+        for sub_ids in grouped_slice(ids):
             where = reduce_ids(table.id, sub_ids)
             cursor.execute(*table.join(user, 'LEFT',
                     Coalesce(table.write_uid, table.create_uid) == user.id)
@@ -272,7 +265,6 @@ class ModelSQL(ModelStorage):
     def __insert_history(cls, ids, deleted=False):
         transaction = Transaction()
         cursor = transaction.cursor
-        in_max = cursor.IN_MAX
         if not cls._history:
             return
         user = transaction.user
@@ -293,8 +285,7 @@ class ModelSQL(ModelStorage):
                 continue
             columns.append(Column(table, fname))
             hcolumns.append(Column(history, fname))
-        for i in range(0, len(ids), in_max):
-            sub_ids = ids[i:i + in_max]
+        for sub_ids in grouped_slice(ids):
             if not deleted:
                 where = reduce_ids(table.id, sub_ids)
                 cursor.execute(*history.insert(hcolumns,
@@ -310,14 +301,13 @@ class ModelSQL(ModelStorage):
             return
         transaction = Transaction()
         cursor = transaction.cursor
-        in_max = cursor.IN_MAX
         table = cls.__table__()
         history = cls.__table_history__()
         columns = []
         hcolumns = []
-        for fname, field in sorted(cls._fields.iteritems()):
-            if hasattr(field, 'set'):
-                continue
+        fnames = sorted(n for n, f in cls._fields.iteritems()
+            if not hasattr(f, 'set'))
+        for fname in fnames:
             columns.append(Column(table, fname))
             if fname == 'write_uid':
                 hcolumns.append(Literal(transaction.user))
@@ -326,16 +316,20 @@ class ModelSQL(ModelStorage):
             else:
                 hcolumns.append(Column(history, fname))
 
+        def is_deleted(values):
+            return all(not v for n, v in zip(fnames, values)
+                if n not in ['id', 'write_uid', 'write_date'])
+
         to_delete = []
         to_update = []
         for id_ in ids:
             column_datetime = Coalesce(history.write_date, history.create_date)
             hwhere = (column_datetime <= datetime) & (history.id == id_)
-            horder = column_datetime.desc
+            horder = (column_datetime.desc, Column(history, '__id').desc)
             cursor.execute(*history.select(*hcolumns,
                     where=hwhere, order_by=horder, limit=1))
             values = cursor.fetchone()
-            if not values:
+            if not values or is_deleted(values):
                 to_delete.append(id_)
             else:
                 to_update.append(id_)
@@ -351,8 +345,7 @@ class ModelSQL(ModelStorage):
                     cursor.execute(*table.insert(columns, [values]))
 
         if to_delete:
-            for i in range(0, len(to_delete), in_max):
-                sub_ids = to_delete[i:i + in_max]
+            for sub_ids in grouped_slice(to_delete):
                 where = reduce_ids(table.id, sub_ids)
                 cursor.execute(*table.delete(where=where))
             cls.__insert_history(to_delete, True)
@@ -363,12 +356,10 @@ class ModelSQL(ModelStorage):
     def __check_timestamp(cls, ids):
         transaction = Transaction()
         cursor = transaction.cursor
-        in_max = cursor.IN_MAX
         table = cls.__table__()
         if not transaction.timestamp:
             return
-        for i in range(0, len(ids), in_max):
-            sub_ids = ids[i:i + in_max]
+        for sub_ids in grouped_slice(ids):
             where = Or()
             for id_ in sub_ids:
                 try:
@@ -394,7 +385,6 @@ class ModelSQL(ModelStorage):
         cursor = transaction.cursor
         pool = Pool()
         Translation = pool.get('ir.translation')
-        in_max = cursor.IN_MAX
 
         super(ModelSQL, cls).create(vlist)
 
@@ -422,8 +412,7 @@ class ModelSQL(ModelStorage):
                     default.append(f)
 
             if default:
-                defaults = cls.default_get(default, with_rec_name=False,
-                    with_on_change=False)
+                defaults = cls.default_get(default, with_rec_name=False)
                 values.update(cls._clean_defaults(defaults))
 
             insert_columns = [table.create_uid, table.create_date]
@@ -455,15 +444,15 @@ class ModelSQL(ModelStorage):
                 new_ids.append(id_new)
             except DatabaseIntegrityError, exception:
                 with Transaction().new_cursor(), \
-                        Transaction().set_user(0):
+                        Transaction().set_context(_check_access=False):
                     cls.__raise_integrity_error(exception, values)
                 raise
 
         domain = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='create')
         if domain:
-            for i in range(0, len(new_ids), in_max):
-                sub_ids = new_ids[i:i + in_max]
+            for sub_ids in grouped_slice(new_ids):
+                sub_ids = list(sub_ids)
                 red_sql = reduce_ids(table.id, sub_ids)
 
                 cursor.execute(*table.select(table.id,
@@ -499,8 +488,8 @@ class ModelSQL(ModelStorage):
         cls.__insert_history(new_ids)
 
         records = cls.browse(new_ids)
-        for i in range(0, len(records), RECORD_CACHE_SIZE):
-            cls._validate(records[i:i + RECORD_CACHE_SIZE])
+        for sub_records in grouped_slice(records, RECORD_CACHE_SIZE):
+            cls._validate(sub_records)
 
         field_names = cls._fields.keys()
         cls._update_mptt(field_names, [new_ids] * len(field_names))
@@ -575,8 +564,8 @@ class ModelSQL(ModelStorage):
             if 'id' not in fields_names:
                 columns.append(table.id.as_('id'))
 
-            for i in range(0, len(ids), in_max):
-                sub_ids = ids[i:i + in_max]
+            for sub_ids in grouped_slice(ids, in_max):
+                sub_ids = list(sub_ids)
                 red_sql = reduce_ids(table.id, sub_ids)
                 where = red_sql
                 if history_clause:
@@ -745,7 +734,6 @@ class ModelSQL(ModelStorage):
         pool = Pool()
         Translation = pool.get('ir.translation')
         Config = pool.get('ir.configuration')
-        in_max = cursor.IN_MAX
 
         assert not len(args) % 2
         all_records = sum(((records, values) + args)[0:None:2], [])
@@ -787,8 +775,8 @@ class ModelSQL(ModelStorage):
                         update_values.append(field.sql_format(value))
 
             domain = pool.get('ir.rule').domain_get(cls.__name__, mode='write')
-            for i in range(0, len(ids), in_max):
-                sub_ids = ids[i:i + in_max]
+            for sub_ids in grouped_slice(ids):
+                sub_ids = list(sub_ids)
                 red_sql = reduce_ids(table.id, sub_ids)
                 where = red_sql
                 if domain:
@@ -811,7 +799,7 @@ class ModelSQL(ModelStorage):
                             where=red_sql))
                 except DatabaseIntegrityError, exception:
                     with Transaction().new_cursor(), \
-                            Transaction().set_user(0):
+                            Transaction().set_context(_check_access=False):
                         cls.__raise_integrity_error(exception, values,
                             values.keys())
                     raise
@@ -826,18 +814,17 @@ class ModelSQL(ModelStorage):
                 if hasattr(field, 'set'):
                     fields_to_set.setdefault(fname, []).extend((ids, value))
 
-            field_names = cls._fields.keys()
+            field_names = values.keys()
             cls._update_mptt(field_names, [ids] * len(field_names), values)
-            all_field_names |= set(values.keys())
+            all_field_names |= set(field_names)
 
         for fname, fargs in fields_to_set.iteritems():
             field = cls._fields[fname]
             field.set(cls, fname, *fargs)
 
         cls.__insert_history(all_ids)
-        for i in range(0, len(all_records), RECORD_CACHE_SIZE):
-            cls._validate(all_records[i:i + RECORD_CACHE_SIZE],
-                field_names=all_field_names)
+        for sub_records in grouped_slice(all_records, RECORD_CACHE_SIZE):
+            cls._validate(sub_records, field_names=all_field_names)
         cls.trigger_write(trigger_eligibles)
 
     @classmethod
@@ -848,7 +835,6 @@ class ModelSQL(ModelStorage):
         pool = Pool()
         Translation = pool.get('ir.translation')
         ids = map(int, records)
-        in_max = cursor.IN_MAX
 
         if not ids:
             return
@@ -871,8 +857,7 @@ class ModelSQL(ModelStorage):
                     and field.model_name == cls.__name__
                     and field.left and field.right):
                 tree_ids[fname] = []
-                for i in range(0, len(ids), in_max):
-                    sub_ids = ids[i:i + in_max]
+                for sub_ids in grouped_slice(ids):
                     where = reduce_ids(Column(table, fname), sub_ids)
                     cursor.execute(*table.select(table.id, where=where))
                     tree_ids[fname] += [x[0] for x in cursor.fetchall()]
@@ -903,8 +888,8 @@ class ModelSQL(ModelStorage):
         domain = pool.get('ir.rule').domain_get(cls.__name__, mode='delete')
 
         if domain:
-            for i in range(0, len(ids), in_max):
-                sub_ids = ids[i:i + in_max]
+            for sub_ids in grouped_slice(ids):
+                sub_ids = list(sub_ids)
                 red_sql = reduce_ids(table.id, sub_ids)
                 cursor.execute(*table.select(table.id,
                         where=red_sql & table.id.in_(domain)))
@@ -916,9 +901,9 @@ class ModelSQL(ModelStorage):
 
         cls.trigger_delete(records)
 
-        for i in range(0, len(ids), in_max):
-            sub_ids = ids[i:i + in_max]
-            sub_records = records[i:i + in_max]
+        for sub_ids, sub_records in izip(
+                grouped_slice(ids), grouped_slice(records)):
+            sub_ids = list(sub_ids)
             red_sql = reduce_ids(table.id, sub_ids)
 
             transaction.delete_records.setdefault(cls.__name__,
@@ -953,7 +938,7 @@ class ModelSQL(ModelStorage):
                     Model.delete(models)
 
             for Model, field_name in foreign_keys_tocheck:
-                with Transaction().set_user(0):
+                with Transaction().set_context(_check_access=False):
                     if Model.search([
                                 (field_name, 'in', sub_ids),
                                 ], order=[]):
@@ -961,7 +946,7 @@ class ModelSQL(ModelStorage):
                         cls.raise_user_error('foreign_model_exist',
                             error_args=error_args)
 
-            super(ModelSQL, cls).delete(sub_records)
+            super(ModelSQL, cls).delete(list(sub_records))
 
             try:
                 cursor.execute(*table.delete(where=red_sql))
@@ -983,7 +968,6 @@ class ModelSQL(ModelStorage):
         Rule = pool.get('ir.rule')
         transaction = Transaction()
         cursor = transaction.cursor
-        in_max = cursor.IN_MAX
 
         # Get domain clauses
         tables, expression = cls.search_domain(domain)
@@ -1034,6 +1018,7 @@ class ModelSQL(ModelStorage):
             columns.append(Coalesce(
                     main_table.write_date,
                     main_table.create_date).as_('_datetime'))
+            columns.append(Column(main_table, '__id'))
         if not query:
             columns += [Column(main_table, name).as_(name)
                 for name, field in cls._fields.iteritems()
@@ -1053,11 +1038,50 @@ class ModelSQL(ModelStorage):
         cursor.execute(*select)
 
         rows = cursor.dictfetchmany(cursor.IN_MAX)
-        cache = cursor.get_cache(transaction.context)
+        cache = cursor.get_cache()
         if cls.__name__ not in cache:
             cache[cls.__name__] = LRUDict(RECORD_CACHE_SIZE)
         delete_records = transaction.delete_records.setdefault(cls.__name__,
             set())
+
+        def filter_history(rows):
+            if not (cls._history and transaction.context.get('_datetime')):
+                return rows
+
+            def history_key(row):
+                return row['_datetime'], row['__id']
+
+            ids_history = {}
+            for row in rows:
+                key = history_key(row)
+                if row['id'] in ids_history:
+                    if key < ids_history[row['id']]:
+                        continue
+                ids_history[row['id']] = key
+
+            to_delete = set()
+            history = cls.__table_history__()
+            for sub_ids in grouped_slice([r['id'] for r in rows]):
+                where = reduce_ids(history.id, sub_ids)
+                cursor.execute(*history.select(history.id, history.write_date,
+                        where=where
+                        & (history.write_date != None)
+                        & (history.create_date == None)
+                        & (history.write_date
+                            <= transaction.context['_datetime'])))
+                for deleted_id, delete_date in cursor.fetchall():
+                    history_date, _ = ids_history[deleted_id]
+                    if isinstance(history_date, basestring):
+                        strptime = datetime.datetime.strptime
+                        format_ = '%Y-%m-%d %H:%M:%S.%f'
+                        history_date = strptime(history_date, format_)
+                    if history_date <= delete_date:
+                        to_delete.add(deleted_id)
+
+            return ifilter(lambda r: history_key(r) == ids_history[r['id']]
+                and r['id'] not in to_delete, rows)
+
+        rows = list(filter_history(rows))
         keys = None
         for data in islice(rows, 0, cache.size_limit):
             if data['id'] in delete_records:
@@ -1065,7 +1089,7 @@ class ModelSQL(ModelStorage):
             if keys is None:
                 keys = data.keys()
                 for k in keys[:]:
-                    if k in ('_timestamp', '_datetime'):
+                    if k in ('_timestamp', '_datetime', '__id'):
                         keys.remove(k)
                         continue
                     field = cls._fields[k]
@@ -1086,34 +1110,7 @@ class ModelSQL(ModelStorage):
             cursor.execute(*table.select(*columns,
                     where=expression, order_by=order_by,
                     limit=limit, offset=offset))
-            rows = cursor.dictfetchall()
-
-        if cls._history and transaction.context.get('_datetime'):
-            ids = []
-            ids_date = {}
-            for data in rows:
-                if data['id'] in ids_date:
-                    if data['_datetime'] <= ids_date[data['id']]:
-                        continue
-                if data['id'] in ids:
-                    ids.remove(data['id'])
-                ids.append(data['id'])
-                ids_date[data['id']] = data['_datetime']
-            to_delete = set()
-            history = cls.__table_history__()
-            for i in range(0, len(ids), in_max):
-                sub_ids = ids[i:i + in_max]
-                where = reduce_ids(history.id, sub_ids)
-                cursor.execute(*history.select(history.id, history.write_date,
-                        where=where
-                        & (history.write_date != None)
-                        & (history.create_date == None)
-                        & (history.write_date
-                            <= transaction.context['_datetime'])))
-                for deleted_id, delete_date in cursor.fetchall():
-                    if ids_date[deleted_id] < delete_date:
-                        to_delete.add(deleted_id)
-            return cls.browse(filter(lambda x: x not in to_delete, ids))
+            rows = filter_history(cursor.dictfetchall())
 
         return cls.browse([x['id'] for x in rows])
 
@@ -1184,8 +1181,7 @@ class ModelSQL(ModelStorage):
                         cls._update_tree(id_, field_name,
                             field.left, field.right)
                 else:
-                    with Transaction().set_user(0):
-                        cls._rebuild_tree(field_name, None, 0)
+                    cls._rebuild_tree(field_name, None, 0)
 
     @classmethod
     def _rebuild_tree(cls, parent, parent_id, left):
@@ -1291,8 +1287,7 @@ class ModelSQL(ModelStorage):
                 sql_clause = '(id != ' + param + ' AND ' + sql_clause + ')'
 
                 in_max = cursor.IN_MAX / (len(columns) + 1)
-                for i in range(0, len(ids), in_max):
-                    sub_ids = ids[i:i + in_max]
+                for sub_ids in grouped_slice(ids, in_max):
                     red_sql = reduce_ids(table.id, sub_ids)
 
                     cursor.execute('SELECT id,' + sql + ' '
@@ -1312,8 +1307,7 @@ class ModelSQL(ModelStorage):
             match = _RE_CHECK.match(sql)
             if match:
                 sql = match.group(1)
-                for i in range(0, len(ids), cursor.IN_MAX):
-                    sub_ids = ids[i:i + cursor.IN_MAX]
+                for sub_ids in grouped_slice(ids):
                     red_sql = reduce_ids(table.id, sub_ids)
                     cursor.execute('SELECT id '
                         'FROM "' + cls._table + '" '
diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py
index 6698c98..232b038 100644
--- a/trytond/model/modelstorage.py
+++ b/trytond/model/modelstorage.py
@@ -14,16 +14,17 @@ from decimal import Decimal
 from itertools import islice, ifilter, chain, izip
 from functools import reduce
 from operator import itemgetter
+from collections import defaultdict
 
 from trytond.model import Model
 from trytond.model import fields
-from trytond.tools import safe_eval, reduce_domain, memoize
+from trytond.tools import reduce_domain, memoize
 from trytond.pyson import PYSONEncoder, PYSONDecoder, PYSON
 from trytond.const import OPERATORS, RECORD_CACHE_SIZE, BROWSE_FIELD_TRESHOLD
 from trytond.transaction import Transaction
 from trytond.pool import Pool
-from trytond.cache import LRUDict
-from trytond.config import CONFIG
+from trytond.cache import LRUDict, freeze
+from trytond import backend
 from trytond.rpc import RPC
 from .modelview import ModelView
 
@@ -451,6 +452,9 @@ class ModelStorage(Model):
                 if not isinstance(value, ModelStorage):
                     break
                 field_name = fields_tree[i]
+                descriptor = None
+                if '.' in field_name:
+                    field_name, descriptor = field_name.split('.')
                 eModel = pool.get(value.__name__)
                 field = eModel._fields[field_name]
                 if field.states and 'invisible' in field.states:
@@ -466,7 +470,10 @@ class ModelStorage(Model):
                     if invisible:
                         value = ''
                         break
-                value = getattr(value, field_name)
+                if descriptor:
+                    value = getattr(field, descriptor)().__get__(value, eModel)
+                else:
+                    value = getattr(value, field_name)
                 if isinstance(value, (list, tuple)):
                     first = True
                     child_fields_names = [(x[:i + 1] == fields_tree[:i + 1] and
@@ -746,7 +753,7 @@ class ModelStorage(Model):
         # Allow root user to update/delete
         if Transaction().user == 0:
             return True
-        with Transaction().set_user(0):
+        with Transaction().set_context(_check_access=False):
             models_data = ModelData.search([
                 ('model', '=', cls.__name__),
                 ('db_id', 'in', map(int, records)),
@@ -758,10 +765,7 @@ class ModelStorage(Model):
             for model_data in models_data:
                 if not model_data.values:
                     continue
-                xml_values = safe_eval(model_data.values, {
-                    'Decimal': Decimal,
-                    'datetime': datetime,
-                    })
+                xml_values = ModelData.load_values(model_data.values)
                 for key, val in values.iteritems():
                     if key in xml_values and val != xml_values[key]:
                         return False
@@ -839,7 +843,7 @@ class ModelStorage(Model):
     def _validate(cls, records, field_names=None):
         pool = Pool()
         # Ensure that records are readable
-        with Transaction().set_user(0, set_context=True):
+        with Transaction().set_context(_check_access=False):
             records = cls.browse(records)
 
         def call(name):
@@ -893,6 +897,7 @@ class ModelStorage(Model):
                 Relation = field.get_target()
             else:
                 Relation = cls
+            domains = defaultdict(list)
             if is_pyson(field.domain):
                 pyson_domain = PYSONEncoder().encode(field.domain)
                 for record in records:
@@ -902,12 +907,13 @@ class ModelStorage(Model):
                     env['time'] = time
                     env['context'] = Transaction().context
                     env['active_id'] = record.id
-                    domain = PYSONDecoder(env).decode(pyson_domain)
-                    validate_relation_domain(
-                        field, [record], Relation, domain)
+                    domain = freeze(PYSONDecoder(env).decode(pyson_domain))
+                    domains[domain].append(record)
             else:
-                validate_relation_domain(
-                    field, records, Relation, field.domain)
+                domains[freeze(field.domain)].extend(records)
+
+            for domain, sub_records in domains.iteritems():
+                validate_relation_domain(field, sub_records, Relation, domain)
 
         def validate_relation_domain(field, records, Relation, domain):
             if field._type in ('many2one', 'one2many', 'many2many', 'one2one'):
@@ -1003,7 +1009,7 @@ class ModelStorage(Model):
                     def raise_user_error(value):
                         error_args = cls._get_error_args(field_name)
                         error_args['digits'] = digits[1]
-                        error_args['value'] = value
+                        error_args['value'] = repr(value)
                         cls.raise_user_error('digits_validation_record',
                             error_args=error_args)
                     if value is None:
@@ -1012,7 +1018,7 @@ class ModelStorage(Model):
                         if (value.quantize(Decimal(str(10.0 ** -digits[1])))
                                 != value):
                             raise_user_error(value)
-                    elif CONFIG.options['db_type'] != 'mysql':
+                    elif backend.name() != 'mysql':
                         if not (round(value, digits[1]) == float(value)):
                             raise_user_error(value)
                 # validate digits
@@ -1138,7 +1144,7 @@ class ModelStorage(Model):
         else:
             self._ids = [id]
 
-        self._cursor_cache = self._cursor.get_cache(self._context)
+        self._cursor_cache = self._cursor.get_cache()
 
         if _local_cache is not None:
             self._local_cache = _local_cache
@@ -1223,6 +1229,14 @@ class ModelStorage(Model):
                 datetime_field = self._fields[field.datetime_field]
                 ffields[field.datetime_field] = datetime_field
 
+        # add depends of field with context
+        for field in ffields.values():
+            if field.context:
+                for context_field_name in field.depends:
+                    context_field = self._fields.get(context_field_name)
+                    if context_field not in ffields:
+                        ffields[context_field_name] = context_field
+
         def filter_(id_):
             return (name not in self._cache.get(id_, {})
                 and name not in self._local_cache.get(id_, {}))
@@ -1258,14 +1272,18 @@ class ModelStorage(Model):
             except KeyError:
                 return value
             ctx = {}
+            if field.context:
+                pyson_context = PYSONEncoder().encode(field.context)
+                ctx.update(PYSONDecoder(data).decode(pyson_context))
             datetime_ = None
             if getattr(field, 'datetime_field', None):
                 datetime_ = data.get(field.datetime_field)
                 ctx = {'_datetime': datetime_}
             with Transaction().set_context(**ctx):
-                local_cache = model2cache.setdefault((Model, datetime_),
+                key = (Model, freeze(ctx))
+                local_cache = model2cache.setdefault(key,
                     LRUDict(RECORD_CACHE_SIZE))
-                ids = model2ids.setdefault((Model, datetime_), [])
+                ids = model2ids.setdefault(key, [])
                 if field._type in ('many2one', 'one2one', 'reference'):
                     ids.append(value)
                     return Model(value, _ids=ids, _local_cache=local_cache)
@@ -1301,13 +1319,14 @@ class ModelStorage(Model):
                     if data['id'] == self.id and fname == name:
                         value = fvalue
                     if (field._type not in ('many2one', 'one2one', 'one2many',
-                                'many2many', 'reference')
+                                'many2many', 'reference', 'binary')
                             and not isinstance(field, fields.Function)):
                         continue
                     if data['id'] not in self._local_cache:
                         self._local_cache[data['id']] = {}
                     self._local_cache[data['id']][fname] = fvalue
                     if (field._type not in ('many2one', 'reference')
+                            or field.context
                             or getattr(field, 'datetime_field', None)
                             or isinstance(field, fields.Function)):
                         del data[fname]
diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py
index ae853e2..643bcba 100644
--- a/trytond/model/modelview.py
+++ b/trytond/model/modelview.py
@@ -253,7 +253,7 @@ class ModelView(Model):
             - relate: a list of available relations
         """
         Action = Pool().get('ir.action.keyword')
-        key = (cls.__name__, repr(Transaction().context))
+        key = cls.__name__
         result = cls._view_toolbar_get_cache.get(key)
         if result:
             return result
@@ -426,7 +426,9 @@ class ModelView(Model):
         # convert attributes into pyson
         encoder = PYSONEncoder()
         for attr in ('states', 'domain', 'spell', 'colors'):
-            if element.get(attr):
+            if (element.get(attr)
+                    # Avoid double evaluation from inherit with different model
+                    and '__' not in element.get(attr)):
                 element.set(attr, encoder.encode(safe_eval(element.get(attr),
                     CONTEXT)))
 
@@ -476,17 +478,22 @@ class ModelView(Model):
         @wraps(func)
         def wrapper(cls, *args, **kwargs):
             pool = Pool()
+            ModelAccess = pool.get('ir.model.access')
             Button = pool.get('ir.model.button')
             User = pool.get('res.user')
 
-            if Transaction().user != 0:
+            if ((Transaction().user != 0)
+                    and Transaction().context.get('_check_access')):
+                ModelAccess.check(cls.__name__, 'read')
+                ModelAccess.check(cls.__name__, 'write')
                 groups = set(User.get_groups())
                 button_groups = Button.get_groups(cls.__name__,
                     func.__name__)
                 if button_groups and not groups & button_groups:
                     raise UserError('Calling button %s on %s is not allowed!'
                         % (func.__name__, cls.__name__))
-            return func(cls, *args, **kwargs)
+            with Transaction().set_context(_check_access=False):
+                return func(cls, *args, **kwargs)
         return wrapper
 
     @staticmethod
diff --git a/trytond/model/union.py b/trytond/model/union.py
new file mode 100644
index 0000000..b65f1ad
--- /dev/null
+++ b/trytond/model/union.py
@@ -0,0 +1,67 @@
+# This file is part of Tryton.  The COPYRIGHT file at the toplevel of this
+# repository contains the full copyright notices and license terms.
+from sql import Union, Column, Literal, Cast
+
+from trytond.model import fields
+from trytond.pool import Pool
+
+
+class UnionMixin:
+    'Mixin to combine models'
+
+    @staticmethod
+    def union_models():
+        return []
+
+    @classmethod
+    def union_shard(cls, column, model):
+        models = cls.union_models()
+        length = len(models)
+        i = models.index(model)
+        return ((column * length) + i)
+
+    @classmethod
+    def union_unshard(cls, record_id):
+        pool = Pool()
+        models = cls.union_models()
+        length = len(models)
+        record_id, i = divmod(record_id, length)
+        Model = pool.get(models[i])
+        return Model(record_id)
+
+    @classmethod
+    def union_column(cls, name, field, table, Model):
+        column = Literal(None)
+        union_field = Model._fields.get(name)
+        if union_field:
+            column = Column(table, union_field.name)
+            if (isinstance(field, fields.Many2One)
+                    and field.model_name == cls.__name__):
+                target_model = union_field.model_name
+                if target_model in cls.union_models():
+                    column = cls.union_shard(column, target_model)
+                else:
+                    column = Literal(None)
+        return column
+
+    @classmethod
+    def union_columns(cls, model):
+        pool = Pool()
+        Model = pool.get(model)
+        table = Model.__table__()
+        columns = [cls.union_shard(table.id, model).as_('id')]
+        for name in sorted(cls._fields.keys()):
+            field = cls._fields[name]
+            if name == 'id' or hasattr(field, 'set'):
+                continue
+            column = cls.union_column(name, field, table, Model)
+            columns.append(Cast(column, field.sql_type().base).as_(name))
+        return table, columns
+
+    @classmethod
+    def table_query(cls):
+        queries = []
+        for model in cls.union_models():
+            table, columns = cls.union_columns(model)
+            queries.append(table.select(*columns))
+        return Union(*queries)
diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py
index e7ee406..7c233b5 100644
--- a/trytond/modules/__init__.py
+++ b/trytond/modules/__init__.py
@@ -14,7 +14,7 @@ from sql import Table
 from sql.functions import Now
 
 import trytond.tools as tools
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.transaction import Transaction
 from trytond.cache import Cache
 import trytond.convert as convert
@@ -135,11 +135,11 @@ class Node(Singleton):
 
 def get_module_info(name):
     "Return the content of the tryton.cfg"
-    config = ConfigParser.ConfigParser()
+    module_config = ConfigParser.ConfigParser()
     with tools.file_open(os.path.join(name, 'tryton.cfg')) as fp:
-        config.readfp(fp)
+        module_config.readfp(fp)
         directory = os.path.dirname(fp.name)
-    info = dict(config.items('tryton'))
+    info = dict(module_config.items('tryton'))
     info['directory'] = directory
     for key in ('depends', 'extras_depend', 'xml'):
         if key in info:
@@ -152,11 +152,7 @@ def create_graph(module_list):
     packages = []
 
     for module in module_list:
-        try:
-            info = get_module_info(module)
-        except IOError:
-            if module != 'all':
-                raise Exception('Module %s not found' % module)
+        info = get_module_info(module)
         packages.append((module, info.get('depends', []),
                 info.get('extras_depend', []), info))
 
@@ -193,18 +189,17 @@ def create_graph(module_list):
     return graph, packages, later
 
 
-def is_module_to_install(module):
-    for kind in ('init', 'update'):
-        if 'all' in CONFIG[kind] and module != 'tests':
-            return True
-        elif module in CONFIG[kind]:
-            return True
+def is_module_to_install(module, update):
+    if module in update:
+        return True
     return False
 
 
-def load_module_graph(graph, pool, lang=None):
+def load_module_graph(graph, pool, update=None, lang=None):
     if lang is None:
-        lang = [CONFIG['language']]
+        lang = [config.get('database', 'language')]
+    if update is None:
+        update = []
     modules_todo = []
     models_to_update_history = set()
     logger = logging.getLogger('modules')
@@ -222,7 +217,7 @@ def load_module_graph(graph, pool, lang=None):
         logger.info(module)
         classes = pool.setup(module)
         package_state = module2state.get(module, 'uninstalled')
-        if (is_module_to_install(module)
+        if (is_module_to_install(module, update)
                 or package_state in ('to install', 'to upgrade')):
             if package_state not in ('to install', 'to upgrade'):
                 if package_state == 'installed':
@@ -362,7 +357,7 @@ def register_classes():
         MODULES.append(module)
 
 
-def load_modules(database_name, pool, update=False, lang=None):
+def load_modules(database_name, pool, update=None, lang=None):
     res = True
 
     def _load_modules():
@@ -372,29 +367,20 @@ def load_modules(database_name, pool, update=False, lang=None):
             # Migration from 2.2: workflow module removed
             cursor.execute(*ir_module.delete(
                     where=(ir_module.name == 'workflow')))
-            if 'all' in CONFIG['init']:
-                cursor.execute(*ir_module.select(ir_module.name,
-                        where=(ir_module.name != 'tests')))
-            else:
-                cursor.execute(*ir_module.select(ir_module.name,
-                        where=ir_module.state.in_(('installed', 'to install',
-                                'to upgrade', 'to remove'))))
+            cursor.execute(*ir_module.select(ir_module.name,
+                    where=ir_module.state.in_(('installed', 'to install',
+                            'to upgrade', 'to remove'))))
         else:
             cursor.execute(*ir_module.select(ir_module.name,
                     where=ir_module.state.in_(('installed', 'to upgrade',
                             'to remove'))))
         module_list = [name for (name,) in cursor.fetchall()]
         if update:
-            for module in CONFIG['init'].keys():
-                if CONFIG['init'][module]:
-                    module_list.append(module)
-            for module in CONFIG['update'].keys():
-                if CONFIG['update'][module]:
-                    module_list.append(module)
+            module_list += update
         graph = create_graph(module_list)[0]
 
         try:
-            load_module_graph(graph, pool, lang)
+            load_module_graph(graph, pool, update, lang)
         except Exception:
             cursor.rollback()
             raise
@@ -423,6 +409,7 @@ def load_modules(database_name, pool, update=False, lang=None):
             Module = pool.get('ir.module.module')
             Module.update_list()
         cursor.commit()
+        Cache.resets(database_name)
 
     if not Transaction().cursor:
         with Transaction().start(database_name, 0):
@@ -433,5 +420,4 @@ def load_modules(database_name, pool, update=False, lang=None):
                 Transaction().reset_context():
             _load_modules()
 
-    Cache.resets(database_name)
     return res
diff --git a/trytond/monitor.py b/trytond/monitor.py
index b067ca0..128a0bf 100644
--- a/trytond/monitor.py
+++ b/trytond/monitor.py
@@ -32,14 +32,17 @@ def _modified(path):
     return False
 
 
-def monitor():
+def monitor(files):
     '''
-    Monitor module files for change
+    Monitor files and module files for change
 
     :return: True if at least one file has changed
     '''
     global _MODULES
     modified = False
+    for file_ in files:
+        if _modified(file_):
+            modified = True
     directories = set()
     for module in sys.modules.keys():
         if not module.startswith('trytond.'):
diff --git a/trytond/pool.py b/trytond/pool.py
index f57f3dc..65c6f49 100644
--- a/trytond/pool.py
+++ b/trytond/pool.py
@@ -128,7 +128,7 @@ class Pool(object):
         '''
         return self._locks[self.database_name]
 
-    def init(self, update=False, lang=None):
+    def init(self, update=None, lang=None):
         '''
         Init pool
         Set update to proceed to update
diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py
index 2d1915d..2ba1d10 100644
--- a/trytond/protocols/dispatcher.py
+++ b/trytond/protocols/dispatcher.py
@@ -1,18 +1,17 @@
 # -*- coding: utf-8 -*-
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import traceback
 import logging
 import time
 import sys
 import pydoc
 
-from sql import Table, Flavor
+from sql import Table
 
 from trytond.pool import Pool
 from trytond import security
 from trytond import backend
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.version import VERSION
 from trytond.transaction import Transaction
 from trytond.cache import Cache
@@ -20,10 +19,11 @@ from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
 from trytond.rpc import RPC
 
+logger = logging.getLogger(__name__)
 
 ir_configuration = Table('ir_configuration')
 ir_lang = Table('ir_lang')
-ir_module = Table('ir_module')
+ir_module = Table('ir_module_module')
 res_user = Table('res_user')
 
 
@@ -40,16 +40,15 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
             except Exception:
                 return False
             res = security.login(database_name, user, session)
-            Cache.clean(database_name)
-            logger = logging.getLogger('dispatcher')
+            with Transaction().start(database_name, 0):
+                Cache.clean(database_name)
+                Cache.resets(database_name)
             msg = res and 'successful login' or 'bad login or password'
             logger.info('%s \'%s\' from %s:%d using %s on database \'%s\''
                 % (msg, user, host, port, protocol, database_name))
-            Cache.resets(database_name)
             return res or False
         elif method == 'logout':
             name = security.logout(database_name, user, session)
-            logger = logging.getLogger('dispatcher')
             logger.info(('logout \'%s\' from %s:%d '
                     'using %s on database \'%s\'')
                 % (name, host, port, protocol, database_name))
@@ -64,6 +63,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
                 ('de_DE', 'Deutsch'),
                 ('en_US', 'English'),
                 ('es_AR', 'Español (Argentina)'),
+                ('es_EC', 'Español (Ecuador)'),
                 ('es_ES', 'Español (España)'),
                 ('es_CO', 'Español (Colombia)'),
                 ('fr_FR', 'Français'),
@@ -81,7 +81,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
             except Exception:
                 return False
         elif method == 'list':
-            if CONFIG['prevent_dblist']:
+            if not config.get('database', 'list'):
                 raise Exception('AccessDenied')
             with Transaction().start(None, 0, close=True) as transaction:
                 return transaction.database.list(transaction.cursor)
@@ -121,7 +121,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
             obj = pool.get(object_name, type=object_type)
             return pydoc.getdoc(getattr(obj, method))
 
-    for count in range(int(CONFIG['retry']), -1, -1):
+    for count in range(config.getint('database', 'retry'), -1, -1):
         try:
             user = security.check(database_name, user, session)
         except DatabaseOperationalError:
@@ -130,7 +130,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
             raise
         break
 
-    Cache.clean(database_name)
     database_list = Pool.database_list()
     pool = Pool(database_name)
     if not database_name in database_list:
@@ -147,9 +146,13 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
         raise UserError('Calling method %s on %s %s is not allowed!'
             % (method, object_type, object_name))
 
-    for count in range(int(CONFIG['retry']), -1, -1):
+    exception_message = ('Exception calling %s.%s.%s from %s@%s:%d/%s' %
+        (object_type, object_name, method, user, host, port, database_name))
+
+    for count in range(config.getint('database', 'retry'), -1, -1):
         with Transaction().start(database_name, user,
                 readonly=rpc.readonly) as transaction:
+            Cache.clean(database_name)
             try:
                 c_args, c_kwargs, transaction.context, transaction.timestamp \
                     = rpc.convert(obj, *args, **kwargs)
@@ -166,23 +169,20 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
                             for i in inst]
                 if not rpc.readonly:
                     transaction.cursor.commit()
-            except DatabaseOperationalError, exception:
+            except DatabaseOperationalError:
                 transaction.cursor.rollback()
                 if count and not rpc.readonly:
                     continue
                 raise
-            except Exception, exception:
-                if CONFIG['verbose'] and not isinstance(exception, (
-                            NotLogged, ConcurrencyException, UserError,
-                            UserWarning)):
-                    tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
-                    logger = logging.getLogger('dispatcher')
-                    logger.error('Exception calling method %s on '
-                        '%s %s from %s@%s:%d/%s:\n'
-                        % (method, object_type, object_name, user, host, port,
-                            database_name) + tb_s)
+            except (NotLogged, ConcurrencyException, UserError, UserWarning):
+                logger.debug(exception_message, exc_info=sys.exc_info())
                 transaction.cursor.rollback()
                 raise
+            except Exception:
+                logger.error(exception_message, exc_info=sys.exc_info())
+                transaction.cursor.rollback()
+                raise
+            Cache.resets(database_name)
         with Transaction().start(database_name, 0) as transaction:
             pool = Pool(database_name)
             Session = pool.get('ir.session')
@@ -193,7 +193,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
                 transaction.cursor.rollback()
             else:
                 transaction.cursor.commit()
-        Cache.resets(database_name)
         return result
 
 
@@ -210,7 +209,6 @@ def create(database_name, password, lang, admin_password):
     Database = backend.get('Database')
     security.check_super(password)
     res = False
-    logger = logging.getLogger('database')
 
     try:
         with Transaction().start(None, 0, close=True, autocommit=True) \
@@ -225,7 +223,7 @@ def create(database_name, password, lang, admin_password):
             transaction.cursor.commit()
 
         pool = Pool(database_name)
-        pool.init(update=True, lang=[lang])
+        pool.init(update=['res', 'ir'], lang=[lang])
         with Transaction().start(database_name, 0) as transaction:
             User = pool.get('res.user')
             Lang = pool.get('ir.lang')
@@ -246,9 +244,8 @@ def create(database_name, password, lang, admin_password):
             transaction.cursor.commit()
             res = True
     except Exception:
-        logger.error('CREATE DB: %s failed' % (database_name,))
-        tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
-        logger.error('Exception in call: \n' + tb_s)
+        logger.error('CREATE DB: %s failed' % database_name,
+            exc_info=sys.exc_info())
         raise
     else:
         logger.info('CREATE DB: %s' % (database_name,))
@@ -261,7 +258,6 @@ def drop(database_name, password):
     Database(database_name).close()
     # Sleep to let connections close
     time.sleep(1)
-    logger = logging.getLogger('database')
 
     with Transaction().start(None, 0, close=True, autocommit=True) \
         as transaction:
@@ -270,9 +266,8 @@ def drop(database_name, password):
             Database.drop(cursor, database_name)
             cursor.commit()
         except Exception:
-            logger.error('DROP DB: %s failed' % (database_name,))
-            tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
-            logger.error('Exception in call: \n' + tb_s)
+            logger.error('DROP DB: %s failed' % database_name,
+                exc_info=sys.exc_info())
             raise
         else:
             logger.info('DROP DB: %s' % (database_name))
@@ -286,7 +281,6 @@ def dump(database_name, password):
     Database(database_name).close()
     # Sleep to let connections close
     time.sleep(1)
-    logger = logging.getLogger('database')
 
     data = Database.dump(database_name)
     logger.info('DUMP DB: %s' % (database_name))
@@ -295,7 +289,6 @@ def dump(database_name, password):
 
 def restore(database_name, password, data, update=False):
     Database = backend.get('Database')
-    logger = logging.getLogger('database')
     security.check_super(password)
     try:
         database = Database().connect()
@@ -307,14 +300,14 @@ def restore(database_name, password, data, update=False):
     Database.restore(database_name, data)
     logger.info('RESTORE DB: %s' % (database_name))
     if update:
-        cursor = Database(database_name).connect().cursor()
-        cursor.execute(*ir_lang.select(ir_lang.code,
-                where=ir_lang.translatable))
-        lang = [x[0] for x in cursor.fetchall()]
-        cursor.execute(*ir_module.update([ir_module.state], ['to upgrade'],
-                where=(ir_module.state == 'installed')))
-        cursor.commit()
-        cursor.close()
+        with Transaction().start(database_name, 0) as transaction:
+            cursor = transaction.cursor
+            cursor.execute(*ir_lang.select(ir_lang.code,
+                    where=ir_lang.translatable))
+            lang = [x[0] for x in cursor.fetchall()]
+            cursor.execute(*ir_module.select(ir_module.name,
+                    where=(ir_module.state == 'installed')))
+            update = [x[0] for x in cursor.fetchall()]
         Pool(database_name).init(update=update, lang=lang)
         logger.info('Update/Init succeed!')
     return True
diff --git a/trytond/protocols/jsonrpc.py b/trytond/protocols/jsonrpc.py
index 27ce605..088d7da 100644
--- a/trytond/protocols/jsonrpc.py
+++ b/trytond/protocols/jsonrpc.py
@@ -2,7 +2,7 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.protocols.sslsocket import SSLSocket
 from trytond.protocols.dispatcher import dispatch
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.protocols.common import daemon, RegisterHandlerMixin
 from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
@@ -33,63 +33,88 @@ except ImportError:
     from StringIO import StringIO
 
 
-def object_hook(dct):
-    if '__class__' in dct:
-        if dct['__class__'] == 'datetime':
-            return datetime.datetime(dct['year'], dct['month'], dct['day'],
-                dct['hour'], dct['minute'], dct['second'], dct['microsecond'])
-        elif dct['__class__'] == 'date':
-            return datetime.date(dct['year'], dct['month'], dct['day'])
-        elif dct['__class__'] == 'time':
-            return datetime.time(dct['hour'], dct['minute'], dct['second'],
-                dct['microsecond'])
-        elif dct['__class__'] == 'buffer':
-            return buffer(base64.decodestring(dct['base64']))
-        elif dct['__class__'] == 'Decimal':
-            return Decimal(dct['decimal'])
-    return dct
+class JSONDecoder(object):
+
+    decoders = {}
+
+    @classmethod
+    def register(cls, klass, decoder):
+        assert klass not in cls.decoders
+        cls.decoders[klass] = decoder
+
+    def __call__(self, dct):
+        if dct.get('__class__') in self.decoders:
+            return self.decoders[dct['__class__']](dct)
+        return dct
+
+JSONDecoder.register('datetime',
+    lambda dct: datetime.datetime(dct['year'], dct['month'], dct['day'],
+        dct['hour'], dct['minute'], dct['second'], dct['microsecond']))
+JSONDecoder.register('date',
+    lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
+JSONDecoder.register('time',
+    lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
+        dct['microsecond']))
+JSONDecoder.register('buffer', lambda dct:
+    buffer(base64.decodestring(dct['base64'])))
+JSONDecoder.register('Decimal', lambda dct: Decimal(dct['decimal']))
 
 
 class JSONEncoder(json.JSONEncoder):
 
+    serializers = {}
+
     def __init__(self, *args, **kwargs):
         super(JSONEncoder, self).__init__(*args, **kwargs)
         # Force to use our custom decimal with simplejson
         self.use_decimal = False
 
+    @classmethod
+    def register(cls, klass, encoder):
+        assert klass not in cls.serializers
+        cls.serializers[klass] = encoder
+
     def default(self, obj):
-        if isinstance(obj, datetime.date):
-            if isinstance(obj, datetime.datetime):
-                return {'__class__': 'datetime',
-                        'year': obj.year,
-                        'month': obj.month,
-                        'day': obj.day,
-                        'hour': obj.hour,
-                        'minute': obj.minute,
-                        'second': obj.second,
-                        'microsecond': obj.microsecond,
-                        }
-            return {'__class__': 'date',
-                    'year': obj.year,
-                    'month': obj.month,
-                    'day': obj.day,
-                    }
-        elif isinstance(obj, datetime.time):
-            return {'__class__': 'time',
-                'hour': obj.hour,
-                'minute': obj.minute,
-                'second': obj.second,
-                'microsecond': obj.microsecond,
-                }
-        elif isinstance(obj, buffer):
-            return {'__class__': 'buffer',
-                'base64': base64.encodestring(obj),
-                }
-        elif isinstance(obj, Decimal):
-            return {'__class__': 'Decimal',
-                'decimal': str(obj),
-                }
-        return super(JSONEncoder, self).default(obj)
+        marshaller = self.serializers.get(type(obj),
+            super(JSONEncoder, self).default)
+        return marshaller(obj)
+
+JSONEncoder.register(datetime.datetime,
+    lambda o: {
+        '__class__': 'datetime',
+        'year': o.year,
+        'month': o.month,
+        'day': o.day,
+        'hour': o.hour,
+        'minute': o.minute,
+        'second': o.second,
+        'microsecond': o.microsecond,
+        })
+JSONEncoder.register(datetime.date,
+    lambda o: {
+        '__class__': 'date',
+        'year': o.year,
+        'month': o.month,
+        'day': o.day,
+        })
+JSONEncoder.register(datetime.time,
+    lambda o: {
+        '__class__': 'time',
+        'hour': o.hour,
+        'minute': o.minute,
+        'second': o.second,
+        'microsecond': o.microsecond,
+        })
+JSONEncoder.register(buffer,
+    lambda o: {
+        '__class__': 'buffer',
+        'base64': base64.encodestring(o),
+        })
+JSONEncoder.register(Decimal,
+    lambda o: {
+        '__class__': 'Decimal',
+        'decimal': str(o),
+        })
 
 
 class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
@@ -111,7 +136,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
         existing method through subclassing is the prefered means
         of changing method dispatch behavior.
         """
-        rawreq = json.loads(data, object_hook=object_hook)
+        rawreq = json.loads(data, object_hook=JSONDecoder())
 
         req_id = rawreq.get('id', 0)
         method = rawreq['method']
@@ -132,10 +157,6 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
             tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
             for path in sys.path:
                 tb_s = tb_s.replace(path, '')
-            if CONFIG['debug_mode']:
-                import pdb
-                traceb = sys.exc_info()[2]
-                pdb.post_mortem(traceb)
             # report exception back to server
             response['error'] = (str(sys.exc_value), tb_s)
 
@@ -204,7 +225,7 @@ class SimpleJSONRPCRequestHandler(RegisterHandlerMixin,
         path = posixpath.normpath(urllib.unquote(path))
         words = path.split('/')
         words = filter(None, words)
-        path = CONFIG['jsondata_path']
+        path = config.get('jsonrpc', 'data')
         for word in words:
             drive, word = os.path.splitdrive(word)
             head, word = os.path.split(word)
@@ -222,7 +243,8 @@ class SimpleJSONRPCRequestHandler(RegisterHandlerMixin,
 
     def send_tryton_url(self, path):
         self.send_response(300)
-        hostname = CONFIG['hostname'] or unicode(socket.getfqdn(), 'utf8')
+        hostname = (config.get('jsonrpc', 'hostname')
+            or unicode(socket.getfqdn(), 'utf8'))
         hostname = '.'.join(encodings.idna.ToASCII(part) for part in
             hostname.split('.'))
         values = {
diff --git a/trytond/protocols/sslsocket.py b/trytond/protocols/sslsocket.py
index c3dcbdb..9c18a26 100644
--- a/trytond/protocols/sslsocket.py
+++ b/trytond/protocols/sslsocket.py
@@ -1,6 +1,6 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.config import CONFIG
+from trytond.config import config
 
 
 def SSLSocket(socket):
@@ -8,6 +8,6 @@ def SSLSocket(socket):
     import ssl
     return ssl.wrap_socket(socket,
         server_side=True,
-        certfile=CONFIG['certificate'],
-        keyfile=CONFIG['privatekey'],
+        certfile=config.get('ssl', 'certificate'),
+        keyfile=config.get('ssl', 'privatekey'),
         ssl_version=ssl.PROTOCOL_SSLv23)
diff --git a/trytond/protocols/webdav.py b/trytond/protocols/webdav.py
index 98b12ff..f820280 100644
--- a/trytond/protocols/webdav.py
+++ b/trytond/protocols/webdav.py
@@ -7,7 +7,6 @@ import urlparse
 import time
 import urllib
 import sys
-import traceback
 import logging
 from threading import local
 import xml.dom.minidom
@@ -21,7 +20,6 @@ from pywebdav.lib.davcmd import copyone, copytree, moveone, movetree, \
     delone, deltree
 from trytond.protocols.sslsocket import SSLSocket
 from trytond.protocols.common import daemon
-from trytond.config import CONFIG
 from trytond.security import login
 from trytond.version import PACKAGE, VERSION, WEBSITE
 from trytond.tools.misc import LocalDict
@@ -36,6 +34,8 @@ domimpl = xml.dom.minidom.getDOMImplementation()
 DAV_VERSION_1['version'] += ',access-control'
 DAV_VERSION_2['version'] += ',access-control'
 
+logger = logging.getLogger(__name__)
+
 
 # Local int for multi-thread
 class LocalInt(local):
@@ -131,12 +131,12 @@ class TrytonDAVInterface(iface.dav_interface):
         self.verbose = False
 
     def _log_exception(self, exception):
-        if CONFIG['verbose'] and not isinstance(exception, (
-                    NotLogged, ConcurrencyException, UserError, UserWarning,
-                    DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden)):
-            tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
-            logger = logging.getLogger('webdav')
-            logger.error('Exception:\n' + tb_s)
+        if isinstance(exception, (NotLogged, ConcurrencyException, UserError,
+                    UserWarning, DAV_Error, DAV_NotFound, DAV_Secret,
+                    DAV_Forbidden)):
+            logger.debug('Exception', exc_info=sys.exc_info())
+        else:
+            logger.error('Exception', exc_info=sys.exc_info())
 
     @staticmethod
     def get_dburi(uri):
@@ -581,7 +581,9 @@ class WebDAVAuthRequestHandler(WebDAVServer.DAVRequestHandler):
             if not user:
                 return None
 
-        Transaction().start(dbname, user)
+        Transaction().start(dbname, user, {
+                '_check_access': True,
+                })
         Cache.clean(dbname)
         return user
 
diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py
index a5bbe2c..7b48c53 100644
--- a/trytond/protocols/xmlrpc.py
+++ b/trytond/protocols/xmlrpc.py
@@ -2,7 +2,6 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.protocols.sslsocket import SSLSocket
 from trytond.protocols.dispatcher import dispatch
-from trytond.config import CONFIG
 from trytond.protocols.common import daemon, RegisterHandlerMixin
 from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
@@ -10,16 +9,18 @@ from trytond import security
 import SimpleXMLRPCServer
 import SocketServer
 import xmlrpclib
-import traceback
 import socket
 import sys
 import base64
 import datetime
 from types import DictType
+import logging
 
 # convert decimal to float before marshalling:
 from decimal import Decimal
 
+logger = logging.getLogger(__name__)
+
 
 def dump_decimal(self, value, write):
     value = {'__class__': 'Decimal',
@@ -74,6 +75,28 @@ def dump_struct(self, value, write, escape=xmlrpclib.escape):
 xmlrpclib.Marshaller.dispatch[DictType] = dump_struct
 
 
+class XMLRPCDecoder(object):
+
+    decoders = {}
+
+    @classmethod
+    def register(cls, klass, decoder):
+        assert klass not in cls.decoders
+        cls.decoders[klass] = decoder
+
+    def __call__(self, dct):
+        if dct.get('__class__') in self.decoders:
+            return self.decoders[dct['__class__']](dct)
+        return dct
+
+XMLRPCDecoder.register('date',
+    lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
+XMLRPCDecoder.register('time',
+    lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
+        dct['microsecond']))
+XMLRPCDecoder.register('Decimal', lambda dct: Decimal(dct['decimal']))
+
+
 def end_struct(self, data):
     mark = self._marks.pop()
     # map structs to Python dictionaries
@@ -81,14 +104,7 @@ def end_struct(self, data):
     items = self._stack[mark:]
     for i in range(0, len(items), 2):
         dct[xmlrpclib._stringify(items[i])] = items[i + 1]
-    if '__class__' in dct:
-        if dct['__class__'] == 'date':
-            dct = datetime.date(dct['year'], dct['month'], dct['day'])
-        elif dct['__class__'] == 'time':
-            dct = datetime.time(dct['hour'], dct['minute'], dct['second'],
-                dct['microsecond'])
-        elif dct['__class__'] == 'Decimal':
-            dct = Decimal(dct['decimal'])
+    dct = XMLRPCDecoder()(dct)
     self._stack[mark:] = [dct]
     self._value = 0
 
@@ -103,6 +119,14 @@ def _end_dateTime(self, data):
 xmlrpclib.Unmarshaller.dispatch["dateTime.iso8601"] = _end_dateTime
 
 
+def _end_base64(self, data):
+    value = xmlrpclib.Binary()
+    value.decode(data)
+    self.append(buffer(value.data))
+    self._value = 0
+xmlrpclib.Unmarshaller.dispatch['base64'] = _end_base64
+
+
 class GenericXMLRPCRequestHandler:
 
     def _dispatch(self, method, params):
@@ -110,6 +134,7 @@ class GenericXMLRPCRequestHandler:
         database_name = self.path[1:]
         user = self.tryton['user']
         session = self.tryton['session']
+        exception_message = 'Exception calling %s%s' % (method, params)
         try:
             try:
                 method_list = method.split('.')
@@ -127,21 +152,15 @@ class GenericXMLRPCRequestHandler:
                 return dispatch(host, port, 'XML-RPC', database_name, user,
                         session, object_type, object_name, method, *params)
             except (NotLogged, ConcurrencyException), exception:
-                raise xmlrpclib.Fault(exception.code,
-                    '\n'.join(exception.args))
+                logger.debug(exception_message, exc_info=sys.exc_info())
+                raise xmlrpclib.Fault(exception.code, str(exception))
             except (UserError, UserWarning), exception:
+                logger.debug(exception_message, exc_info=sys.exc_info())
                 error, description = exception.args
-                raise xmlrpclib.Fault(exception.code,
-                    '\n'.join((error,) + description))
-            except Exception:
-                tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
-                for path in sys.path:
-                    tb_s = tb_s.replace(path, '')
-                if CONFIG['debug_mode']:
-                    import pdb
-                    traceb = sys.exc_info()[2]
-                    pdb.post_mortem(traceb)
-                raise xmlrpclib.Fault(255, str(sys.exc_value) + '\n' + tb_s)
+                raise xmlrpclib.Fault(exception.code, str(exception))
+            except Exception, exception:
+                logger.error(exception_message, exc_info=sys.exc_info())
+                raise xmlrpclib.Fault(255, str(exception))
         finally:
             security.logout(database_name, user, session)
 
diff --git a/trytond/pyson.py b/trytond/pyson.py
index 6771f20..bab351c 100644
--- a/trytond/pyson.py
+++ b/trytond/pyson.py
@@ -28,25 +28,25 @@ class PYSON(object):
             return Not(self)
 
     def __and__(self, other):
+        if (isinstance(other, PYSON)
+                and other.types() != set([bool])):
+            other = Bool(other)
         if (isinstance(self, And)
                 and not isinstance(self, Or)):
             self._statements.append(other)
             return self
-        if (isinstance(other, PYSON)
-                and other.types() != set([bool])):
-            other = Bool(other)
         if self.types() != set([bool]):
             return And(Bool(self), other)
         else:
             return And(self, other)
 
     def __or__(self, other):
-        if isinstance(self, Or):
-            self._statements.append(other)
-            return self
         if (isinstance(other, PYSON)
                 and other.types() != set([bool])):
             other = Bool(other)
+        if isinstance(self, Or):
+            self._statements.append(other)
+            return self
         if self.types() != set([bool]):
             return Or(Bool(self), other)
         else:
diff --git a/trytond/report/report.py b/trytond/report/report.py
index 42a6d5d..b0a3ef1 100644
--- a/trytond/report/report.py
+++ b/trytond/report/report.py
@@ -21,11 +21,12 @@ except ImportError:
     Manifest, MANIFEST = None, None
 from genshi.filters import Translator
 import lxml.etree
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.pool import Pool, PoolBase
 from trytond.transaction import Transaction
 from trytond.url import URLMixin
 from trytond.rpc import RPC
+from trytond.exceptions import UserError
 
 MIMETYPES = {
     'odt': 'application/vnd.oasis.opendocument.text',
@@ -102,6 +103,20 @@ class Report(URLMixin, PoolBase):
             }
 
     @classmethod
+    def check_access(cls):
+        pool = Pool()
+        ActionReport = pool.get('ir.action.report')
+        User = pool.get('res.user')
+
+        if Transaction().user == 0:
+            return
+
+        groups = set(User.get_groups())
+        report_groups = ActionReport.get_groups(cls.__name__)
+        if report_groups and not groups & report_groups:
+            raise UserError('Calling report %s is not allowed!' % cls.__name__)
+
+    @classmethod
     def execute(cls, ids, data):
         '''
         Execute the report on record ids.
@@ -120,6 +135,7 @@ class Report(URLMixin, PoolBase):
                 ])
         if not action_reports:
             raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+        cls.check_access()
         action_report = action_reports[0]
         records = None
         model = action_report.model or data.get('model')
@@ -295,7 +311,7 @@ class Report(URLMixin, PoolBase):
         oext = FORMAT2EXT.get(output_format, output_format)
         with os.fdopen(fd, 'wb+') as fp:
             fp.write(data)
-        cmd = ['unoconv', '--connection=%s' % CONFIG['unoconv'],
+        cmd = ['unoconv', '--connection=%s' % config.get('report', 'unoconv'),
             '-f', oext, '--stdout', path]
         try:
             proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
diff --git a/trytond/res/group.py b/trytond/res/group.py
index 800b13d..e24f631 100644
--- a/trytond/res/group.py
+++ b/trytond/res/group.py
@@ -3,8 +3,8 @@
 "Group"
 from itertools import chain
 from ..model import ModelView, ModelSQL, fields
-from ..transaction import Transaction
 from ..pool import Pool, PoolMeta
+from ..tools import grouped_slice
 
 __all__ = [
     'Group', 'Group2',
@@ -19,8 +19,7 @@ class MenuMany2Many(fields.Many2Many):
                 values=values)
         menu_ids = list(set(chain(*res.values())))
         test_ids = []
-        for i in range(0, len(menu_ids), Transaction().cursor.IN_MAX):
-            sub_ids = menu_ids[i:i + Transaction().cursor.IN_MAX]
+        for sub_ids in grouped_slice(menu_ids):
             test_ids.append(map(int, Menu.search([
                             ('id', 'in', sub_ids),
                             ])))
diff --git a/trytond/res/ir.xml b/trytond/res/ir.xml
index 73c8d96..041e090 100644
--- a/trytond/res/ir.xml
+++ b/trytond/res/ir.xml
@@ -265,14 +265,6 @@ this repository contains the full copyright notices and license terms. -->
             <field name="perm_create" eval="False"/>
             <field name="perm_delete" eval="False"/>
         </record>
-        <record model="ir.model.access" id="access_ir_model_data_admin">
-            <field name="model" search="[('model', '=', 'ir.model.data')]"/>
-            <field name="group" ref="group_admin"/>
-            <field name="perm_read" eval="True"/>
-            <field name="perm_write" eval="True"/>
-            <field name="perm_create" eval="True"/>
-            <field name="perm_delete" eval="True"/>
-        </record>
         <record model="ir.model.access" id="access_ir_cron">
             <field name="model" search="[('model', '=', 'ir.cron')]"/>
             <field name="perm_read" eval="True"/>
@@ -746,6 +738,16 @@ this repository contains the full copyright notices and license terms. -->
             <field name="group" ref="group_admin"/>
         </record>
 
+        <record model="ir.model.button" id="model_data_sync_button">
+            <field name="name">sync</field>
+            <field name="model" search="[('model', '=', 'ir.model.data')]"/>
+        </record>
+        <record model="ir.model.button-res.group"
+            id="model_data_sync_button_group_admin">
+            <field name="button" ref="model_data_sync_button"/>
+            <field name="group" ref="group_admin"/>
+        </record>
+
         <record model="ir.ui.view" id="sequence_type_view_form">
           <field name="model">ir.sequence.type</field>
           <field name="inherit" ref="ir.sequence_type_view_form"/>
diff --git a/trytond/res/locale/ca_ES.po b/trytond/res/locale/ca_ES.po
index 71decc4..25cdfb9 100644
--- a/trytond/res/locale/ca_ES.po
+++ b/trytond/res/locale/ca_ES.po
@@ -350,7 +350,7 @@ msgstr "Direcció idioma"
 
 msgctxt "field:res.user,login:"
 msgid "Login"
-msgstr "Nom d'usuari"
+msgstr "Nom usuari"
 
 msgctxt "field:res.user,menu:"
 msgid "Menu Action"
@@ -486,7 +486,7 @@ msgstr "ID"
 
 msgctxt "field:res.user.login.attempt,login:"
 msgid "Login"
-msgstr "Nom d'usuari"
+msgstr "Nom usuari"
 
 msgctxt "field:res.user.login.attempt,rec_name:"
 msgid "Name"
diff --git a/trytond/res/locale/de_DE.po b/trytond/res/locale/de_DE.po
index 7b1e739..40fed63 100644
--- a/trytond/res/locale/de_DE.po
+++ b/trytond/res/locale/de_DE.po
@@ -739,4 +739,4 @@ msgstr "Hinzufügen"
 
 msgctxt "wizard_button:res.user.config,user,end:"
 msgid "End"
-msgstr "Ende"
+msgstr "Fertig"
diff --git a/trytond/res/locale/es_AR.po b/trytond/res/locale/es_AR.po
index f889836..71897ae 100644
--- a/trytond/res/locale/es_AR.po
+++ b/trytond/res/locale/es_AR.po
@@ -102,7 +102,7 @@ msgstr "Usuario creación"
 
 msgctxt "field:ir.model.field-res.group,field:"
 msgid "Model Field"
-msgstr "Model de Campo"
+msgstr "Campo del modelo"
 
 msgctxt "field:ir.model.field-res.group,group:"
 msgid "Group"
@@ -562,11 +562,11 @@ msgstr "Acción - Grupo"
 
 msgctxt "model:ir.cron,name:cron_trigger_time"
 msgid "Run On Time Triggers"
-msgstr "Ejecutar Disparadores Al Tiempo"
+msgstr "Ejecutar disparadores por tiempo"
 
 msgctxt "model:ir.model.button-res.group,name:"
 msgid "Model Button - Group"
-msgstr "Modelo de Botón - Grupo"
+msgstr "Modelo Botón - Grupo"
 
 msgctxt "model:ir.model.field-res.group,name:"
 msgid "Model Field Group Rel"
@@ -574,11 +574,11 @@ msgstr "Relación entre grupo y campo del modelo"
 
 msgctxt "model:ir.rule.group-res.group,name:"
 msgid "Rule Group - Group"
-msgstr "Regla de grupo - grupo"
+msgstr "Regla de grupo - Grupo"
 
 msgctxt "model:ir.rule.group-res.user,name:"
 msgid "Rule Group - User"
-msgstr "Regla de grupo - usuario"
+msgstr "Regla de grupo - Usuario"
 
 msgctxt "model:ir.sequence.type-res.group,name:"
 msgid "Sequence Type - Group"
@@ -618,7 +618,7 @@ msgstr "Administrador"
 
 msgctxt "model:res.user,name:user_trigger"
 msgid "Cron Trigger"
-msgstr "Disparador del Programador de tareas"
+msgstr "Programador de disparadores"
 
 msgctxt "model:res.user-ir.action,name:"
 msgid "User - Action"
@@ -630,7 +630,7 @@ msgstr "Usuario - Grupo"
 
 msgctxt "model:res.user.config.start,name:"
 msgid "User Config Init"
-msgstr "Inicio de Configuración de Usuario"
+msgstr "Configuración inicial de Usuario"
 
 msgctxt "model:res.user.login.attempt,name:"
 msgid "Login Attempt"
diff --git a/trytond/res/locale/es_CO.po b/trytond/res/locale/es_CO.po
index b351bbe..4f741c0 100644
--- a/trytond/res/locale/es_CO.po
+++ b/trytond/res/locale/es_CO.po
@@ -8,7 +8,7 @@ msgstr "El nombre del grupo debe ser único!"
 
 msgctxt "error:res.user:"
 msgid "Wrong password!"
-msgstr "¡Contraseña incorrecta!"
+msgstr "Contraseña incorrecta!"
 
 msgctxt "error:res.user:"
 msgid "You can not have two users with the same login!"
@@ -394,7 +394,7 @@ msgstr "Barra de Estado"
 
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
-msgstr "Avisos"
+msgstr "Advertencias"
 
 msgctxt "field:res.user,write_date:"
 msgid "Write Date"
@@ -478,7 +478,7 @@ msgstr "Fecha de Creación"
 
 msgctxt "field:res.user.login.attempt,create_uid:"
 msgid "Create User"
-msgstr "Usuario creación"
+msgstr "Creado por Usuario"
 
 msgctxt "field:res.user.login.attempt,id:"
 msgid "ID"
@@ -618,11 +618,11 @@ msgstr "Administrador"
 
 msgctxt "model:res.user,name:user_trigger"
 msgid "Cron Trigger"
-msgstr "Disparador del Programador de tareas"
+msgstr "Disparador del Programador de Tareas"
 
 msgctxt "model:res.user-ir.action,name:"
 msgid "User - Action"
-msgstr "res.user-ir.action"
+msgstr "Usuario - Acción"
 
 msgctxt "model:res.user-res.group,name:"
 msgid "User - Group"
@@ -638,7 +638,7 @@ msgstr "Intento de Login"
 
 msgctxt "model:res.user.warning,name:"
 msgid "User Warning"
-msgstr "Aviso a Usuario"
+msgstr "Advertencia a Usuario"
 
 msgctxt "view:ir.module.module:"
 msgid "Cancel Installation"
diff --git a/trytond/res/locale/es_CO.po b/trytond/res/locale/es_EC.po
similarity index 95%
copy from trytond/res/locale/es_CO.po
copy to trytond/res/locale/es_EC.po
index b351bbe..bdae57a 100644
--- a/trytond/res/locale/es_CO.po
+++ b/trytond/res/locale/es_EC.po
@@ -4,7 +4,7 @@ msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:res.group:"
 msgid "The name of the group must be unique!"
-msgstr "El nombre del grupo debe ser único!"
+msgstr "¡El nombre del grupo debe ser único!"
 
 msgctxt "error:res.user:"
 msgid "Wrong password!"
@@ -12,7 +12,7 @@ msgstr "¡Contraseña incorrecta!"
 
 msgctxt "error:res.user:"
 msgid "You can not have two users with the same login!"
-msgstr "No puede tener dos usuarios con el mismo nombre de usuario!"
+msgstr "¡No puede tener dos usuarios con el mismo nombre de usuario!"
 
 msgctxt "error:res.user:"
 msgid ""
@@ -102,7 +102,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.model.field-res.group,field:"
 msgid "Model Field"
-msgstr "Modelo de Campo"
+msgstr "Campo del Modelo"
 
 msgctxt "field:ir.model.field-res.group,group:"
 msgid "Group"
@@ -146,7 +146,7 @@ msgstr "Nombre"
 
 msgctxt "field:ir.rule.group-res.group,rule_group:"
 msgid "Rule Group"
-msgstr "Regla de Grupo"
+msgstr "Grupo de Reglas"
 
 msgctxt "field:ir.rule.group-res.group,write_date:"
 msgid "Write Date"
@@ -174,7 +174,7 @@ msgstr "Nombre"
 
 msgctxt "field:ir.rule.group-res.user,rule_group:"
 msgid "Rule Group"
-msgstr "Regla de Grupo"
+msgstr "Grupo de Reglas"
 
 msgctxt "field:ir.rule.group-res.user,user:"
 msgid "User"
@@ -366,7 +366,7 @@ msgstr "Contraseña"
 
 msgctxt "field:res.user,password_hash:"
 msgid "Password Hash"
-msgstr "Contraseña Hash"
+msgstr "Hash de Contraseña"
 
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
@@ -394,7 +394,7 @@ msgstr "Barra de Estado"
 
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
-msgstr "Avisos"
+msgstr "Advertencias"
 
 msgctxt "field:res.user,write_date:"
 msgid "Write Date"
@@ -538,11 +538,11 @@ msgstr "Modificado por Usuario"
 
 msgctxt "help:ir.sequence.type,groups:"
 msgid "Groups allowed to edit the sequences of this type"
-msgstr "Grupos autorizados para editar secuencias en este tipo"
+msgstr "Grupos autorizados para editar las secuencias de este tipo"
 
 msgctxt "help:res.user,actions:"
 msgid "Actions that will be run at login"
-msgstr "Acciones que se ejecutan al iniciar sesión"
+msgstr "Acciones que se ejecutarán al iniciar sesión"
 
 msgctxt "model:ir.action,name:act_group_form"
 msgid "Groups"
@@ -566,7 +566,7 @@ msgstr "Ejecutar Disparadores a Tiempo"
 
 msgctxt "model:ir.model.button-res.group,name:"
 msgid "Model Button - Group"
-msgstr "Modelo de Botón - Grupo"
+msgstr "Botón de Modelo - Grupo"
 
 msgctxt "model:ir.model.field-res.group,name:"
 msgid "Model Field Group Rel"
@@ -574,15 +574,15 @@ msgstr "Relación entre grupo y campo del modelo"
 
 msgctxt "model:ir.rule.group-res.group,name:"
 msgid "Rule Group - Group"
-msgstr "Regla de Grupo - Grupo"
+msgstr "Grupo de Reglas - Grupo"
 
 msgctxt "model:ir.rule.group-res.user,name:"
 msgid "Rule Group - User"
-msgstr "Regla de Grupo - Usuario"
+msgstr "Grupo de Reglas - Usuario"
 
 msgctxt "model:ir.sequence.type-res.group,name:"
 msgid "Sequence Type - Group"
-msgstr "Secuencia de Tipo - Grupo"
+msgstr "Tipo de Secuencia - Grupo"
 
 msgctxt "model:ir.ui.menu,name:menu_group_form"
 msgid "Groups"
@@ -622,7 +622,7 @@ msgstr "Disparador del Programador de tareas"
 
 msgctxt "model:res.user-ir.action,name:"
 msgid "User - Action"
-msgstr "res.user-ir.action"
+msgstr "Usuario - Acción"
 
 msgctxt "model:res.user-res.group,name:"
 msgid "User - Group"
@@ -630,15 +630,15 @@ msgstr "Usuario - Grupo"
 
 msgctxt "model:res.user.config.start,name:"
 msgid "User Config Init"
-msgstr "Inicio de Configuración de Usuario"
+msgstr "Configuración Inicial de Usuario"
 
 msgctxt "model:res.user.login.attempt,name:"
 msgid "Login Attempt"
-msgstr "Intento de Login"
+msgstr "Intento de Inicio de Sesión"
 
 msgctxt "model:res.user.warning,name:"
 msgid "User Warning"
-msgstr "Aviso a Usuario"
+msgstr "Advertencia al Usuario"
 
 msgctxt "view:ir.module.module:"
 msgid "Cancel Installation"
@@ -654,15 +654,15 @@ msgstr "Cancelar Actualización"
 
 msgctxt "view:ir.module.module:"
 msgid "Mark for Installation"
-msgstr "Instalar"
+msgstr "Marcar para Instalar"
 
 msgctxt "view:ir.module.module:"
 msgid "Mark for Uninstallation (beta)"
-msgstr "Desinstalar (beta)"
+msgstr "Marcar para Desinstalar (beta)"
 
 msgctxt "view:ir.module.module:"
 msgid "Mark for Upgrade"
-msgstr "Actualizar"
+msgstr "Marcar para Actualizar"
 
 msgctxt "view:res.group:"
 msgid "Access Permissions"
@@ -686,7 +686,7 @@ msgstr "Agregar Usuarios"
 
 msgctxt "view:res.user.config.start:"
 msgid "Be careful that the login must be unique!"
-msgstr "Asegúrese de que los nombres de usuario sean únicos!"
+msgstr "¡Asegúrese de que el nombre de usuario sea único!"
 
 msgctxt "view:res.user.config.start:"
 msgid "You can now add some users into the system."
diff --git a/trytond/res/locale/es_ES.po b/trytond/res/locale/es_ES.po
index 7ed0da6..18997e6 100644
--- a/trytond/res/locale/es_ES.po
+++ b/trytond/res/locale/es_ES.po
@@ -350,7 +350,7 @@ msgstr "Dirección del idioma"
 
 msgctxt "field:res.user,login:"
 msgid "Login"
-msgstr "Nombre de usuario"
+msgstr "Nombre usuario"
 
 msgctxt "field:res.user,menu:"
 msgid "Menu Action"
@@ -486,7 +486,7 @@ msgstr "ID"
 
 msgctxt "field:res.user.login.attempt,login:"
 msgid "Login"
-msgstr "Nombre de usuario"
+msgstr "Nombre usuario"
 
 msgctxt "field:res.user.login.attempt,rec_name:"
 msgid "Name"
diff --git a/trytond/res/locale/fr_FR.po b/trytond/res/locale/fr_FR.po
index 758ba94..7ed7195 100644
--- a/trytond/res/locale/fr_FR.po
+++ b/trytond/res/locale/fr_FR.po
@@ -4,15 +4,15 @@ msgstr "Content-Type: text/plain; charset=utf-8\n"
 
 msgctxt "error:res.group:"
 msgid "The name of the group must be unique!"
-msgstr "Le nom du groupe doit être unique !"
+msgstr "Le nom du groupe doit être unique !"
 
 msgctxt "error:res.user:"
 msgid "Wrong password!"
-msgstr "Mauvais mot de passe !"
+msgstr "Mauvais mot de passe !"
 
 msgctxt "error:res.user:"
 msgid "You can not have two users with the same login!"
-msgstr "Vous ne pouvez pas créer deux utilisateur avec le même identifiant !"
+msgstr "Vous ne pouvez pas créer deux utilisateur avec le même identifiant !"
 
 msgctxt "error:res.user:"
 msgid ""
@@ -330,7 +330,7 @@ msgstr "Créé par"
 
 msgctxt "field:res.user,email:"
 msgid "Email"
-msgstr "E-mail"
+msgstr "Email"
 
 msgctxt "field:res.user,groups:"
 msgid "Groups"
@@ -562,7 +562,7 @@ msgstr "Action - Groupe"
 
 msgctxt "model:ir.cron,name:cron_trigger_time"
 msgid "Run On Time Triggers"
-msgstr "Lance les déclencheurs \"À temps\""
+msgstr "Lance les déclencheurs « À temps »"
 
 msgctxt "model:ir.model.button-res.group,name:"
 msgid "Model Button - Group"
diff --git a/trytond/res/locale/sl_SI.po b/trytond/res/locale/sl_SI.po
index e481897..8b96eb7 100644
--- a/trytond/res/locale/sl_SI.po
+++ b/trytond/res/locale/sl_SI.po
@@ -21,7 +21,7 @@ msgid ""
 "created by the system (updates, module installation, ...)"
 msgstr ""
 "Skrbnika ni možno odstraniti, ker se interno uporablja za vire, \n"
-"ki jih ustvarja sistem (posodobitve, nameščanje modulov, ...)"
+"ki jih izdeluje sistem (posodobitve, nameščanje modulov, ...)"
 
 msgctxt "field:ir.action-res.group,action:"
 msgid "Action"
@@ -29,11 +29,11 @@ msgstr "Ukrep"
 
 msgctxt "field:ir.action-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.action-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.action-res.group,group:"
 msgid "Group"
@@ -65,11 +65,11 @@ msgstr "Gumb"
 
 msgctxt "field:ir.model.button-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.button-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.button-res.group,group:"
 msgid "Group"
@@ -93,11 +93,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.model.field-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.model.field-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.model.field-res.group,field:"
 msgid "Model Field"
@@ -125,11 +125,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.rule.group-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.rule.group-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.rule.group-res.group,group:"
 msgid "Group"
@@ -157,11 +157,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.rule.group-res.user,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.rule.group-res.user,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.rule.group-res.user,id:"
 msgid "ID"
@@ -201,11 +201,11 @@ msgstr "Uporabniške skupine"
 
 msgctxt "field:ir.sequence.type-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.sequence.type-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.sequence.type-res.group,group:"
 msgid "User Groups"
@@ -233,11 +233,11 @@ msgstr "Zapisal"
 
 msgctxt "field:ir.ui.menu-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:ir.ui.menu-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:ir.ui.menu-res.group,group:"
 msgid "Group"
@@ -265,11 +265,11 @@ msgstr "Zapisal"
 
 msgctxt "field:res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:res.group,field_access:"
 msgid "Access Field"
@@ -321,11 +321,11 @@ msgstr "Aktivno"
 
 msgctxt "field:res.user,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:res.user,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:res.user,email:"
 msgid "Email"
@@ -409,11 +409,11 @@ msgstr "Ukrep"
 
 msgctxt "field:res.user-ir.action,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:res.user-ir.action,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:res.user-ir.action,id:"
 msgid "ID"
@@ -437,11 +437,11 @@ msgstr "Zapisal"
 
 msgctxt "field:res.user-res.group,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:res.user-res.group,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:res.user-res.group,group:"
 msgid "Group"
@@ -473,11 +473,11 @@ msgstr "ID"
 
 msgctxt "field:res.user.login.attempt,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:res.user.login.attempt,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:res.user.login.attempt,id:"
 msgid "ID"
@@ -505,11 +505,11 @@ msgstr "Vedno"
 
 msgctxt "field:res.user.warning,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:res.user.warning,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:res.user.warning,id:"
 msgid "ID"
diff --git a/trytond/res/user.py b/trytond/res/user.py
index 78bea12..19e1ba8 100644
--- a/trytond/res/user.py
+++ b/trytond/res/user.py
@@ -23,12 +23,12 @@ except ImportError:
 
 from ..model import ModelView, ModelSQL, fields
 from ..wizard import Wizard, StateView, Button, StateTransition
-from ..tools import safe_eval
+from ..tools import grouped_slice
 from .. import backend
 from ..transaction import Transaction
 from ..cache import Cache
 from ..pool import Pool
-from ..config import CONFIG
+from ..config import config
 from ..pyson import PYSONEncoder
 from ..rpc import RPC
 
@@ -77,9 +77,9 @@ class User(ModelSQL, ModelView):
     def __setup__(cls):
         super(User, cls).__setup__()
         cls.__rpc__.update({
-                'get_preferences': RPC(),
-                'set_preferences': RPC(readonly=False),
-                'get_preferences_fields_view': RPC(),
+                'get_preferences': RPC(check_access=False),
+                'set_preferences': RPC(readonly=False, check_access=False),
+                'get_preferences_fields_view': RPC(check_access=False),
                 })
         cls._sql_constraints += [
             ('login_key', 'UNIQUE (login)',
@@ -198,17 +198,14 @@ class User(ModelSQL, ModelView):
     @staticmethod
     def get_sessions(users, name):
         Session = Pool().get('ir.session')
-        cursor = Transaction().cursor
         now = datetime.datetime.now()
-        timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
+        timeout = datetime.timedelta(
+            seconds=config.getint('session', 'timeout'))
         result = dict((u.id, 0) for u in users)
-        for i in range(0, len(users), cursor.IN_MAX):
-            sub_ids = [u.id for u in users[i:i + cursor.IN_MAX]]
-
-            with Transaction().set_user(0):
-                sessions = Session.search([
-                        ('create_uid', 'in', sub_ids),
-                        ], order=[('create_uid', 'ASC')])
+        for sub_ids in grouped_slice(users):
+            sessions = Session.search([
+                    ('create_uid', 'in', sub_ids),
+                    ], order=[('create_uid', 'ASC')])
 
             def filter_(session):
                 timestamp = session.write_date or session.create_date
@@ -359,8 +356,7 @@ class User(ModelSQL, ModelView):
         if preferences is not None:
             return preferences.copy()
         user = Transaction().user
-        with Transaction().set_user(0, set_context=True):
-            user = cls(user)
+        user = cls(user)
         preferences = cls._get_preferences(user, context_only=context_only)
         cls._get_preferences_cache.set(key, preferences)
         return preferences.copy()
@@ -375,8 +371,7 @@ class User(ModelSQL, ModelView):
         values_clean = values.copy()
         fields = cls._preferences_fields + cls._context_fields
         user_id = Transaction().user
-        with Transaction().set_user(0):
-            user = cls(user_id)
+        user = cls(user_id)
         for field in values:
             if field not in fields or field == 'groups':
                 del values_clean[field]
@@ -391,8 +386,7 @@ class User(ModelSQL, ModelView):
                     values_clean['language'] = langs[0].id
                 else:
                     del values_clean['language']
-        with Transaction().set_user(0):
-            cls.write([user], values_clean)
+        cls.write([user], values_clean)
 
     @classmethod
     def get_preferences_fields_view(cls):
@@ -558,7 +552,7 @@ class LoginAttempt(ModelSQL):
     @staticmethod
     def delay():
         return (datetime.datetime.now()
-            - datetime.timedelta(seconds=int(CONFIG['session_timeout'])))
+            - datetime.timedelta(seconds=config.getint('session', 'timeout')))
 
     @classmethod
     def add(cls, login):
diff --git a/trytond/rpc.py b/trytond/rpc.py
index 1e3e2a9..4fc1f7a 100644
--- a/trytond/rpc.py
+++ b/trytond/rpc.py
@@ -10,16 +10,19 @@ class RPC(object):
     readonly: The transaction mode
     instantiate: The position or the slice of the arguments to be instanciated
     result: The function to transform the result
+    check_access: If access right must be checked
     '''
 
-    __slots__ = ('readonly', 'instantiate', 'result')
+    __slots__ = ('readonly', 'instantiate', 'result', 'check_access')
 
-    def __init__(self, readonly=True, instantiate=None, result=None):
+    def __init__(self, readonly=True, instantiate=None, result=None,
+            check_access=True):
         self.readonly = readonly
         self.instantiate = instantiate
         if result is None:
             result = lambda r: r
         self.result = result
+        self.check_access = check_access
 
     def convert(self, obj, *args, **kwargs):
         args = list(args)
@@ -32,6 +35,8 @@ class RPC(object):
         if '_timestamp' in context:
             timestamp = context['_timestamp']
             del context['_timestamp']
+        if self.check_access:
+            context['_check_access'] = True
         if self.instantiate is not None:
 
             def instance(data):
diff --git a/trytond/security.py b/trytond/security.py
index 7d3e85b..4e92b00 100644
--- a/trytond/security.py
+++ b/trytond/security.py
@@ -1,7 +1,13 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+import os
+try:
+    import crypt
+except ImportError:
+    pass
+
 from trytond.pool import Pool
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.transaction import Transaction
 from trytond.exceptions import NotLogged
 
@@ -48,10 +54,10 @@ def logout(dbname, user, session):
 
 
 def check_super(passwd):
-    if passwd == CONFIG['admin_passwd']:
+    cryptedpasswd = config.get('session', 'super_pwd')
+    if cryptedpasswd and crypt.crypt(passwd, cryptedpasswd) == cryptedpasswd:
         return True
-    else:
-        raise Exception('AccessDenied')
+    raise Exception('AccessDenied')
 
 
 def check(dbname, user, session):
diff --git a/trytond/server.py b/trytond/server.py
index 720b224..a5df713 100644
--- a/trytond/server.py
+++ b/trytond/server.py
@@ -4,6 +4,7 @@
 %prog [options]
 """
 import logging
+import logging.config
 import logging.handlers
 import sys
 import os
@@ -12,7 +13,7 @@ import time
 from getpass import getpass
 import threading
 
-from trytond.config import CONFIG
+from trytond.config import config, parse_listen
 from trytond import backend
 from trytond.pool import Pool
 from trytond.monitor import monitor
@@ -22,59 +23,37 @@ from .transaction import Transaction
 class TrytonServer(object):
 
     def __init__(self, options):
-        format = '[%(asctime)s] %(levelname)s:%(name)s:%(message)s'
-        datefmt = '%a %b %d %H:%M:%S %Y'
-        logging.basicConfig(level=logging.INFO, format=format,
+
+        config.update_etc(options.configfile)
+
+        if options.logconf:
+            logging.config.fileConfig(options.logconf)
+            logging.getLogger('server').info('using %s as logging '
+                'configuration file', options.logconf)
+        else:
+            logformat = '[%(asctime)s] %(levelname)s:%(name)s:%(message)s'
+            datefmt = '%a %b %d %H:%M:%S %Y'
+            logging.basicConfig(level=logging.INFO, format=logformat,
                 datefmt=datefmt)
 
-        CONFIG.update_etc(options['configfile'])
-        CONFIG.update_cmdline(options)
-
-        if CONFIG['logfile']:
-            logf = CONFIG['logfile']
-            # test if the directories exist, else create them
-            try:
-                diff = 0
-                if os.path.isfile(logf):
-                    diff = int(time.time()) - int(os.stat(logf)[-1])
-                handler = logging.handlers.TimedRotatingFileHandler(
-                    logf, 'D', 1, 30)
-                handler.rolloverAt -= diff
-            except Exception, exception:
-                sys.stderr.write(
-                    "ERROR: couldn't create the logfile directory:"
-                    + str(exception))
-            else:
-                formatter = logging.Formatter(format, datefmt)
-                # tell the handler to use this format
-                handler.setFormatter(formatter)
-
-                # add the handler to the root logger
-                logging.getLogger().addHandler(handler)
-                logging.getLogger().setLevel(logging.INFO)
-        elif os.name != 'nt':
-            reverse = '\x1b[7m'
-            reset = '\x1b[0m'
-            # reverse color for error and critical messages
-            for level in logging.ERROR, logging.CRITICAL:
-                msg = reverse + logging.getLevelName(level) + reset
-                logging.addLevelName(level, msg)
-
-        self.logger = logging.getLogger("server")
-
-        if CONFIG.configfile:
+        self.logger = logging.getLogger(__name__)
+
+        if options.configfile:
             self.logger.info('using %s as configuration file'
-                % CONFIG.configfile)
+                % options.configfile)
         else:
             self.logger.info('using default configuration')
         self.logger.info('initialising distributed objects services')
         self.xmlrpcd = []
         self.jsonrpcd = []
         self.webdavd = []
+        self.options = options
+
+        if time.tzname != ('UTC', 'UTC'):
+            self.logger.error('timezeone is not set to UTC')
 
     def run(self):
         "Run the server and never return"
-        update = bool(CONFIG['init'] or CONFIG['update'])
         init = {}
 
         signal.signal(signal.SIGINT, lambda *a: self.stop())
@@ -84,22 +63,18 @@ class TrytonServer(object):
         if hasattr(signal, 'SIGUSR1'):
             signal.signal(signal.SIGUSR1, lambda *a: self.restart())
 
-        if CONFIG['pidfile']:
-            with open(CONFIG['pidfile'], 'w') as fd_pid:
+        if self.options.pidfile:
+            with open(self.options.pidfile, 'w') as fd_pid:
                 fd_pid.write("%d" % (os.getpid()))
 
-        if not CONFIG["db_name"] \
-                and bool(CONFIG['init'] or CONFIG['update']):
-            raise Exception('Missing database option!')
-
-        if not update:
+        if not self.options.update:
             self.start_servers()
 
-        for db_name in CONFIG["db_name"]:
+        for db_name in self.options.database_names:
             init[db_name] = False
             with Transaction().start(db_name, 0) as transaction:
                 cursor = transaction.cursor
-                if CONFIG['init']:
+                if self.options.update:
                     if not cursor.test():
                         self.logger.info("init db")
                         backend.get('Database').init(cursor)
@@ -108,8 +83,8 @@ class TrytonServer(object):
                 elif not cursor.test():
                     raise Exception("'%s' is not a Tryton database!" % db_name)
 
-        for db_name in CONFIG["db_name"]:
-            if update:
+        for db_name in self.options.database_names:
+            if self.options.update:
                 with Transaction().start(db_name, 0) as transaction:
                     cursor = transaction.cursor
                     if not cursor.test():
@@ -120,12 +95,9 @@ class TrytonServer(object):
                     lang = [x[0] for x in cursor.fetchall()]
             else:
                 lang = None
-            Pool(db_name).init(update=update, lang=lang)
-
-        for kind in ('init', 'update'):
-            CONFIG[kind] = {}
+            Pool(db_name).init(update=self.options.update, lang=lang)
 
-        for db_name in CONFIG['db_name']:
+        for db_name in self.options.database_names:
             if init[db_name]:
                 # try to read password from environment variable
                 # TRYTONPASSFILE, empty TRYTONPASSFILE ignored
@@ -161,14 +133,14 @@ class TrytonServer(object):
                             })
                     transaction.cursor.commit()
 
-        if update:
+        if self.options.update:
             self.logger.info('Update/Init succeed!')
             logging.shutdown()
             sys.exit(0)
 
         threads = {}
         while True:
-            if CONFIG['cron']:
+            if self.options.cron:
                 for dbname in Pool.database_list():
                     thread = threads.get(dbname)
                     if thread and thread.is_alive():
@@ -187,42 +159,41 @@ class TrytonServer(object):
                             args=(dbname,), kwargs={})
                     thread.start()
                     threads[dbname] = thread
-            if CONFIG['auto_reload']:
+            if self.options.dev:
                 for _ in range(60):
-                    if monitor():
+                    if monitor([self.options.configfile]
+                            if self.options.configfile else []):
                         self.restart()
                     time.sleep(1)
             else:
                 time.sleep(60)
 
     def start_servers(self):
+        ssl = config.get('ssl', 'privatekey')
         # Launch Server
-        if CONFIG['jsonrpc']:
+        if config.get('jsonrpc', 'listen'):
             from trytond.protocols.jsonrpc import JSONRPCDaemon
-            for hostname, port in CONFIG['jsonrpc']:
-                self.jsonrpcd.append(JSONRPCDaemon(hostname, port,
-                    CONFIG['ssl_jsonrpc']))
+            for hostname, port in parse_listen(
+                    config.get('jsonrpc', 'listen')):
+                self.jsonrpcd.append(JSONRPCDaemon(hostname, port, ssl))
                 self.logger.info("starting JSON-RPC%s protocol on %s:%d" %
-                    (CONFIG['ssl_jsonrpc'] and ' SSL' or '', hostname or '*',
-                        port))
+                    (ssl and ' SSL' or '', hostname or '*', port))
 
-        if CONFIG['xmlrpc']:
+        if config.get('xmlrpc', 'listen'):
             from trytond.protocols.xmlrpc import XMLRPCDaemon
-            for hostname, port in CONFIG['xmlrpc']:
-                self.xmlrpcd.append(XMLRPCDaemon(hostname, port,
-                    CONFIG['ssl_xmlrpc']))
+            for hostname, port in parse_listen(
+                    config.get('xmlrpc', 'listen')):
+                self.xmlrpcd.append(XMLRPCDaemon(hostname, port, ssl))
                 self.logger.info("starting XML-RPC%s protocol on %s:%d" %
-                    (CONFIG['ssl_xmlrpc'] and ' SSL' or '', hostname or '*',
-                        port))
+                    (ssl and ' SSL' or '', hostname or '*', port))
 
-        if CONFIG['webdav']:
+        if config.get('webdav', 'listen'):
             from trytond.protocols.webdav import WebDAVServerThread
-            for hostname, port in CONFIG['webdav']:
-                self.webdavd.append(WebDAVServerThread(hostname, port,
-                    CONFIG['ssl_webdav']))
+            for hostname, port in parse_listen(
+                    config.get('webdav', 'listen')):
+                self.webdavd.append(WebDAVServerThread(hostname, port, ssl))
                 self.logger.info("starting WebDAV%s protocol on %s:%d" %
-                    (CONFIG['ssl_webdav'] and ' SSL' or '', hostname or '*',
-                        port))
+                    (ssl and ' SSL' or '', hostname or '*', port))
 
         for servers in (self.xmlrpcd, self.jsonrpcd, self.webdavd):
             for server in servers:
@@ -234,8 +205,8 @@ class TrytonServer(object):
                 server.stop()
                 server.join()
         if exit:
-            if CONFIG['pidfile']:
-                os.unlink(CONFIG['pidfile'])
+            if self.options.pidfile:
+                os.unlink(self.options.pidfile)
             logging.getLogger('server').info('stopped')
             logging.shutdown()
             sys.exit(0)
diff --git a/trytond/tests/__init__.py b/trytond/tests/__init__.py
index 8da0e0a..6e456ca 100644
--- a/trytond/tests/__init__.py
+++ b/trytond/tests/__init__.py
@@ -13,6 +13,7 @@ from .wizard import *
 from .workflow import *
 from .copy_ import *
 from history import *
+from .field_context import *
 
 
 def register():
@@ -97,6 +98,15 @@ def register():
         URLObject,
         ModelSQLRequiredField,
         ModelSQLTimestamp,
+        Model4Union1,
+        Model4Union2,
+        Model4Union3,
+        Model4Union4,
+        Union,
+        UnionUnion,
+        Model4UnionTree1,
+        Model4UnionTree2,
+        UnionTree,
         MPTT,
         ImportDataBoolean,
         ImportDataInteger,
@@ -141,6 +151,9 @@ def register():
         Many2OneTarget,
         Many2OneDomainValidation,
         TestHistory,
+        TestHistoryLine,
+        FieldContextChild,
+        FieldContextParent,
         module='tests', type_='model')
     Pool.register(
         TestWizard,
diff --git a/trytond/tests/field_context.py b/trytond/tests/field_context.py
new file mode 100644
index 0000000..e575b7d
--- /dev/null
+++ b/trytond/tests/field_context.py
@@ -0,0 +1,25 @@
+# This file is part of Tryton.  The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+from trytond.model import ModelSQL, fields
+from trytond.pyson import Eval
+
+__all__ = [
+    'FieldContextParent',
+    'FieldContextChild',
+    ]
+
+
+class FieldContextParent(ModelSQL):
+    'Field Context Parent'
+    __name__ = 'test.field_context.parent'
+    name = fields.Char('Name')
+    child = fields.Many2One('test.field_context.child', 'Child',
+        context={
+            'name': Eval('name'),
+            })
+
+
+class FieldContextChild(ModelSQL):
+    'Field Context Child'
+    __name__ = 'test.field_context.child'
diff --git a/trytond/tests/history.py b/trytond/tests/history.py
index b23d8ec..ed1b24b 100644
--- a/trytond/tests/history.py
+++ b/trytond/tests/history.py
@@ -2,7 +2,7 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSQL, fields
 
-__all__ = ['TestHistory']
+__all__ = ['TestHistory', 'TestHistoryLine']
 
 
 class TestHistory(ModelSQL):
@@ -10,3 +10,12 @@ class TestHistory(ModelSQL):
     __name__ = 'test.history'
     _history = True
     value = fields.Integer('Value')
+    lines = fields.One2Many('test.history.line', 'history', 'Lines')
+
+
+class TestHistoryLine(ModelSQL):
+    'Test History Line'
+    __name__ = 'test.history.line'
+    _history = True
+    history = fields.Many2One('test.history', 'History')
+    name = fields.Char('Name')
diff --git a/trytond/tests/model.py b/trytond/tests/model.py
index 80ed3d4..1815389 100644
--- a/trytond/tests/model.py
+++ b/trytond/tests/model.py
@@ -1,9 +1,12 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.model import ModelSingleton, ModelSQL, fields
+from trytond.model import ModelSingleton, ModelSQL, UnionMixin, fields
 
 __all__ = [
     'Singleton', 'URLObject', 'ModelSQLRequiredField', 'ModelSQLTimestamp',
+    'Model4Union1', 'Model4Union2', 'Model4Union3', 'Model4Union4',
+    'Union', 'UnionUnion',
+    'Model4UnionTree1', 'Model4UnionTree2', 'UnionTree',
     ]
 
 
@@ -34,3 +37,74 @@ class ModelSQLRequiredField(ModelSQL):
 class ModelSQLTimestamp(ModelSQL):
     'Model to test timestamp'
     __name__ = 'test.modelsql.timestamp'
+
+
+class Model4Union1(ModelSQL):
+    'Model for union 1'
+    __name__ = 'test.model.union1'
+    name = fields.Char('Name')
+    optional = fields.Char('Optional')
+
+
+class Model4Union2(ModelSQL):
+    'Model for union 2'
+    __name__ = 'test.model.union2'
+    name = fields.Char('Name')
+
+
+class Model4Union3(ModelSQL):
+    'Model for union 3'
+    __name__ = 'test.model.union3'
+    name = fields.Char('Name')
+
+
+class Model4Union4(ModelSQL):
+    'Model for union 4'
+    __name__ = 'test.model.union4'
+    name = fields.Char('Name')
+
+
+class Union(UnionMixin, ModelSQL):
+    'Union'
+    __name__ = 'test.union'
+    name = fields.Char('Name')
+    optional = fields.Char('Optional')
+
+    @staticmethod
+    def union_models():
+        return ['test.model.union%s' % i for i in range(1, 4)]
+
+
+class UnionUnion(UnionMixin, ModelSQL):
+    'Union of union'
+    __name__ = 'test.union.union'
+    name = fields.Char('Name')
+
+    @staticmethod
+    def union_models():
+        return ['test.union', 'test.model.union4']
+
+
+class Model4UnionTree1(ModelSQL):
+    'Model for union tree 1'
+    __name__ = 'test.model.union.tree1'
+    name = fields.Char('Name')
+
+
+class Model4UnionTree2(ModelSQL):
+    'Model for union tree 2'
+    __name__ = 'test.model.union.tree2'
+    name = fields.Char('Name')
+    parent = fields.Many2One('test.model.union.tree1', 'Parent')
+
+
+class UnionTree(UnionMixin, ModelSQL):
+    'Union tree'
+    __name__ = 'test.union.tree'
+    name = fields.Char('Name')
+    parent = fields.Many2One('test.union.tree', 'Parent')
+    childs = fields.One2Many('test.union.tree', 'parent', 'Childs')
+
+    @staticmethod
+    def union_models():
+        return ['test.model.union.tree1', 'test.model.union.tree2']
diff --git a/trytond/tests/run-tests.py b/trytond/tests/run-tests.py
index f576c34..f6dd242 100755
--- a/trytond/tests/run-tests.py
+++ b/trytond/tests/run-tests.py
@@ -8,7 +8,8 @@ import time
 import unittest
 import sys
 
-from trytond.config import CONFIG
+from trytond.config import config
+from trytond import backend
 
 if __name__ != '__main__':
     raise ImportError('%s can not be imported' % __name__)
@@ -22,18 +23,17 @@ parser.add_argument("-m", "--modules", action="store_true", dest="modules",
 parser.add_argument("-v", action="count", default=0, dest="verbosity",
     help="Increase verbosity")
 parser.add_argument('tests', metavar='test', nargs='*')
+parser.epilog = ('The database name can be specified in the DB_NAME '
+    'environment variable.')
 opt = parser.parse_args()
 
-CONFIG['db_type'] = 'sqlite'
-CONFIG.update_etc(opt.config)
-if not CONFIG['admin_passwd']:
-    CONFIG['admin_passwd'] = 'admin'
+config.update_etc(opt.config)
 
-if CONFIG['db_type'] == 'sqlite':
+if backend.name() == 'sqlite':
     database_name = ':memory:'
 else:
     database_name = 'test_' + str(int(time.time()))
-os.environ['DB_NAME'] = database_name
+os.environ.setdefault('DB_NAME', database_name)
 
 from trytond.tests.test_tryton import all_suite, modules_suite
 if not opt.modules:
diff --git a/trytond/tests/test.py b/trytond/tests/test.py
index f6cbaf7..5f812f2 100644
--- a/trytond/tests/test.py
+++ b/trytond/tests/test.py
@@ -604,6 +604,7 @@ class Selection(ModelSQL):
     select = fields.Selection([
             ('', ''), ('arabic', 'Arabic'), ('hexa', 'Hexadecimal')],
         'Selection')
+    select_string = select.translated('select')
     dyn_select = fields.Selection('get_selection',
         'Instance Dynamic Selection')
     dyn_select_static = fields.Selection('static_selection',
diff --git a/trytond/tests/test_access.py b/trytond/tests/test_access.py
index 6322ac4..3251a77 100644
--- a/trytond/tests/test_access.py
+++ b/trytond/tests/test_access.py
@@ -5,6 +5,10 @@ import unittest
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.transaction import Transaction
+from trytond.exceptions import UserError
+
+CONTEXT = CONTEXT.copy()
+CONTEXT['_check_access'] = True
 
 
 class ModelAccessTestCase(unittest.TestCase):
@@ -42,7 +46,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_read': False,
                     })
-            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(UserError, self.test_access.read, [test.id])
 
             # Two access rules with one group allowed
             group, = self.group.search([('users', '=', USER)])
@@ -70,11 +74,11 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_read': False,
                     })
-            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(UserError, self.test_access.read, [test.id])
 
             # One access disallowed for one group
             self.model_access.delete([model_access_wo_group])
-            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(UserError, self.test_access.read, [test.id])
 
             # One access allowed for one group
             self.model_access.write([model_access_w_group], {
@@ -123,7 +127,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_write': False,
                     })
-            self.assertRaises(Exception, self.test_access.write, [test], {})
+            self.assertRaises(UserError, self.test_access.write, [test], {})
 
             # Two access rules with one group allowed
             group, = self.group.search([('users', '=', USER)])
@@ -150,11 +154,11 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_write': False,
                     })
-            self.assertRaises(Exception, self.test_access.write, [test], {})
+            self.assertRaises(UserError, self.test_access.write, [test], {})
 
             # One access disallowed for one group
             self.model_access.delete([model_access_wo_group])
-            self.assertRaises(Exception, self.test_access.write, [test], {})
+            self.assertRaises(UserError, self.test_access.write, [test], {})
 
             # One access allowed for one group
             self.model_access.write([model_access_w_group], {
@@ -201,7 +205,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_create': False,
                     })
-            self.assertRaises(Exception, self.test_access.create, {})
+            self.assertRaises(UserError, self.test_access.create, {})
 
             # Two access rules with one group allowed
             group, = self.group.search([('users', '=', USER)])
@@ -229,11 +233,11 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_create': False,
                     })
-            self.assertRaises(Exception, self.test_access.create, [{}])
+            self.assertRaises(UserError, self.test_access.create, [{}])
 
             # One access disallowed for one group
             self.model_access.delete([model_access_wo_group])
-            self.assertRaises(Exception, self.test_access.create, [{}])
+            self.assertRaises(UserError, self.test_access.create, [{}])
 
             # One access allowed for one group
             self.model_access.write([model_access_w_group], {
@@ -282,7 +286,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_delete': False,
                     })
-            self.assertRaises(Exception, self.test_access.delete,
+            self.assertRaises(UserError, self.test_access.delete,
                 [tests.pop()])
 
             # Two access rules with one group allowed
@@ -311,12 +315,12 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access.write([model_access_wo_group], {
                     'perm_delete': False,
                     })
-            self.assertRaises(Exception, self.test_access.delete,
+            self.assertRaises(UserError, self.test_access.delete,
                 [tests.pop()])
 
             # One access disallowed for one group
             self.model_access.delete([model_access_wo_group])
-            self.assertRaises(Exception, self.test_access.delete,
+            self.assertRaises(UserError, self.test_access.delete,
                 [tests.pop()])
 
             # One access allowed for one group
@@ -400,11 +404,11 @@ class ModelFieldAccessTestCase(unittest.TestCase):
                     'perm_read': False,
                     })
 
-            self.assertRaises(Exception, self.test_access.read, [test.id],
+            self.assertRaises(UserError, self.test_access.read, [test.id],
                 ['field1'])
             self.test_access.read([test.id], ['field2'])
-            self.assertRaises(Exception, self.test_access.read, [test.id])
-            self.assertRaises(Exception, getattr, test, 'field1')
+            self.assertRaises(UserError, self.test_access.read, [test.id])
+            self.assertRaises(UserError, getattr, test, 'field1')
             test.field2
             transaction.cursor.cache.clear()
             test = self.test_access(test.id)
@@ -453,22 +457,22 @@ class ModelFieldAccessTestCase(unittest.TestCase):
             self.field_access.write([field_access_wo_group], {
                 'perm_read': False,
                 })
-            self.assertRaises(Exception, self.test_access.read, [test.id],
+            self.assertRaises(UserError, self.test_access.read, [test.id],
                 ['field1'])
             self.test_access.read([test.id], ['field2'])
-            self.assertRaises(Exception, self.test_access.read, [test.id])
-            self.assertRaises(Exception, getattr, test, 'field1')
+            self.assertRaises(UserError, self.test_access.read, [test.id])
+            self.assertRaises(UserError, getattr, test, 'field1')
             test.field2
             transaction.cursor.cache.clear()
             test = self.test_access(test.id)
 
             # One access disallowed for one group
             self.field_access.delete([field_access_wo_group])
-            self.assertRaises(Exception, self.test_access.read, [test.id],
+            self.assertRaises(UserError, self.test_access.read, [test.id],
                 ['field1'])
             self.test_access.read([test.id], ['field2'])
-            self.assertRaises(Exception, self.test_access.read, [test.id])
-            self.assertRaises(Exception, getattr, test, 'field1')
+            self.assertRaises(UserError, self.test_access.read, [test.id])
+            self.assertRaises(UserError, getattr, test, 'field1')
             test.field2
             transaction.cursor.cache.clear()
             test = self.test_access(test.id)
@@ -537,11 +541,11 @@ class ModelFieldAccessTestCase(unittest.TestCase):
                 'perm_read': False,
                 })
             self.test_access.read([test.id], ['field1'])
-            self.assertRaises(Exception, self.test_access.read, [test.id],
+            self.assertRaises(UserError, self.test_access.read, [test.id],
                 ['field2'])
-            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(UserError, self.test_access.read, [test.id])
             test.field1
-            self.assertRaises(Exception, getattr, test, 'field2')
+            self.assertRaises(UserError, getattr, test, 'field2')
             transaction.cursor.cache.clear()
             test = self.test_access(test.id)
 
@@ -549,13 +553,13 @@ class ModelFieldAccessTestCase(unittest.TestCase):
             self.field_access.write([field_access1], {
                     'perm_read': False,
                     })
-            self.assertRaises(Exception, self.test_access.read, [test.id],
+            self.assertRaises(UserError, self.test_access.read, [test.id],
                 ['field1'])
-            self.assertRaises(Exception, self.test_access.read, [test.id],
+            self.assertRaises(UserError, self.test_access.read, [test.id],
                 ['field2'])
-            self.assertRaises(Exception, self.test_access.read, [test.id])
-            self.assertRaises(Exception, getattr, test, 'field1')
-            self.assertRaises(Exception, getattr, test, 'field2')
+            self.assertRaises(UserError, self.test_access.read, [test.id])
+            self.assertRaises(UserError, getattr, test, 'field1')
+            self.assertRaises(UserError, getattr, test, 'field2')
             transaction.cursor.cache.clear()
             test = self.test_access(test.id)
 
@@ -607,10 +611,10 @@ class ModelFieldAccessTestCase(unittest.TestCase):
                     })
 
             self.test_access.write([test], {})
-            self.assertRaises(Exception, self.test_access.write, [test],
+            self.assertRaises(UserError, self.test_access.write, [test],
                 {'field1': 'ham'})
             self.test_access.write([test], {'field2': 'spam'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
@@ -660,10 +664,10 @@ class ModelFieldAccessTestCase(unittest.TestCase):
                     'perm_write': False,
                     })
             self.test_access.write([test], {})
-            self.assertRaises(Exception, self.test_access.write, [test],
+            self.assertRaises(UserError, self.test_access.write, [test],
                 {'field1': 'ham'})
             self.test_access.write([test], {'field2': 'spam'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
@@ -671,10 +675,10 @@ class ModelFieldAccessTestCase(unittest.TestCase):
             # One access disallowed for one group
             self.field_access.delete([field_access_wo_group])
             self.test_access.write([test], {})
-            self.assertRaises(Exception, self.test_access.write, [test],
+            self.assertRaises(UserError, self.test_access.write, [test],
                 {'field1': 'ham'})
             self.test_access.write([test], {'field2': 'ham'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
@@ -744,9 +748,9 @@ class ModelFieldAccessTestCase(unittest.TestCase):
                     })
             self.test_access.write([test], {})
             self.test_access.write([test], {'field1': 'ham'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field2': 'spam'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
@@ -756,11 +760,11 @@ class ModelFieldAccessTestCase(unittest.TestCase):
                     'perm_write': False,
                     })
             self.test_access.write([test], {})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field1': 'ham'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field2': 'spam'})
-            self.assertRaises(Exception, self.test_access.write, [test], {
+            self.assertRaises(UserError, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
diff --git a/trytond/tests/test_exportdata.py b/trytond/tests/test_exportdata.py
index 989d2d2..417b52b 100644
--- a/trytond/tests/test_exportdata.py
+++ b/trytond/tests/test_exportdata.py
@@ -216,8 +216,9 @@ class ExportDataTestCase(unittest.TestCase):
                         'selection': 'select1',
                         }])
             self.assertEqual(
-                self.export_data.export_data([export1], ['selection']),
-                [['select1']])
+                self.export_data.export_data([export1], ['selection',
+                        'selection.translated']),
+                [['select1', 'Select 1']])
 
             export2, = self.export_data.create([{
                         'selection': None,
diff --git a/trytond/tests/test_field_context.py b/trytond/tests/test_field_context.py
new file mode 100644
index 0000000..99a9cbc
--- /dev/null
+++ b/trytond/tests/test_field_context.py
@@ -0,0 +1,37 @@
+# This file is part of Tryton.  The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+import unittest
+
+from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
+        install_module
+from trytond.transaction import Transaction
+
+
+class FieldContextTestCase(unittest.TestCase):
+    "Test context on field"
+
+    def setUp(self):
+        install_module('tests')
+
+    def test_context(self):
+        Parent = POOL.get('test.field_context.parent')
+        Child = POOL.get('test.field_context.child')
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            child = Child()
+            child.save()
+            parent = Parent(name='foo', child=child)
+            parent.save()
+            self.assertEqual(parent.child._context['name'], 'foo')
+
+            parent.name = 'bar'
+            parent.save()
+            self.assertEqual(parent.child._context['name'], 'bar')
+
+
+def suite():
+    func = unittest.TestLoader().loadTestsFromTestCase
+    suite = unittest.TestSuite()
+    for testcase in (FieldContextTestCase,):
+        suite.addTests(func(testcase))
+    return suite
diff --git a/trytond/tests/test_fields.py b/trytond/tests/test_fields.py
index 8d260cc..2c436f6 100644
--- a/trytond/tests/test_fields.py
+++ b/trytond/tests/test_fields.py
@@ -567,7 +567,7 @@ class FieldsTestCase(unittest.TestCase):
                     'float': 'test',
                     })
 
-            self.assertRaises(Exception, self.float_required.create, [{}])
+            self.assertRaises(UserError, self.float_required.create, [{}])
             transaction.cursor.rollback()
 
             float5, = self.float_required.create([{
@@ -581,17 +581,17 @@ class FieldsTestCase(unittest.TestCase):
                         }])
             self.assert_(float6)
 
-            self.assertRaises(Exception, self.float_digits.create, [{
+            self.assertRaises(UserError, self.float_digits.create, [{
                         'digits': 1,
                         'float': 1.11,
                         }])
 
-            self.assertRaises(Exception, self.float_digits.write,
+            self.assertRaises(UserError, self.float_digits.write,
                 [float6], {
                     'float': 1.11,
                     })
 
-            self.assertRaises(Exception, self.float_digits.write,
+            self.assertRaises(UserError, self.float_digits.write,
                 [float6], {
                     'digits': 0,
                     })
@@ -603,6 +603,42 @@ class FieldsTestCase(unittest.TestCase):
 
             transaction.cursor.rollback()
 
+    def test0031float(self):
+        'Test float search with None'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            float_none, float0, float1 = self.float.create([{
+                        'float': None,
+                        }, {
+                        'float': 0,
+                        }, {
+                        'float': 1,
+                        }])
+            self.assertEqual([float_none], self.float.search([
+                        ('float', '=', None),
+                        ]))
+            self.assertEqual([float0], self.float.search([
+                        ('float', '=', 0),
+                        ]))
+            self.assertEqual([float1], self.float.search([
+                        ('float', '>', 0),
+                        ]))
+
+            self.assertEqual([float0, float1], self.float.search([
+                        ('float', '!=', None),
+                        ]))
+            self.assertEqual([float1], self.float.search([
+                        ('float', '!=', 0),
+                        ]))
+            self.assertEqual([float0], self.float.search([
+                        ('float', '<', 1),
+                        ]))
+
+            self.assertEqual([float_none, float1], self.float.search([
+                        'OR',
+                        ('float', '>', 0),
+                        ('float', '=', None),
+                        ]))
+
     def test0040numeric(self):
         'Test Numeric'
         with Transaction().start(DB_NAME, USER,
@@ -770,7 +806,7 @@ class FieldsTestCase(unittest.TestCase):
                     'numeric': 'test',
                     })
 
-            self.assertRaises(Exception, self.numeric_required.create, [{}])
+            self.assertRaises(UserError, self.numeric_required.create, [{}])
             transaction.cursor.rollback()
 
             numeric5, = self.numeric_required.create([{
@@ -784,22 +820,22 @@ class FieldsTestCase(unittest.TestCase):
                         }])
             self.assert_(numeric6)
 
-            self.assertRaises(Exception, self.numeric_digits.create, [{
+            self.assertRaises(UserError, self.numeric_digits.create, [{
                         'digits': 1,
                         'numeric': Decimal('1.11'),
                         }])
 
-            self.assertRaises(Exception, self.numeric_digits.write,
+            self.assertRaises(UserError, self.numeric_digits.write,
                 [numeric6], {
                     'numeric': Decimal('1.11'),
                     })
 
-            self.assertRaises(Exception, self.numeric_digits.write,
+            self.assertRaises(UserError, self.numeric_digits.write,
                 [numeric6], {
                     'numeric': Decimal('0.10000000000000001'),
                     })
 
-            self.assertRaises(Exception, self.numeric_digits.write,
+            self.assertRaises(UserError, self.numeric_digits.write,
                 [numeric6], {
                     'digits': 0,
                     })
@@ -825,6 +861,42 @@ class FieldsTestCase(unittest.TestCase):
                     ])
             self.assertEqual(numerics, [numeric1])
 
+    def test0042numeric(self):
+        'Test numeric search with None'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            numeric_none, numeric0, numeric1 = self.numeric.create([{
+                        'numeric': None,
+                        }, {
+                        'numeric': 0,
+                        }, {
+                        'numeric': 1,
+                        }])
+            self.assertEqual([numeric_none], self.numeric.search([
+                        ('numeric', '=', None),
+                        ]))
+            self.assertEqual([numeric0], self.numeric.search([
+                        ('numeric', '=', 0),
+                        ]))
+            self.assertEqual([numeric1], self.numeric.search([
+                        ('numeric', '>', 0),
+                        ]))
+
+            self.assertEqual([numeric0, numeric1], self.numeric.search([
+                        ('numeric', '!=', None),
+                        ]))
+            self.assertEqual([numeric1], self.numeric.search([
+                        ('numeric', '!=', 0),
+                        ]))
+            self.assertEqual([numeric0], self.numeric.search([
+                        ('numeric', '<', 1),
+                        ]))
+
+            self.assertEqual([numeric_none, numeric1], self.numeric.search([
+                        'OR',
+                        ('numeric', '>', 0),
+                        ('numeric', '=', None),
+                        ]))
+
     def test0050char(self):
         'Test Char'
         with Transaction().start(DB_NAME, USER,
@@ -1025,10 +1097,10 @@ class FieldsTestCase(unittest.TestCase):
                     })
             self.assertEqual(char2.char, 'Test')
 
-            self.assertRaises(Exception, self.char_required.create, [{}])
+            self.assertRaises(UserError, self.char_required.create, [{}])
             transaction.cursor.rollback()
 
-            self.assertRaises(Exception, self.char_required.create, [{
+            self.assertRaises(UserError, self.char_required.create, [{
                     'char': '',
                     }])
             transaction.cursor.rollback()
@@ -1275,7 +1347,7 @@ class FieldsTestCase(unittest.TestCase):
                     })
             self.assertEqual(text2.text, 'Test')
 
-            self.assertRaises(Exception, self.text_required.create, [{}])
+            self.assertRaises(UserError, self.text_required.create, [{}])
             transaction.cursor.rollback()
 
             text5, = self.text_required.create([{
@@ -1288,11 +1360,11 @@ class FieldsTestCase(unittest.TestCase):
                         }])
             self.assert_(text6)
 
-            self.assertRaises(Exception, self.text_size.create, [{
+            self.assertRaises(UserError, self.text_size.create, [{
                         'text': 'foobar',
                         }])
 
-            self.assertRaises(Exception, self.text_size.write, [text6], {
+            self.assertRaises(UserError, self.text_size.write, [text6], {
                     'text': 'foobar',
                     })
 
@@ -1546,7 +1618,7 @@ class FieldsTestCase(unittest.TestCase):
             self.assert_(date5)
             self.assertEqual(date5.date, datetime.date(2009, 1, 1))
 
-            self.assertRaises(Exception, self.date_required.create, [{}])
+            self.assertRaises(UserError, self.date_required.create, [{}])
             transaction.cursor.rollback()
 
             date6, = self.date_required.create([{
@@ -1798,7 +1870,7 @@ class FieldsTestCase(unittest.TestCase):
             self.assertEqual(datetime5.datetime,
                 datetime.datetime(2009, 1, 1, 12, 0, 0))
 
-            self.assertRaises(Exception, self.datetime_required.create, [{}])
+            self.assertRaises(UserError, self.datetime_required.create, [{}])
             transaction.cursor.rollback()
 
             datetime6, = self.datetime_required.create([{
@@ -1826,7 +1898,7 @@ class FieldsTestCase(unittest.TestCase):
             self.assert_(self.datetime_format.create([{
                             'datetime': datetime.datetime(2009, 1, 1, 12, 30),
                             }]))
-            self.assertRaises(Exception, self.datetime_format.create, [{
+            self.assertRaises(UserError, self.datetime_format.create, [{
                         'datetime': datetime.datetime(2009, 1, 1, 12, 30, 25),
                         }])
 
@@ -2045,7 +2117,7 @@ class FieldsTestCase(unittest.TestCase):
             self.assert_(time5)
             self.assertEqual(time5.time, datetime.time(12, 0))
 
-            self.assertRaises(Exception, self.time_required.create, [{}])
+            self.assertRaises(UserError, self.time_required.create, [{}])
             transaction.cursor.rollback()
 
             time6, = self.time_required.create([{
@@ -2068,7 +2140,7 @@ class FieldsTestCase(unittest.TestCase):
             self.assert_(self.time_format.create([{
                         'time': datetime.time(12, 30),
                         }]))
-            self.assertRaises(Exception, self.time_format.create, [{
+            self.assertRaises(UserError, self.time_format.create, [{
                     'time': datetime.time(12, 30, 25),
                     }])
 
@@ -2155,18 +2227,18 @@ class FieldsTestCase(unittest.TestCase):
                     })
             self.assertEqual(one2one2.one2one, None)
 
-            self.assertRaises(Exception, self.one2one.create, [{
+            self.assertRaises(UserError, self.one2one.create, [{
                         'name': 'one2one3',
                         'one2one': target1.id,
                         }])
             transaction.cursor.rollback()
 
-            self.assertRaises(Exception, self.one2one.write, [one2one2], {
+            self.assertRaises(UserError, self.one2one.write, [one2one2], {
                     'one2one': target1.id,
                     })
             transaction.cursor.rollback()
 
-            self.assertRaises(Exception, self.one2one_required.create, [{
+            self.assertRaises(UserError, self.one2one_required.create, [{
                         'name': 'one2one3',
                         }])
             transaction.cursor.rollback()
@@ -2184,7 +2256,7 @@ class FieldsTestCase(unittest.TestCase):
             target4, = self.one2one_target.create([{
                         'name': 'target4',
                         }])
-            self.assertRaises(Exception, self.one2one_domain.create, [{
+            self.assertRaises(UserError, self.one2one_domain.create, [{
                         'name': 'one2one4',
                         'one2one': target4.id,
                         }])
@@ -2367,7 +2439,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 transaction.cursor.rollback()
 
-            self.assertRaises(Exception, self.one2many_required.create, [{
+            self.assertRaises(UserError, self.one2many_required.create, [{
                         'name': 'origin3',
                         }])
             transaction.cursor.rollback()
@@ -2385,14 +2457,14 @@ class FieldsTestCase(unittest.TestCase):
             self.one2many_size.create([{
                         'targets': [('create', [{}])] * 3,
                         }])
-            self.assertRaises(Exception, self.one2many_size.create, [{
+            self.assertRaises(UserError, self.one2many_size.create, [{
                         'targets': [('create', [{}])] * 4,
                         }])
             self.one2many_size_pyson.create([{
                         'limit': 4,
                         'targets': [('create', [{}])] * 4,
                         }])
-            self.assertRaises(Exception, self.one2many_size_pyson.create, [{
+            self.assertRaises(UserError, self.one2many_size_pyson.create, [{
                         'limit': 2,
                         'targets': [('create', [{}])] * 4,
                         }])
@@ -2561,7 +2633,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 transaction.cursor.rollback()
 
-            self.assertRaises(Exception, self.many2many_required.create, [{
+            self.assertRaises(UserError, self.many2many_required.create, [{
                         'name': 'origin3',
                         }])
             transaction.cursor.rollback()
@@ -2697,7 +2769,7 @@ class FieldsTestCase(unittest.TestCase):
                         }])
             self.assert_(reference3)
 
-            self.assertRaises(Exception, self.reference_required.create, [{
+            self.assertRaises(UserError, self.reference_required.create, [{
                         'name': 'reference4',
                         }])
             transaction.cursor.rollback()
@@ -2988,12 +3060,14 @@ class FieldsTestCase(unittest.TestCase):
             selection1, = self.selection.create([{'select': 'arabic'}])
             self.assert_(selection1)
             self.assertEqual(selection1.select, 'arabic')
+            self.assertEqual(selection1.select_string, 'Arabic')
 
             selection2, = self.selection.create([{'select': None}])
             self.assert_(selection2)
             self.assertEqual(selection2.select, None)
+            self.assertEqual(selection2.select_string, '')
 
-            self.assertRaises(Exception, self.selection.create,
+            self.assertRaises(UserError, self.selection.create,
                 [{'select': 'chinese'}])
 
             selection3, = self.selection.create(
@@ -3014,15 +3088,15 @@ class FieldsTestCase(unittest.TestCase):
             self.assertEqual(selection5.select, 'hexa')
             self.assertEqual(selection5.dyn_select, None)
 
-            self.assertRaises(Exception, self.selection.create,
+            self.assertRaises(UserError, self.selection.create,
                 [{'select': 'arabic', 'dyn_select': '0x3'}])
-            self.assertRaises(Exception, self.selection.create,
+            self.assertRaises(UserError, self.selection.create,
                 [{'select': 'hexa', 'dyn_select': '3'}])
 
-            self.assertRaises(Exception, self.selection_required.create, [{}])
+            self.assertRaises(UserError, self.selection_required.create, [{}])
             transaction.cursor.rollback()
 
-            self.assertRaises(Exception, self.selection_required.create,
+            self.assertRaises(UserError, self.selection_required.create,
                 [{'select': None}])
             transaction.cursor.rollback()
 
@@ -3048,13 +3122,13 @@ class FieldsTestCase(unittest.TestCase):
             dict3, = self.dict_default.create([{}])
             self.assert_(dict3.dico == {'a': 1})
 
-            self.assertRaises(Exception, self.dict_required.create, [{}])
+            self.assertRaises(UserError, self.dict_required.create, [{}])
             transaction.cursor.rollback()
 
             dict4, = self.dict_required.create([{'dico': dict(a=1)}])
             self.assert_(dict4.dico == {'a': 1})
 
-            self.assertRaises(Exception, self.dict_required.create,
+            self.assertRaises(UserError, self.dict_required.create,
                 [{'dico': {}}])
             transaction.cursor.rollback()
 
@@ -3081,13 +3155,13 @@ class FieldsTestCase(unittest.TestCase):
             bin3, = self.binary_default.create([{}])
             self.assert_(bin3.binary == buffer('default'))
 
-            self.assertRaises(Exception, self.binary_required.create, [{}])
+            self.assertRaises(UserError, self.binary_required.create, [{}])
             transaction.cursor.rollback()
 
             bin4, = self.binary_required.create([{'binary': buffer('baz')}])
             self.assert_(bin4.binary == buffer('baz'))
 
-            self.assertRaises(Exception, self.binary_required.create,
+            self.assertRaises(UserError, self.binary_required.create,
                 [{'binary': buffer('')}])
 
             transaction.cursor.rollback()
diff --git a/trytond/tests/test_history.py b/trytond/tests/test_history.py
index 5b4c064..74c9176 100644
--- a/trytond/tests/test_history.py
+++ b/trytond/tests/test_history.py
@@ -7,7 +7,7 @@ from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.transaction import Transaction
 from trytond.exceptions import UserError
-from trytond.config import CONFIG
+from trytond import backend
 
 
 class HistoryTestCase(unittest.TestCase):
@@ -16,6 +16,17 @@ class HistoryTestCase(unittest.TestCase):
     def setUp(self):
         install_module('tests')
 
+    def tearDown(self):
+        History = POOL.get('test.history')
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            cursor = transaction.cursor
+            table = History.__table__()
+            history_table = History.__table_history__()
+            cursor.execute(*table.delete())
+            cursor.execute(*history_table.delete())
+            cursor.commit()
+
     def test0010read(self):
         'Test read history'
         History = POOL.get('test.history')
@@ -65,7 +76,7 @@ class HistoryTestCase(unittest.TestCase):
             with Transaction().set_context(_datetime=datetime.datetime.min):
                 self.assertRaises(UserError, History.read, [history_id])
 
-    @unittest.skipIf(CONFIG['db_type'] in ('sqlite', 'mysql'),
+    @unittest.skipIf(backend.name() in ('sqlite', 'mysql'),
         'now() is not the start of the transaction')
     def test0020read_same_timestamp(self):
         'Test read history with same timestamp'
@@ -173,6 +184,204 @@ class HistoryTestCase(unittest.TestCase):
             History.restore_history([history_id], datetime.datetime.min)
             self.assertRaises(UserError, History.read, [history_id])
 
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            History.delete([History(history_id)])
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            History.restore_history([history_id], datetime.datetime.max)
+            self.assertRaises(UserError, History.read, [history_id])
+
+    @unittest.skipIf(backend.name() in ('sqlite', 'mysql'),
+        'now() is not the start of the transaction')
+    def test0045restore_history_same_timestamp(self):
+        'Test restore history with same timestamp'
+        History = POOL.get('test.history')
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(value=1)
+            history.save()
+            history_id = history.id
+            first = history.create_date
+            history.value = 2
+            history.save()
+            second = history.create_date
+
+            self.assertEqual(first, second)
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 3
+            history.save()
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            History.restore_history([history_id], first)
+            history = History(history_id)
+            self.assertEqual(history.value, 2)
+
+    def test0050ordered_search(self):
+        'Test ordered search of history models'
+        History = POOL.get('test.history')
+        order = [('value', 'ASC')]
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(value=1)
+            history.save()
+            first_id = history.id
+            first_stamp = history.create_date
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(value=2)
+            history.save()
+            second_id = history.id
+            second_stamp = history.create_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            first, second = History.search([], order=order)
+
+            self.assertEqual(first.id, first_id)
+            self.assertEqual(second.id, second_id)
+
+            first.value = 3
+            first.save()
+            third_stamp = first.write_date
+            transaction.cursor.commit()
+
+        results = [
+            (first_stamp, [first]),
+            (second_stamp, [first, second]),
+            (third_stamp, [second, first]),
+            (datetime.datetime.now(), [second, first]),
+            (datetime.datetime.max, [second, first]),
+            ]
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            for timestamp, instances in results:
+                with Transaction().set_context(_datetime=timestamp):
+                    records = History.search([], order=order)
+                    self.assertEqual(records, instances)
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            to_delete, _ = History.search([], order=order)
+
+            self.assertEqual(to_delete.id, second.id)
+
+            History.delete([to_delete])
+            transaction.cursor.commit()
+
+        results = [
+            (first_stamp, [first]),
+            (second_stamp, [first, second]),
+            (third_stamp, [second, first]),
+            (datetime.datetime.now(), [first]),
+            (datetime.datetime.max, [first]),
+            ]
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            for timestamp, instances in results:
+                with Transaction().set_context(_datetime=timestamp,
+                        from_test=True):
+                    records = History.search([], order=order)
+                    self.assertEqual(records, instances)
+
+    @unittest.skipIf(backend.name() in ('sqlite', 'mysql'),
+        'now() is not the start of the transaction')
+    def test0060_ordered_search_same_timestamp(self):
+        'Test ordered search  with same timestamp'
+        History = POOL.get('test.history')
+        order = [('value', 'ASC')]
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(value=1)
+            history.save()
+            first_stamp = history.create_date
+            history.value = 4
+            history.save()
+            second_stamp = history.write_date
+
+            self.assertEqual(first_stamp, second_stamp)
+            transaction.cursor.commit()
+
+        results = [
+            (second_stamp, [history], [4]),
+            (datetime.datetime.now(), [history], [4]),
+            (datetime.datetime.max, [history], [4]),
+            ]
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            for timestamp, instances, values in results:
+                with Transaction().set_context(_datetime=timestamp,
+                        last_test=True):
+                    records = History.search([], order=order)
+                    self.assertEqual(records, instances)
+                    self.assertEqual([x.value for x in records], values)
+
+    def test0070_browse(self):
+        'Test browsing history'
+        History = POOL.get('test.history')
+        Line = POOL.get('test.history.line')
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(value=1)
+            history.save()
+            history_id = history.id
+            line_a = Line(name='a', history=history)
+            line_a.save()
+            line_a_id = line_a.id
+            line_b = Line(name='b', history=history)
+            line_b.save()
+            line_b_id = line_b.id
+
+            first_stamp = line_b.create_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 2
+            history.save()
+
+            Line.delete([Line(line_b_id)])
+
+            line_a = Line(line_a_id)
+            line_a.name = 'c'
+            line_a.save()
+
+            second_stamp = line_a.write_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            self.assertEqual(history.value, 2)
+            self.assertEqual([l.name for l in history.lines], ['c'])
+
+            with Transaction().set_context(_datetime=first_stamp):
+                history = History(history_id)
+            self.assertEqual(history.value, 1)
+            self.assertEqual([l.name for l in history.lines], ['a', 'b'])
+
+            with Transaction().set_context(_datetime=second_stamp):
+                history = History(history_id)
+            self.assertEqual(history.value, 2)
+            self.assertEqual([l.name for l in history.lines], ['c'])
+
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(HistoryTestCase)
diff --git a/trytond/tests/test_modelsql.py b/trytond/tests/test_modelsql.py
index 233ed9a..e9ea26d 100644
--- a/trytond/tests/test_modelsql.py
+++ b/trytond/tests/test_modelsql.py
@@ -5,7 +5,7 @@
 import unittest
 import time
 
-from trytond.config import CONFIG
+from trytond import backend
 from trytond.exceptions import UserError, ConcurrencyException
 from trytond.transaction import Transaction
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
@@ -20,13 +20,12 @@ class ModelSQLTestCase(unittest.TestCase):
         self.modelsql = POOL.get('test.modelsql')
         self.modelsql_timestamp = POOL.get('test.modelsql.timestamp')
 
+    @unittest.skipIf(backend.name() == 'sqlite',
+        'SQLite not concerned because tryton don\'t set "NOT NULL"'
+        'constraint: "ALTER TABLE" don\'t support NOT NULL constraint'
+        'without default value')
     def test0010required_field_missing(self):
         'Test error message when a required field is missing'
-        if CONFIG['db_type'] not in ('postgresql', 'mysql'):
-            # SQLite not concerned because tryton don't set "NOT NULL"
-            # constraint: 'ALTER TABLE' don't support NOT NULL constraint
-            # without default value
-            return
         fields = {
             'desc': '',
             'integer': 0,
@@ -56,7 +55,7 @@ class ModelSQLTestCase(unittest.TestCase):
             timestamp = self.modelsql_timestamp.read([record.id],
                 ['_timestamp'])[0]['_timestamp']
 
-            if CONFIG['db_type'] in ('sqlite', 'mysql'):
+            if backend.name() in ('sqlite', 'mysql'):
                 # timestamp precision of sqlite is the second
                 time.sleep(1)
 
diff --git a/trytond/tests/test_mptt.py b/trytond/tests/test_mptt.py
index 7631456..6bbd37b 100644
--- a/trytond/tests/test_mptt.py
+++ b/trytond/tests/test_mptt.py
@@ -3,6 +3,7 @@
 #this repository contains the full copyright notices and license terms.
 import sys
 import unittest
+from mock import patch
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.transaction import Transaction
@@ -163,6 +164,23 @@ class MPTTTestCase(unittest.TestCase):
 
             transaction.cursor.rollback()
 
+    def test0060_update_only_if_parent_is_modified(self):
+        'The left and right fields must only be updated if parent is modified'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            records = self.mptt.search([
+                    ('parent', '=', None),
+                    ])
+            with patch.object(self.mptt, '_update_tree') as mock:
+                self.mptt.write(records, {'name': 'Parent Records'})
+                self.assertFalse(mock.called)
+
+                first_parent, second_parent = records[:2]
+                self.mptt.write(list(first_parent.childs), {
+                        'parent': second_parent.id,
+                        })
+
+                self.assertTrue(mock.called)
+
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(MPTTTestCase)
diff --git a/trytond/tests/test_protocols.py b/trytond/tests/test_protocols.py
new file mode 100644
index 0000000..ee445ca
--- /dev/null
+++ b/trytond/tests/test_protocols.py
@@ -0,0 +1,76 @@
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+
+import unittest
+import json
+import datetime
+from decimal import Decimal
+
+from trytond.protocols.jsonrpc import JSONEncoder, JSONDecoder
+from trytond.protocols.xmlrpc import xmlrpclib
+
+
+class JSONTestCase(unittest.TestCase):
+    'Test JSON'
+
+    def dumps_loads(self, value):
+        self.assertEqual(json.loads(
+                json.dumps(value, cls=JSONEncoder),
+                object_hook=JSONDecoder()), value)
+
+    def test_datetime(self):
+        'Test datetime'
+        self.dumps_loads(datetime.datetime.now())
+
+    def test_date(self):
+        'Test date'
+        self.dumps_loads(datetime.date.today())
+
+    def test_time(self):
+        'Test time'
+        self.dumps_loads(datetime.datetime.now().time())
+
+    def test_buffer(self):
+        'Test buffer'
+        self.dumps_loads(buffer('foo'))
+
+    def test_decimal(self):
+        'Test Decimal'
+        self.dumps_loads(Decimal('3.141592653589793'))
+
+
+class XMLTestCase(unittest.TestCase):
+    'Test XML'
+
+    def dumps_loads(self, value):
+        s = xmlrpclib.dumps((value,))
+        result, _ = xmlrpclib.loads(s)
+        result, = result
+        self.assertEqual(value, result)
+
+    def test_decimal(self):
+        'Test Decimal'
+        self.dumps_loads(Decimal('3.141592653589793'))
+
+    def test_buffer(self):
+        'Test buffer'
+        self.dumps_loads(buffer('foo'))
+
+    def test_date(self):
+        'Test date'
+        self.dumps_loads(datetime.date.today())
+
+    def test_time(self):
+        'Test time'
+        self.dumps_loads(datetime.datetime.now().time())
+
+    def test_none(self):
+        'Test None'
+        self.dumps_loads(None)
+
+
+def suite():
+    suite_ = unittest.TestSuite()
+    suite_.addTests(unittest.TestLoader().loadTestsFromTestCase(JSONTestCase))
+    suite_.addTests(unittest.TestLoader().loadTestsFromTestCase(XMLTestCase))
+    return suite_
diff --git a/trytond/tests/test_sequence.py b/trytond/tests/test_sequence.py
index 727bb1c..802372d 100644
--- a/trytond/tests/test_sequence.py
+++ b/trytond/tests/test_sequence.py
@@ -6,6 +6,7 @@ import datetime
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.transaction import Transaction
+from trytond.exceptions import UserError
 
 
 class SequenceTestCase(unittest.TestCase):
@@ -58,7 +59,7 @@ class SequenceTestCase(unittest.TestCase):
             self.assertNotEqual(self.sequence.get_id(sequence), timestamp)
 
             next_timestamp = self.sequence._timestamp(sequence)
-            self.assertRaises(Exception, self.sequence.write, [sequence], {
+            self.assertRaises(UserError, self.sequence.write, [sequence], {
                     'last_timestamp': next_timestamp + 100,
                     })
 
@@ -82,7 +83,7 @@ class SequenceTestCase(unittest.TestCase):
             self.assertNotEqual(self.sequence.get_id(sequence), timestamp)
 
             next_timestamp = self.sequence._timestamp(sequence)
-            self.assertRaises(Exception, self.sequence.write, [sequence], {
+            self.assertRaises(UserError, self.sequence.write, [sequence], {
                     'last_timestamp': next_timestamp + 100,
                     })
 
diff --git a/trytond/tests/test_tools.py b/trytond/tests/test_tools.py
index 0b9523c..3a5c3ad 100644
--- a/trytond/tests/test_tools.py
+++ b/trytond/tests/test_tools.py
@@ -60,7 +60,7 @@ class ToolsTestCase(unittest.TestCase):
 
     def test0060safe_eval_builtin(self):
         'Attempt to access a unsafe builtin'
-        self.assertRaises(Exception, safe_eval, "open('test.txt', 'w')")
+        self.assertRaises(NameError, safe_eval, "open('test.txt', 'w')")
 
     def test0061safe_eval_getattr(self):
         'Attempt to get arround direct attr access'
@@ -68,12 +68,12 @@ class ToolsTestCase(unittest.TestCase):
 
     def test0062safe_eval_func_globals(self):
         'Attempt to access global enviroment where fun was defined'
-        self.assertRaises(Exception, safe_eval,
+        self.assertRaises(SyntaxError, safe_eval,
                 "def x(): pass; print x.func_globals")
 
     def test0063safe_eval_lowlevel(self):
         "Lowlevel tricks to access 'object'"
-        self.assertRaises(Exception, safe_eval,
+        self.assertRaises(ValueError, safe_eval,
                 "().__class__.mro()[1].__subclasses__()")
 
     def test0070datetime_strftime(self):
diff --git a/trytond/tests/test_trigger.py b/trytond/tests/test_trigger.py
index 23bd815..200aedd 100644
--- a/trytond/tests/test_trigger.py
+++ b/trytond/tests/test_trigger.py
@@ -10,6 +10,7 @@ from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.tests.trigger import TRIGGER_LOGS
 from trytond.transaction import Transaction
+from trytond.exceptions import UserError
 
 
 class TriggerTestCase(unittest.TestCase):
@@ -24,8 +25,7 @@ class TriggerTestCase(unittest.TestCase):
 
     def test0010constraints(self):
         'Test constraints'
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             model, = self.model.search([
                     ('model', '=', 'test.triggered'),
                     ])
@@ -43,25 +43,27 @@ class TriggerTestCase(unittest.TestCase):
                 }
             self.assert_(self.trigger.create([values]))
 
-            # on_exclusive
-            for i in range(1, 4):
-                for combination in combinations(
-                        ['create', 'write', 'delete'], i):
+        # on_exclusive
+        for i in range(1, 4):
+            for combination in combinations(
+                    ['create', 'write', 'delete'], i):
+                with Transaction().start(DB_NAME, USER, context=CONTEXT):
                     combination_values = values.copy()
                     for mode in combination:
                         combination_values['on_%s' % mode] = True
-                    self.assertRaises(Exception, self.trigger.create,
+                    self.assertRaises(UserError, self.trigger.create,
                         [combination_values])
 
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             # check_condition
             condition_values = values.copy()
             condition_values['condition'] = '='
-            self.assertRaises(Exception, self.trigger.create,
+            self.assertRaises(UserError, self.trigger.create,
                 [condition_values])
 
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             # Restart the cache on the get_triggers method of ir.trigger
             self.trigger._get_triggers_cache.clear()
-            transaction.cursor.rollback()
 
     def test0020on_create(self):
         'Test on_create'
diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py
index dd0cf7d..94bfab9 100644
--- a/trytond/tests/test_tryton.py
+++ b/trytond/tests/test_tryton.py
@@ -7,15 +7,17 @@ import unittest
 import doctest
 from lxml import etree
 
-from trytond.config import CONFIG
 from trytond.pool import Pool
 from trytond import backend
-from trytond.protocols.dispatcher import create
+from trytond.protocols.dispatcher import create, drop
 from trytond.transaction import Transaction
 from trytond.pyson import PYSONEncoder, Eval
+from trytond.exceptions import UserError
+from trytond import security
 
 __all__ = ['POOL', 'DB_NAME', 'USER', 'USER_PASSWORD', 'CONTEXT',
-    'install_module', 'test_view', 'test_depends', 'doctest_dropdb',
+    'install_module', 'test_view', 'test_depends',
+    'doctest_setup', 'doctest_teardown',
     'suite', 'all_suite', 'modules_suite']
 
 Pool.start()
@@ -26,6 +28,7 @@ DB_NAME = os.environ['DB_NAME']
 DB = backend.get('Database')(DB_NAME)
 Pool.test = True
 POOL = Pool(DB_NAME)
+security.check_super = lambda *a, **k: True
 
 
 class ModelViewTestCase(unittest.TestCase):
@@ -38,8 +41,8 @@ class ModelViewTestCase(unittest.TestCase):
 
     def test0000test(self):
         'Test test'
-        self.assertRaises(Exception, install_module, 'nosuchmodule')
-        self.assertRaises(Exception, test_view, 'nosuchmodule')
+        self.assertRaises(UserError, install_module, 'nosuchmodule')
+        self.assertRaises(UserError, test_view, 'nosuchmodule')
 
     def test0010ir(self):
         'Test ir'
@@ -71,13 +74,7 @@ def install_module(name):
     '''
     Install module for the tested database
     '''
-    Database = backend.get('Database')
-    database = Database().connect()
-    cursor = database.cursor()
-    databases = database.list(cursor)
-    cursor.close()
-    if DB_NAME not in databases:
-        create(DB_NAME, CONFIG['admin_passwd'], 'en_US', USER_PASSWORD)
+    create_db()
     with Transaction().start(DB_NAME, USER,
             context=CONTEXT) as transaction:
         Module = POOL.get('ir.module.module')
@@ -176,16 +173,32 @@ def test_depends():
                         list(depends - set(model._fields)), mname, fname))
 
 
-def doctest_dropdb(test):
-    '''Remove SQLite memory database'''
-    from trytond.backend.sqlite.database import Database as SQLiteDatabase
-    database = SQLiteDatabase().connect()
-    cursor = database.cursor(autocommit=True)
-    try:
-        SQLiteDatabase.drop(cursor, ':memory:')
-        cursor.commit()
-    finally:
-        cursor.close()
+def db_exist():
+    Database = backend.get('Database')
+    database = Database().connect()
+    cursor = database.cursor()
+    databases = database.list(cursor)
+    cursor.close()
+    return DB_NAME in databases
+
+
+def create_db():
+    if not db_exist():
+        create(DB_NAME, None, 'en_US', USER_PASSWORD)
+
+
+def drop_db():
+    if db_exist():
+        drop(DB_NAME, None)
+
+
+def drop_create():
+    if db_exist:
+        drop_db()
+    create_db()
+
+doctest_setup = lambda test: drop_create()
+doctest_teardown = lambda test: drop_db()
 
 
 def suite():
diff --git a/trytond/tests/test_union.py b/trytond/tests/test_union.py
new file mode 100644
index 0000000..723c38c
--- /dev/null
+++ b/trytond/tests/test_union.py
@@ -0,0 +1,102 @@
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+import unittest
+
+from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
+        install_module
+from trytond.transaction import Transaction
+
+
+class UnionMixinTestCase(unittest.TestCase):
+    'Test UnionMixin'
+
+    def setUp(self):
+        install_module('tests')
+
+    def test_union(self):
+        'Test union'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            Union = POOL.get('test.union')
+            for i in range(1, 4):
+                Model = POOL.get('test.model.union%s' % i)
+                for j in range(3):
+                    model = Model(name='%s - %s' % (i, j))
+                    if hasattr(Model, 'optional'):
+                        model.optional = 'optional'
+                    model.save()
+
+            self.assertEqual(len(Union.search([])), 9)
+            record, = Union.search([
+                    ('name', '=', '2 - 2'),
+                    ])
+            self.assertEqual(record.name, '2 - 2')
+            self.assertEqual(record.optional, None)
+
+            record, = Union.search([
+                    ('optional', '=', 'optional'),
+                    ], limit=1)
+            self.assertEqual(record.optional, 'optional')
+
+    def test_union_union(self):
+        'Test union of union'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            Union = POOL.get('test.union.union')
+            for i in range(1, 5):
+                Model = POOL.get('test.model.union%s' % i)
+                for j in range(3):
+                    model = Model(name='%s - %s' % (i, j))
+                    model.save()
+
+            self.assertEqual(len(Union.search([])), 12)
+            record, = Union.search([
+                    ('name', '=', '2 - 2'),
+                    ])
+            self.assertEqual(record.name, '2 - 2')
+            record, = Union.search([
+                    ('name', '=', '4 - 1'),
+                    ])
+            self.assertEqual(record.name, '4 - 1')
+
+    def test_union_tree(self):
+        'Test union tree'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            Union = POOL.get('test.union.tree')
+            Model1 = POOL.get('test.model.union.tree1')
+            Model2 = POOL.get('test.model.union.tree2')
+
+            roots = Model1.create([{
+                        'name': 'Root1',
+                        }, {
+                        'name': 'Root2',
+                        }, {
+                        'name': 'Root3',
+                        }])
+
+            Model2.create([{
+                        'name': 'Not child',  # To shift ids
+                        }, {
+                        'name': 'Child1',
+                        'parent': roots[1].id,
+                        }, {
+                        'name': 'Child2',
+                        'parent': roots[1].id,
+                        }, {
+                        'name': 'Child3',
+                        'parent': roots[2].id,
+                        }])
+
+            uroots = Union.search([('parent', '=', None)],
+                order=[('name', 'ASC')])
+
+            self.assertEqual(len(uroots), 4)
+            names = [r.name for r in uroots]
+            self.assertEqual(names, ['Not child', 'Root1', 'Root2', 'Root3'])
+            childs = [len(r.childs) for r in uroots]
+            self.assertEqual(childs, [0, 0, 2, 1])
+            child_names = sorted((r.name for r in uroots[2].childs))
+            self.assertEqual(child_names, ['Child1', 'Child2'])
+            self.assertEqual(uroots[3].childs[0].name, 'Child3')
+
+
+def suite():
+    return unittest.TestLoader().loadTestsFromTestCase(UnionMixinTestCase)
diff --git a/trytond/tools/misc.py b/trytond/tools/misc.py
index 80caf52..ebe7c66 100644
--- a/trytond/tools/misc.py
+++ b/trytond/tools/misc.py
@@ -12,10 +12,13 @@ import smtplib
 import dis
 from decimal import Decimal
 from array import array
+from itertools import islice
 from sql import Literal
 from sql.operators import Or
-from trytond.config import CONFIG
+
 from trytond.const import OPERATORS
+from trytond.config import config, parse_uri
+from trytond.transaction import Transaction
 
 
 def find_in_path(name):
@@ -32,42 +35,7 @@ def find_in_path(name):
     return name
 
 
-def find_pg_tool(name):
-    if CONFIG['pg_path'] and CONFIG['pg_path'] != 'None':
-        return os.path.join(CONFIG['pg_path'], name)
-    else:
-        return find_in_path(name)
-
-
-def exec_pg_command(name, *args):
-    prog = find_pg_tool(name)
-    if not prog:
-        raise Exception('Couldn\'t find %s' % name)
-    args2 = (os.path.basename(prog),) + args
-    return os.spawnv(os.P_WAIT, prog, args2)
-
-
-def exec_pg_command_pipe(name, *args):
-    prog = find_pg_tool(name)
-    if not prog:
-        raise Exception('Couldn\'t find %s' % name)
-    if os.name == "nt":
-        cmd = '"' + prog + '" ' + ' '.join(args)
-    else:
-        cmd = prog + ' ' + ' '.join(args)
-
-    # if db_password is set in configuration we should pass
-    # an environment variable PGPASSWORD to our subprocess
-    # see libpg documentation
-    child_env = dict(os.environ)
-    if CONFIG['db_password']:
-        child_env['PGPASSWORD'] = CONFIG['db_password']
-    pipe = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
-            stdout=subprocess.PIPE, env=child_env)
-    return pipe
-
-
-def exec_command_pipe(name, *args):
+def exec_command_pipe(name, *args, **kwargs):
     prog = find_in_path(name)
     if not prog:
         raise Exception('Couldn\'t find %s' % name)
@@ -75,7 +43,11 @@ def exec_command_pipe(name, *args):
         cmd = '"' + prog + '" ' + ' '.join(args)
     else:
         cmd = prog + ' ' + ' '.join(args)
-    return os.popen2(cmd, 'b')
+    child_env = dict(os.environ)
+    if kwargs.get('env'):
+        child_env.update(kwargs['env'])
+    return subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
+        stdout=subprocess.PIPE, env=child_env)
 
 
 def file_open(name, mode="r", subdir='modules'):
@@ -132,17 +104,17 @@ def get_smtp_server():
     :return: A SMTP instance. The quit() method must be call when all
     the calls to sendmail() have been made.
     """
-    if CONFIG['smtp_ssl']:
-        smtp_server = smtplib.SMTP_SSL(CONFIG['smtp_server'],
-                CONFIG['smtp_port'])
+    uri = parse_uri(config.get('email', 'uri'))
+    if uri.scheme.startswith('smtps'):
+        smtp_server = smtplib.SMTP_SSL(uri.hostname, uri.port)
     else:
-        smtp_server = smtplib.SMTP(CONFIG['smtp_server'], CONFIG['smtp_port'])
+        smtp_server = smtplib.SMTP(uri.hostname, uri.port)
 
-    if CONFIG['smtp_tls']:
+    if 'tls' in uri.scheme:
         smtp_server.starttls()
 
-    if CONFIG['smtp_user'] and CONFIG['smtp_password']:
-        smtp_server.login(CONFIG['smtp_user'], CONFIG['smtp_password'])
+    if uri.username and uri.password:
+        smtp_server.login(uri.username, uri.password)
 
     return smtp_server
 
@@ -328,6 +300,7 @@ def reduce_ids(field, ids):
     '''
     Return a small SQL expression for the list of ids and the sql column
     '''
+    ids = list(ids)
     if not ids:
         return Literal(False)
     assert all(x.is_integer() for x in ids if isinstance(x, float)), \
@@ -442,3 +415,11 @@ def reduce_domain(domain):
         else:
             result.append(arg)
     return result
+
+
+def grouped_slice(records, count=None):
+    'Grouped slice'
+    if count is None:
+        count = Transaction().cursor.IN_MAX
+    for i in xrange(0, len(records), count):
+        yield islice(records, i, i + count)
diff --git a/trytond/url.py b/trytond/url.py
index ce6dd11..be17878 100644
--- a/trytond/url.py
+++ b/trytond/url.py
@@ -5,12 +5,12 @@ import encodings.idna
 import urllib
 import socket
 
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.transaction import Transaction
 
 __all__ = ['URLMixin']
 
-HOSTNAME = (CONFIG['hostname_jsonrpc']
+HOSTNAME = (config.get('jsonrpc', 'hostname')
     or unicode(socket.getfqdn(), 'utf8'))
 HOSTNAME = '.'.join(encodings.idna.ToASCII(part) if part else ''
     for part in HOSTNAME.split('.'))
diff --git a/trytond/version.py b/trytond/version.py
index 063c087..a68a845 100644
--- a/trytond/version.py
+++ b/trytond/version.py
@@ -1,6 +1,6 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 PACKAGE = "trytond"
-VERSION = "3.2.3"
+VERSION = "3.4.0"
 LICENSE = "GPL-3"
 WEBSITE = "http://www.tryton.org/"
diff --git a/trytond/webdav/locale/sl_SI.po b/trytond/webdav/locale/es_EC.po
similarity index 72%
copy from trytond/webdav/locale/sl_SI.po
copy to trytond/webdav/locale/es_EC.po
index c9550c2..cac92a0 100644
--- a/trytond/webdav/locale/sl_SI.po
+++ b/trytond/webdav/locale/es_EC.po
@@ -7,28 +7,28 @@ msgid ""
 "You can not create an attachment named \"%(attachment)s in collection "
 "\"%(collection)s\" because there is already a collection with that name."
 msgstr ""
-"Priloge z imenom \"%(attachment)s\" v zbirki \"%(collection)s\" ni možno "
-"ustvariti, ker že obstaja zbirka z istim imenom."
+"No puede crear un adjunto llamado \"%(attachment)s\" en la colección "
+"\"%(collections)s\" porque ya existe una colección con este nombre."
 
 msgctxt "error:webdav.collection:"
 msgid "The collection name must be unique inside a collection!"
-msgstr "Ime zbirke mora biti edinstveno znotraj zbirke."
+msgstr "¡El nombre de la colección debe ser único dentro de una colección!"
 
 msgctxt "error:webdav.collection:"
 msgid ""
 "You can not create a collection named \"%(parent)s\" in collection "
 "\"%(child)s\" because there is already a file with that name."
 msgstr ""
-"Zbirke z imenom \"%(parent)s\" v zbirki \"%(child)s\" ni možno ustvariti, "
-"ker že obstaja datoteka z istim imenom."
+"No puede crear una colección llamada \"%(parent)s\" en la colección "
+"\"%(child)s\" porque ya existe un archivo con ese nombre."
 
 msgctxt "field:ir.attachment,path:"
 msgid "Path"
-msgstr "Pot"
+msgstr "Ruta"
 
 msgctxt "field:ir.attachment,shares:"
 msgid "Shares"
-msgstr "Skupne rabe"
+msgstr "Recursos Compartidos"
 
 msgctxt "field:ir.attachment,url:"
 msgid "URL"
@@ -36,23 +36,23 @@ msgstr "URL"
 
 msgctxt "field:webdav.collection,childs:"
 msgid "Children"
-msgstr "Podzbirke"
+msgstr "Hijos"
 
 msgctxt "field:webdav.collection,complete_name:"
 msgid "Complete Name"
-msgstr "Polno ime"
+msgstr "Nombre Completo"
 
 msgctxt "field:webdav.collection,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Fecha de Creación"
 
 msgctxt "field:webdav.collection,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Creado por Usuario"
 
 msgctxt "field:webdav.collection,domain:"
 msgid "Domain"
-msgstr "Domena"
+msgstr "Dominio"
 
 msgctxt "field:webdav.collection,id:"
 msgid "ID"
@@ -60,39 +60,39 @@ msgstr "ID"
 
 msgctxt "field:webdav.collection,model:"
 msgid "Model"
-msgstr "Model"
+msgstr "Modelo"
 
 msgctxt "field:webdav.collection,name:"
 msgid "Name"
-msgstr "Naziv"
+msgstr "Nombre"
 
 msgctxt "field:webdav.collection,parent:"
 msgid "Parent"
-msgstr "Prednik"
+msgstr "Padre"
 
 msgctxt "field:webdav.collection,rec_name:"
 msgid "Name"
-msgstr "Ime"
+msgstr "Nombre"
 
 msgctxt "field:webdav.collection,write_date:"
 msgid "Write Date"
-msgstr "Zapisano"
+msgstr "Fecha de Modificación"
 
 msgctxt "field:webdav.collection,write_uid:"
 msgid "Write User"
-msgstr "Zapisal"
+msgstr "Modificado por Usuario"
 
 msgctxt "field:webdav.share,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Fecha de Creación"
 
 msgctxt "field:webdav.share,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Creado por Usuario"
 
 msgctxt "field:webdav.share,expiration_date:"
 msgid "Expiration Date"
-msgstr "Datum veljavnosti"
+msgstr "Fecha de Expiración"
 
 msgctxt "field:webdav.share,id:"
 msgid "ID"
@@ -100,19 +100,19 @@ msgstr "ID"
 
 msgctxt "field:webdav.share,key:"
 msgid "Key"
-msgstr "Ključ"
+msgstr "Clave"
 
 msgctxt "field:webdav.share,note:"
 msgid "Note"
-msgstr "Opomba"
+msgstr "Nota"
 
 msgctxt "field:webdav.share,path:"
 msgid "Path"
-msgstr "Pot"
+msgstr "Ruta"
 
 msgctxt "field:webdav.share,rec_name:"
 msgid "Name"
-msgstr "Ime"
+msgstr "Nombre"
 
 msgctxt "field:webdav.share,url:"
 msgid "URL"
@@ -120,39 +120,39 @@ msgstr "URL"
 
 msgctxt "field:webdav.share,user:"
 msgid "User"
-msgstr "Uporabnik"
+msgstr "Usuario"
 
 msgctxt "field:webdav.share,write_date:"
 msgid "Write Date"
-msgstr "Zapisano"
+msgstr "Fecha de Modificación"
 
 msgctxt "field:webdav.share,write_uid:"
 msgid "Write User"
-msgstr "Zapisal"
+msgstr "Modificado por Usuario"
 
 msgctxt "model:ir.action,name:act_collection_list"
 msgid "Collections"
-msgstr "Zbirke"
+msgstr "Colecciones"
 
 msgctxt "model:ir.action,name:act_collection_tree"
 msgid "Collections"
-msgstr "Zbirke"
+msgstr "Colecciones"
 
 msgctxt "model:ir.action,name:act_share_list"
 msgid "Shares"
-msgstr "Skupne rabe"
+msgstr "Recursos Compartidos"
 
 msgctxt "model:ir.ui.menu,name:menu_collection_list"
 msgid "Collections"
-msgstr "Zbirke"
+msgstr "Colecciones"
 
 msgctxt "model:ir.ui.menu,name:menu_collection_tree"
 msgid "Collections"
-msgstr "Zbirke"
+msgstr "Colecciones"
 
 msgctxt "model:ir.ui.menu,name:menu_share_list"
 msgid "Shares"
-msgstr "Skupne rabe"
+msgstr "Recursos Compartidos"
 
 msgctxt "model:ir.ui.menu,name:menu_webdav"
 msgid "WebDAV"
@@ -160,11 +160,11 @@ msgstr "WebDAV"
 
 msgctxt "model:webdav.collection,name:"
 msgid "Collection"
-msgstr "Zbirka"
+msgstr "Colección"
 
 msgctxt "model:webdav.share,name:"
 msgid "Share"
-msgstr "Skupna raba"
+msgstr "Recurso Compartido"
 
 msgctxt "view:ir.attachment:"
 msgid "WebDAV"
@@ -172,16 +172,16 @@ msgstr "WebDAV"
 
 msgctxt "view:webdav.collection:"
 msgid "Collection"
-msgstr "Zbirka"
+msgstr "Colección"
 
 msgctxt "view:webdav.collection:"
 msgid "Collections"
-msgstr "Zbirke"
+msgstr "Colecciones"
 
 msgctxt "view:webdav.share:"
 msgid "Share"
-msgstr "Skupna raba"
+msgstr "Recurso Compartido"
 
 msgctxt "view:webdav.share:"
 msgid "Shares"
-msgstr "Skupne rabe"
+msgstr "Recursos Compartidos"
diff --git a/trytond/webdav/locale/fr_FR.po b/trytond/webdav/locale/fr_FR.po
index c94374f..1d0062d 100644
--- a/trytond/webdav/locale/fr_FR.po
+++ b/trytond/webdav/locale/fr_FR.po
@@ -7,20 +7,20 @@ msgid ""
 "You can not create an attachment named \"%(attachment)s in collection "
 "\"%(collection)s\" because there is already a collection with that name."
 msgstr ""
-"Vous ne pouvez créer une pièce jointe nommée \"%(attachment)s dans la "
-"collection \"%(collection)s\" car il y a déjà une collection avec ce nom."
+"Vous ne pouvez créer une pièce jointe nommée « %(attachment)s » dans la "
+"collection « %(collection)s » car il y a déjà une collection avec ce nom."
 
 msgctxt "error:webdav.collection:"
 msgid "The collection name must be unique inside a collection!"
-msgstr "Le nom de collection doit être unique au sein d'une collection !"
+msgstr "Le nom de collection doit être unique au sein d'une collection !"
 
 msgctxt "error:webdav.collection:"
 msgid ""
 "You can not create a collection named \"%(parent)s\" in collection "
 "\"%(child)s\" because there is already a file with that name."
 msgstr ""
-"Vous ne pouvez pas créer une collection nommée \"%(parent)s\" dans la "
-"collection \"%(child)s\" car il y a déjà un fichier avec ce nom."
+"Vous ne pouvez pas créer une collection nommée « %(parent)s » dans la "
+"collection « %(child)s » car il y a déjà un fichier avec ce nom."
 
 msgctxt "field:ir.attachment,path:"
 msgid "Path"
diff --git a/trytond/webdav/locale/sl_SI.po b/trytond/webdav/locale/sl_SI.po
index c9550c2..b1f2bb1 100644
--- a/trytond/webdav/locale/sl_SI.po
+++ b/trytond/webdav/locale/sl_SI.po
@@ -8,7 +8,7 @@ msgid ""
 "\"%(collection)s\" because there is already a collection with that name."
 msgstr ""
 "Priloge z imenom \"%(attachment)s\" v zbirki \"%(collection)s\" ni možno "
-"ustvariti, ker že obstaja zbirka z istim imenom."
+"izdelati, ker že obstaja zbirka z istim imenom."
 
 msgctxt "error:webdav.collection:"
 msgid "The collection name must be unique inside a collection!"
@@ -19,8 +19,8 @@ msgid ""
 "You can not create a collection named \"%(parent)s\" in collection "
 "\"%(child)s\" because there is already a file with that name."
 msgstr ""
-"Zbirke z imenom \"%(parent)s\" v zbirki \"%(child)s\" ni možno ustvariti, "
-"ker že obstaja datoteka z istim imenom."
+"Zbirke z imenom \"%(parent)s\" v zbirki \"%(child)s\" ni možno izdelati, ker"
+" že obstaja datoteka z istim imenom."
 
 msgctxt "field:ir.attachment,path:"
 msgid "Path"
@@ -44,11 +44,11 @@ msgstr "Polno ime"
 
 msgctxt "field:webdav.collection,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:webdav.collection,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:webdav.collection,domain:"
 msgid "Domain"
@@ -84,11 +84,11 @@ msgstr "Zapisal"
 
 msgctxt "field:webdav.share,create_date:"
 msgid "Create Date"
-msgstr "Ustvarjeno"
+msgstr "Izdelano"
 
 msgctxt "field:webdav.share,create_uid:"
 msgid "Create User"
-msgstr "Ustvaril"
+msgstr "Izdelal"
 
 msgctxt "field:webdav.share,expiration_date:"
 msgid "Expiration Date"
diff --git a/trytond/webdav/webdav.py b/trytond/webdav/webdav.py
index ef55270..9817f49 100644
--- a/trytond/webdav/webdav.py
+++ b/trytond/webdav/webdav.py
@@ -18,8 +18,9 @@ from trytond.model import ModelView, ModelSQL, fields
 from trytond.tools import reduce_ids
 from trytond.transaction import Transaction
 from trytond.pool import Pool
-from trytond.config import CONFIG
+from trytond.config import config
 from trytond.pyson import Eval
+from trytond.tools import grouped_slice
 
 __all__ = [
     'Collection', 'Share', 'Attachment',
@@ -27,11 +28,11 @@ __all__ = [
 
 
 def get_webdav_url():
-    if CONFIG['ssl_webdav']:
+    if config.get('ssl', 'privatekey'):
         protocol = 'https'
     else:
         protocol = 'http'
-    hostname = (CONFIG['hostname_webdav']
+    hostname = (config.get('webdav', 'hostname')
         or unicode(socket.getfqdn(), 'utf8'))
     hostname = '.'.join(encodings.idna.ToASCII(part) for part in
         hostname.split('.'))
@@ -434,8 +435,7 @@ class Collection(ModelSQL, ModelView):
                 res = None
                 cursor = Transaction().cursor
                 table = Model.__table__()
-                for i in range(0, len(ids), cursor.IN_MAX):
-                    sub_ids = ids[i:i + cursor.IN_MAX]
+                for sub_ids in grouped_slice(ids):
                     red_sql = reduce_ids(table.id, sub_ids)
                     cursor.execute(*table.select(table.id,
                             Extract('EPOCH', table.create_date),
@@ -471,8 +471,7 @@ class Collection(ModelSQL, ModelView):
                 res = None
                 cursor = Transaction().cursor
                 table = Model.__table__()
-                for i in range(0, len(ids), cursor.IN_MAX):
-                    sub_ids = ids[i:i + cursor.IN_MAX]
+                for sub_ids in grouped_slice(ids):
                     red_sql = reduce_ids(table.id, sub_ids)
                     cursor.execute(*table.select(table.id,
                             Extract('EPOCH',
diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py
index c3c725e..e92c35c 100644
--- a/trytond/wizard/wizard.py
+++ b/trytond/wizard/wizard.py
@@ -13,10 +13,11 @@ from trytond.pool import Pool, PoolBase
 from trytond.transaction import Transaction
 from trytond.error import WarningErrorMixin
 from trytond.url import URLMixin
-from trytond.protocols.jsonrpc import object_hook, JSONEncoder
+from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder
 from trytond.model.fields import states_validate
 from trytond.pyson import PYSONEncoder
 from trytond.rpc import RPC
+from trytond.exceptions import UserError
 
 
 class Button(object):
@@ -152,7 +153,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase):
         cls.__rpc__ = {
             'create': RPC(readonly=False),
             'delete': RPC(readonly=False),
-            'execute': RPC(readonly=False),
+            'execute': RPC(readonly=False, check_access=False),
             }
         cls._error_messages = {}
 
@@ -181,9 +182,31 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase):
         Translation.register_error_messages(cls, module_name)
 
     @classmethod
+    def check_access(cls):
+        pool = Pool()
+        ModelAccess = pool.get('ir.model.access')
+        ActionWizard = pool.get('ir.action.wizard')
+        User = pool.get('res.user')
+        context = Transaction().context
+
+        if Transaction().user == 0:
+            return
+
+        model = context.get('active_model')
+        if model:
+            ModelAccess.check(model, 'read')
+            ModelAccess.check(model, 'write')
+        groups = set(User.get_groups())
+        wizard_groups = ActionWizard.get_groups(cls.__name__,
+            action_id=context.get('action_id'))
+        if wizard_groups and not groups & wizard_groups:
+            raise UserError('Calling wizard %s is not allowed!' % cls.__name__)
+
+    @classmethod
     def create(cls):
         "Create a session"
         Session = Pool().get('ir.session.wizard')
+        cls.check_access()
         return (Session.create([{}])[0].id, cls.start_state, cls.end_state)
 
     @classmethod
@@ -215,6 +238,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase):
                 - ``defaults``: a dictionary with default values
                 - ``buttons``: a list of buttons
         '''
+        cls.check_access()
         wizard = cls(session_id)
         for key, values in data.iteritems():
             record = getattr(wizard, key)
@@ -265,7 +289,7 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase):
         self._session_id = session_id
         session = Session(session_id)
         data = json.loads(session.data.encode('utf-8'),
-            object_hook=object_hook)
+            object_hook=JSONDecoder())
         for state_name, state in self.states.iteritems():
             if isinstance(state, StateView):
                 Target = pool.get(state.model_name)
-- 
tryton-server



More information about the tryton-debian-vcs mailing list