[tryton-debian-vcs] tryton-server branch debian updated. debian/3.0.4-1-2-g566c9f8

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Tue Apr 22 13:12:24 UTC 2014


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

commit 566c9f8ecc23c196feeb1c04280a21287fcc8f59
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Apr 22 15:00:35 2014 +0200

    Bumping minimal required Python version to 2.7.

diff --git a/debian/control b/debian/control
index ece3cad..3606d6d 100644
--- a/debian/control
+++ b/debian/control
@@ -10,7 +10,7 @@ Standards-Version: 3.9.5
 Homepage: http://www.tryton.org/
 Vcs-Browser: http://anonscm.debian.org/gitweb/?p=tryton/tryton-server.git
 Vcs-Git: git://anonscm.debian.org/tryton/tryton-server.git
-X-Python-Version: >= 2.6
+X-Python-Version: >= 2.7
 
 Package: tryton-server
 Architecture: all
commit 599c8a28723b34a50706c079b5bec4f788bfc84e
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Apr 22 14:24:43 2014 +0200

    Merging upstream version 3.2.0.

diff --git a/CHANGELOG b/CHANGELOG
index 186ace0..cf11373 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,14 +1,37 @@
-Version 3.0.4 - 2014-04-07
-* Bug fixes (see mercurial logs for details)
-
-Version 3.0.3 - 2014-03-22
-* Bug fixes (see mercurial logs for details)
-
-Version 3.0.2 - 2014-01-18
-* Bug fixes (see mercurial logs for details)
-
-Version 3.0.1 - 2013-12-04
+Version 3.2.0 - 2014-04-21
 * Bug fixes (see mercurial logs for details)
+* Add restore_history to ModelSQL
+* Add history revisions
+* Add the multi selection widget
+* Add index to one2many's on_change
+* Remove auto-refresh on Action Window
+* Add support of domain for non-relation field
+* Manage microseconds in JSON-RPC and XML-RPC
+* Remove Sha field
+* Add password widget
+* Add Len to PYSON
+* Use bcrypt to hash password if possible
+* Use a sequence of ids, values to set fields
+* Client side actions on button and wizard
+* Add depends attribute to data tag
+* Add tree_invisible attribute to button in tree view
+* Drop support of Python 2.6
+* Deprecate on_change, on_change_with, selection_change_with and autocomplete
+  field arguments
+* Add fields.depends decorator
+* Add run-tests
+* Validate only modified and dependant fields on model write
+* Improve error messages by showing the failing value
+* Remove relation field actions:
+    - delete_all
+    - unlink_all
+    - set
+* Rename relation field action unlink into remove
+* Use a sequence of records, values in write
+* set_context of Transaction.set_user is restricted to root
+* Add a "copy" action to One2Many and Many2Many's set method
+* Force UTC as timezone (migration script available on tryton-tools)
+* Add relation_field for many2one
 
 Version 3.0.0 - 2013-10-21
 * Bug fixes (see mercurial logs for details)
diff --git a/PKG-INFO b/PKG-INFO
index 52c4611..e0f280b 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: trytond
-Version: 3.0.4
+Version: 3.2.0
 Summary: Tryton server
 Home-page: http://www.tryton.org/
 Author: Tryton
-Author-email: UNKNOWN
+Author-email: issue_tracker at tryton.org
 License: GPL-3
-Download-URL: http://downloads.tryton.org/3.0/
+Download-URL: http://downloads.tryton.org/3.2/
 Description: trytond
         =======
         
@@ -56,6 +56,8 @@ Description: trytond
         
             * MySQL can not create indexes containing text or blob fields.
         
+            * Timestamp has a precision of second which is used for optimistic lock.
+        
             * Tryton uses a DECIMAL(65, 30) for Decimal fields and DOUBLE(255, 30) for
               Float fields.
         
@@ -67,6 +69,8 @@ Description: trytond
         
             * SQL constraints are validated by Tryton instead of database.
         
+            * Timestamp has a precision of second which is used for optimistic lock.
+        
         Support
         -------
         
@@ -93,7 +97,8 @@ Description: trytond
         
           http://www.tryton.org/
         
-Platform: UNKNOWN
+Keywords: business application platform ERP
+Platform: any
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: No Input/Output (Daemon)
 Classifier: Framework :: Tryton
@@ -110,6 +115,5 @@ Classifier: Natural Language :: Russian
 Classifier: Natural Language :: Slovenian
 Classifier: Natural Language :: Spanish
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
diff --git a/README b/README
index b79cb86..91ae41b 100644
--- a/README
+++ b/README
@@ -47,6 +47,8 @@ backends available. Here are some warnings about using other backends:
 
     * MySQL can not create indexes containing text or blob fields.
 
+    * Timestamp has a precision of second which is used for optimistic lock.
+
     * Tryton uses a DECIMAL(65, 30) for Decimal fields and DOUBLE(255, 30) for
       Float fields.
 
@@ -58,6 +60,8 @@ backends available. Here are some warnings about using other backends:
 
     * SQL constraints are validated by Tryton instead of database.
 
+    * Timestamp has a precision of second which is used for optimistic lock.
+
 Support
 -------
 
diff --git a/bin/trytond b/bin/trytond
index a55d02c..d8b84ef 100755
--- a/bin/trytond
+++ b/bin/trytond
@@ -3,7 +3,7 @@
 #this repository contains the full copyright notices and license terms.
 import sys
 import os
-import optparse
+import argparse
 
 DIR = os.path.abspath(os.path.normpath(os.path.join(__file__,
     '..', '..', 'trytond')))
@@ -17,37 +17,39 @@ from trytond.version import VERSION
 def parse_commandline():
     options = {}
 
-    parser = optparse.OptionParser(version=VERSION)
+    parser = argparse.ArgumentParser(prog='trytond')
 
-    parser.add_option("-c", "--config", dest="config",
+    parser.add_argument('--version', action='version',
+        version='%(prog)s ' + VERSION)
+    parser.add_argument("-c", "--config", dest="config",
         help="specify config file")
-    parser.add_option('--debug', dest='debug_mode', action='store_true',
+    parser.add_argument('--debug', dest='debug_mode', action='store_true',
         help='enable debug mode (start post-mortem debugger if exceptions'
         ' occur)')
-    parser.add_option("-v", "--verbose", action="store_true",
+    parser.add_argument("-v", "--verbose", action="store_true",
         dest="verbose", help="enable verbose mode")
 
-    parser.add_option("-d", "--database", dest="db_name",
+    parser.add_argument("-d", "--database", dest="db_name",
         help="specify the database name")
-    parser.add_option("-i", "--init", dest="init",
+    parser.add_argument("-i", "--init", dest="init",
         help="init a module (use \"all\" for all modules)")
-    parser.add_option("-u", "--update", dest="update",
+    parser.add_argument("-u", "--update", dest="update",
         help="update a module (use \"all\" for all modules)")
 
-    parser.add_option("--pidfile", dest="pidfile",
+    parser.add_argument("--pidfile", dest="pidfile",
         help="file where the server pid will be stored")
-    parser.add_option("--logfile", dest="logfile",
+    parser.add_argument("--logfile", dest="logfile",
         help="file where the server log will be stored")
-    parser.add_option("--disable-cron", dest="cron",
+    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. '\
-            'The config file can be specified in the TRYTOND_CONFIG '\
-            'environment variable.'
+    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. '
+        'The config file can be specified in the TRYTOND_CONFIG '
+        'environment variable.')
 
-    (opt, _) = parser.parse_args()
+    opt = parser.parse_args()
 
     if opt.config:
         options['configfile'] = opt.config
diff --git a/doc/conf.py b/doc/conf.py
index a5b9b8d..71c121e 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.0'
+version = '3.2'
 # The full version, including alpha/beta/rc tags.
-release = '3.0'
+release = '3.2'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst
index 451392f..85fd0ca 100644
--- a/doc/ref/models/fields.rst
+++ b/doc/ref/models/fields.rst
@@ -49,12 +49,6 @@ If ``True``, the field is not editable in the client. Default is ``False``.
 A :ref:`domain <topics-domain>` constraint that will be applied on the field
 value.
 
-.. warning::
-
-    For now it only works on relational fields like :class:`Many2One`,
-    :class:`One2Many` and :class:`Many2Many`.
-..
-
 ``states``
 ----------
 
@@ -72,12 +66,14 @@ with the values of the record.
 
 If true, the content of the field will be indexed.
 
+.. _ref-models-fields-on_change:
+
 ``on_change``
 -------------
 
 .. attribute:: Field.on_change
 
-A list of field names. If this attribute is set, the client will call the
+A set of field names. If this attribute is set, the client will call the
 method ``on_change_<field name>`` of the model when the user changes the
 current field value and will give the values of each fields in this list. The
 method signature is::
@@ -88,15 +84,19 @@ This method must return a dictionary with the values of fields to be updated.
 
 .. note::
 
-    The on_change_<field name> methods are runnin in a rollbacked transaction.
+    The on_change_<field name> methods are running in a rollbacked transaction.
 ..
 
+The set of field names could be filled by using the decorator :meth:`depends`.
+
+.. _ref-models-fields-on_change_with:
+
 ``on_change_with``
 ------------------
 
 .. attribute:: Field.on_change_with
 
-A list of field names. Same like :attr:`on_change`, but defined the other way
+A set of field names. Same like :attr:`on_change`, but defined the other way
 around. If this attribute is set, the client will call the method
 ``on_change_with_<field name>`` of the model when the user changes one of the
 fields defined in the list and will give the values of each fields in this
@@ -112,6 +112,8 @@ This method must return the new value of the field.
 
 ..
 
+The set of field names could be filled by using the decorator :meth:`depends`.
+
 ``depends``
 -----------
 
@@ -173,6 +175,10 @@ could be updated to add new joins)::
     Return the namedtuple('SQLType', 'base type') which defines the SQL type to
     use for creation and casting.
 
+.. method:: Field.sql_column(table)
+
+    Return the Column instance based on table.
+
 Default value
 =============
 
@@ -189,6 +195,15 @@ The method signature is::
 
 Where ``tables`` is a nested dictionary, see :meth:`~Field.convert_domain`.
 
+Depends
+=======
+
+.. method:: depends([\*fields[, methods]])
+
+A decorator to define the field names on which the decorated method depends.
+The `methods` argument can be used to duplicate the field names from other
+fields. This is usefull if the decorated method calls another method.
+
 Field types
 ===========
 
@@ -234,7 +249,7 @@ A single line string field.
 
 .. attribute:: Char.autocomplete
 
-    A list of field names. If this attribute is set, the client will call the
+    A set of field names. If this attribute is set, the client will call the
     method ``autocomplete_<field name>`` of the model when the user changes one
     of those field value. The method signature is::
 
@@ -242,15 +257,7 @@ A single line string field.
 
     This method must return a list of string that will populate the
     ComboboxEntry in the client.
-
-Sha
----
-
-.. class:: Sha(string[, \**options])
-
-A string field which value will be stored with a `secure hash algorithm`_.
-
-.. _`secure hash algorithm`: http://en.wikipedia.org/wiki/Secure_Hash_Algorithm
+    The set of field names could be filled by using the decorator :meth:`depends`.
 
 Text
 ----
@@ -395,10 +402,11 @@ A string field with limited values to choice.
 
 .. attribute:: Selection.selection_change_with
 
-    A list of field names. If this attribute is set, the client will call the
+    A set of field names. If this attribute is set, the client will call the
     ``selection`` method of the model when the user changes on of the fields
     defined in the list and will give the values of each fields in the list.
     The ``selection`` method should be an instance method.
+    The set of field names could be filled by using the decorator :meth:`depends`.
 
 .. attribute:: Selection.translate_selection
 
@@ -495,21 +503,19 @@ This field accepts as written value a list of tuples like this:
     - ``('create', [{<field name>: value, ...}, ...])``: it will create new
       target records and link them to this one.
 
-    - ``('write'[, ids, ...], {<field name>: value, ...})``: it will write
-      values to target ids.
+    - ``('write'[[, ids, ...], {<field name>: value, ...}, ...])``: it will
+      write values to target ids.
 
     - ``('delete'[, ids, ...])``: it will delete the target ids.
 
-    - ``('delete_all')``: it will delete all the target records.
-
     - ``('add'[, ids, ...])``: it will link the target ids to this record.
 
-    - ``('unlink'[, ids, ...])``: it will unlink the target ids from this
+    - ``('remove'[, ids, ...])``: it will unlink the target ids from this
       record.
 
-    - ``('unlink_all')``: it will unlink all the target records.
-
-    - ``('set'[, ids, ...])``: it will link only the target ids to this record.
+    - ``('copy', ids[, {<field name>: value, ...}])``: it will copy the target
+      ids to this record. Optional field names and values may be added to
+      override some of the fields of the copied records.
 
 :class:`One2Many` has some extra required arguments:
 
@@ -549,7 +555,11 @@ Many2Many
 
 .. class:: Many2Many(relation_name, origin, target, string[, order[, datetime_field[, size[, \**options]]]])
 
-A many-to-many relation field.
+A many-to-many relation field. It requires to have the opposite origin
+:class:`Many2One` field or a:class:`Reference` field defined on the relation
+model and a :class:`Many2One` field pointing to the target.
+
+This field accepts as written value a list of tuples like the :class:`One2Many`.
 
 :class:`Many2Many` has some extra required arguments:
 
diff --git a/doc/ref/models/models.rst b/doc/ref/models/models.rst
index 54b092c..5ac7918 100644
--- a/doc/ref/models/models.rst
+++ b/doc/ref/models/models.rst
@@ -76,12 +76,13 @@ Class methods:
         warning states by users.
     ..
 
-.. classmethod:: Model.default_get(fields_names[, with_rec_name])
+.. classmethod:: Model.default_get(fields_names[, with_rec_name[, with_on_change]])
 
     Return 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])
 
@@ -238,7 +239,7 @@ CLass methods:
     Return a list of values for the ids. If ``fields_names`` is set, there will
     be only values for these fields otherwise it will be for all fields.
 
-.. classmethod:: ModelStorage.write(records, values)
+.. classmethod:: ModelStorage.write(records, values, [[records, values], ...])
 
     Write ``values`` on the list of records.  ``values`` is a dictionary with
     fields names as key and writen values as value.
@@ -400,6 +401,20 @@ Class methods:
     Could be overrided to use a custom SQL query instead of a table of the
     database. It should return a SQL FromItem.
 
+.. classmethod:: ModelSQL.history_revisions(ids)
+
+    Return a sorted list of all revisions for ids. The list is composed of
+    the date, id and username of the revision.
+
+.. classmethod:: ModelSQL.restore_history(ids, datetime)
+
+    Restore the record ids from history at the specified date time.
+    Restoring a record will still generate an entry in the history table.
+
+    .. warning::
+        No access rights are verified and the records are not validated.
+    ..
+
 .. classmethod:: ModelStorage.search(domain[, offset[, limit[, order[, count[, query]]]]])
 
     Return a list of records that match the :ref:`domain <topics-domain>` or
diff --git a/doc/ref/pyson.rst b/doc/ref/pyson.rst
index 5f012b9..fd1640b 100644
--- a/doc/ref/pyson.rst
+++ b/doc/ref/pyson.rst
@@ -250,6 +250,11 @@ Arguments:
 ``delta_microseconds``
     Contains a PYSON statement of type int or long.
 
+.. class:: Len(value)
+
+A :class:`Len` object represents the PYSON ``Len()`` statement for length of a
+dictionary, list or string. Returns the number of items in ``value``.
+
 .. class:: Id(module, fs_id)
 
 An :class:`Id` object represents the PYSON ``Id()`` statement for filesystem id
diff --git a/doc/ref/rpc.rst b/doc/ref/rpc.rst
index cb7f2e3..ba0e6f9 100644
--- a/doc/ref/rpc.rst
+++ b/doc/ref/rpc.rst
@@ -17,7 +17,7 @@ Instance attributes are:
 
 .. attribute:: RPC.instantiate
 
-    The position of the argument to be instanciated
+    The position or the slice of the argument to be instanciated
 
 .. attribute:: RPC.result
 
diff --git a/doc/ref/transaction.rst b/doc/ref/transaction.rst
index 6c06292..2ddc292 100644
--- a/doc/ref/transaction.rst
+++ b/doc/ref/transaction.rst
@@ -17,6 +17,10 @@ database transaction.
 
     The database cursor.
 
+.. attribute:: Transaction.database
+
+    The database.
+
 .. attribute:: Transaction.user
 
     The id of the user.
@@ -39,7 +43,7 @@ database transaction.
 
     Count the number of modification made in this transaction.
 
-.. method:: Transaction.start(database_name, user[, context])
+.. method:: Transaction.start(database_name, user[, readonly[, context[, close[, autocommit]]]])
 
     Start a new transaction and return a `context manager`_.
 
@@ -64,7 +68,7 @@ database transaction.
     Modify the cursor of the transaction and return a `context manager`_. The
     previous cursor will be restored when exiting the `with` statement.
 
-.. method:: Transaction.new_cursor()
+.. method:: Transaction.new_cursor([autocommit[, readonly]])
 
     Change the cursor of the transaction with a new one on the same database
     and return a `context manager`_. The previous cursor will be restored when
diff --git a/doc/ref/wizard.rst b/doc/ref/wizard.rst
index 6cc0674..62267e4 100644
--- a/doc/ref/wizard.rst
+++ b/doc/ref/wizard.rst
@@ -32,6 +32,9 @@ Class attributes are:
 .. attribute:: Wizard.end_state
 
     It contains the name of the ending state.
+    If an instance method with this name exists on the wizard, it will be
+    called on deletion of the wizard and it may return one of the :ref:`client
+    side action keywords <topics-views-client-actions>`.
 
 .. attribute:: Wizard.__rpc__
 
@@ -70,8 +73,8 @@ Class methods are:
     Execute the wizard for the state.
     `session_id` is a session id.
     `data` is a dictionary with the session data to update.
-    `active_id`, `active_ids` and `active_model` must be set in the context
-    according to the records on which the wizard is run.
+    `active_id`, `active_ids`, `active_model` and `action_id` must be set in
+    the context according to the records on which the wizard is run.
 
 =====
 State
diff --git a/doc/topics/index.rst b/doc/topics/index.rst
index ad1fcae..bae4107 100644
--- a/doc/topics/index.rst
+++ b/doc/topics/index.rst
@@ -12,6 +12,7 @@ Introduction to all the key parts of trytond:
     install
     models/index
     models/fields_default_value
+    models/fields_on_change
     domain
     pyson
     actions
diff --git a/doc/topics/install.rst b/doc/topics/install.rst
index 00d91ce..a5eb993 100644
--- a/doc/topics/install.rst
+++ b/doc/topics/install.rst
@@ -7,9 +7,9 @@ How to install Tryton
 Prerequisites
 =============
 
-    * Python 2.6 or later (http://www.python.org/)
+    * Python 2.7 or later (http://www.python.org/)
     * lxml 2.0 or later (http://codespeak.net/lxml/)
-    * relatorio 0.2.0 or later (http://relatorio.openhex.org/)
+    * 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)
     * python-sql 0.2 or later (http://code.google.com/p/python-sql/)
@@ -17,7 +17,6 @@ Prerequisites
     * Optional: MySQL-python (http://sourceforge.net/projects/mysql-python/)
     * Optional: pywebdav 0.9.8 or later (http://code.google.com/p/pywebdav/)
     * Optional: pydot (http://code.google.com/p/pydot/)
-    * Optional: pytz (http://pytz.sourceforge.net/)
     * Optional: unoconv http://dag.wieers.com/home-made/unoconv/)
     * Optional: sphinx (http://sphinx.pocoo.org/)
     * Optional: simplejson (http://undefined.org/python/#simplejson)
diff --git a/doc/topics/models/fields_on_change.rst b/doc/topics/models/fields_on_change.rst
new file mode 100644
index 0000000..e47c787
--- /dev/null
+++ b/doc/topics/models/fields_on_change.rst
@@ -0,0 +1,65 @@
+.. _topcis-fields_on_change:
+
+===================
+on_change of fields
+===================
+
+Tryton allows developers to define methods that can be called once a field's
+value has changed by the user this is the :ref:`ref-models-fields-on_change`
+method.  The method has the following name::
+
+    Model.on_change_<field name>
+
+This is an instance method, an instance of ``Model`` will be created by
+using the values from the form's fields specified by the ``on_change`` list
+defined on the field.
+
+There is also a way to define a method that must update a field whenever any
+field from a predefined list is modified. This list is defined by the
+:ref:`ref-models-fields-on_change_with` attribute of the field. The method
+that will be called has the following name::
+
+    Model.on_change_with_<field_name>
+
+Just like for the classic ``on_change``, an instance of ``Model`` will be
+created by using the values entered in the form's fields specified by the
+``on_change_with`` attribute.
+
+on_change & on_change_with return values
+----------------------------------------
+
+The return value of the method called will depend of the type of the call that
+occured. In case of an ``on_change`` the returned value will be a dictionary
+whose keys are field names to be modified and whose values will be the
+corresponding new value. In case of an ``on_change_with`` the returned value
+will be the new value of the field.
+
+Pay attention that the new value of a field differs according to its type.
+Simple fields require the value to be of the same type as the field.
+
+Relation fields require some more explanations:
+
+    - a ``field.One2Many`` or a ``field.Many2Many`` will accept either:
+
+        - a list of ``id`` denoting the new value that will replace all
+          previously ids.
+
+        - a dictionary composed of three keys: ``update``, ``add`` and
+          ``remove``.
+
+          The ``update`` key has as value a list of dictionary that denotes the
+          new value of the target's fields. The lines affected by the change
+          are found using the ``id`` key of the dictionary.
+
+          The ``add`` key have as value a list of tuple, the first element is
+          the index where a new line should be added, the second element is a
+          dictionary using the same convention as the dictionaries for the
+          ``update`` key.
+
+          The ``remove`` key will have as value a list of ids that will be
+          remove from the field.
+
+          Records that are not yet saved receive temporary ids (negative
+          integers) that can be used in with ``update`` or ``remove`` to
+          interact with them.
+
diff --git a/doc/topics/modules/index.rst b/doc/topics/modules/index.rst
index fe1a7e4..fa79017 100644
--- a/doc/topics/modules/index.rst
+++ b/doc/topics/modules/index.rst
@@ -110,9 +110,13 @@ Here is the list of the tags:
     * ``tryton``: The main tag of the xml
 
     * ``data``: Define a set of data inside the file. It can have the
-      attributes ``noupdate`` to prevent the framework to update the records,
-      ``skiptest`` to prevent import of data when running tests and ``grouped``
-      to create records at the end with a grouped call.
+      attributes:
+
+        * ``noupdate`` to prevent the framework to update the records,
+        * ``skiptest`` to prevent import of data when running tests,
+        * ``depends`` to import data only if all modules in the comma separated
+          module list value are installed,
+        * ``grouped`` to create records at the end with a grouped call.
 
     * ``record``: Create a record of the model defined by the attribute
       ``model`` in the database. The ``id`` attribute can be used to refer to
diff --git a/doc/topics/reports/index.rst b/doc/topics/reports/index.rst
index f732835..633820e 100644
--- a/doc/topics/reports/index.rst
+++ b/doc/topics/reports/index.rst
@@ -254,8 +254,8 @@ the default style.
 
 .. _Genshi XML Templates: http://genshi.edgewall.org/wiki/Documentation/0.5.x/xml-templates.html
 
-.. _Quick Example: http://relatorio.openhex.org/wiki/QuickExample
+.. _Quick Example: http://code.google.com/p/python-relatorio/wiki/QuickExample
 
-.. _In Depth Introduction: http://relatorio.openhex.org/wiki/IndepthIntroduction
+.. _In Depth Introduction: http://code.google.com/p/python-relatorio/wiki/IndepthIntroduction
 
-.. _Example Documents: http://relatorio.openhex.org/browser/examples
+.. _Example Documents: http://code.google.com/p/python-relatorio/source/browse/#hg%2Fexamples
diff --git a/doc/topics/views/index.rst b/doc/topics/views/index.rst
index bf9da74..04203c5 100644
--- a/doc/topics/views/index.rst
+++ b/doc/topics/views/index.rst
@@ -296,6 +296,9 @@ newline
 
 Force to use a new row.
 
+
+.. _form-button:
+
 button
 ^^^^^^
 
@@ -308,6 +311,22 @@ Display a button.
 
         ``button(cls, records)``
 
+      The function may return an `ir.action` id or one of those client side
+      action keywords:
+
+.. _topics-views-client-actions:
+
+        * ``new``: to create a new record
+        * ``delete``: to delete the selected records
+        * ``remove``: to remove the record if it has a parent
+        * ``copy``: to copy the selected records
+        * ``next``: to go to the next record
+        * ``previous``: to go to the previous record
+        * ``close``: to close the current tab
+        * ``switch <view type>``: to switch the view to the defined type
+        * ``reload context``: to reload user context
+        * ``reload menu``: to reload menu
+
     * ``icon``
 
     * ``confirm``: A text that will be displayed in a confirmation popup when
@@ -517,6 +536,11 @@ diplayed in the same column.
     * ``icon``: The name of the field that contains the name of the icon to
       display or the name of the icon.
 
+button
+^^^^^^
+
+Same as in form-button_.
+
 Example
 -------
 
@@ -535,6 +559,22 @@ Example
       <field name="sequence" tree_invisible="1"/>
   </tree>
 
+button
+^^^^^^
+
+Display a button.
+
+    * ``string``: The string that will be displayed inside the button.
+
+    * ``name``: The name of the function that will be called. The function must
+      have this syntax:
+
+        ``button(cls, records)``
+
+    * ``confirm``: A text that will be displayed in a confirmation popup when
+      the button is clicked.
+
+    * ``help``: see in common-attributes-help_
 
 Graph view
 ==========
diff --git a/etc/trytond.conf b/etc/trytond.conf
index 6208dd5..f70a6d6 100644
--- a/etc/trytond.conf
+++ b/etc/trytond.conf
@@ -86,6 +86,3 @@ jsonrpc = localhost:8000
 
 # Default language code
 # language = en_US
-
-# Timezone of the server
-# timezone = False
diff --git a/setup.py b/setup.py
index d13df20..793dac8 100644
--- a/setup.py
+++ b/setup.py
@@ -5,20 +5,33 @@
 from setuptools import setup, find_packages
 import os
 
+PACKAGE, VERSION, LICENSE, WEBSITE = None, None, None, None
 execfile(os.path.join('trytond', 'version.py'))
 
 
 def read(fname):
     return open(os.path.join(os.path.dirname(__file__), fname)).read()
 
+major_version, minor_version, _ = VERSION.split('.', 2)
+major_version = int(major_version)
+minor_version = int(minor_version)
+
+download_url = 'http://downloads.tryton.org/%s.%s/' % (
+    major_version, minor_version)
+if minor_version % 2:
+    VERSION = '%s.%s.dev0' % (major_version, minor_version)
+    download_url = 'hg+http://hg.tryton.org/%s#egg=%s-%s' % (
+        PACKAGE, PACKAGE, VERSION)
+
 setup(name=PACKAGE,
     version=VERSION,
     description='Tryton server',
     long_description=read('README'),
     author='Tryton',
+    author_email='issue_tracker at tryton.org',
     url=WEBSITE,
-    download_url=("http://downloads.tryton.org/" +
-        VERSION.rsplit('.', 1)[0] + '/'),
+    download_url=download_url,
+    keywords='business application platform ERP',
     packages=find_packages(exclude=['*.modules.*', 'modules.*', 'modules',
             '*.proteus.*', 'proteus.*', 'proteus']),
     package_data={
@@ -51,10 +64,10 @@ setup(name=PACKAGE,
         'Natural Language :: Slovenian',
         'Natural Language :: Spanish',
         'Operating System :: OS Independent',
-        'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
         'Topic :: Software Development :: Libraries :: Application Frameworks',
         ],
+    platforms='any',
     license=LICENSE,
     install_requires=[
         'lxml >= 2.0',
@@ -70,12 +83,12 @@ setup(name=PACKAGE,
         'WebDAV': ['PyWebDAV >= 0.9.8'],
         'unoconv': ['unoconv'],
         'graphviz': ['pydot'],
-        'timezone': ['pytz'],
         'simplejson': ['simplejson'],
         'cdecimal': ['cdecimal'],
         'Levenshtein': ['python-Levenshtein'],
+        'BCrypt': ['bcrypt'],
         },
     zip_safe=False,
     test_suite='trytond.tests',
     test_loader='trytond.test_loader:Loader',
-)
+    )
diff --git a/trytond.egg-info/PKG-INFO b/trytond.egg-info/PKG-INFO
index 52c4611..e0f280b 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.0.4
+Version: 3.2.0
 Summary: Tryton server
 Home-page: http://www.tryton.org/
 Author: Tryton
-Author-email: UNKNOWN
+Author-email: issue_tracker at tryton.org
 License: GPL-3
-Download-URL: http://downloads.tryton.org/3.0/
+Download-URL: http://downloads.tryton.org/3.2/
 Description: trytond
         =======
         
@@ -56,6 +56,8 @@ Description: trytond
         
             * MySQL can not create indexes containing text or blob fields.
         
+            * Timestamp has a precision of second which is used for optimistic lock.
+        
             * Tryton uses a DECIMAL(65, 30) for Decimal fields and DOUBLE(255, 30) for
               Float fields.
         
@@ -67,6 +69,8 @@ Description: trytond
         
             * SQL constraints are validated by Tryton instead of database.
         
+            * Timestamp has a precision of second which is used for optimistic lock.
+        
         Support
         -------
         
@@ -93,7 +97,8 @@ Description: trytond
         
           http://www.tryton.org/
         
-Platform: UNKNOWN
+Keywords: business application platform ERP
+Platform: any
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: No Input/Output (Daemon)
 Classifier: Framework :: Tryton
@@ -110,6 +115,5 @@ Classifier: Natural Language :: Russian
 Classifier: Natural Language :: Slovenian
 Classifier: Natural Language :: Spanish
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
diff --git a/trytond.egg-info/SOURCES.txt b/trytond.egg-info/SOURCES.txt
index 6c96d96..d3ca803 100644
--- a/trytond.egg-info/SOURCES.txt
+++ b/trytond.egg-info/SOURCES.txt
@@ -28,6 +28,7 @@ doc/topics/install.rst
 doc/topics/pyson.rst
 doc/topics/wizard.rst
 doc/topics/models/fields_default_value.rst
+doc/topics/models/fields_on_change.rst
 doc/topics/models/index.rst
 doc/topics/modules/index.rst
 doc/topics/reports/index.rst
@@ -183,6 +184,7 @@ trytond/ir/view/model_field_list.xml
 trytond/ir/view/model_form.xml
 trytond/ir/view/model_list.xml
 trytond/ir/view/model_print_model_graph_start_form.xml
+trytond/ir/view/module_config_wizard_done_form.xml
 trytond/ir/view/module_config_wizard_first_form.xml
 trytond/ir/view/module_config_wizard_item_list.xml
 trytond/ir/view/module_config_wizard_other_form.xml
@@ -297,10 +299,12 @@ trytond/tests/__init__.py
 trytond/tests/access.py
 trytond/tests/copy_.py
 trytond/tests/export_data.py
+trytond/tests/history.py
 trytond/tests/import_data.py
 trytond/tests/import_data.xml
 trytond/tests/model.py
 trytond/tests/mptt.py
+trytond/tests/run-tests.py
 trytond/tests/sequence.xml
 trytond/tests/test.py
 trytond/tests/test_access.py
@@ -308,6 +312,7 @@ trytond/tests/test_cache.py
 trytond/tests/test_copy.py
 trytond/tests/test_exportdata.py
 trytond/tests/test_fields.py
+trytond/tests/test_history.py
 trytond/tests/test_importdata.py
 trytond/tests/test_mixins.py
 trytond/tests/test_modelsingleton.py
@@ -319,6 +324,7 @@ trytond/tests/test_tools.py
 trytond/tests/test_transaction.py
 trytond/tests/test_trigger.py
 trytond/tests/test_tryton.py
+trytond/tests/test_user.py
 trytond/tests/test_wizard.py
 trytond/tests/test_workflow.py
 trytond/tests/trigger.py
@@ -331,7 +337,6 @@ trytond/tools/StringMatcher.py
 trytond/tools/__init__.py
 trytond/tools/datetime_strftime.py
 trytond/tools/misc.py
-trytond/tools/ordereddict.py
 trytond/tools/singleton.py
 trytond/webdav/__init__.py
 trytond/webdav/tryton.cfg
diff --git a/trytond.egg-info/requires.txt b/trytond.egg-info/requires.txt
index efcab94..25bd7b5 100644
--- a/trytond.egg-info/requires.txt
+++ b/trytond.egg-info/requires.txt
@@ -23,8 +23,8 @@ simplejson
 [MySQL]
 MySQL-python
 
-[timezone]
-pytz
+[BCrypt]
+bcrypt
 
 [Levenshtein]
 python-Levenshtein
diff --git a/trytond/__init__.py b/trytond/__init__.py
index 6fb585c..80f6ae6 100644
--- a/trytond/__init__.py
+++ b/trytond/__init__.py
@@ -1,5 +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 os
+import time
+
 from . import server
 
 __all__ = ['server']
+
+os.environ['TZ'] = 'UTC'
+if hasattr(time, 'tzset'):
+    time.tzset()
diff --git a/trytond/backend/database.py b/trytond/backend/database.py
index c80e370..2979c66 100644
--- a/trytond/backend/database.py
+++ b/trytond/backend/database.py
@@ -41,7 +41,8 @@ class DatabaseInterface(object):
         '''
         raise NotImplementedError
 
-    def create(self, cursor, database_name):
+    @staticmethod
+    def create(cursor, database_name):
         '''
         Create a database
 
@@ -49,7 +50,8 @@ class DatabaseInterface(object):
         '''
         raise NotImplementedError
 
-    def drop(self, cursor, database_name):
+    @staticmethod
+    def drop(cursor, database_name):
         '''
         Drop a database
 
diff --git a/trytond/backend/mysql/database.py b/trytond/backend/mysql/database.py
index 1be7c0b..f61136f 100644
--- a/trytond/backend/mysql/database.py
+++ b/trytond/backend/mysql/database.py
@@ -100,19 +100,23 @@ class Database(DatabaseInterface):
         if CONFIG['db_password']:
             args['passwd'] = CONFIG['db_password']
         conn = MySQLdb.connect(**args)
-        return Cursor(conn, self.database_name)
+        cursor = Cursor(conn, self.database_name)
+        cursor.execute('SET time_zone = `UTC`')
+        return cursor
 
     def close(self):
         return
 
-    def create(self, cursor, database_name):
+    @classmethod
+    def create(cls, cursor, database_name):
         cursor.execute('CREATE DATABASE `' + database_name + '` '
             'DEFAULT CHARACTER SET = \'utf8\'')
-        Database._list_cache = None
+        cls._list_cache = None
 
-    def drop(self, cursor, database_name):
+    @classmethod
+    def drop(cls, cursor, database_name):
         cursor.execute('DROP DATABASE `' + database_name + '`')
-        Database._list_cache = None
+        cls._list_cache = None
 
     @staticmethod
     def dump(database_name):
diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py
index 761a055..ca444cd 100644
--- a/trytond/backend/postgresql/database.py
+++ b/trytond/backend/postgresql/database.py
@@ -26,8 +26,6 @@ from sql import Flavor
 __all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError',
     'Cursor']
 
-RE_FROM = re.compile('.* from "?([a-zA-Z_0-9]+)"?.*$')
-RE_INTO = re.compile('.* into "?([a-zA-Z_0-9]+)"?.*$')
 RE_VERSION = re.compile(r'\S+ (\d+)\.(\d+)')
 
 os.environ['PGTZ'] = os.environ.get('TZ', '')
@@ -87,14 +85,16 @@ class Database(DatabaseInterface):
         self._connpool.closeall()
         self._connpool = None
 
-    def create(self, cursor, database_name):
+    @classmethod
+    def create(cls, cursor, database_name):
         cursor.execute('CREATE DATABASE "' + database_name + '" '
             'TEMPLATE template0 ENCODING \'unicode\'')
-        Database._list_cache = None
+        cls._list_cache = None
 
-    def drop(self, cursor, database_name):
+    @classmethod
+    def drop(cls, cursor, database_name):
         cursor.execute('DROP DATABASE "' + database_name + '"')
-        Database._list_cache = None
+        cls._list_cache = None
 
     def get_version(self, cursor):
         if self.database_name not in self._version_cache:
@@ -353,7 +353,7 @@ class Cursor(CursorInterface):
         return self.cursor.fetchone()[0]
 
     def lock(self, table):
-        self.cursor.execute('LOCK "%s"' % table)
+        self.cursor.execute('LOCK "%s" IN EXCLUSIVE MODE' % table)
 
     def has_constraint(self):
         return True
diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py
index d0e4b52..9f132a0 100644
--- a/trytond/backend/sqlite/database.py
+++ b/trytond/backend/sqlite/database.py
@@ -8,6 +8,7 @@ import datetime
 import time
 import sys
 import threading
+import math
 
 _FIX_ROWCOUNT = False
 try:
@@ -132,6 +133,10 @@ class SQLiteOverlay(Function):
         return string[:from_ - 1] + placing_string + string[from_ - 1 + for_:]
 
 
+def sign(value):
+    return math.copysign(1, value)
+
+
 MAPPING = {
     Extract: SQLiteExtract,
     Position: SQLitePosition,
@@ -148,8 +153,7 @@ class Database(DatabaseInterface):
 
     def __new__(cls, database_name=':memory:'):
         if (database_name == ':memory:'
-                and hasattr(cls._local, 'memory_database')
-                and cls._local.memory_database):
+                and getattr(cls._local, 'memory_database', None)):
             return cls._local.memory_database
         return DatabaseInterface.__new__(cls, database_name=database_name)
 
@@ -178,6 +182,7 @@ class Database(DatabaseInterface):
         if sqlite.sqlite_version_info < (3, 3, 14):
             self._conn.create_function('replace', 3, replace)
         self._conn.create_function('now', 0, now)
+        self._conn.create_function('sign', 1, sign)
         self._conn.execute('PRAGMA foreign_keys = ON')
         return self
 
@@ -197,7 +202,8 @@ class Database(DatabaseInterface):
             return
         self._conn = None
 
-    def create(self, cursor, database_name):
+    @staticmethod
+    def create(cursor, database_name):
         if database_name == ':memory:':
             path = ':memory:'
         else:
@@ -209,9 +215,10 @@ class Database(DatabaseInterface):
             cursor = conn.cursor()
             cursor.close()
 
-    def drop(self, cursor, database_name):
+    @classmethod
+    def drop(cls, cursor, database_name):
         if database_name == ':memory:':
-            self._conn = None
+            cls._local.memory_database._conn = None
             return
         if os.sep in database_name:
             return
@@ -356,21 +363,23 @@ class Cursor(CursorInterface):
         self._conn.rollback()
 
     def test(self):
+        sqlite_master = Table('sqlite_master')
+        select = sqlite_master.select(sqlite_master.name)
+        select.where = sqlite_master.type == 'table'
+        select.where &= sqlite_master.name.in_([
+                'ir_model',
+                'ir_model_field',
+                'ir_ui_view',
+                'ir_ui_menu',
+                'res_user',
+                'res_group',
+                'ir_module_module',
+                'ir_module_module_dependency',
+                'ir_translation',
+                'ir_lang',
+                ])
         try:
-            self.cursor.execute("SELECT name "
-                "FROM sqlite_master "
-                "WHERE type = 'table' AND name in ("
-                    "'ir_model', "
-                    "'ir_model_field', "
-                    "'ir_ui_view', "
-                    "'ir_ui_menu', "
-                    "'res_user', "
-                    "'res_group', "
-                    "'ir_module_module', "
-                    "'ir_module_module_dependency', "
-                    "'ir_translation', "
-                    "'ir_lang'"
-                    ")")
+            self.cursor.execute(*select)
         except Exception:
             return False
         return len(self.cursor.fetchall()) != 0
diff --git a/trytond/cache.py b/trytond/cache.py
index e7ccbd2..c6d7adb 100644
--- a/trytond/cache.py
+++ b/trytond/cache.py
@@ -2,10 +2,11 @@
 #this repository contains the full copyright notices and license terms.
 import datetime
 from threading import Lock
+from collections import OrderedDict
+
 from trytond.transaction import Transaction
 from trytond.config import CONFIG
 from trytond import backend
-from trytond.tools import OrderedDict
 
 __all__ = ['Cache', 'LRUDict']
 
diff --git a/trytond/config.py b/trytond/config.py
index 417ea58..ba67332 100644
--- a/trytond/config.py
+++ b/trytond/config.py
@@ -11,7 +11,6 @@ except ImportError:
     sys.modules['cdecimal'] = decimal
 import os
 import ConfigParser
-import time
 import getpass
 import socket
 
@@ -84,15 +83,9 @@ class ConfigManager(object):
             'unoconv': 'pipe,name=trytond;urp;StarOffice.ComponentContext',
             'retry': 5,
             'language': 'en_US',
-            'timezone': time.tzname[0] or time.tzname[1],
         }
         self.configfile = None
 
-    def set_timezone(self):
-        os.environ['TZ'] = self.get('timezone')
-        if hasattr(time, 'tzset'):
-            time.tzset()
-
     def update_cmdline(self, cmdline_options):
         self.options.update(cmdline_options)
 
diff --git a/trytond/convert.py b/trytond/convert.py
index 259db7f..378da55 100644
--- a/trytond/convert.py
+++ b/trytond/convert.py
@@ -137,6 +137,9 @@ class MenuitemTagHandler:
             else:
                 values['name'] = action_name
 
+        if values.get('sequence'):
+            values['sequence'] = int(values['sequence'])
+
         self.values = values
 
     def characters(self, data):
@@ -402,7 +405,13 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
         self.module_state = module_state
         self.grouped = None
         self.grouped_creations = defaultdict(dict)
+        self.grouped_write = defaultdict(list)
+        self.grouped_model_data = []
         self.skip_data = False
+        Module = pool.get('ir.module.module')
+        self.installed_modules = [m.name for m in Module.search([
+                    ('state', 'in', ['installed', 'to upgrade']),
+                    ])]
 
         # Tag handlders are used to delegate the processing
         self.taghandlerlist = {
@@ -454,6 +463,11 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
                     self.skip_data = True
                 else:
                     self.skip_data = False
+                depends = attributes.get('depends', '').split(',')
+                depends = [m.strip() for m in depends if m]
+                if depends:
+                    if not all((m in self.installed_modules for m in depends)):
+                        self.skip_data = True
 
             elif name == "tryton":
                 pass
@@ -475,6 +489,13 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
             for model, values in self.grouped_creations.iteritems():
                 self.create_records(model, values.values(), values.keys())
             self.grouped_creations.clear()
+            for key, actions in self.grouped_write.iteritems():
+                module, model = key
+                self.write_records(module, model, *actions)
+            self.grouped_write.clear()
+        if self.grouped_model_data:
+            self.ModelData.write(*self.grouped_model_data)
+            del self.grouped_model_data[:]
 
         # Closing tag found, if we are in a delegation the handler
         # know what to do:
@@ -664,42 +685,12 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
                 # and no user changed the value in the db:
                 to_update[key] = values[key]
 
-            # if there is values to update:
-            if to_update:
-                # write the values in the db:
-                with Transaction().set_context(
-                        module=module, language='en_US'):
-                    Model.write([record], to_update)
-                self.fs2db.reset_browsercord(
-                    module, Model.__name__, [record.id])
-
-            if to_update:
-                # re-read it: this ensure that we store the real value
-                # in the model_data table:
-                record = self.fs2db.get_browserecord(
-                    module, Model.__name__, record.id)
-                if not record:
-                    record = Model(record.id)
-                for key in to_update:
-                    values[key] = self._clean_value(key, record)
-
-            if module != self.module:
-                temp_values = old_values.copy()
-                temp_values.update(values)
-                values = temp_values
-
-            if values != old_values:
-                self.ModelData.write([self.ModelData(mdata_id)], {
-                    'fs_id': fs_id,
-                    'model': model,
-                    'module': module,
-                    'db_id': record.id,
-                    'values': str(values),
-                    'date_update': datetime.datetime.now(),
-                    })
-            # reset_browsercord to keep cache memory low
-            self.fs2db.reset_browsercord(module, Model.__name__, [record.id])
-
+            if self.grouped:
+                self.grouped_write[(module, model)].extend(
+                    (record, to_update, old_values, fs_id, mdata_id))
+            else:
+                self.write_records(module, model, record, to_update,
+                    old_values, fs_id, mdata_id)
         else:
             if self.grouped:
                 self.grouped_creations[model][fs_id] = values
@@ -739,6 +730,57 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
         self.fs2db.reset_browsercord(self.module, model,
             [r.id for r in records])
 
+    def write_records(self, module, model,
+            record, values, old_values, fs_id, mdata_id, *args):
+        args = (record, values, old_values, fs_id, mdata_id) + args
+        Model = self.pool.get(model)
+
+        actions = iter(args)
+        to_update = []
+        for record, values, _, _, _ in zip(*((actions,) * 5)):
+            if values:
+                to_update += [[record], values]
+        # if there is values to update:
+        if to_update:
+            # write the values in the db:
+            with Transaction().set_context(
+                    module=module, language='en_US'):
+                Model.write(*to_update)
+            self.fs2db.reset_browsercord(
+                module, Model.__name__, sum(to_update[::2], []))
+
+        actions = iter(to_update)
+        for records, values in zip(actions, actions):
+            record, = records
+            # re-read it: this ensure that we store the real value
+            # in the model_data table:
+            record = self.fs2db.get_browserecord(
+                module, Model.__name__, record.id)
+            if not record:
+                record = Model(record.id)
+            for key in values:
+                values[key] = self._clean_value(key, record)
+
+        actions = iter(args)
+        for record, values, old_values, fs_id, mdata_id in zip(
+                *((actions,) * 5)):
+            temp_values = old_values.copy()
+            temp_values.update(values)
+            values = temp_values
+
+            if values != old_values:
+                self.grouped_model_data.extend(([self.ModelData(mdata_id)], {
+                            'fs_id': fs_id,
+                            'model': model,
+                            'module': module,
+                            'db_id': record.id,
+                            'values': str(values),
+                            'date_update': datetime.datetime.now(),
+                            }))
+
+        # reset_browsercord to keep cache memory low
+        self.fs2db.reset_browsercord(module, Model.__name__, args[::5])
+
 
 def post_import(pool, module, to_delete):
     """
diff --git a/trytond/ir/__init__.py b/trytond/ir/__init__.py
index ef6e0c1..062897b 100644
--- a/trytond/ir/__init__.py
+++ b/trytond/ir/__init__.py
@@ -72,6 +72,7 @@ def register():
         ModuleConfigWizardItem,
         ModuleConfigWizardFirst,
         ModuleConfigWizardOther,
+        ModuleConfigWizardDone,
         ModuleInstallUpgradeStart,
         ModuleInstallUpgradeDone,
         Cache,
diff --git a/trytond/ir/action.py b/trytond/ir/action.py
index 25a5bfa..873a131 100644
--- a/trytond/ir/action.py
+++ b/trytond/ir/action.py
@@ -56,9 +56,9 @@ class Action(ModelSQL, ModelView):
         return True
 
     @classmethod
-    def write(cls, actions, values):
+    def write(cls, actions, values, *args):
         pool = Pool()
-        super(Action, cls).write(actions, values)
+        super(Action, cls).write(actions, values, *args)
         pool.get('ir.action.keyword')._get_keyword_cache.clear()
 
     @classmethod
@@ -194,9 +194,12 @@ class ActionKeyword(ModelSQL, ModelView):
         return super(ActionKeyword, cls).create(new_vlist)
 
     @classmethod
-    def write(cls, keywords, vals):
-        vals = cls._convert_vals(vals)
-        super(ActionKeyword, cls).write(keywords, vals)
+    def write(cls, keywords, values, *args):
+        actions = iter((keywords, values) + args)
+        args = []
+        for keywords, values in zip(actions, actions):
+            args.extend((keywords, cls._convert_vals(values)))
+        super(ActionKeyword, cls).write(*args)
         ModelView._fields_view_get_cache.clear()
         ModelView._view_toolbar_get_cache.clear()
         cls._get_keyword_cache.clear()
@@ -223,6 +226,7 @@ class ActionKeyword(ModelSQL, ModelView):
                     ('model', '=', model + ',' + str(model_id)),
                     ],
                 ]
+        clause = [clause, ('action.active', '=', True)]
         action_keywords = cls.search(clause, order=[])
         types = defaultdict(list)
         for action_keyword in action_keywords:
@@ -328,10 +332,10 @@ class ActionMixin(ModelSQL):
         return new_records
 
     @classmethod
-    def write(cls, records, values):
+    def write(cls, records, values, *args):
         pool = Pool()
         ActionKeyword = pool.get('ir.action.keyword')
-        super(ActionMixin, cls).write(records, values)
+        super(ActionMixin, cls).write(records, values, *args)
         ActionKeyword._get_keyword_cache.clear()
 
     @classmethod
@@ -364,8 +368,12 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
     report_name = fields.Char('Internal Name', required=True)
     report = fields.Char('Path')
     report_content_custom = fields.Binary('Content')
-    report_content = fields.Function(fields.Binary('Content'),
-            'get_report_content', setter='set_report_content')
+    report_content = fields.Function(fields.Binary('Content',
+            filename='report_content_name'),
+        'get_report_content', setter='set_report_content')
+    report_content_name = fields.Function(fields.Char('Content Name',
+            on_change_with=['name', 'template_extension']),
+        'on_change_with_report_content_name')
     action = fields.Many2One('ir.action', 'Action', required=True,
             ondelete='CASCADE')
     style = fields.Property(fields.Char('Style',
@@ -594,6 +602,11 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
     def set_report_content(cls, records, name, value):
         cls.write(records, {'%s_custom' % name: value})
 
+    def on_change_with_report_content_name(self, name=None):
+        if not self.name:
+            return
+        return ''.join([self.name, os.extsep, self.template_extension])
+
     @classmethod
     def get_style_content(cls, reports, name):
         contents = {}
@@ -644,13 +657,18 @@ class ActionReport(ActionMixin, ModelSQL, ModelView):
         return new_reports
 
     @classmethod
-    def write(cls, reports, vals):
+    def write(cls, reports, values, *args):
         context = Transaction().context
         if 'module' in context:
-            vals = vals.copy()
-            vals['module'] = context['module']
-
-        super(ActionReport, cls).write(reports, vals)
+            actions = iter((reports, values) + args)
+            args = []
+            for reports, values in zip(actions, actions):
+                values = values.copy()
+                values['module'] = context['module']
+                args.extend((reports, values))
+            reports, values = args[:2]
+            args = args[2:]
+        super(ActionReport, cls).write(reports, values, *args)
 
 
 class ActionActWindow(ActionMixin, ModelSQL, ModelView):
@@ -668,8 +686,6 @@ class ActionActWindow(ActionMixin, ModelSQL, ModelView):
     domains = fields.Function(fields.Binary('Domains'), 'get_domains')
     limit = fields.Integer('Limit', required=True,
             help='Default limit for the list view')
-    auto_refresh = fields.Integer('Auto-Refresh', required=True,
-            help='Add an auto-refresh on the view')
     action = fields.Many2One('ir.action', 'Action', required=True,
             ondelete='CASCADE')
     window_name = fields.Boolean('Window Name',
@@ -701,14 +717,20 @@ class ActionActWindow(ActionMixin, ModelSQL, ModelView):
     @classmethod
     def __register__(cls, module_name):
         cursor = Transaction().cursor
+        TableHandler = backend.get('TableHandler')
         act_window = cls.__table__()
         super(ActionActWindow, cls).__register__(module_name)
 
+        table = TableHandler(cursor, cls, module_name)
+
         # Migration from 2.0: new search_value format
         cursor.execute(*act_window.update(
                 [act_window.search_value], ['[]'],
                 where=act_window.search_value == '{}'))
 
+        # Migration from 3.0: auto_refresh removed
+        table.drop_column('auto_refresh')
+
     @staticmethod
     def default_type():
         return 'ir.action.act_window'
@@ -722,10 +744,6 @@ class ActionActWindow(ActionMixin, ModelSQL, ModelView):
         return 0
 
     @staticmethod
-    def default_auto_refresh():
-        return 0
-
-    @staticmethod
     def default_window_name():
         return True
 
@@ -907,9 +925,9 @@ class ActionActWindowView(ModelSQL, ModelView):
         return windows
 
     @classmethod
-    def write(cls, windows, values):
+    def write(cls, windows, values, *args):
         pool = Pool()
-        super(ActionActWindowView, cls).write(windows, values)
+        super(ActionActWindowView, cls).write(windows, values, *args)
         pool.get('ir.action.keyword')._get_keyword_cache.clear()
 
     @classmethod
diff --git a/trytond/ir/attachment.py b/trytond/ir/attachment.py
index 09e340e..d261509 100644
--- a/trytond/ir/attachment.py
+++ b/trytond/ir/attachment.py
@@ -1,11 +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.
 import os
-try:
-    import hashlib
-except ImportError:
-    hashlib = None
-    import md5
+import hashlib
 from sql.operators import Concat
 
 from ..model import ModelView, ModelSQL, fields
@@ -39,8 +35,7 @@ class Attachment(ModelSQL, ModelView):
                 'invisible': Eval('type') != 'data',
                 }, depends=['type']), 'get_data', setter='set_data')
     description = fields.Text('Description')
-    summary = fields.Function(fields.Char('Summary',
-        on_change_with=['description']), 'on_change_with_summary')
+    summary = fields.Function(fields.Char('Summary'), 'on_change_with_summary')
     resource = fields.Reference('Resource', selection='models_get',
         select=True)
     link = fields.Char('Link', states={
@@ -148,10 +143,7 @@ class Attachment(ModelSQL, ModelView):
         directory = os.path.join(CONFIG['data_path'], db_name)
         if not os.path.isdir(directory):
             os.makedirs(directory, 0770)
-        if hashlib:
-            digest = hashlib.md5(value).hexdigest()
-        else:
-            digest = md5.new(value).hexdigest()
+        digest = hashlib.md5(value).hexdigest()
         directory = os.path.join(directory, digest[0:2], digest[2:4])
         if not os.path.isdir(directory):
             os.makedirs(directory, 0770)
@@ -191,6 +183,7 @@ class Attachment(ModelSQL, ModelView):
             'collision': collision,
             })
 
+    @fields.depends('description')
     def on_change_with_summary(self, name=None):
         return firstline(self.description or '')
 
@@ -231,10 +224,14 @@ class Attachment(ModelSQL, ModelView):
         super(Attachment, cls).delete(attachments)
 
     @classmethod
-    def write(cls, attachments, vals):
-        cls.check_access([a.id for a in attachments], mode='write')
-        super(Attachment, cls).write(attachments, vals)
-        cls.check_access(attachments, mode='write')
+    def write(cls, attachments, values, *args):
+        all_attachments = []
+        actions = iter((attachments, values) + args)
+        for records, _ in zip(actions, actions):
+            all_attachments += records
+        cls.check_access([a.id for a in all_attachments], mode='write')
+        super(Attachment, cls).write(attachments, values, *args)
+        cls.check_access(all_attachments, mode='write')
 
     @classmethod
     def create(cls, vlist):
diff --git a/trytond/ir/cron.py b/trytond/ir/cron.py
index 904a2fd..f255284 100644
--- a/trytond/ir/cron.py
+++ b/trytond/ir/cron.py
@@ -1,6 +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 contextlib
 import datetime
 from dateutil.relativedelta import relativedelta
 import traceback
@@ -167,8 +166,8 @@ class Cron(ModelSQL, ModelView):
             req_user = cron.request_user
             language = (req_user.language.code if req_user.language
                     else Config.get_language())
-            with contextlib.nested(Transaction().set_user(cron.user.id),
-                    Transaction().set_context(language=language)):
+            with Transaction().set_user(cron.user.id), \
+                    Transaction().set_context(language=language):
                 cls.send_error_message(cron)
 
     @classmethod
diff --git a/trytond/ir/date.py b/trytond/ir/date.py
index 2af7467..fabf04a 100644
--- a/trytond/ir/date.py
+++ b/trytond/ir/date.py
@@ -22,8 +22,8 @@ class Date(Model):
                 })
 
     @staticmethod
-    def today():
+    def today(timezone=None):
         '''
         Return the current date
         '''
-        return datetime.date.today()
+        return datetime.datetime.now(timezone).date()
diff --git a/trytond/ir/lang.py b/trytond/ir/lang.py
index d2270b9..a841274 100644
--- a/trytond/ir/lang.py
+++ b/trytond/ir/lang.py
@@ -213,12 +213,12 @@ class Lang(ModelSQL, ModelView):
         return languages
 
     @classmethod
-    def write(cls, langs, vals):
+    def write(cls, langs, values, *args):
         pool = Pool()
         Translation = pool.get('ir.translation')
         # Clear cache
         cls._lang_cache.clear()
-        super(Lang, cls).write(langs, vals)
+        super(Lang, cls).write(langs, values, *args)
         Translation._get_language_cache.clear()
 
     @classmethod
diff --git a/trytond/ir/locale/bg_BG.po b/trytond/ir/locale/bg_BG.po
index 8f008d2..56ebd1b 100644
--- a/trytond/ir/locale/bg_BG.po
+++ b/trytond/ir/locale/bg_BG.po
@@ -21,20 +21,26 @@ msgid "You are not allowed to delete this record."
 msgstr "Нямате права да изтривате този запис."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "Полето \"%s\" от \"%s\" има много десетични цифри"
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "Стойността на полето \"%s\" от \"%s\" не е валидно според домейна."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
+msgstr ""
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
-msgstr "Не може да изтривате записи \"%s\" защото се използват в поле \"%s\" от \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
+msgstr ""
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "Стойността на полето \"%s\" от \"%s\" не съществува."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr ""
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -242,10 +248,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr ""
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Ключа %r не е намерен в полето за избор %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -275,19 +277,21 @@ msgid "Relation not found: %r in %s"
 msgstr "Не е намерена зависимост: %r в %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Полето \"%s\" от \"%s\" е задължително."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr ""
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Полето \"%s\" от \"%s\" е задължително."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr ""
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "Липсват функции за търсене върху поле \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
 msgstr ""
 
 msgctxt "error:selection_value_notfound:"
@@ -295,11 +299,11 @@ msgid "Value not in the selection for field \"%s\"."
 msgstr ""
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "Полето \"%s\" от \"%s\" е много дълго."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
+msgstr ""
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
 msgstr ""
 
 msgctxt "error:too_many_relations_found:"
@@ -401,10 +405,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Активен"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Самообновяване"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Стойност на котекст"
@@ -724,6 +724,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Съдържание"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr ""
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Вътрешно име"
@@ -1608,6 +1612,11 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Променено от"
 
+#, fuzzy
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2669,10 +2678,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Променено от"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Добавяне на самообновяване на изгледа"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Ограничение по подразбиране за изглед със списък"
@@ -3114,6 +3119,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Модул"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr ""
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Първи помощник на конфигуриране на модул"
@@ -3770,6 +3779,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Описание на модел"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr ""
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr ""
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Добре дошли в помощника за конфигуриране на модула!"
@@ -4034,6 +4051,11 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Печат"
 
+#, fuzzy
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Добре"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Добре"
diff --git a/trytond/ir/locale/ca_ES.po b/trytond/ir/locale/ca_ES.po
index b07b6b7..1f2be61 100644
--- a/trytond/ir/locale/ca_ES.po
+++ b/trytond/ir/locale/ca_ES.po
@@ -23,20 +23,30 @@ msgid "You are not allowed to delete this record."
 msgstr "No podeu eliminar aquest registre."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "El camp \"%s\" de \"%s\" té massa decimals."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
+"El nombre de dígits \"%(digits)s\" del camp \"%(field)s\" de %\"(value)s\" "
+"és superior al límit."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
+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."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
-msgstr "No podeu eliminar \"%s\" perquè s'utilitzen en els camps \"%s\" de \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
+msgstr ""
+"No es poden eliminar els registres perquè es fan servir al camp "
+"\"%(field)s\" de \"%(models)\"."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "El valor del camp \"%s\" de \"%s\" no existeix."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr "El valor \"%(value)s\" del camp \"%(field)s\" de \"%(model)s\" no existeix."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -83,13 +93,13 @@ msgid ""
 msgstr ""
 "Ha fallat l'acció quan s'executava: \"%s\"\n"
 "%s\n"
-" Traceback: \n"
+" Traça del programa: \n"
 "\n"
 "%s\n"
 
 msgctxt "error:ir.lang:"
 msgid "Default language can not be deleted."
-msgstr "El idioma per defecte no es pot eliminar."
+msgstr "L'idioma per defecte no es pot eliminar."
 
 msgctxt "error:ir.lang:"
 msgid "Invalid date format \"%(format)s\" on \"%(language)s\" language."
@@ -97,7 +107,7 @@ msgstr "El format de la data \"%(format)s\" de \"%(language)s\" no és correcte.
 
 msgctxt "error:ir.lang:"
 msgid "Invalid grouping \"%(grouping)s\" on \"%(language)s\" language."
-msgstr "La agrupació \"%(grouping)s\" de \"%(language)s\" no és correcte."
+msgstr "L'agrupació \"%(grouping)s\" de \"%(language)s\" no és correcte."
 
 msgctxt "error:ir.lang:"
 msgid "The default language must be translatable."
@@ -105,15 +115,15 @@ msgstr "L'idioma per defecte ha de ser traduïble."
 
 msgctxt "error:ir.lang:"
 msgid "decimal_point and thousands_sep must be different!"
-msgstr "decimal_point i thousands_sep han de ser diferents."
+msgstr "El punt decimal i el separador de milers han de ser diferents."
 
 msgctxt "error:ir.model.access:"
 msgid "You can not create this kind of document! (%s)"
-msgstr "No podeu crear aquest tipus de document. (%s)"
+msgstr "No podeu crear aquest tipus de document (%s)."
 
 msgctxt "error:ir.model.access:"
 msgid "You can not delete this document! (%s)"
-msgstr "No podeu eliminar aquest document (%s)"
+msgstr "No podeu eliminar aquest document (%s)."
 
 msgctxt "error:ir.model.access:"
 msgid "You can not read this document! (%s)"
@@ -125,7 +135,7 @@ msgstr "No podeu modificar aquest document (%s)."
 
 msgctxt "error:ir.model.button:"
 msgid "The button name in model must be unique!"
-msgstr "El nom del butó del model ha de ser únic."
+msgstr "El nom del botó del model ha de ser únic."
 
 msgctxt "error:ir.model.data:"
 msgid "The triple (fs_id, module, model) must be unique!"
@@ -141,7 +151,7 @@ msgstr "No podeu modificar el camp (%s.%s)."
 
 msgctxt "error:ir.model.field:"
 msgid "Model Field name \"%s\" is not a valid python identifier."
-msgstr "El nom del model \"%s\" no és un identificador de python correcte."
+msgstr "El nom del model \"%s\" no és un identificador de Python correcte."
 
 msgctxt "error:ir.model.field:"
 msgid "The field name in model must be unique!"
@@ -149,7 +159,7 @@ msgstr "El nom del camp ha de ser únic."
 
 msgctxt "error:ir.model:"
 msgid "Module name \"%s\" is not a valid python identifier."
-msgstr "El nom del model \"%s\" no és un identificador de python correcte."
+msgstr "El nom del model \"%s\" no és un identificador de Python correcte."
 
 msgctxt "error:ir.model:"
 msgid "The model must be unique!"
@@ -161,12 +171,13 @@ msgstr "La dependència ha de ser únic en el mòdul."
 
 msgctxt "error:ir.module.module:"
 msgid "Missing dependencies %s for module \"%s\""
-msgstr "No es troba la dependècia %s pel mòdul \"%s\"."
+msgstr "No es troba la dependència %s pel mòdul \"%s\"."
 
 msgctxt "error:ir.module.module:"
 msgid "The modules you are trying to uninstall depends on installed modules:"
 msgstr ""
-"Els mòduls que està intentant desinstal·lar depenen de mòduls instal·lats:"
+"Els mòduls que esteu intentant desinstal·lar depenen d'altres mòduls "
+"instal·lats:"
 
 msgctxt "error:ir.module.module:"
 msgid "The name of the module must be unique!"
@@ -194,7 +205,7 @@ msgstr "Sufix \"%(sufix)s\" de la seqüència \"%(sequence)s\" no és correcte."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
-msgstr "La darrera data-hora no pot ser del futur a la seqüència \"%s\"."
+msgstr "L'última data-hora no pot ser del futur a la seqüència \"%s\"."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Missing sequence."
@@ -202,7 +213,7 @@ msgstr "No es troba la seqüència."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Timestamp rounding should be greater than 0"
-msgstr "L'arrodiment Timestamp ha de ser mes gran que 0."
+msgstr "L'arrodoniment de data-hora ha de ser més gran que 0."
 
 msgctxt "error:ir.sequence:"
 msgid "Invalid prefix \"%(prefix)s\" on sequence \"%(sequence)s\"."
@@ -214,7 +225,7 @@ msgstr "Sufix \"%(sufix)s\" de la seqüència \"%(sequence)s\" no és correcte."
 
 msgctxt "error:ir.sequence:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
-msgstr "La darrera data-hora no pot ser del futur a la seqüència \"%s\"."
+msgstr "L'última data-hora no pot ser del futur a la seqüència \"%s\"."
 
 msgctxt "error:ir.sequence:"
 msgid "Missing sequence."
@@ -222,7 +233,7 @@ msgstr "No es troba la seqüència."
 
 msgctxt "error:ir.sequence:"
 msgid "Timestamp rounding should be greater than 0"
-msgstr "L'arrodiment Timestamp ha de ser mes gran que 0."
+msgstr "L'arrodoniment de data-hora ha de ser més gran que 0."
 
 msgctxt "error:ir.translation:"
 msgid "Translation must be unique"
@@ -234,18 +245,18 @@ msgid ""
 "translation by module %(overriding_module)s"
 msgstr ""
 "No podeu exportar la traducció %(name)s perquè s'utilitza en el mòdul "
-"%(overriding_module)s"
+"%(overriding_module)s."
 
 msgctxt "error:ir.trigger:"
 msgid "\"On Time\" and others are mutually exclusive!"
-msgstr "\"Al moment\" i altres son mutuament exclusius."
+msgstr "\"Al moment\" i altres són mútuament exclusius."
 
 msgctxt "error:ir.trigger:"
 msgid ""
 "Condition \"%(condition)s\" is not a valid python expression on trigger "
 "\"%(trigger)s\"."
 msgstr ""
-"La condició \"%(condition)s\" no és una expressió python correcte en el "
+"La condició \"%(condition)s\" no és una expressió Python correcte en el "
 "disparador \"%(trigger)s\"."
 
 msgctxt "error:ir.ui.menu:"
@@ -256,10 +267,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "XML en les vistes \"%s\" no és correcte."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "No es troba la clau %r en la selecció del camp %r."
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -281,44 +288,50 @@ msgid ""
 "Recursion error: Record \"%(rec_name)s\" with parent \"%(parent_rec_name)s\""
 " was configured as ancestor of itself."
 msgstr ""
-"Error de recurssió: El registre \"%(rec_name)s\" amb el pare "
+"Error de recursivitat: El registre \"%(rec_name)s\" amb el pare "
 "\"%(parent_rec_name)s\" es va configurar com pare de si mateix."
 
 msgctxt "error:reference_syntax_error:"
 msgid "Syntax error for reference %r in %s"
-msgstr "Error de sintaxis en la referència %r a %s"
+msgstr "Error de sintaxi en la referència %r a %s."
 
 msgctxt "error:relation_not_found:"
 msgid "Relation not found: %r in %s"
-msgstr "No s'ha trobat la relació: %r a %s"
+msgstr "No s'ha trobat la relació: %r a %s."
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El camp \"%s\" de \"%s\" es requerit."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El camp \"%(field)s\" de \"%(model)s\" es obligatori."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El camp \"%s\" de \"%s\" es requerit."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El camp \"%(field)s\" de \"%(model)s\" es obligatori."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "No es troba la funció de cerca del camp \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "El camp \"%s\" de \"%s\" no es troba en la selecció."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
+msgstr ""
+"El valor \"%(value)s\" del camp \"%(field)s\" de \"%(model)s\" no està a la "
+"selecció."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
 msgstr "El valor no es troba en la selecció en el camp \"%s\"."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "El camp \"%s\" de \"%s\" és massa llarg."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
+msgstr "La mida \"%(size)s\" del camp \"%(field)s\" de \"%(model)s\" es massa gran."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "El valor d'hora del camp \"%s\" a \"%s\" no és correcte."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
+"El valor de temps \"%(value)s\" del camp %(field)s\" de \"%(model)s\" no és "
+"vàlid."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -346,7 +359,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 sintaxis XML id %r a %s"
+msgstr "Error de sintaxi XML id %r a %s."
 
 msgctxt "error:xml_record_desc:"
 msgid "This record is part of the base configuration."
@@ -420,10 +433,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Actiu"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Actualizació automatitzada"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Context"
@@ -478,7 +487,7 @@ msgstr "PySON Context"
 
 msgctxt "field:ir.action.act_window,pyson_domain:"
 msgid "PySON Domain"
-msgstr "PySON Domain"
+msgstr "Domini PySON"
 
 msgctxt "field:ir.action.act_window,pyson_order:"
 msgid "PySON Order"
@@ -486,7 +495,7 @@ msgstr "Ordre PySON"
 
 msgctxt "field:ir.action.act_window,pyson_search_value:"
 msgid "PySON Search Criteria"
-msgstr "PySON Search Criteria"
+msgstr "Criteri de cerca PySON"
 
 msgctxt "field:ir.action.act_window,rec_name:"
 msgid "Name"
@@ -666,7 +675,7 @@ msgstr "Direcció impressió"
 
 msgctxt "field:ir.action.report,email:"
 msgid "Email"
-msgstr "Email"
+msgstr "Correu electrònic"
 
 msgctxt "field:ir.action.report,extension:"
 msgid "Extension"
@@ -702,7 +711,7 @@ msgstr "Nom"
 
 msgctxt "field:ir.action.report,pyson_email:"
 msgid "PySON Email"
-msgstr "PySON Email"
+msgstr "Correu electrònic PySON"
 
 msgctxt "field:ir.action.report,rec_name:"
 msgid "Name"
@@ -720,6 +729,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Contingut"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Nom del contingut"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Nom intern"
@@ -830,7 +843,7 @@ msgstr "Usuari creació"
 
 msgctxt "field:ir.action.wizard,email:"
 msgid "Email"
-msgstr "Email"
+msgstr "Correu electrònic"
 
 msgctxt "field:ir.action.wizard,groups:"
 msgid "Groups"
@@ -874,7 +887,7 @@ msgstr "Finestra"
 
 msgctxt "field:ir.action.wizard,wiz_name:"
 msgid "Wizard name"
-msgstr "Nom del assistent"
+msgstr "Nom de l'assistent"
 
 msgctxt "field:ir.action.wizard,write_date:"
 msgid "Write Date"
@@ -886,7 +899,7 @@ msgstr "Usuari modificació"
 
 msgctxt "field:ir.attachment,collision:"
 msgid "Collision"
-msgstr "Col·lissió"
+msgstr "Col·lisió"
 
 msgctxt "field:ir.attachment,create_date:"
 msgid "Create Date"
@@ -978,7 +991,7 @@ msgstr "Nom"
 
 msgctxt "field:ir.cache,timestamp:"
 msgid "Timestamp"
-msgstr "Timestamp"
+msgstr "Data-hora"
 
 msgctxt "field:ir.cache,write_date:"
 msgid "Write Date"
@@ -1042,7 +1055,7 @@ msgstr "ID"
 
 msgctxt "field:ir.cron,interval_number:"
 msgid "Interval Number"
-msgstr "Número d'intèrval"
+msgstr "Número d'interval"
 
 msgctxt "field:ir.cron,interval_type:"
 msgid "Interval Unit"
@@ -1058,7 +1071,7 @@ msgstr "Nom"
 
 msgctxt "field:ir.cron,next_call:"
 msgid "Next Call"
-msgstr "Següent execussió"
+msgstr "Següent execució"
 
 msgctxt "field:ir.cron,number_calls:"
 msgid "Number of Calls"
@@ -1074,11 +1087,11 @@ msgstr "Repeteix perduts"
 
 msgctxt "field:ir.cron,request_user:"
 msgid "Request User"
-msgstr "Sol·licitud usuari"
+msgstr "Usuari sol·licitant"
 
 msgctxt "field:ir.cron,user:"
 msgid "Execution User"
-msgstr "Usuari execussió"
+msgstr "Usuari execució"
 
 msgctxt "field:ir.cron,write_date:"
 msgid "Write Date"
@@ -1210,7 +1223,7 @@ msgstr "Separador de milers"
 
 msgctxt "field:ir.lang,translatable:"
 msgid "Translatable"
-msgstr "Traduible"
+msgstr "Traduïble"
 
 msgctxt "field:ir.lang,write_date:"
 msgid "Write Date"
@@ -1580,6 +1593,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Usuari modificació"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -1834,7 +1851,7 @@ msgstr "ID"
 
 msgctxt "field:ir.sequence,last_timestamp:"
 msgid "Last Timestamp"
-msgstr "Últim Timestamp"
+msgstr "Última data-hora"
 
 msgctxt "field:ir.sequence,name:"
 msgid "Sequence Name"
@@ -1854,7 +1871,7 @@ msgstr "Següent número"
 
 msgctxt "field:ir.sequence,padding:"
 msgid "Number padding"
-msgstr "Omplenat del número"
+msgstr "Emplenat del número"
 
 msgctxt "field:ir.sequence,prefix:"
 msgid "Prefix"
@@ -1866,15 +1883,15 @@ msgstr "Nom"
 
 msgctxt "field:ir.sequence,suffix:"
 msgid "Suffix"
-msgstr "Suffix"
+msgstr "Sufix"
 
 msgctxt "field:ir.sequence,timestamp_offset:"
 msgid "Timestamp Offset"
-msgstr "Timestamp Offset"
+msgstr "Desfasament de data-hora"
 
 msgctxt "field:ir.sequence,timestamp_rounding:"
 msgid "Timestamp Rounding"
-msgstr "Arrondiment Tmestamp"
+msgstr "Arronodiment de data-hora"
 
 msgctxt "field:ir.sequence,type:"
 msgid "Type"
@@ -1910,7 +1927,7 @@ msgstr "ID"
 
 msgctxt "field:ir.sequence.strict,last_timestamp:"
 msgid "Last Timestamp"
-msgstr "Últim Timestamp"
+msgstr "Última data-hora"
 
 msgctxt "field:ir.sequence.strict,name:"
 msgid "Sequence Name"
@@ -1930,7 +1947,7 @@ msgstr "Següent número"
 
 msgctxt "field:ir.sequence.strict,padding:"
 msgid "Number padding"
-msgstr "Omplenat del número"
+msgstr "Emplenat del número"
 
 msgctxt "field:ir.sequence.strict,prefix:"
 msgid "Prefix"
@@ -1942,15 +1959,15 @@ msgstr "Nom"
 
 msgctxt "field:ir.sequence.strict,suffix:"
 msgid "Suffix"
-msgstr "Suffix"
+msgstr "Sufix"
 
 msgctxt "field:ir.sequence.strict,timestamp_offset:"
 msgid "Timestamp Offset"
-msgstr "Timestamp Offset"
+msgstr "Desfasament de data-hora"
 
 msgctxt "field:ir.sequence.strict,timestamp_rounding:"
 msgid "Timestamp Rounding"
-msgstr "Arrondiment Tmestamp"
+msgstr "Arronodiment de data-hora"
 
 msgctxt "field:ir.sequence.strict,type:"
 msgid "Type"
@@ -2082,7 +2099,7 @@ msgstr "Mòdul"
 
 msgctxt "field:ir.translation,name:"
 msgid "Field Name"
-msgstr "Nom del camp"
+msgstr "Nom"
 
 msgctxt "field:ir.translation,overriding_module:"
 msgid "Overriding Module"
@@ -2326,7 +2343,7 @@ msgstr "Fills"
 
 msgctxt "field:ir.ui.menu,complete_name:"
 msgid "Complete Name"
-msgstr "Nom compet"
+msgstr "Nom complet"
 
 msgctxt "field:ir.ui.menu,create_date:"
 msgid "Create Date"
@@ -2442,7 +2459,7 @@ msgstr "ID"
 
 msgctxt "field:ir.ui.view,inherit:"
 msgid "Inherited View"
-msgstr "Vista heredada"
+msgstr "Vista heretada"
 
 msgctxt "field:ir.ui.view,model:"
 msgid "Model"
@@ -2608,21 +2625,17 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Usuari modificació"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Afegeix un actualizació a la vista automatitzada"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
-msgstr "Limit per defecte en les vistes de llista"
+msgstr "Límit per defecte en les vistes de llista."
 
 msgctxt "help:ir.action.act_window,search_value:"
 msgid "Default search criteria for the list view"
-msgstr "Cerca per defecte en les vistes de llista"
+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 "Utilitza el nom de l'acció pel nom de la finestra."
 
 msgctxt "help:ir.action.report,email:"
 msgid ""
@@ -2638,27 +2651,27 @@ msgid ""
 "compatible format"
 msgstr ""
 "Deixeu-ho en blanc per mantenir el mateix que a la plantilla, veieu la "
-"documentació de l'unoconv per conèixer formats compatibles"
+"documentació de l'unoconv per conèixer formats compatibles."
 
 msgctxt "help:ir.action.report,style:"
 msgid "Define the style to apply on the report."
-msgstr "Defineix un esti per aplicar en els informes."
+msgstr "Defineix un estil per aplicar als informes."
 
 msgctxt "help:ir.action.wizard,window:"
 msgid "Run wizard in a new window"
-msgstr "Executa l'assistent en una nova finestra"
+msgstr "Executa l'assistent en una nova finestra."
 
 msgctxt "help:ir.cron,number_calls:"
 msgid ""
 "Number of times the function is called, a negative number indicates that the"
 " function will always be called"
 msgstr ""
-"Número d'execussions de la funció, un número negatiu indica que aqusta "
-"funció sempre s'executarà."
+"Número d'execucions de la funció, un número negatiu indica que aquesta "
+"funció s'executarà sempre."
 
 msgctxt "help:ir.cron,request_user:"
 msgid "The user who will receive requests in case of failure"
-msgstr "L'usuari que rebrà solicituds en cas d'errors."
+msgstr "L'usuari que rebrà sol·licituds en cas d'errors."
 
 msgctxt "help:ir.cron,user:"
 msgid "The user used to execute this action"
@@ -2674,15 +2687,15 @@ msgstr "Mòdul en què es defineix aquest model."
 
 msgctxt "help:ir.model.data,db_id:"
 msgid "The id of the record in the database."
-msgstr "El id del registre de la base de dades."
+msgstr "L'id del registre de la base de dades."
 
 msgctxt "help:ir.model.data,fs_id:"
 msgid "The id of the record as known on the file system."
-msgstr "El id del registre que es conneix en el sistema de fitxers."
+msgstr "L'id del registre com es coneix al sistema de fitxers."
 
 msgctxt "help:ir.model.field,module:"
 msgid "Module in which this field is defined"
-msgstr "Mòdul en el qual es defineix aquest camp"
+msgstr "Mòdul en el qual es defineix aquest camp."
 
 msgctxt "help:ir.model.print_model_graph.start,filter:"
 msgid ""
@@ -2692,11 +2705,11 @@ msgstr "Una expressió regular de Python per excloure models de les gràfiques."
 
 msgctxt "help:ir.rule,domain:"
 msgid "Domain is evaluated with \"user\" as the current user"
-msgstr "El domini es evaluat amb el usuari \"user\" que es el usuari actual."
+msgstr "El domini s'avalua amb \"user\" com l'usuari actual."
 
 msgctxt "help:ir.rule.group,default_p:"
 msgid "Add this rule to all users by default"
-msgstr "Afegeix aquesta regla a tots els usuaris per defecte"
+msgstr "Afegeix aquesta regla a tots els usuaris per defecte."
 
 msgctxt "help:ir.rule.group,global_p:"
 msgid ""
@@ -2704,18 +2717,18 @@ msgid ""
 "so every users must follow this rule"
 msgstr ""
 "Marcar la regla global \n"
-"per tant els usuari podràn seguir aquesta regla"
+"per tant tots els usuari hauran de seguir aquesta regla."
 
 msgctxt "help:ir.rule.group,rules:"
 msgid "The rule is satisfied if at least one test is True"
-msgstr "La regla es correcta si l'últim test és cert."
+msgstr "La regla es compleix si almenys un test és cert."
 
 msgctxt "help:ir.trigger,condition:"
 msgid ""
 "A Python statement evaluated with record represented by \"self\"\n"
 "It triggers the action if true."
 msgstr ""
-"Una declaració de Python evaluat amb el registre representat per \"self\".\n"
+"Una declaració de Python avaluat amb el registre representat per \"self\".\n"
 "Llença l'acció si la condició es certa."
 
 msgctxt "help:ir.trigger,limit_number:"
@@ -2731,12 +2744,12 @@ msgid ""
 "Set a minimum delay in minutes between call to \"Action Function\" for the same record.\n"
 "0 for no delay."
 msgstr ""
-"Intérval mínim en minuts entre les crides \"Acció de funció\" pel mateix registre.\n"
-"0 per no intérval."
+"Interval mínim en minuts entre les crides \"Acció de funció\" pel mateix registre.\n"
+"0 per no interval."
 
 msgctxt "help:ir.ui.view_search,domain:"
 msgid "The PYSON domain"
-msgstr "El domini PYSON"
+msgstr "El domini PYSON."
 
 msgctxt "model:ir.action,name:"
 msgid "Action"
@@ -2800,7 +2813,7 @@ msgstr "Accés model"
 
 msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
-msgstr "Butons"
+msgstr "Botons"
 
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
@@ -2972,7 +2985,7 @@ msgstr "Espanyol (Argentina)"
 
 msgctxt "model:ir.lang,name:lang_bg"
 msgid "Bulgarian"
-msgstr "Bulgaria"
+msgstr "Búlgar"
 
 msgctxt "model:ir.lang,name:lang_ca"
 msgid "Català"
@@ -2980,7 +2993,7 @@ msgstr "Català"
 
 msgctxt "model:ir.lang,name:lang_cs"
 msgid "Czech"
-msgstr "Check"
+msgstr "Txec"
 
 msgctxt "model:ir.lang,name:lang_de"
 msgid "German"
@@ -2996,7 +3009,7 @@ msgstr "Espanyol (Espanya)"
 
 msgctxt "model:ir.lang,name:lang_es_CO"
 msgid "Spanish (Colombia)"
-msgstr "Espanyol (Colombia)"
+msgstr "Espanyol (Colòmbia)"
 
 msgctxt "model:ir.lang,name:lang_fr"
 msgid "French"
@@ -3028,7 +3041,7 @@ msgstr "Accés model"
 
 msgctxt "model:ir.model.button,name:"
 msgid "Model Button"
-msgstr "Butó model"
+msgstr "Botó model"
 
 msgctxt "model:ir.model.data,name:"
 msgid "Model data"
@@ -3050,6 +3063,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Mòdul"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Assistent de configuració del modul finalitzat"
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Inicialització de l'assistent de configuració del mòdul"
@@ -3060,7 +3077,7 @@ msgstr "Assistent configuració després d'instal·lar un mòdul"
 
 msgctxt "model:ir.module.module.config_wizard.other,name:"
 msgid "Module Config Wizard Other"
-msgstr "Un altre asistent de configuració del mòdul"
+msgstr "Un altre assistent de configuració del mòdul"
 
 msgctxt "model:ir.module.module.dependency,name:"
 msgid "Module dependency"
@@ -3076,7 +3093,7 @@ msgstr "Inici instal·lació/actualització mòdul"
 
 msgctxt "model:ir.property,name:"
 msgid "Property"
-msgstr "Property"
+msgstr "Propietat"
 
 msgctxt "model:ir.rule,name:"
 msgid "Rule"
@@ -3128,11 +3145,11 @@ msgstr "Exporta traducció"
 
 msgctxt "model:ir.translation.set.start,name:"
 msgid "Set Translation"
-msgstr "Estableix traduccions"
+msgstr "Defineix traduccions"
 
 msgctxt "model:ir.translation.set.succeed,name:"
 msgid "Set Translation"
-msgstr "Estableix traduccions"
+msgstr "Defineix traduccions"
 
 msgctxt "model:ir.translation.update.start,name:"
 msgid "Update translation"
@@ -3224,7 +3241,7 @@ msgstr "Accés model"
 
 msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
-msgstr "Butons"
+msgstr "Botons"
 
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
@@ -3292,7 +3309,7 @@ msgstr "Traduccions"
 
 msgctxt "model:ir.ui.menu,name:menu_translation_set"
 msgid "Set Translations"
-msgstr "Estableix traduccions"
+msgstr "Defineix traduccions"
 
 msgctxt "model:ir.ui.menu,name:menu_translation_update"
 msgid "Synchronize Translations"
@@ -3436,7 +3453,7 @@ msgstr "Realitzat"
 
 msgctxt "selection:ir.module.module.config_wizard.item,state:"
 msgid "Open"
-msgstr "Per obrir"
+msgstr "Pendent"
 
 msgctxt "selection:ir.module.module.dependency,state:"
 msgid "Installed"
@@ -3464,11 +3481,11 @@ msgstr "No es coneix"
 
 msgctxt "selection:ir.sequence,type:"
 msgid "Decimal Timestamp"
-msgstr "Decimal Tiemstamp"
+msgstr "Data-hora decimal"
 
 msgctxt "selection:ir.sequence,type:"
 msgid "Hexadecimal Timestamp"
-msgstr "Marca de temps hexadecimal"
+msgstr "Data-hora hexadecimal"
 
 msgctxt "selection:ir.sequence,type:"
 msgid "Incremental"
@@ -3476,11 +3493,11 @@ msgstr "Increment"
 
 msgctxt "selection:ir.sequence.strict,type:"
 msgid "Decimal Timestamp"
-msgstr "Decimal Tiemstamp"
+msgstr "Data-hora decimal"
 
 msgctxt "selection:ir.sequence.strict,type:"
 msgid "Hexadecimal Timestamp"
-msgstr "Marca de temps hexadecimal"
+msgstr "Data-hora hexadecimal"
 
 msgctxt "selection:ir.sequence.strict,type:"
 msgid "Incremental"
@@ -3516,7 +3533,7 @@ msgstr "Vista"
 
 msgctxt "selection:ir.translation,type:"
 msgid "Wizard Button"
-msgstr "Butó del assistent"
+msgstr "Botó de l'assistent"
 
 msgctxt "selection:ir.ui.menu,action:"
 msgid ""
@@ -3656,7 +3673,7 @@ msgstr "Exportacions"
 
 msgctxt "view:ir.lang:"
 msgid "Date Formatting"
-msgstr "Formateig data"
+msgstr "Format data"
 
 msgctxt "view:ir.lang:"
 msgid "Language"
@@ -3676,11 +3693,11 @@ msgstr "Control accés"
 
 msgctxt "view:ir.model.button:"
 msgid "Button"
-msgstr "Butons"
+msgstr "Botons"
 
 msgctxt "view:ir.model.button:"
 msgid "Buttons"
-msgstr "Butons"
+msgstr "Botons"
 
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
@@ -3702,6 +3719,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Descripció model"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Configuració del mòdul"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr "S'ha realitzat la configuració correctament."
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Benvingut a la configuració del mòdul."
@@ -3788,14 +3813,13 @@ msgstr "Propietats"
 
 msgctxt "view:ir.property:"
 msgid "Property"
-msgstr "Property"
+msgstr "Propietat"
 
 msgctxt "view:ir.rule.group:"
 msgid ""
 "If there is no test defined, the rule is always satisfied if not global"
 msgstr ""
-"Si no hi ha un test definit, la regla es sempre satisfactoria si no és "
-"global."
+"Si no hi ha un test definit, la regla es compleix sempre si no és global."
 
 msgctxt "view:ir.rule.group:"
 msgid "Record rules"
@@ -3803,7 +3827,7 @@ msgstr "Regles dels registres"
 
 msgctxt "view:ir.rule.group:"
 msgid "The rule is satisfied if at least one test is True"
-msgstr "La regla es correcta si l'últim test és cert."
+msgstr "La regla es compleix si almenys un test és cert."
 
 msgctxt "view:ir.rule:"
 msgid "Test"
@@ -3847,7 +3871,7 @@ msgstr "Seqüències"
 
 msgctxt "view:ir.sequence:"
 msgid "Timestamp"
-msgstr "Timestamp"
+msgstr "Data-hora"
 
 msgctxt "view:ir.sequence:"
 msgid "Year:"
@@ -3879,7 +3903,7 @@ msgstr "Exporta traducció"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Set Translations"
-msgstr "Estableix traduccions"
+msgstr "Defineix traduccions"
 
 msgctxt "view:ir.translation.set.start:"
 msgid "Synchronize Translations?"
@@ -3891,7 +3915,7 @@ msgstr "Les traduccions s'han realitzat correctament."
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Translations"
-msgstr "Estableix traduccions"
+msgstr "Defineix traduccions"
 
 msgctxt "view:ir.translation.update.start:"
 msgid "Synchronize Translations"
@@ -3969,9 +3993,13 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Imprimir"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "D'acord"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
-msgstr "Dacord"
+msgstr "D'acord"
 
 msgctxt "wizard_button:ir.module.module.config_wizard,first,end:"
 msgid "Cancel"
@@ -3987,7 +4015,7 @@ msgstr "Cancel·la"
 
 msgctxt "wizard_button:ir.module.module.install_upgrade,done,config:"
 msgid "Ok"
-msgstr "Dacord"
+msgstr "D'acord"
 
 msgctxt "wizard_button:ir.module.module.install_upgrade,start,end:"
 msgid "Cancel"
@@ -4007,7 +4035,7 @@ msgstr "Cancel·la"
 
 msgctxt "wizard_button:ir.translation.clean,succeed,end:"
 msgid "Ok"
-msgstr "Dacord"
+msgstr "D'acord"
 
 msgctxt "wizard_button:ir.translation.export,result,end:"
 msgid "Close"
@@ -4027,7 +4055,7 @@ msgstr "Cancel·la"
 
 msgctxt "wizard_button:ir.translation.set,start,set_:"
 msgid "Set"
-msgstr "Estableix"
+msgstr "Defineix"
 
 msgctxt "wizard_button:ir.translation.set,succeed,end:"
 msgid "Ok"
diff --git a/trytond/ir/locale/cs_CZ.po b/trytond/ir/locale/cs_CZ.po
index 315e563..78ce3a0 100644
--- a/trytond/ir/locale/cs_CZ.po
+++ b/trytond/ir/locale/cs_CZ.po
@@ -19,19 +19,25 @@ msgid "You are not allowed to delete this record."
 msgstr ""
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
 msgstr ""
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
 msgstr ""
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
 msgstr ""
 
 msgctxt "error:ir.action.act_window:"
@@ -239,10 +245,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr ""
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr ""
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -270,11 +272,11 @@ msgid "Relation not found: %r in %s"
 msgstr ""
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
 msgstr ""
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
 msgstr ""
 
 msgctxt "error:search_function_missing:"
@@ -282,7 +284,9 @@ msgid "Missing search function on field \"%s\"."
 msgstr ""
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
 msgstr ""
 
 msgctxt "error:selection_value_notfound:"
@@ -290,11 +294,11 @@ msgid "Value not in the selection for field \"%s\"."
 msgstr ""
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
 msgstr ""
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
 msgstr ""
 
 msgctxt "error:too_many_relations_found:"
@@ -393,10 +397,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr ""
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr ""
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr ""
@@ -693,6 +693,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr ""
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr ""
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr ""
@@ -1553,6 +1557,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr ""
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr ""
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr ""
@@ -2581,10 +2589,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr ""
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr ""
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr ""
@@ -3009,6 +3013,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr ""
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr ""
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr ""
@@ -3661,6 +3669,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr ""
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr ""
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr ""
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr ""
@@ -3924,6 +3940,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr ""
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr ""
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr ""
diff --git a/trytond/ir/locale/de_DE.po b/trytond/ir/locale/de_DE.po
index f6a900a..22986fe 100644
--- a/trytond/ir/locale/de_DE.po
+++ b/trytond/ir/locale/de_DE.po
@@ -23,30 +23,32 @@ msgid "You are not allowed to delete this record."
 msgstr "Keine Löschberechtigung für diesen Datensatz"
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "Das Feld \"%s\" in \"%s\" weist zu viele Dezimalstellen auf."
-
-msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
 msgstr ""
-"Der Wert von Feld \"%s\" in \"%s\" liegt nicht im gültigen Wertebereich "
-"(Domain)!"
+"Die Anzahl der Nachkommastellen \"%(digits)s\" in Feld \"%(field)s\" in "
+"\"%(value)s\" überschreitet die erlaubte Größe."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid with his domain."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
 msgstr ""
-"Der Wert von Feld \"%s\" in \"%s\" liegt nicht im gültigen Wertebereich "
-"(Domain)!"
+"Der Wert von Feld \"%(field)s\" in \"%(model)s\" liegt nicht im gültigen "
+"Wertebereich (Domain)."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
-"\"%s\" Datensätze konnten nicht gelöscht werden, weil sie in Feld \"%s\" in "
-"\"%s\" verwendet werden."
+"Die Datensätze konnten nicht gelöscht werden, da sie in Feld \"%(field)s\" "
+"von \"%(model)s\" verwendet werden."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "Der Wert von Feld \"%s\" in \"%s\" existiert nicht."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr "Der Wert \"%(value)s\" für Feld \"%(field)s\" in \"%(model)s\" existiert nicht."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -275,10 +277,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "Ungültige XML-Daten für Sicht \"%s\"."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Schlüsselwert %r nicht gefunden in Auswahlfeld %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -312,32 +310,38 @@ msgid "Relation not found: %r in %s"
 msgstr "Beziehung nicht gefunden: %r in %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Feld \"%s\" in \"%s\" ist erforderlich."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "Feld \"%(field)s\" in \"%(model)s\" ist erforderlich."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Feld \"%s\" in \"%s\" ist erforderlich."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "Feld \"%(field)s\" in \"%(model)s\" ist erforderlich."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "Fehlende Suchfunktion für Feld \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "Feld \"%s\" in \"%s\" ist nicht in der Auswahl."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
+msgstr ""
+"Der Wert \"%(value)s\" von Feld \"%(field)s\" in \"%(model)s\" ist nicht in "
+"der Auswahl enthalten."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
 msgstr "Wert nicht in der Auswahl von Feld \"%s\"."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "Der Wert von Feld \"%s\" in \"%s\" ist zu lang."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
+msgstr "Die Länge \"%(size)s\" für Feld \"%(field)s\" in \"%(model)s\" ist zu lang."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "Der Wert der Zeitangabe in Feld \"%s\" in \"%s\" ist nicht gültig"
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
+"Der Wert der Zeit \"%(value)s\" für Feld \"%(field)s\" in \"%(model)s\" ist "
+"ungültig."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -439,10 +443,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Aktiv"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Automatisches Auffrischen"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Kontext"
@@ -739,6 +739,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Inhalt"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Inhalt Name"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Interner Name"
@@ -1599,6 +1603,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Letzte Änderung durch"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2165,7 +2173,7 @@ msgstr "Sprache"
 
 msgctxt "field:ir.translation.export.start,module:"
 msgid "Module"
-msgstr "Module"
+msgstr "Modul"
 
 msgctxt "field:ir.translation.set.start,id:"
 msgid "ID"
@@ -2627,10 +2635,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Letzte Änderung durch"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Die Sicht wird automatisch aufgefrischt"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Standardobergrenze für die Listenanscht"
@@ -3072,7 +3076,11 @@ msgstr "Modellgraph drucken"
 
 msgctxt "model:ir.module.module,name:"
 msgid "Module"
-msgstr "Module"
+msgstr "Modul"
+
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Modulkonfigurationswizard Erledigt"
 
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
@@ -3726,6 +3734,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Modell Beschreibung"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Modulkonfiguration"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr "Die Konfiguration ist beendet."
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Willkommen bei der Modulkonfiguration!"
@@ -4025,6 +4041,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Drucken"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "OK"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "OK"
diff --git a/trytond/ir/locale/es_AR.po b/trytond/ir/locale/es_AR.po
index 3054aad..d727c5e 100644
--- a/trytond/ir/locale/es_AR.po
+++ b/trytond/ir/locale/es_AR.po
@@ -23,22 +23,31 @@ msgid "You are not allowed to delete this record."
 msgstr "No está autorizado a borrar este registro."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "El campo «%s» en «%s» tiene demasiados dígitos decimales."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
+"El número de decimales «%(digits)s» del campo «%(field)s» en «%(value)s» "
+"excede su límite."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "El valor del campo «%s» en «%s» no es válido según su dominio."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
+msgstr ""
+"El valor del campo «%(field)s» en «%(model)s» no es válido según su dominio."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
-"No se pudieron borrar «%s» registros porque se usan en el campo «%s» de "
-"«%s»."
+"No se pueden eliminar los registros porque son usados en el campo "
+"«%(field)s» de «%(model)s»."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "El valor del campo «%s» en «%s» no existe."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+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\"."
@@ -263,10 +272,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "XML de la vista «%s» no es correcto."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Clave %r no encontrada en campo de selección %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -300,32 +305,40 @@ msgid "Relation not found: %r in %s"
 msgstr "Relación no encontrada: %r en %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El campo «%s» en «%s» es requerido."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El campo «%(field)s» en «%(model)s» es requerido."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El campo «%s» en «%s» es requerido."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+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 de búsqueda del campo «%s»."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "El campo «%s» en «%s» no se encuentra en la selección."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
+msgstr ""
+"El valor «%(value)s» del campo «%(field)s» en «%(model)s» no está en la "
+"selección."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
 msgstr "El valor no se encuentra en la selección para el campo «%s»."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "El campo «%s» en «%s» es muy largo."
+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 «%(model)s» es demasiado "
+"largo."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "El valor de tiempo del campo «%s» en «%s» no es válido."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
+"El valor de tiempo «%(value)s» del campo «%(field)s» en «%(model)s» no es "
+"válido."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -427,10 +440,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Activo"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Auto-Recargar"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Valor del contexto"
@@ -473,7 +482,7 @@ msgstr "Límite"
 
 msgctxt "field:ir.action.act_window,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.act_window,order:"
 msgid "Order Value"
@@ -557,11 +566,11 @@ msgstr "ID"
 
 msgctxt "field:ir.action.act_window.domain,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.act_window.domain,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.act_window.domain,sequence:"
 msgid "Sequence"
@@ -705,7 +714,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.action.report,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.report,pyson_email:"
 msgid "PySON Email"
@@ -727,6 +736,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Contenido"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Nombre del contenido"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Nombre interno"
@@ -793,7 +806,7 @@ msgstr "Palabras clave"
 
 msgctxt "field:ir.action.url,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.url,rec_name:"
 msgid "Name"
@@ -861,7 +874,7 @@ msgstr "Modelo"
 
 msgctxt "field:ir.action.wizard,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.wizard,rec_name:"
 msgid "Name"
@@ -1013,7 +1026,7 @@ msgstr "idioma"
 
 msgctxt "field:ir.configuration,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.configuration,write_date:"
 msgid "Write Date"
@@ -1349,11 +1362,11 @@ msgstr "Modelo"
 
 msgctxt "field:ir.model.button,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.button,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.button,write_date:"
 msgid "Write Date"
@@ -1517,7 +1530,7 @@ msgstr "Acceso para escribir"
 
 msgctxt "field:ir.model.field.access,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.field.access,write_date:"
 msgid "Write Date"
@@ -1587,6 +1600,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Usuario modificación"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2021,7 +2038,7 @@ msgstr "Clave"
 
 msgctxt "field:ir.session,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.session,write_date:"
 msgid "Write Date"
@@ -2049,7 +2066,7 @@ msgstr "ID"
 
 msgctxt "field:ir.session.wizard,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.session.wizard,write_date:"
 msgid "Write Date"
@@ -2213,7 +2230,7 @@ msgstr "Modelo"
 
 msgctxt "field:ir.trigger,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.trigger,on_create:"
 msgid "On Create"
@@ -2233,7 +2250,7 @@ msgstr "Al Escribir"
 
 msgctxt "field:ir.trigger,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.trigger,write_date:"
 msgid "Write Date"
@@ -2257,7 +2274,7 @@ msgstr "ID"
 
 msgctxt "field:ir.trigger.log,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.trigger.log,record_id:"
 msgid "Record ID"
@@ -2297,7 +2314,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.ui.icon,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.icon,path:"
 msgid "SVG Path"
@@ -2305,7 +2322,7 @@ msgstr "Ruta al SVG"
 
 msgctxt "field:ir.ui.icon,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.icon,sequence:"
 msgid "Sequence"
@@ -2401,7 +2418,7 @@ msgstr "Menú"
 
 msgctxt "field:ir.ui.menu.favorite,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.menu.favorite,sequence:"
 msgid "Sequence"
@@ -2461,7 +2478,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.ui.view,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.view,priority:"
 msgid "Priority"
@@ -2509,11 +2526,11 @@ msgstr "Modelo"
 
 msgctxt "field:ir.ui.view_search,name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.view_search,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.view_search,user:"
 msgid "User"
@@ -2615,10 +2632,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Usuario modificación"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Añade un refresco automático a la vista"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Límite predeterminado para la vista en lista"
@@ -3059,6 +3072,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Módulo"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Asistente de configuración de módulo - Finalizado"
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Asistente de configuración inicial del módulo"
@@ -3712,6 +3729,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Descripción del modelo"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Configuración de módulo"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr "La configuración ha finalizado."
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "¡Bienvenido al asistente de configuración del módulo!"
@@ -4010,6 +4035,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Imprimir"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Aceptar"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Aceptar"
diff --git a/trytond/ir/locale/es_CO.po b/trytond/ir/locale/es_CO.po
index 299c7d8..01c59ac 100644
--- a/trytond/ir/locale/es_CO.po
+++ b/trytond/ir/locale/es_CO.po
@@ -7,7 +7,7 @@ msgid ""
 "You try to bypass an access rule!\n"
 "(Document type: %s)"
 msgstr ""
-"Está intentando evitar una regla de acceso\n"
+"Está tratando de saltarse una regla de acceso!\n"
 "(Tipo de documento: %s)"
 
 msgctxt "error:access_error:"
@@ -21,22 +21,34 @@ msgid "You are not allowed to delete this record."
 msgstr "No está autorizado a borrar este registro."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "El campo \"%s\" en \"%s\" tiene demasiados dígitos decimales."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
+"El numero de decimales \"%(digits)s\" del campo \"%(field)s\" en el valor "
+"\"%(value)s\" excede el limite."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "El valor del campo «%s» en «%s» no es válido según su dominio."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
+msgstr ""
+"El valor del campo \"%(field)s\" en \"%(model)s\" no es válido según su "
+"dominio."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
-"No se pudieron borrar «%s» registros porque se usan en el campo «%s» de "
-"«%s»."
+"No se pudieron borrar los registros porque son usados en el campo "
+"\"%(field)s\" de \"%(model)s\"."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "El valor del campo «%s» en «%s» no existe."
+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."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -50,15 +62,15 @@ msgstr ""
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid view \"%(view)s\" for action \"%(action)s\"."
-msgstr "Vista inválida \"%(view)s\" para la acción \"%(action)s\""
+msgstr "Vista \"%(view)s\" inválida para la acción \"%(action)s\"."
 
 msgctxt "error:ir.action.keyword:"
 msgid "Wrong wizard model in keyword action \"%s\"."
-msgstr "Errado modelo de asistente en la acción clave \"%s\"."
+msgstr "Errado modelo asistente para acción la \"%s\"."
 
 msgctxt "error:ir.action.report:"
 msgid "Invalid email definition on report \"%s\"."
-msgstr "Definición de correo electronico inválida en informe \"%s\""
+msgstr "El correo electronico definido en el informe \"%s\" es inválido."
 
 msgctxt "error:ir.action.report:"
 msgid "The internal name must be unique by module!"
@@ -92,7 +104,7 @@ msgstr "Los lenguajes por defecto no pueden ser borrados."
 
 msgctxt "error:ir.lang:"
 msgid "Invalid date format \"%(format)s\" on \"%(language)s\" language."
-msgstr "Formato de fecha inválido \"%(format)s\" en lenguaje \"%(languages)s\"."
+msgstr "Formato de fecha inválido \"%(format)s\" para el lenguaje \"%(languages)s\"."
 
 msgctxt "error:ir.lang:"
 msgid "Invalid grouping \"%(grouping)s\" on \"%(language)s\" language."
@@ -100,11 +112,11 @@ msgstr "Agrupamiento inválido \"%(grouping)s\" en lenguaje \"%(languages)s\"."
 
 msgctxt "error:ir.lang:"
 msgid "The default language must be translatable."
-msgstr "El lenguaje por defecto debe ser traducible."
+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 "decimal_point y «thousands_sep» deben ser distintos"
 
 msgctxt "error:ir.model.access:"
 msgid "You can not create this kind of document! (%s)"
@@ -124,15 +136,15 @@ 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!"
-msgstr "¡La terna (fs_id, module, model) debe ser única!"
+msgstr "La terna (fs_id, modulo, modelo) debe ser única!"
 
 msgctxt "error:ir.model.field.access:"
 msgid "You can not read the field! (%s.%s)"
-msgstr "¡No puede leer el campo! (%s.%s)"
+msgstr "No puede leer el campo! (%s.%s)"
 
 msgctxt "error:ir.model.field.access:"
 msgid "You can not write on the field! (%s.%s)"
@@ -140,8 +152,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 campo del Nombre del Modelo \"%s\" no es un identificador python válido."
+msgstr "El nombre del Modelo Campo \"%s\" no es un identificador python válido."
 
 msgctxt "error:ir.model.field:"
 msgid "The field name in model must be unique!"
@@ -161,7 +172,7 @@ msgstr "¡Las dependencias por módulo deben ser únicas!"
 
 msgctxt "error:ir.module.module:"
 msgid "Missing dependencies %s for module \"%s\""
-msgstr "Faltan las dependencias %s para el módulo «%s»"
+msgstr "Faltan las dependencias %s para el módulo \"%s\""
 
 msgctxt "error:ir.module.module:"
 msgid "The modules you are trying to uninstall depends on installed modules:"
@@ -179,7 +190,7 @@ msgstr ""
 
 msgctxt "error:ir.rule.group:"
 msgid "Global and Default are mutually exclusive!"
-msgstr "«Global» y «Predeterminado» son mutuamente excluyentes"
+msgstr "Global y Predeterminado son mutuamente excluyentes"
 
 msgctxt "error:ir.rule:"
 msgid "Invalid domain in rule \"%s\"."
@@ -196,11 +207,12 @@ msgstr "Sufijo inválido \"%(suffix)s\" en secuencia \"%(sequence)s\"."
 msgctxt "error:ir.sequence.strict:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
 msgstr ""
-"La ultima marca de tiempo no puede situarse en una secuencia futura \"%s\"."
+"La ultima marca de tiempo no puede estar en el futuro para la secuencia "
+"\"%s\"."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Missing sequence."
-msgstr "Falta la secuencia!"
+msgstr "Falta la secuencia."
 
 msgctxt "error:ir.sequence.strict:"
 msgid "Timestamp rounding should be greater than 0"
@@ -217,11 +229,12 @@ msgstr "Sufijo inválido \"%(suffix)s\" en secuencia \"%(sequence)s\"."
 msgctxt "error:ir.sequence:"
 msgid "Last Timestamp cannot be in the future on sequence \"%s\"."
 msgstr ""
-"La ultima marca de tiempo no puede situarse en una secuencia futura \"%s\"."
+"La ultima marca de tiempo no puede estar en el futuro para la secuencia "
+"\"%s\"."
 
 msgctxt "error:ir.sequence:"
 msgid "Missing sequence."
-msgstr "Falta la secuencia!"
+msgstr "Falta la secuencia."
 
 msgctxt "error:ir.sequence:"
 msgid "Timestamp rounding should be greater than 0"
@@ -241,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 ""
@@ -259,10 +272,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "XML inválido para la vista \"%s\"."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Clave %r no encontrada en campo de selección %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -294,32 +303,40 @@ msgid "Relation not found: %r in %s"
 msgstr "Relación no encontrada: %r en %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El campo «%s» en «%s» es necesario."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El campo \"%(field)s\" en el modelo \"%(model)s\" es obligatorio."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El campo «%s» en «%s» es necesario."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El campo \"%(field)s\" en el modelo \"%(model)s\" es obligatorio."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
-msgstr "¡Falta función de búsqueda en el campo \"%s\"."
+msgstr "Falta la función en el campo \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "El campo \"%s\" en \"%s\" no esta en la selección."
+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."
 
 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 esta en la selección para el campo \"%s\"."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "El campo «%s» en «%s» es muy largo."
+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."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "El valor del tiempo en el campo \"%s\" en \"%s\" no es válido."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
+"El valor del tiempo \"%(value)s\" del campo \"%(field)s\" en \"%(model)s\" "
+"no es válido."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -337,7 +354,9 @@ msgctxt "error:write_error:"
 msgid ""
 "You try to write on records that don't exist anymore.\n"
 "(Document type: %s)"
-msgstr "Esta intentando modificar registros que ya no existen."
+msgstr ""
+"Esta intentando modificar registros que ya no existen.\n"
+"(Tipo de documento : %s)"
 
 msgctxt "error:write_xml_record:"
 msgid "You are not allowed to modify this record."
@@ -377,7 +396,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Teclas"
 
 msgctxt "field:ir.action,name:"
 msgid "Name"
@@ -419,13 +438,9 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Activo"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Auto-Recargar"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
-msgstr "Valor del contexto"
+msgstr "Valor del Contexto"
 
 msgctxt "field:ir.action.act_window,create_date:"
 msgid "Create Date"
@@ -437,7 +452,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.action.act_window,domain:"
 msgid "Domain Value"
-msgstr "Valor del dominio"
+msgstr "Valor del Dominio"
 
 msgctxt "field:ir.action.act_window,domains:"
 msgid "Domains"
@@ -457,7 +472,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.act_window,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Teclas"
 
 msgctxt "field:ir.action.act_window,limit:"
 msgid "Limit"
@@ -465,7 +480,7 @@ msgstr "Límite"
 
 msgctxt "field:ir.action.act_window,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.act_window,order:"
 msgid "Order Value"
@@ -497,7 +512,7 @@ msgstr "Modelo"
 
 msgctxt "field:ir.action.act_window,search_value:"
 msgid "Search Criteria"
-msgstr "Criterio de búsqueda"
+msgstr "Criterio de Búsqueda"
 
 msgctxt "field:ir.action.act_window,type:"
 msgid "Type"
@@ -549,11 +564,11 @@ msgstr "ID"
 
 msgctxt "field:ir.action.act_window.domain,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.act_window.domain,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.act_window.domain,sequence:"
 msgid "Sequence"
@@ -625,7 +640,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.keyword,keyword:"
 msgid "Keyword"
-msgstr "Palabra clave"
+msgstr "Tecla"
 
 msgctxt "field:ir.action.keyword,model:"
 msgid "Model"
@@ -661,7 +676,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.action.report,direct_print:"
 msgid "Direct Print"
-msgstr "Impresión directa"
+msgstr "Impresión Directa"
 
 msgctxt "field:ir.action.report,email:"
 msgid "Email"
@@ -685,7 +700,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.report,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Teclas"
 
 msgctxt "field:ir.action.report,model:"
 msgid "Model"
@@ -697,7 +712,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.action.report,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.report,pyson_email:"
 msgid "PySON Email"
@@ -719,6 +734,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Contenido"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Nombre del Contenido"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Nombre Interno"
@@ -781,11 +800,11 @@ msgstr "ID"
 
 msgctxt "field:ir.action.url,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Teclas"
 
 msgctxt "field:ir.action.url,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.url,rec_name:"
 msgid "Name"
@@ -845,7 +864,7 @@ msgstr "ID"
 
 msgctxt "field:ir.action.wizard,keywords:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Teclas"
 
 msgctxt "field:ir.action.wizard,model:"
 msgid "Model"
@@ -853,7 +872,7 @@ msgstr "Modelo"
 
 msgctxt "field:ir.action.wizard,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.action.wizard,rec_name:"
 msgid "Name"
@@ -929,7 +948,7 @@ msgstr "Enlace"
 
 msgctxt "field:ir.attachment,name:"
 msgid "Name"
-msgstr "Nombre del adjunto"
+msgstr "Nombre"
 
 msgctxt "field:ir.attachment,rec_name:"
 msgid "Name"
@@ -1005,7 +1024,7 @@ msgstr "Lenguaje"
 
 msgctxt "field:ir.configuration,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.configuration,write_date:"
 msgid "Write Date"
@@ -1041,11 +1060,11 @@ msgstr "ID"
 
 msgctxt "field:ir.cron,interval_number:"
 msgid "Interval Number"
-msgstr "Número intervalo"
+msgstr "Número de Intervalo"
 
 msgctxt "field:ir.cron,interval_type:"
 msgid "Interval Unit"
-msgstr "Unidad intervalo"
+msgstr "Unidad Intervalo"
 
 msgctxt "field:ir.cron,model:"
 msgid "Model"
@@ -1057,7 +1076,7 @@ msgstr "Nombre"
 
 msgctxt "field:ir.cron,next_call:"
 msgid "Next Call"
-msgstr "Próxima Llamada"
+msgstr "Siguiente Llamada"
 
 msgctxt "field:ir.cron,number_calls:"
 msgid "Number of Calls"
@@ -1293,19 +1312,19 @@ msgstr "Modelo"
 
 msgctxt "field:ir.model.access,perm_create:"
 msgid "Create Access"
-msgstr "Acceso para crear"
+msgstr "Permiso para Crear"
 
 msgctxt "field:ir.model.access,perm_delete:"
 msgid "Delete Access"
-msgstr "Acceso para borrar"
+msgstr "Permiso para Borrar"
 
 msgctxt "field:ir.model.access,perm_read:"
 msgid "Read Access"
-msgstr "Acceso para leer"
+msgstr "Permiso para Leer"
 
 msgctxt "field:ir.model.access,perm_write:"
 msgid "Write Access"
-msgstr "Acceso para escribir"
+msgstr "Permiso para Modificar"
 
 msgctxt "field:ir.model.access,rec_name:"
 msgid "Name"
@@ -1341,11 +1360,11 @@ msgstr "Modelo"
 
 msgctxt "field:ir.model.button,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.button,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.button,write_date:"
 msgid "Write Date"
@@ -1365,11 +1384,11 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.model.data,date_init:"
 msgid "Init Date"
-msgstr "Fecha de inicio"
+msgstr "Fecha Inicial"
 
 msgctxt "field:ir.model.data,date_update:"
 msgid "Update Date"
-msgstr "Fecha de actualización"
+msgstr "Fecha de Actualización"
 
 msgctxt "field:ir.model.data,db_id:"
 msgid "Resource ID"
@@ -1377,7 +1396,7 @@ 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 Sistema de Archivos"
 
 msgctxt "field:ir.model.data,id:"
 msgid "ID"
@@ -1421,7 +1440,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.model.field,field_description:"
 msgid "Field Description"
-msgstr "Descripción del campo"
+msgstr "Descripción del Campo"
 
 msgctxt "field:ir.model.field,groups:"
 msgid "Groups"
@@ -1453,7 +1472,7 @@ msgstr "Nombre"
 
 msgctxt "field:ir.model.field,relation:"
 msgid "Model Relation"
-msgstr "Relación del modelo"
+msgstr "Modelo Relación"
 
 msgctxt "field:ir.model.field,ttype:"
 msgid "Field Type"
@@ -1493,23 +1512,23 @@ msgstr "ID"
 
 msgctxt "field:ir.model.field.access,perm_create:"
 msgid "Create Access"
-msgstr "Acceso para Crear"
+msgstr "Permiso para Crear"
 
 msgctxt "field:ir.model.field.access,perm_delete:"
 msgid "Delete Access"
-msgstr "Acceso para Borrar"
+msgstr "Permiso para Borrar"
 
 msgctxt "field:ir.model.field.access,perm_read:"
 msgid "Read Access"
-msgstr "Acceso para Leer"
+msgstr "Permiso para Leer"
 
 msgctxt "field:ir.model.field.access,perm_write:"
 msgid "Write Access"
-msgstr "Acceso para Modificar"
+msgstr "Permiso para Modificar"
 
 msgctxt "field:ir.model.field.access,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.field.access,write_date:"
 msgid "Write Date"
@@ -1579,6 +1598,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Modificado por Usuario"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -1777,19 +1800,19 @@ msgstr "Nombre"
 
 msgctxt "field:ir.rule.group,perm_create:"
 msgid "Create Access"
-msgstr "Acceso para crear"
+msgstr "Permiso para Crear"
 
 msgctxt "field:ir.rule.group,perm_delete:"
 msgid "Delete Access"
-msgstr "Acceso para borrar"
+msgstr "Permiso para Borrar"
 
 msgctxt "field:ir.rule.group,perm_read:"
 msgid "Read Access"
-msgstr "Acceso para leer"
+msgstr "Permiso para Leer"
 
 msgctxt "field:ir.rule.group,perm_write:"
 msgid "Write Access"
-msgstr "Acceso para escribir"
+msgstr "Permiso para Modificar"
 
 msgctxt "field:ir.rule.group,rec_name:"
 msgid "Name"
@@ -1841,15 +1864,15 @@ msgstr "Nombre de Secuencia"
 
 msgctxt "field:ir.sequence,number_increment:"
 msgid "Increment Number"
-msgstr "Cantidad a incrementar"
+msgstr "Número de Incremento"
 
 msgctxt "field:ir.sequence,number_next:"
 msgid "Next Number"
-msgstr "Siguiente número"
+msgstr "Número Siguiente"
 
 msgctxt "field:ir.sequence,number_next_internal:"
 msgid "Next Number"
-msgstr "Siguiente número"
+msgstr "Número Siguiente"
 
 msgctxt "field:ir.sequence,padding:"
 msgid "Number padding"
@@ -1917,15 +1940,15 @@ msgstr "Nombre de Secuencia"
 
 msgctxt "field:ir.sequence.strict,number_increment:"
 msgid "Increment Number"
-msgstr "Cantidad a incrementar"
+msgstr "Número de Incremento"
 
 msgctxt "field:ir.sequence.strict,number_next:"
 msgid "Next Number"
-msgstr "Número siguiente"
+msgstr "Número Siguiente"
 
 msgctxt "field:ir.sequence.strict,number_next_internal:"
 msgid "Next Number"
-msgstr "Siguiente número"
+msgstr "Número Siguiente"
 
 msgctxt "field:ir.sequence.strict,padding:"
 msgid "Number padding"
@@ -1965,7 +1988,7 @@ msgstr "Modificado por Usuario"
 
 msgctxt "field:ir.sequence.type,code:"
 msgid "Sequence Code"
-msgstr "Código de la Secuencia"
+msgstr "Código de Secuencia"
 
 msgctxt "field:ir.sequence.type,create_date:"
 msgid "Create Date"
@@ -2013,7 +2036,7 @@ msgstr "Clave"
 
 msgctxt "field:ir.session,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.session,write_date:"
 msgid "Write Date"
@@ -2041,7 +2064,7 @@ msgstr "ID"
 
 msgctxt "field:ir.session.wizard,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.session.wizard,write_date:"
 msgid "Write Date"
@@ -2061,7 +2084,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.translation,fuzzy:"
 msgid "Fuzzy"
-msgstr "Necesita revisión"
+msgstr "Vago"
 
 msgctxt "field:ir.translation,id:"
 msgid "ID"
@@ -2165,11 +2188,11 @@ msgstr "Idioma"
 
 msgctxt "field:ir.trigger,action_function:"
 msgid "Action Function"
-msgstr "Función de Acción"
+msgstr "Acción Función"
 
 msgctxt "field:ir.trigger,action_model:"
 msgid "Action Model"
-msgstr "Modelo de Acción"
+msgstr "Acción en Modelo"
 
 msgctxt "field:ir.trigger,active:"
 msgid "Active"
@@ -2205,11 +2228,11 @@ msgstr "Modelo"
 
 msgctxt "field:ir.trigger,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.trigger,on_create:"
 msgid "On Create"
-msgstr "Al ser creado"
+msgstr "Al Crearse"
 
 msgctxt "field:ir.trigger,on_delete:"
 msgid "On Delete"
@@ -2221,11 +2244,11 @@ msgstr "Al Tiempo"
 
 msgctxt "field:ir.trigger,on_write:"
 msgid "On Write"
-msgstr "Al Escribir"
+msgstr "Al Modificar"
 
 msgctxt "field:ir.trigger,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.trigger,write_date:"
 msgid "Write Date"
@@ -2249,7 +2272,7 @@ msgstr "ID"
 
 msgctxt "field:ir.trigger.log,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.trigger.log,record_id:"
 msgid "Record ID"
@@ -2289,7 +2312,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.ui.icon,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.icon,path:"
 msgid "SVG Path"
@@ -2297,7 +2320,7 @@ msgstr "Ruta al SVG"
 
 msgctxt "field:ir.ui.icon,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.icon,sequence:"
 msgid "Sequence"
@@ -2441,7 +2464,7 @@ msgstr "ID"
 
 msgctxt "field:ir.ui.view,inherit:"
 msgid "Inherited View"
-msgstr "Vista heredada"
+msgstr "Vista Heredada"
 
 msgctxt "field:ir.ui.view,model:"
 msgid "Model"
@@ -2453,7 +2476,7 @@ msgstr "Módulo"
 
 msgctxt "field:ir.ui.view,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.view,priority:"
 msgid "Priority"
@@ -2465,7 +2488,7 @@ msgstr "Nombre"
 
 msgctxt "field:ir.ui.view,type:"
 msgid "View Type"
-msgstr "Tipo de vista"
+msgstr "Tipo de Vista"
 
 msgctxt "field:ir.ui.view,write_date:"
 msgid "Write Date"
@@ -2501,11 +2524,11 @@ msgstr "Modelo"
 
 msgctxt "field:ir.ui.view_search,name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.view_search,rec_name:"
 msgid "Name"
-msgstr "Nombre de Campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.ui.view_search,user:"
 msgid "User"
@@ -2607,17 +2630,13 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Modificado por Usuario"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Añade un refresco automático a la vista"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Límite predeterminado para la vista en lista"
 
 msgctxt "help:ir.action.act_window,search_value:"
 msgid "Default search criteria for the list view"
-msgstr "Criterio de búsqueda predeterminado para la vista en lista"
+msgstr "Criterio de búsqueda por defecto para la vista en lista"
 
 msgctxt "help:ir.action.act_window,window_name:"
 msgid "Use the action name as window name"
@@ -2637,11 +2656,11 @@ msgid ""
 "compatible format"
 msgstr ""
 "Dejar vacío para el mismo como plantilla, consulte la documentación de "
-"unoconv por formato compatible"
+"unoconv sobre formatos compatibles."
 
 msgctxt "help:ir.action.report,style:"
 msgid "Define the style to apply on the report."
-msgstr "Definir el estilo a aplicar al informe."
+msgstr "Define el estilo a aplicar en el informe."
 
 msgctxt "help:ir.action.wizard,window:"
 msgid "Run wizard in a new window"
@@ -2688,8 +2707,8 @@ msgid ""
 "Entering a Python Regular Expression will exclude matching models from the "
 "graph."
 msgstr ""
-"Introducir una Expresión Regular de Python excluirá modelos equivalentes de "
-"la gráfica."
+"Ingresando una Expresión Regular de Python se excluirán modelos que "
+"concuerden con el gráfico."
 
 msgctxt "help:ir.rule,domain:"
 msgid "Domain is evaluated with \"user\" as the current user"
@@ -2697,7 +2716,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 esta regla para todos los usuarios de forma predeterminada"
+msgstr "Añadir la regla a todos los usuarios por defecto"
 
 msgctxt "help:ir.rule.group,global_p:"
 msgid ""
@@ -2745,7 +2764,7 @@ msgstr "Acción"
 
 msgctxt "model:ir.action,name:act_action_act_window_form"
 msgid "Window Actions"
-msgstr "Acciones de la ventana"
+msgstr "Ventana de Acciones"
 
 msgctxt "model:ir.action,name:act_action_form"
 msgid "Actions"
@@ -2769,7 +2788,7 @@ msgstr "Adjuntos"
 
 msgctxt "model:ir.action,name:act_config_wizard_item_form"
 msgid "Config Wizard Items"
-msgstr "Configurar Elementos del Asistente"
+msgstr "Asistentes de Configuración"
 
 msgctxt "model:ir.action,name:act_cron_form"
 msgid "Scheduled Actions"
@@ -2797,7 +2816,7 @@ msgstr "Menú"
 
 msgctxt "model:ir.action,name:act_model_access_form"
 msgid "Models Access"
-msgstr "Acceso a los modelos"
+msgstr "Permisos de Modelos"
 
 msgctxt "model:ir.action,name:act_model_button_form"
 msgid "Buttons"
@@ -2805,7 +2824,7 @@ msgstr "Botones"
 
 msgctxt "model:ir.action,name:act_model_field_access_form"
 msgid "Fields Access"
-msgstr "Campos de Acceso"
+msgstr "Permisos de Campos"
 
 msgctxt "model:ir.action,name:act_model_fields_form"
 msgid "Fields"
@@ -2817,7 +2836,7 @@ msgstr "Modelos"
 
 msgctxt "model:ir.action,name:act_module_config"
 msgid "Configure Modules"
-msgstr "Configure Modulos"
+msgstr "Configuración de Modulos"
 
 msgctxt "model:ir.action,name:act_module_config_wizard"
 msgid "Module Configuration"
@@ -2841,7 +2860,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"
@@ -2869,7 +2888,7 @@ msgstr "Traducciones"
 
 msgctxt "model:ir.action,name:act_translation_set"
 msgid "Set Report Translations"
-msgstr "Establecer traducciones de los informes"
+msgstr "Establecer Traducciones de los Informes"
 
 msgctxt "model:ir.action,name:act_translation_update"
 msgid "Synchronize Translations"
@@ -2897,7 +2916,7 @@ msgstr "Estado de Arbol"
 
 msgctxt "model:ir.action,name:act_view_tree_width_form"
 msgid "View Tree Width"
-msgstr "Ancho de la vista en árbol"
+msgstr "Ancho de Columna"
 
 msgctxt "model:ir.action,name:print_model_graph"
 msgid "Graph"
@@ -2909,23 +2928,23 @@ msgstr "Gráfico"
 
 msgctxt "model:ir.action.act_window,name:"
 msgid "Action act window"
-msgstr "Ventana de acciones"
+msgstr "Acción ventana acción"
 
 msgctxt "model:ir.action.act_window.domain,name:"
 msgid "Action act window domain"
-msgstr "Accion dominio de ventana de acción"
+msgstr "Accion en dominio de ventana de acción"
 
 msgctxt "model:ir.action.act_window.view,name:"
 msgid "Action act window view"
-msgstr "Vista de ventana de acción"
+msgstr "Acción en vista de ventana de acción"
 
 msgctxt "model:ir.action.keyword,name:"
 msgid "Action keyword"
-msgstr "Palabra clave de acción"
+msgstr "Teclas de acción "
 
 msgctxt "model:ir.action.report,name:"
 msgid "Action report"
-msgstr "Informe de acción"
+msgstr "Acción en Reporte"
 
 msgctxt "model:ir.action.url,name:"
 msgid "Action URL"
@@ -2933,7 +2952,7 @@ msgstr "URL de la acción"
 
 msgctxt "model:ir.action.wizard,name:"
 msgid "Action wizard"
-msgstr "Asistente de la acción"
+msgstr "Acción del Asistente"
 
 msgctxt "model:ir.attachment,name:"
 msgid "Attachment"
@@ -3025,7 +3044,7 @@ msgstr "Modelo"
 
 msgctxt "model:ir.model.access,name:"
 msgid "Model access"
-msgstr "Modelo de accesos"
+msgstr "Permisos de modelo"
 
 msgctxt "model:ir.model.button,name:"
 msgid "Model Button"
@@ -3041,7 +3060,7 @@ msgstr "Modelo de campo"
 
 msgctxt "model:ir.model.field.access,name:"
 msgid "Model Field Access"
-msgstr "Modelo de Acceso a Campo"
+msgstr "Permiso de Modelo Campo"
 
 msgctxt "model:ir.model.print_model_graph.start,name:"
 msgid "Print Model Graph"
@@ -3051,6 +3070,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Módulo"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Configuración del Módulo Terminada"
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Primer Asistente de Configuración de Módulo"
@@ -3062,7 +3085,7 @@ msgstr ""
 
 msgctxt "model:ir.module.module.config_wizard.other,name:"
 msgid "Module Config Wizard Other"
-msgstr "Otro Asistente de Configuración de Módulo"
+msgstr "Siguiente Asistente de Configuración de Módulo"
 
 msgctxt "model:ir.module.module.dependency,name:"
 msgid "Module dependency"
@@ -3070,11 +3093,11 @@ msgstr "Dependencias del módulo"
 
 msgctxt "model:ir.module.module.install_upgrade.done,name:"
 msgid "Module Install Upgrade Done"
-msgstr "Instalación de Módulo de Mejora Terminada"
+msgstr "Actualización del Módulo Terminada"
 
 msgctxt "model:ir.module.module.install_upgrade.start,name:"
 msgid "Module Install Upgrade Start"
-msgstr "Iniciar la instalación de actualizaciones de módulos"
+msgstr "Inicio de Asistente de Instalación del Módulo"
 
 msgctxt "model:ir.property,name:"
 msgid "Property"
@@ -3166,7 +3189,7 @@ msgstr "Acciones"
 
 msgctxt "model:ir.ui.menu,name:menu_action_act_window"
 msgid "Window Actions"
-msgstr "Acciones de Ventana"
+msgstr "Ventana de Acciones"
 
 msgctxt "model:ir.ui.menu,name:menu_action_report_form"
 msgid "Reports"
@@ -3190,11 +3213,11 @@ msgstr "Adjuntos"
 
 msgctxt "model:ir.ui.menu,name:menu_config_wizard_item_form"
 msgid "Config Wizard Items"
-msgstr "Configurar Elementos del Asistente"
+msgstr "Asistentes de Configuración"
 
 msgctxt "model:ir.ui.menu,name:menu_cron_form"
 msgid "Scheduled Actions"
-msgstr "Acciones programadas"
+msgstr "Acciones Programadas"
 
 msgctxt "model:ir.ui.menu,name:menu_export_form"
 msgid "Exports"
@@ -3222,7 +3245,7 @@ msgstr "Menú"
 
 msgctxt "model:ir.ui.menu,name:menu_model_access_form"
 msgid "Models Access"
-msgstr "Acceso a los modelos"
+msgstr "Permisos de Modelos"
 
 msgctxt "model:ir.ui.menu,name:menu_model_button_form"
 msgid "Buttons"
@@ -3230,7 +3253,7 @@ msgstr "Botones"
 
 msgctxt "model:ir.ui.menu,name:menu_model_field_access_form"
 msgid "Fields Access"
-msgstr "Campos de Acceso"
+msgstr "Permisos de Campos"
 
 msgctxt "model:ir.ui.menu,name:menu_model_form"
 msgid "Models"
@@ -3262,7 +3285,7 @@ msgstr "Propiedades por Defecto"
 
 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"
@@ -3322,7 +3345,7 @@ msgstr "Estado de Arbol"
 
 msgctxt "model:ir.ui.menu,name:menu_view_tree_width"
 msgid "View Tree Width"
-msgstr "Ancho de Vista Árbol"
+msgstr "Ancho de Columna"
 
 msgctxt "model:ir.ui.menu,name:model_model_fields_form"
 msgid "Fields"
@@ -3350,11 +3373,11 @@ msgstr "Estado de Vista de Arbol"
 
 msgctxt "model:ir.ui.view_tree_width,name:"
 msgid "View Tree Width"
-msgstr "Ancho de la vista en árbol"
+msgstr "Ancho de Columna"
 
 msgctxt "selection:ir.action.keyword,keyword:"
 msgid "Action form"
-msgstr "Formulario de acción"
+msgstr "Acción formulario"
 
 msgctxt "selection:ir.action.keyword,keyword:"
 msgid "Action tree"
@@ -3410,7 +3433,7 @@ msgstr "De izquierda a derecha"
 
 msgctxt "selection:ir.lang,direction:"
 msgid "Right-to-left"
-msgstr "De derecha a izquierda"
+msgstr "Derecha-a-Izquierda"
 
 msgctxt "selection:ir.module.module,state:"
 msgid "Installed"
@@ -3586,19 +3609,19 @@ msgstr "General"
 
 msgctxt "view:ir.action.act_window:"
 msgid "Open Window"
-msgstr "Abrir ventana"
+msgstr "Abrir Ventana"
 
 msgctxt "view:ir.action.act_window:"
 msgid "Open a Window"
-msgstr "Abrir una ventana"
+msgstr "Abrir una Ventana"
 
 msgctxt "view:ir.action.keyword:"
 msgid "Keyword"
-msgstr "Palabra clave"
+msgstr "Tecla"
 
 msgctxt "view:ir.action.keyword:"
 msgid "Keywords"
-msgstr "Palabras clave"
+msgstr "Teclas"
 
 msgctxt "view:ir.action.report:"
 msgid "General"
@@ -3646,11 +3669,11 @@ msgstr "Acción a disparar"
 
 msgctxt "view:ir.cron:"
 msgid "Scheduled Action"
-msgstr "Acción programada"
+msgstr "Acción Programada"
 
 msgctxt "view:ir.cron:"
 msgid "Scheduled Actions"
-msgstr "Acciones programadas"
+msgstr "Acciones Programadas"
 
 msgctxt "view:ir.export:"
 msgid "Exports"
@@ -3686,7 +3709,7 @@ msgstr "Botones"
 
 msgctxt "view:ir.model.field.access:"
 msgid "Field Access"
-msgstr "Acceso al Campo"
+msgstr "Permiso de Campo"
 
 msgctxt "view:ir.model.field:"
 msgid "Field"
@@ -3704,6 +3727,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Descripción del modelo"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Configuración del módulo"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr "La configuracion ha terminado."
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Bienvenido al asistente de configuración del módulo!"
@@ -3718,7 +3749,7 @@ msgstr ""
 
 msgctxt "view:ir.module.module.config_wizard.item:"
 msgid "Config Wizard Items"
-msgstr "Configurar Elementos del Asistente"
+msgstr "Asistentes de Configuración"
 
 msgctxt "view:ir.module.module.config_wizard.other:"
 msgid "Configuration Wizard Next Step!"
@@ -3734,7 +3765,7 @@ 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 Finalizada"
 
 msgctxt "view:ir.module.module.install_upgrade.done:"
 msgid "The modules have been upgraded / installed !"
@@ -3746,7 +3777,7 @@ msgstr "Esta operación puede demorar varios minutos."
 
 msgctxt "view:ir.module.module.install_upgrade.start:"
 msgid "System Upgrade"
-msgstr "Mejora del Sistema"
+msgstr "Actualización del Sistema"
 
 msgctxt "view:ir.module.module.install_upgrade.start:"
 msgid "Your system will be upgraded."
@@ -3801,7 +3832,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"
@@ -3837,7 +3868,7 @@ msgstr "Mes:"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Sequences Strict"
-msgstr "Secuencias estrictas"
+msgstr "Secuencias Estrictas"
 
 msgctxt "view:ir.sequence.strict:"
 msgid "Year:"
@@ -3845,7 +3876,7 @@ msgstr "Año:"
 
 msgctxt "view:ir.sequence.type:"
 msgid "Sequence Type"
-msgstr "Tipo de secuencia"
+msgstr "Tipo de Secuencia"
 
 msgctxt "view:ir.sequence:"
 msgid "${day}"
@@ -3893,7 +3924,7 @@ msgstr "Limpiar Traducciones"
 
 msgctxt "view:ir.translation.clean.start:"
 msgid "Clean Translations?"
-msgstr "¿Limpiar Traducciones?"
+msgstr "Limpiar Traducciones?"
 
 msgctxt "view:ir.translation.clean.succeed:"
 msgid "Clean Translations"
@@ -3979,22 +4010,21 @@ msgctxt "view:ir.ui.view_search:"
 msgid "View Searches"
 msgstr "Vista Busquedas"
 
-#, fuzzy
 msgctxt "view:ir.ui.view_tree_state:"
 msgid "View Tree State"
 msgstr "Estado de Vista de Arbol"
 
 msgctxt "view:ir.ui.view_tree_state:"
 msgid "Views Tree State"
-msgstr ""
+msgstr "Estado de Vista de Arbol"
 
 msgctxt "view:ir.ui.view_tree_width:"
 msgid "View Tree Width"
-msgstr "Ancho de la vista en árbol"
+msgstr "Ancho de Columna"
 
 msgctxt "view:ir.ui.view_tree_width:"
 msgid "Views Tree Width"
-msgstr "Ancho de la vista en árbol"
+msgstr "Anchos de Columna"
 
 msgctxt "wizard_button:ir.model.print_model_graph,start,end:"
 msgid "Cancel"
@@ -4004,6 +4034,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Imprimir"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Aceptar"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Aceptar"
diff --git a/trytond/ir/locale/es_ES.po b/trytond/ir/locale/es_ES.po
index 3592f99..bdb021d 100644
--- a/trytond/ir/locale/es_ES.po
+++ b/trytond/ir/locale/es_ES.po
@@ -23,20 +23,32 @@ msgid "You are not allowed to delete this record."
 msgstr "No está autorizado para eliminar este registro."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "El campo \"%s\" en \"%s\" contiene demasiados decimales."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
+"El número de dígitos \"%(digits)s\" del campo \"%(field)s\" de \"%(value)s\""
+" es superior a su límite."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "El valor del campo \"%s\" de \"%s\" no es correcto según su dominio."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
+msgstr ""
+"El valor del campo \"%(field)s\" de \"%(model)s\" no es correcto según su "
+"dominio."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
-msgstr "No puede eliminar \"%s\" registros porque se usan en el campo \"%s\" de \"%s\"."
+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\". "
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "El valor del campo \"%s\" de \"%s\" no existe."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr "El valor \"%(value)s\" del campo \"%(field)s\" de \"%(model)s\" no existe."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -141,7 +153,7 @@ msgstr "No puede modificar 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 \"%s\" no es un identificador de python correcto."
+msgstr "El nombre del modelo \"%s\" no es un identificador de Python correcto."
 
 msgctxt "error:ir.model.field:"
 msgid "The field name in model must be unique!"
@@ -149,7 +161,7 @@ msgstr "El nombre del campo en el modelo debe ser único."
 
 msgctxt "error:ir.model:"
 msgid "Module name \"%s\" is not a valid python identifier."
-msgstr "El nombre del model \"%s\" no es un identificador de python correcto."
+msgstr "El nombre del model \"%s\" no es un identificador de Python correcto."
 
 msgctxt "error:ir.model:"
 msgid "The model must be unique!"
@@ -166,7 +178,7 @@ msgstr "Faltan las dependencias %s para el módulo \"%s\"."
 msgctxt "error:ir.module.module:"
 msgid "The modules you are trying to uninstall depends on installed modules:"
 msgstr ""
-"Los módulos que esta intentando desintalar depende de otros módulos "
+"Los módulos que está intentando desinstalar depende de otros módulos "
 "instalados:"
 
 msgctxt "error:ir.module.module:"
@@ -246,8 +258,8 @@ msgid ""
 "Condition \"%(condition)s\" is not a valid python expression on trigger "
 "\"%(trigger)s\"."
 msgstr ""
-"La condición \"%(condition)s\" no es una expresión python válida en el "
-"disparador \"%(trigger)s\"."
+"La condición \"%(condition)s\" no es una expresión Python válida en el "
+"disparador \"P%(trigger)s\"."
 
 msgctxt "error:ir.ui.menu:"
 msgid "\"%s\" is not a valid menu name because it is not allowed to contain \" / \"."
@@ -257,10 +269,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "XML de la vista \"%s\" no es correcto."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "No se ha encontrado la clave %r en el campo selección %r."
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -282,7 +290,7 @@ msgid ""
 "Recursion error: Record \"%(rec_name)s\" with parent \"%(parent_rec_name)s\""
 " was configured as ancestor of itself."
 msgstr ""
-"Error de recursión: El registro \"%(rec_name)s\" con el padre "
+"Error de recursividad: El registro \"%(rec_name)s\" con el padre "
 "\"%(parent_rec_name)s\" se configuró como padre de si mismo."
 
 msgctxt "error:reference_syntax_error:"
@@ -294,32 +302,40 @@ msgid "Relation not found: %r in %s"
 msgstr "No se ha encontrado la relación: %r en %s."
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El campo \"%s\" en \"%s\" es requerido."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El campo \"%(field)s\" de \"%(model)s\" es obligatorio."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "El campo \"%s\" en \"%s\" es requerido."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "El campo \"%(field)s\" de \"%(model)s\" es obligatorio."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "Falta la función de búsqueda del campo \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "El campo \"%s\" en \"%s\" no se encuentra en la selección."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
+msgstr ""
+"El valor \"%(value)s\" del campo \"%(field)s\" de \"%(model)s\" no está en "
+"la selección."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
 msgstr "El valor no se encuentra en la selección en el campo \"%s\"."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "El campo \"%s\" en \"%s\" es demasiado largo."
+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\" del \"%(model)s\" es "
+"demasiado grande."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "El valor tiempo del campo \"%s\" en \"%s\" no es correcto."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
+"El valor temporal \"%(value)s\" del campo \"%(field)s\" de \"%(model)s) no "
+"es valido."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -421,10 +437,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Activo"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Auto-Refresco"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Valor del contexto"
@@ -721,6 +733,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Contenido"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Nombre del contenido"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Nombre interno"
@@ -1047,7 +1063,7 @@ msgstr "Número de intervalos"
 
 msgctxt "field:ir.cron,interval_type:"
 msgid "Interval Unit"
-msgstr "Unidad de intérvalo"
+msgstr "Unidad de intervalo"
 
 msgctxt "field:ir.cron,model:"
 msgid "Model"
@@ -1581,6 +1597,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Usuario modificación"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2609,10 +2629,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Usuario modificación"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Añade un auto-refresco a la vista."
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Límite por defecto en las vistas de lista."
@@ -2739,7 +2755,7 @@ msgstr ""
 
 msgctxt "help:ir.ui.view_search,domain:"
 msgid "The PYSON domain"
-msgstr "Dominio PYSON"
+msgstr "Dominio PYSON."
 
 msgctxt "model:ir.action,name:"
 msgid "Action"
@@ -2851,7 +2867,7 @@ msgstr "Secuencias"
 
 msgctxt "model:ir.action,name:act_sequence_strict_form"
 msgid "Sequences Strict"
-msgstr "Secuencia estricta"
+msgstr "Secuencias estrictas"
 
 msgctxt "model:ir.action,name:act_sequence_type_form"
 msgid "Sequence Types"
@@ -3053,6 +3069,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Módulo"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Asitente de configuración de los módulos realizado"
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Inicio asistente de configuración del módulo"
@@ -3275,7 +3295,7 @@ msgstr "Secuencias"
 
 msgctxt "model:ir.ui.menu,name:menu_sequence_strict_form"
 msgid "Sequences Strict"
-msgstr "Secuencia estricta"
+msgstr "Secuencias estrictas"
 
 msgctxt "model:ir.ui.menu,name:menu_sequences"
 msgid "Sequences"
@@ -3307,7 +3327,7 @@ msgstr "Disparadores"
 
 msgctxt "model:ir.ui.menu,name:menu_ui"
 msgid "User Interface"
-msgstr "Interfície del usuario"
+msgstr "Interfaz de usuario"
 
 msgctxt "model:ir.ui.menu,name:menu_view"
 msgid "Views"
@@ -3439,7 +3459,7 @@ msgstr "Realizado"
 
 msgctxt "selection:ir.module.module.config_wizard.item,state:"
 msgid "Open"
-msgstr "Para abrir"
+msgstr "Pendiente"
 
 msgctxt "selection:ir.module.module.dependency,state:"
 msgid "Installed"
@@ -3705,6 +3725,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Descripción modelo"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Configuración de los módulos"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr "La configuración se ha realizado correctamente."
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Bienvenido a la configuración del módulo."
@@ -3722,7 +3750,7 @@ msgstr "Asistente de configuración"
 
 msgctxt "view:ir.module.module.config_wizard.other:"
 msgid "Configuration Wizard Next Step!"
-msgstr "Siguiente paso asistente de configuracion."
+msgstr "Siguiente paso asistente de configuración."
 
 msgctxt "view:ir.module.module.dependency:"
 msgid "Dependencies"
@@ -3889,7 +3917,7 @@ msgstr "¿Sincronizar las traducciones?"
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Succeed!"
-msgstr "Las traducciones se han realitzado correctament."
+msgstr "Las traducciones se han realizado correctamente."
 
 msgctxt "view:ir.translation.set.succeed:"
 msgid "Set Translations"
@@ -3971,6 +3999,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Imprimir"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Aceptar"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Aceptar"
@@ -4029,7 +4061,7 @@ msgstr "Cancelar"
 
 msgctxt "wizard_button:ir.translation.set,start,set_:"
 msgid "Set"
-msgstr "Grupo"
+msgstr "Definir"
 
 msgctxt "wizard_button:ir.translation.set,succeed,end:"
 msgid "Ok"
diff --git a/trytond/ir/locale/fr_FR.po b/trytond/ir/locale/fr_FR.po
index 989c2ba..8cc677d 100644
--- a/trytond/ir/locale/fr_FR.po
+++ b/trytond/ir/locale/fr_FR.po
@@ -20,25 +20,35 @@ msgstr ""
 
 msgctxt "error:delete_xml_record:"
 msgid "You are not allowed to delete this record."
-msgstr "Vous n'êtes pas authorisé à supprimer cet enregistrement"
+msgstr "Vous n'êtes pas autorisé à supprimer cet enregistrement"
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "Le champ \"%s\" de \"%s\" à trop de décimales."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
+"Le nombre de décimales \"%(digits)s\" du champ \"%(field)s\" dépasse sa "
+"limite."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "La valeur du champ \"%s\" de \"%s\" n'est pas valide suivant son domaine."
+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 "
+"son domaine."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
-"Impossible de supprimer les enregistrements \"%s\" car ils sont utilisé dans"
-" le champ \"%s\" de \"%s\"."
+"Impossible de supprimer les enregistrements car ils sont utilisés dans le "
+"champ \"%(field)s\" de \"%(model)s\"."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "La valeur du champ \"%s\" de \"%s\" est absente."
+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."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -266,10 +276,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "XML invalide sur la vue \"%s\"."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Clé %r non trouvée dans la sélection %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -303,32 +309,36 @@ msgid "Relation not found: %r in %s"
 msgstr "Relation non trouvée : %r dans %r"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Le champ \"%s\" de \"%s\" est requis."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "Le champ \"%(field)s\" sur \"%(model)s\" est requis."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Le champ \"%s\" de \"%s\" est requis."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+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\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "Le champ \"%s\" sur \"%s\" n'est pas dans la sélection."
+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 "
+"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\"."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "Le champ \"%s\" de \"%s\" est trop long"
+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."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "La valeur de type temps du champ \"%s\" de \"%s\" n'est pas valide"
+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."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -430,10 +440,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Actif"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Auto-rafraîchissement"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Valeur du contexte"
@@ -730,6 +736,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Contenu"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Nom du contenu"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Nom interne"
@@ -1590,6 +1600,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Mis à jour par"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2618,10 +2632,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Mis à jour par"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Ajoute un auto-rafraîchissement sur la vue"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Limite par défaut pour la vue liste"
@@ -3061,6 +3071,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Module"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Assistant de configuration de module - Fin"
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Assistant de configuration de module - Première"
@@ -3713,6 +3727,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Description de modèle"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Configuration de module"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+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 !"
@@ -4012,6 +4034,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Imprimer"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Ok"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Ok"
diff --git a/trytond/ir/locale/nl_NL.po b/trytond/ir/locale/nl_NL.po
index c191cfe..daa70aa 100644
--- a/trytond/ir/locale/nl_NL.po
+++ b/trytond/ir/locale/nl_NL.po
@@ -21,22 +21,26 @@ msgid "You are not allowed to delete this record."
 msgstr "Het is u niet toegestaan dit item te verwijderen."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "Het veld \"%s\" in \"%s\" heeft te veel decimalen."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "De waarde van het veld \"%s\" in \"%s\" is niet geldig binnen zijn domein."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
+msgstr ""
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
-"Kon items \"%s\" niet verwijderen omdat ze in gebruik waren in veld \"%s\" "
-"van \"%s\"."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "De waarde van veld \"%s\" in \"%s\" bestaat niet."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr ""
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -244,10 +248,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr ""
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Sleutel %r niet gevonden in selectie veld %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -277,19 +277,21 @@ msgid "Relation not found: %r in %s"
 msgstr "Relatie niet gevonden: %r in %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Het veld \"%s\" in \"%s\" is vereist."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr ""
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Het veld \"%s\" in \"%s\" is vereist."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr ""
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "Zoekfunctie ontbreekt voor veld \" %s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
 msgstr ""
 
 msgctxt "error:selection_value_notfound:"
@@ -297,11 +299,11 @@ msgid "Value not in the selection for field \"%s\"."
 msgstr ""
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "Veld \"%s\" op \"%s\" is te lang."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
+msgstr ""
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
 msgstr ""
 
 msgctxt "error:too_many_relations_found:"
@@ -404,10 +406,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Actief"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Automatisch verversen"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Samenhang waarde"
@@ -723,6 +721,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Inhoud"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr ""
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Interne naam"
@@ -1614,6 +1616,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr ""
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr ""
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr ""
@@ -2671,10 +2677,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr ""
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Voegt automatisch verversen toe"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Standaard begrenzing voor dit aanzicht"
@@ -3108,6 +3110,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Module"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr ""
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Module EHBC"
@@ -3773,6 +3779,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Model omschrijving"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr ""
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr ""
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Welkom bij de module configuratie assistent!"
@@ -4046,6 +4060,11 @@ msgid "Print"
 msgstr ""
 
 #, fuzzy
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Oké"
+
+#, fuzzy
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Oké"
diff --git a/trytond/ir/locale/ru_RU.po b/trytond/ir/locale/ru_RU.po
index 5e5ffb1..6af0951 100644
--- a/trytond/ir/locale/ru_RU.po
+++ b/trytond/ir/locale/ru_RU.po
@@ -23,24 +23,26 @@ msgid "You are not allowed to delete this record."
 msgstr "Вы не можете удалить эту запись."
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "Поле \"%s\" на \"%s\" содержит очень много десятичных знаков."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
 msgstr ""
-"Значение поля \"%s\" в таблице \"%s\" не действует в соответствии с его "
-"домена."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
 msgstr ""
-"Не могу удалить \"%s\" записей, поскольку они используются в поле \"%s\" из "
-"таблицы \"%s\"."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "Значение поля \"%s\" в таблице \"%s\" отсутствует."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr ""
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -252,10 +254,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "Некорректный XML для вида \"%s\"."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Ключ %r не найден в поле выделения %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -287,19 +285,21 @@ msgid "Relation not found: %r in %s"
 msgstr "Связь не найдена: %r в %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Поле \"%s\" на \"%s\" необходимо."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr ""
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Поле \"%s\" на \"%s\" необходимо."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr ""
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "Отсутствует функция поиска для поля \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
 msgstr ""
 
 msgctxt "error:selection_value_notfound:"
@@ -307,12 +307,12 @@ msgid "Value not in the selection for field \"%s\"."
 msgstr "Значение не выделено в поле \"%s\"."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "Поле \"%s\" в таблице \"%s\" слишком длинное."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
+msgstr ""
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "Значение времени поля \"%s\" в \"%s\" некорректное."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -414,10 +414,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Действующий"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Авто-Обновление"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Значение контекста"
@@ -714,6 +710,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Содержание"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr ""
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Внутреннее наименование"
@@ -1574,6 +1574,11 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Изменено пользователем"
 
+#, fuzzy
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2611,10 +2616,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Изменено пользователем"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Добавить авто-обновление в просмотр"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "По умолчанию кол-во записей в списке"
@@ -3055,6 +3056,10 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Модуль"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr ""
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
 msgstr "Мастер конфигурации модуля"
@@ -3708,6 +3713,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Описание модели"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr ""
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr ""
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Добро пожаловать в мастер конфигурации модуля!"
@@ -3976,6 +3989,11 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Печать"
 
+#, fuzzy
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "Ок"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "Ок"
diff --git a/trytond/ir/locale/sl_SI.po b/trytond/ir/locale/sl_SI.po
index ff857f9..59694de 100644
--- a/trytond/ir/locale/sl_SI.po
+++ b/trytond/ir/locale/sl_SI.po
@@ -23,20 +23,32 @@ msgid "You are not allowed to delete this record."
 msgstr "Nimate dovoljenja za brisanje tega zapisa"
 
 msgctxt "error:digits_validation_record:"
-msgid "The field \"%s\" on \"%s\" has too many decimal digits."
-msgstr "Polje \"%s\" pri \"%s\" ima preveč decimalk."
+msgid ""
+"The number of digits \"%(digits)s\" of field \"%(field)s\" on \"%(value)s\" "
+"exceeds it's limit."
+msgstr ""
+"Število decimalk \"%(digits)s\" polja \"%(field)s\" pri \"%(model)s\" je "
+"preseženo."
 
 msgctxt "error:domain_validation_record:"
-msgid "The value of the field \"%s\" on \"%s\" is not valid according to its domain."
-msgstr "Vrednost polja \"%s\" pri \"%s\" ni veljavna glede na svojo domeno."
+msgid ""
+"The value of the field \"%(field)s\" on \"%(model)s\" is not valid according"
+" to its domain."
+msgstr ""
+"Vrednost polja \"%(field)s\" pri \"%(model)s\" ni veljavna glede na svojo "
+"domeno."
 
 msgctxt "error:foreign_model_exist:"
-msgid "Could not delete \"%s\" records because they are used on field \"%s\" of \"%s\"."
-msgstr "\"%s\" zapisov ni možno zbrisati, ker so uporabljeni v polju \"%s\" od \"%s\"."
+msgid ""
+"Could not delete the records because they are used on field \"%(field)s\" of"
+" \"%(model)s\"."
+msgstr ""
+"Zapisov ni možno zbrisati, ker so uporabljeni v polju \"%(field)s\" modela "
+"\"%(model)s\"."
 
 msgctxt "error:foreign_model_missing:"
-msgid "The value of field \"%s\" on \"%s\" doesn't exist."
-msgstr "Vrednost polja \"%s\" pri \"%s\" ne obstaja."
+msgid "The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" doesn't exist."
+msgstr "Vrednost \"%(value)s\" polja \"%(field)s\" pri \"%(model)s\" ne obstaja."
 
 msgctxt "error:ir.action.act_window:"
 msgid "Invalid context \"%(context)s\" on action \"%(action)s\"."
@@ -255,10 +267,6 @@ msgctxt "error:ir.ui.view:"
 msgid "Invalid XML for view \"%s\"."
 msgstr "Neveljaven XML za pogled \"%s\"."
 
-msgctxt "error:not_found_in_selection:"
-msgid "Key %r not found in selection field %r"
-msgstr "Ključa %r ni možno najti v izbirnem polju %r"
-
 msgctxt "error:read_error:"
 msgid ""
 "You try to read records that don't exist anymore!\n"
@@ -292,32 +300,36 @@ msgid "Relation not found: %r in %s"
 msgstr "Veze ni možno najti: %r v %s"
 
 msgctxt "error:required_field:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Polje \"%s\" v \"%s\" je obvezno."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "Polje \"%(field)s\" v \"%(model)s\" je obvezno."
 
 msgctxt "error:required_validation_record:"
-msgid "The field \"%s\" on \"%s\" is required."
-msgstr "Polje \"%s\" v \"%s\" je obvezno."
+msgid "The field \"%(field)s\" on \"%(model)s\" is required."
+msgstr "Polje \"%(field)s\" v \"%(model)s\" je obvezno."
 
 msgctxt "error:search_function_missing:"
 msgid "Missing search function on field \"%s\"."
 msgstr "Manjka funkcija iskanja pri polju \"%s\"."
 
 msgctxt "error:selection_validation_record:"
-msgid "The field \"%s\" on \"%s\" is not in the selection."
-msgstr "Polje \"%s\" pri \"%s\" ni v izboru."
+msgid ""
+"The value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not in "
+"the selection."
+msgstr "Vrednost \"%(value)s\" polja \"%(field)s\" v \"%(model)s\" ni v izboru."
 
 msgctxt "error:selection_value_notfound:"
 msgid "Value not in the selection for field \"%s\"."
 msgstr "Vrednost za polje \"%s\" ni v izboru."
 
 msgctxt "error:size_validation_record:"
-msgid "The field \"%s\" on \"%s\" is too long."
-msgstr "Polje \"%s\" v \"%s\" je predolgo."
+msgid "The size \"%(size)s\" of the field \"%(field)s\" on \"%(model)s\" is too long."
+msgstr "Velikost \"%(size)s\" polja \"%(field)s\" v \"%(model)s\" je presežena."
 
 msgctxt "error:time_format_validation_record:"
-msgid "The time value of field \"%s\" on \"%s\" is not valid."
-msgstr "Časovna vrednost v polju \"%s\" pri \"%s\" je neveljavna."
+msgid "The time value \"%(value)s\" of field \"%(field)s\" on \"%(model)s\" is not valid."
+msgstr ""
+"Časovna vrednost \"%(value)s\" v polju \"%(field)s\" pri \"%(model)s\" je "
+"neveljavna."
 
 msgctxt "error:too_many_relations_found:"
 msgid "Too many relations found: %r in %s"
@@ -419,10 +431,6 @@ msgctxt "field:ir.action.act_window,active:"
 msgid "Active"
 msgstr "Aktivno"
 
-msgctxt "field:ir.action.act_window,auto_refresh:"
-msgid "Auto-Refresh"
-msgstr "Samodejna osvežitev"
-
 msgctxt "field:ir.action.act_window,context:"
 msgid "Context Value"
 msgstr "Vrednost konteksta"
@@ -719,6 +727,10 @@ msgctxt "field:ir.action.report,report_content_custom:"
 msgid "Content"
 msgstr "Vsebina"
 
+msgctxt "field:ir.action.report,report_content_name:"
+msgid "Content Name"
+msgstr "Ime vsebine"
+
 msgctxt "field:ir.action.report,report_name:"
 msgid "Internal Name"
 msgstr "Interni naziv"
@@ -1579,6 +1591,10 @@ msgctxt "field:ir.module.module,write_uid:"
 msgid "Write User"
 msgstr "Zapisal"
 
+msgctxt "field:ir.module.module.config_wizard.done,id:"
+msgid "ID"
+msgstr "ID"
+
 msgctxt "field:ir.module.module.config_wizard.first,id:"
 msgid "ID"
 msgstr "ID"
@@ -2081,7 +2097,7 @@ msgstr "Modul"
 
 msgctxt "field:ir.translation,name:"
 msgid "Field Name"
-msgstr "Ime polja"
+msgstr "Polje"
 
 msgctxt "field:ir.translation,overriding_module:"
 msgid "Overriding Module"
@@ -2607,10 +2623,6 @@ msgctxt "field:ir.ui.view_tree_width,write_uid:"
 msgid "Write User"
 msgstr "Zapisal"
 
-msgctxt "help:ir.action.act_window,auto_refresh:"
-msgid "Add an auto-refresh on the view"
-msgstr "Dodaj samoosvežitev na pogledu"
-
 msgctxt "help:ir.action.act_window,limit:"
 msgid "Default limit for the list view"
 msgstr "Privzeta meja za naštevni pogled"
@@ -3049,9 +3061,13 @@ msgctxt "model:ir.module.module,name:"
 msgid "Module"
 msgstr "Modul"
 
+msgctxt "model:ir.module.module.config_wizard.done,name:"
+msgid "Module Config Wizard Done"
+msgstr "Konfiguracija modula zaključek"
+
 msgctxt "model:ir.module.module.config_wizard.first,name:"
 msgid "Module Config Wizard First"
-msgstr "Nastavitveni čarovnik"
+msgstr "Konfiguracija modula zagon"
 
 msgctxt "model:ir.module.module.config_wizard.item,name:"
 msgid "Config wizard to run after installing module"
@@ -3059,7 +3075,7 @@ msgstr "Nastavitveni čarovnik, ki se zažene po namestitvi modula"
 
 msgctxt "model:ir.module.module.config_wizard.other,name:"
 msgid "Module Config Wizard Other"
-msgstr "Dodatna konfiguracija modula"
+msgstr "Konfiguracija modula drugo"
 
 msgctxt "model:ir.module.module.dependency,name:"
 msgid "Module dependency"
@@ -3067,11 +3083,11 @@ msgstr "Odvisnost modula"
 
 msgctxt "model:ir.module.module.install_upgrade.done,name:"
 msgid "Module Install Upgrade Done"
-msgstr "Namestitev/nadgradnja modula končana."
+msgstr "Namestitev modula zaključek"
 
 msgctxt "model:ir.module.module.install_upgrade.start,name:"
 msgid "Module Install Upgrade Start"
-msgstr "Start za namestitev/nadgradnjo modula"
+msgstr "Namestitev modula zagon"
 
 msgctxt "model:ir.property,name:"
 msgid "Property"
@@ -3701,6 +3717,14 @@ msgctxt "view:ir.model:"
 msgid "Model Description"
 msgstr "Opis modela"
 
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "Module configuration"
+msgstr "Konfiguracija modula"
+
+msgctxt "view:ir.module.module.config_wizard.done:"
+msgid "The configuration is done."
+msgstr "Konfiguracija je zaključena."
+
 msgctxt "view:ir.module.module.config_wizard.first:"
 msgid "Welcome to the module configuration wizard!"
 msgstr "Dobrodošli v čarovniku za nastavitev modulov"
@@ -3964,6 +3988,10 @@ msgctxt "wizard_button:ir.model.print_model_graph,start,print_:"
 msgid "Print"
 msgstr "Natisni"
 
+msgctxt "wizard_button:ir.module.module.config_wizard,done,end:"
+msgid "Ok"
+msgstr "V redu"
+
 msgctxt "wizard_button:ir.module.module.config_wizard,first,action:"
 msgid "Ok"
 msgstr "V redu"
diff --git a/trytond/ir/model.py b/trytond/ir/model.py
index 72e7a28..cb63303 100644
--- a/trytond/ir/model.py
+++ b/trytond/ir/model.py
@@ -68,6 +68,7 @@ class Model(ModelSQL, ModelView):
         cls._order.insert(0, ('model', 'ASC'))
         cls.__rpc__.update({
                 'list_models': RPC(),
+                'list_history': RPC(),
                 'global_search': RPC(),
                 })
 
@@ -130,6 +131,12 @@ class Model(ModelSQL, ModelView):
             return [m.model for m in models]
 
     @classmethod
+    def list_history(cls):
+        'Return a list of all models with history'
+        return [name for name, model in Pool().iterobject()
+            if getattr(model, '_history', False)]
+
+    @classmethod
     def create(cls, vlist):
         pool = Pool()
         Property = pool.get('ir.property')
@@ -139,10 +146,10 @@ class Model(ModelSQL, ModelView):
         return res
 
     @classmethod
-    def write(cls, models, vals):
+    def write(cls, models, values, *args):
         pool = Pool()
         Property = pool.get('ir.property')
-        super(Model, cls).write(models, vals)
+        super(Model, cls).write(models, values, *args)
         # Restart the cache of models_get
         Property._models_get_cache.clear()
 
@@ -527,8 +534,40 @@ class ModelAccess(ModelSQL, ModelView):
         return True
 
     @classmethod
-    def write(cls, accesses, vals):
-        super(ModelAccess, cls).write(accesses, vals)
+    def check_relation(cls, model_name, field_name, mode='read'):
+        'Check access to relation field for model_name and mode'
+        pool = Pool()
+        Model = pool.get(model_name)
+        field = getattr(Model, field_name)
+        if field._type in ('one2many', 'many2one'):
+            return cls.check(field.model_name, mode=mode,
+                raise_exception=False)
+        elif field._type in ('many2many', 'one2one'):
+            if (field.target
+                    and not cls.check(field.target, mode=mode,
+                        raise_exception=False)):
+                return False
+            elif (field.relation_name
+                    and not cls.check(field.relation_name, mode=mode,
+                        raise_exception=False)):
+                return False
+            else:
+                return True
+        elif field._type == 'reference':
+            selection = field.selection
+            if isinstance(selection, basestring):
+                selection = getattr(Model, field.selection)()
+            for model_name, _ in selection:
+                if not cls.check(model_name, mode=mode,
+                        raise_exception=False):
+                    return False
+            return True
+        else:
+            return True
+
+    @classmethod
+    def write(cls, accesses, values, *args):
+        super(ModelAccess, cls).write(accesses, values, *args)
         # Restart the cache
         cls._get_access_cache.clear()
         ModelView._fields_view_get_cache.clear()
@@ -681,8 +720,8 @@ class ModelFieldAccess(ModelSQL, ModelView):
         return True
 
     @classmethod
-    def write(cls, field_accesses, vals):
-        super(ModelFieldAccess, cls).write(field_accesses, vals)
+    def write(cls, field_accesses, values, *args):
+        super(ModelFieldAccess, cls).write(field_accesses, values, *args)
         # Restart the cache
         cls._get_access_cache.clear()
         ModelView._fields_view_get_cache.clear()
@@ -730,8 +769,8 @@ class ModelButton(ModelSQL, ModelView):
         return result
 
     @classmethod
-    def write(cls, buttons, values):
-        super(ModelButton, cls).write(buttons, values)
+    def write(cls, buttons, values, *args):
+        super(ModelButton, cls).write(buttons, values, *args)
         # Restart the cache for get_groups
         cls._groups_cache.clear()
 
@@ -813,8 +852,8 @@ class ModelData(ModelSQL, ModelView):
         return False
 
     @classmethod
-    def write(cls, data, values):
-        super(ModelData, cls).write(data, values)
+    def write(cls, data, values, *args):
+        super(ModelData, cls).write(data, values, *args)
         # Restart the cache for get_id
         cls._get_id_cache.clear()
 
diff --git a/trytond/ir/module/module.py b/trytond/ir/module/module.py
index 5322ca4..605a614 100644
--- a/trytond/ir/module/module.py
+++ b/trytond/ir/module/module.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 functools import wraps
+
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.modules import create_graph, get_module_list, get_module_info
 from trytond.wizard import Wizard, StateView, Button, StateTransition, \
@@ -12,12 +14,23 @@ from trytond.rpc import RPC
 
 __all__ = [
     'Module', 'ModuleDependency', 'ModuleConfigWizardItem',
-    'ModuleConfigWizardFirst', 'ModuleConfigWizardOther', 'ModuleConfigWizard',
+    'ModuleConfigWizardFirst', 'ModuleConfigWizardOther',
+    'ModuleConfigWizardDone', 'ModuleConfigWizard',
     'ModuleInstallUpgradeStart', 'ModuleInstallUpgradeDone',
     'ModuleInstallUpgrade', 'ModuleConfig',
     ]
 
 
+def filter_state(state):
+    def filter(func):
+        @wraps(func)
+        def wrapper(cls, modules):
+            modules = [m for m in modules if m.state == state]
+            return func(cls, modules)
+        return wrapper
+    return filter
+
+
 class Module(ModelSQL, ModelView):
     "Module"
     __name__ = "ir.module.module"
@@ -97,14 +110,14 @@ class Module(ModelSQL, ModelView):
     @classmethod
     def get_childs(cls, modules, name):
         child_ids = dict((m.id, []) for m in modules)
-        names = [m.name for m in modules]
+        name2id = dict((m.name, m.id) for m in modules)
         childs = cls.search([
-                ('dependencies.name', 'in', names),
+                ('dependencies.name', 'in', name2id.keys()),
                 ])
         for child in childs:
             for dep in child.dependencies:
-                if dep.module.id in child_ids:
-                    child_ids[dep.module.id].append(child.id)
+                if dep.name in name2id:
+                    child_ids[name2id[dep.name]].append(child.id)
         return child_ids
 
     @classmethod
@@ -121,34 +134,38 @@ class Module(ModelSQL, ModelView):
 
     @classmethod
     def on_write(cls, modules):
-        ids = set()
-        graph, packages, later = create_graph(get_module_list())
-        for module in modules:
-            if module.name not in graph:
-                continue
+        dependencies = set()
+
+        def get_parents(module):
+            parents = set(p.id for p in module.parents)
+            for p in module.parents:
+                parents.update(get_parents(p))
+            return parents
+
+        def get_childs(module):
+            childs = set(c.id for c in module.childs)
+            for c in module.childs:
+                childs.update(get_childs(c))
+            return childs
 
-            def get_parents(module):
-                parents = set(p.name for p in module.parents)
-                for p in module.parents:
-                    parents.update(get_parents(p))
-                return parents
-            dependencies = get_parents(module)
-
-            def get_childs(module):
-                childs = set(c.name for c in module.childs)
-                for c in module.childs:
-                    childs.update(get_childs(c))
-                return childs
+        for module in modules:
+            dependencies.update(get_parents(module))
             dependencies.update(get_childs(module))
-            ids |= set(x.id for x in cls.search([
-                        ('name', 'in', list(dependencies)),
-                        ]))
-        return list(ids)
+        return list(dependencies)
 
     @classmethod
     @ModelView.button
+    @filter_state('uninstalled')
     def install(cls, modules):
+        modules_install = set(modules)
         graph, packages, later = create_graph(get_module_list())
+
+        def get_parents(module):
+            parents = set(p for p in module.parents)
+            for p in module.parents:
+                parents.update(get_parents(p))
+            return parents
+
         for module in modules:
             if module.name not in graph:
                 missings = []
@@ -157,24 +174,25 @@ class Module(ModelSQL, ModelView):
                         missings = [x for x in deps if x not in graph]
                 cls.raise_user_error('missing_dep', (missings, module.name))
 
-            def get_parents(module):
-                parents = set(p.name for p in module.parents)
-                for p in module.parents:
-                    parents.update(get_parents(p))
-                return parents
-            dependencies = list(get_parents(module))
-            modules_install = cls.search([
-                    ('name', 'in', dependencies),
-                    ('state', '=', 'uninstalled'),
-                    ])
-            cls.write(modules_install + [module], {
-                    'state': 'to install',
-                    })
+            modules_install.update((m for m in get_parents(module)
+                    if m.state == 'uninstalled'))
+        cls.write(list(modules_install), {
+                'state': 'to install',
+                })
 
     @classmethod
     @ModelView.button
+    @filter_state('installed')
     def upgrade(cls, modules):
+        modules_installed = set(modules)
         graph, packages, later = create_graph(get_module_list())
+
+        def get_childs(module):
+            childs = set(c for c in module.childs)
+            for c in module.childs:
+                childs.update(get_childs(c))
+            return childs
+
         for module in modules:
             if module.name not in graph:
                 missings = []
@@ -183,24 +201,15 @@ class Module(ModelSQL, ModelView):
                         missings = [x for x in deps if x not in graph]
                 cls.raise_user_error('missing_dep', (missings, module.name))
 
-            def get_childs(name, graph):
-                childs = set(x.name for x in graph[name].childs)
-                childs2 = set()
-                for child in childs:
-                    childs2.update(get_childs(child, graph))
-                childs.update(childs2)
-                return childs
-            dependencies = list(get_childs(module.name, graph))
-            modules_installed = cls.search([
-                    ('name', 'in', dependencies),
-                    ('state', '=', 'installed'),
-                    ])
-            cls.write(modules_installed + [module], {
-                    'state': 'to upgrade',
-                    })
+            modules_installed.update((m for m in get_childs(module)
+                    if m.state == 'installed'))
+        cls.write(list(modules_installed), {
+                'state': 'to upgrade',
+                })
 
     @classmethod
     @ModelView.button
+    @filter_state('to install')
     def install_cancel(cls, modules):
         cls.write(modules, {
                 'state': 'uninstalled',
@@ -208,6 +217,7 @@ class Module(ModelSQL, ModelView):
 
     @classmethod
     @ModelView.button
+    @filter_state('installed')
     def uninstall(cls, modules):
         cursor = Transaction().cursor
         for module in modules:
@@ -227,11 +237,13 @@ class Module(ModelSQL, ModelView):
 
     @classmethod
     @ModelView.button
+    @filter_state('to remove')
     def uninstall_cancel(cls, modules):
         cls.write(modules, {'state': 'installed'})
 
     @classmethod
     @ModelView.button
+    @filter_state('to upgrade')
     def upgrade_cancel(cls, modules):
         cls.write(modules, {'state': 'installed'})
 
@@ -378,6 +390,11 @@ class ModuleConfigWizardOther(ModelView):
         return 100.0 * done / all
 
 
+class ModuleConfigWizardDone(ModelView):
+    'Module Config Wizard Done'
+    __name__ = 'ir.module.module.config_wizard.done'
+
+
 class ModuleConfigWizard(Wizard):
     'Run config wizards'
     __name__ = 'ir.module.module.config_wizard'
@@ -414,6 +431,10 @@ class ModuleConfigWizard(Wizard):
             Button('Next', 'action', 'tryton-go-next', default=True),
             ])
     action = ConfigStateAction()
+    done = StateView('ir.module.module.config_wizard.done',
+        'ir.module_config_wizard_done_view_form', [
+            Button('Ok', 'end', 'tryton-close', default=True),
+            ])
 
     def transition_start(self):
         res = self.transition_action()
@@ -429,7 +450,10 @@ class ModuleConfigWizard(Wizard):
                 ])
         if items:
             return 'other'
-        return 'end'
+        return 'done'
+
+    def end(self):
+        return 'reload menu'
 
 
 class ModuleInstallUpgradeStart(ModelView):
diff --git a/trytond/ir/module/module.xml b/trytond/ir/module/module.xml
index 33a716a..8dc897f 100644
--- a/trytond/ir/module/module.xml
+++ b/trytond/ir/module/module.xml
@@ -89,6 +89,12 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">module_config_wizard_other_form</field>
         </record>
 
+        <record model="ir.ui.view" id="module_config_wizard_done_view_form">
+            <field name="model">ir.module.module.config_wizard.done</field>
+            <field name="type">form</field>
+            <field name="name">module_config_wizard_done_form</field>
+        </record>
+
         <record model="ir.action.wizard" id="act_module_install_upgrade">
             <field name="name">Perform Pending Installation/Upgrade</field>
             <field name="wiz_name">ir.module.module.install_upgrade</field>
diff --git a/trytond/ir/rule.py b/trytond/ir/rule.py
index 99c0f14..479f162 100644
--- a/trytond/ir/rule.py
+++ b/trytond/ir/rule.py
@@ -1,6 +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 contextlib
 import time
 import datetime
 
@@ -86,8 +85,8 @@ class RuleGroup(ModelSQL, ModelView):
         return res
 
     @classmethod
-    def write(cls, groups, vals):
-        super(RuleGroup, cls).write(groups, vals)
+    def write(cls, groups, vals, *args):
+        super(RuleGroup, cls).write(groups, vals, *args)
         # Restart the cache on the domain_get method of ir.rule
         Pool().get('ir.rule')._domain_get_cache.clear()
 
@@ -215,8 +214,8 @@ class Rule(ModelSQL, ModelView):
         clause_global = {}
         ctx = cls._get_context()
         # Use root user without context to prevent recursion
-        with contextlib.nested(Transaction().set_user(0),
-                Transaction().set_context(user=0)):
+        with Transaction().set_user(0), \
+                Transaction().set_context(user=0):
             for rule in cls.browse(ids):
                 assert rule.domain, ('Rule domain empty,'
                     'check if migration was done')
@@ -255,8 +254,8 @@ class Rule(ModelSQL, ModelView):
             clause = ['AND', clause_global, clause]
 
         # Use root to prevent infinite recursion
-        with contextlib.nested(Transaction().set_user(0),
-                Transaction().set_context(active_test=False, user=0)):
+        with Transaction().set_user(0), \
+                Transaction().set_context(active_test=False, user=0):
             query = obj.search(clause, order=[], query=True)
 
         cls._domain_get_cache.set(key, query)
@@ -276,7 +275,7 @@ class Rule(ModelSQL, ModelView):
         return res
 
     @classmethod
-    def write(cls, rules, vals):
-        super(Rule, cls).write(rules, vals)
+    def write(cls, rules, vals, *args):
+        super(Rule, cls).write(rules, vals, *args)
         # Restart the cache on the domain_get method
         cls._domain_get_cache.clear()
diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py
index 8637f48..d1b3c73 100644
--- a/trytond/ir/sequence.py
+++ b/trytond/ir/sequence.py
@@ -184,12 +184,13 @@ class Sequence(ModelSQL, ModelView):
         return sequences
 
     @classmethod
-    def write(cls, sequences, values):
-        result = super(Sequence, cls).write(sequences, values)
+    def write(cls, sequences, values, *args):
+        super(Sequence, cls).write(sequences, values, *args)
         if sql_sequence and not cls._strict:
-            for sequence in sequences:
-                sequence.update_sql_sequence(values.get('number_next'))
-        return result
+            actions = iter((sequences, values) + args)
+            for sequences, values in zip(actions, actions):
+                for sequence in sequences:
+                    sequence.update_sql_sequence(values.get('number_next'))
 
     @classmethod
     def delete(cls, sequences):
diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py
index e6a3f57..17e7079 100644
--- a/trytond/ir/translation.py
+++ b/trytond/ir/translation.py
@@ -1,6 +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 contextlib
 try:
     import cStringIO as StringIO
 except ImportError:
@@ -10,10 +9,7 @@ import polib
 import xml.dom.minidom
 from difflib import SequenceMatcher
 import os
-try:
-    from hashlib import md5
-except ImportError:
-    from md5 import md5
+from hashlib import md5
 from lxml import etree
 from itertools import izip
 from sql import Column
@@ -731,13 +727,17 @@ class Translation(ModelSQL, ModelView):
         return super(Translation, cls).create(vlist)
 
     @classmethod
-    def write(cls, translations, vals):
+    def write(cls, translations, values, *args):
         cls._translation_cache.clear()
         ModelView._fields_view_get_cache.clear()
-        if 'src' in vals:
-            vals = vals.copy()
-            vals['src_md5'] = cls.get_src_md5(vals.get('src'))
-        return super(Translation, cls).write(translations, vals)
+        actions = iter((translations, values) + args)
+        args = []
+        for translations, values in zip(actions, actions):
+            if 'src' in values:
+                values = values.copy()
+                values['src_md5'] = cls.get_src_md5(values.get('src'))
+            args.extend((translations, values))
+        return super(Translation, cls).write(*args)
 
     @classmethod
     def extra_model_data(cls, model_data):
@@ -750,6 +750,25 @@ class Translation(ModelSQL, ModelView):
                 ):
             yield 'ir.action'
 
+    @property
+    def unique_key(self):
+        if self.type in ('odt', 'view', 'wizard_button', 'selection', 'error'):
+            return (self.name, self.res_id, self.type, self.src)
+        elif self.type in ('field', 'model', 'help'):
+            return (self.name, self.res_id, self.type)
+
+    @classmethod
+    def from_poentry(cls, entry):
+        'Returns a translation instance for a entry of pofile and its res_id'
+        ttype, name, res_id = entry.msgctxt.split(':')
+        src = entry.msgid
+        value = entry.msgstr
+        fuzzy = 'fuzzy' in entry.flags
+
+        translation = cls(name=name, type=ttype, src=src, fuzzy=fuzzy,
+            value=value)
+        return translation, res_id
+
     @classmethod
     def translation_import(cls, lang, module, po_path):
         pool = Pool()
@@ -778,20 +797,15 @@ class Translation(ModelSQL, ModelView):
                 ('module', '=', module),
                 ], order=[])
         for translation in module_translations:
-            if translation.type in ('odt', 'view', 'wizard_button',
-                    'selection', 'error'):
-                key = (translation.name, translation.res_id, translation.type,
-                    translation.src)
-            elif translation.type in ('field', 'model', 'help'):
-                key = (translation.name, translation.res_id, translation.type)
-            else:
-                raise Exception('Unknow translation type: %s'
-                    % translation.type)
+            key = translation.unique_key
+            if not key:
+                raise ValueError('Unknow translation type: %s' %
+                    translation.type)
             key2ids.setdefault(key, []).append(translation.id)
             if len(module_translations) <= RECORD_CACHE_SIZE:
                 id2translation[translation.id] = translation
 
-        def override_translation(ressource_id, new_translation, src, fuzzy):
+        def override_translation(ressource_id, new_translation):
             res_id_module, res_id = ressource_id.split('.')
             if res_id:
                 model_data, = ModelData.search([
@@ -801,23 +815,23 @@ class Translation(ModelSQL, ModelView):
                 res_id = model_data.db_id
             else:
                 res_id = -1
-            with contextlib.nested(Transaction().set_user(0),
-                    Transaction().set_context(module=res_id_module)):
+            with Transaction().set_user(0), \
+                    Transaction().set_context(module=res_id_module):
                 domain = [
-                    ('name', '=', name),
+                    ('name', '=', new_translation.name),
                     ('res_id', '=', res_id),
-                    ('lang', '=', lang),
-                    ('type', '=', ttype),
+                    ('lang', '=', new_translation.lang),
+                    ('type', '=', new_translation.type),
                     ('module', '=', res_id_module),
                     ]
-                if ttype in ('odt', 'view', 'wizard_button', 'selection',
-                        'error'):
-                    domain.append(('src', '=', src))
+                if new_translation.type in ('odt', 'view', 'wizard_button',
+                        'selection', 'error'):
+                    domain.append(('src', '=', new_translation.src))
                 translation, = cls.search(domain)
-                if translation.value != new_translation:
-                    translation.value = new_translation
+                if translation.value != new_translation.value:
+                    translation.value = new_translation.value
                     translation.overriding_module = module
-                    translation.fuzzy = fuzzy
+                    translation.fuzzy = new_translation.fuzzy
                     translation.save()
 
         # Make a first loop to retreive translation ids in the right order to
@@ -832,17 +846,16 @@ class Translation(ModelSQL, ModelView):
                 id2translation = dict((t.id, t)
                     for t in cls.browse(translation_ids))
             for entry in pofile:
-                ttype, name, res_id = entry.msgctxt.split(':')
-                src = entry.msgid
-                value = entry.msgstr
-                fuzzy = 'fuzzy' in entry.flags
+                translation, res_id = cls.from_poentry(entry)
+                translation.lang = lang
+                translation.module = module
                 noupdate = False
 
                 if '.' in res_id:
-                    override_translation(res_id, value, ttype, fuzzy)
+                    override_translation(res_id, translation)
                     continue
 
-                model = name.split(',')[0]
+                model = translation.name.split(',')[0]
                 if (model in fs_id2prop
                         and res_id in fs_id2prop[model]):
                     res_id, noupdate = fs_id2prop[model][res_id]
@@ -851,53 +864,44 @@ class Translation(ModelSQL, ModelView):
                     try:
                         res_id = int(res_id)
                     except ValueError:
-                        continue
+                        res_id = None
                 if not res_id:
                     res_id = -1
 
-                if ttype in ('odt', 'view', 'wizard_button', 'selection',
-                        'error'):
-                    key = (name, res_id, ttype, src)
-                elif ttype in('field', 'model', 'help'):
-                    key = (name, res_id, ttype)
-                else:
-                    raise Exception('Unknow translation type: %s' % ttype)
+                translation.res_id = res_id
+                key = translation.unique_key
+                if not key:
+                    raise ValueError('Unknow translation type: %s' %
+                        translation.type)
                 ids = key2ids.get(key, [])
 
                 if not processing:
                     translation_ids.extend(ids)
                     continue
 
-                with contextlib.nested(Transaction().set_user(0),
-                        Transaction().set_context(module=module)):
-                    if not ids:
-                        to_create.append({
-                            'name': name,
-                            'res_id': res_id,
-                            'lang': lang,
-                            'type': ttype,
-                            'src': src,
-                            'value': value,
-                            'fuzzy': fuzzy,
-                            'module': module,
-                            'overriding_module': None,
-                            })
-                    else:
-                        translations2 = []
-                        for translation_id in ids:
-                            translation = id2translation[translation_id]
-                            if translation.value != value \
-                                    or translation.fuzzy != fuzzy:
-                                translations2.append(translation)
-                        if translations2 and not noupdate:
-                            cls.write(translations2, {
-                                'value': value,
-                                'fuzzy': fuzzy,
-                                })
+                if not ids:
+                    to_create.append(translation._save_values)
+                else:
+                    to_write = []
+                    for translation_id in ids:
+                        old_translation = id2translation[translation_id]
+                        if (old_translation.value != translation.value
+                                or old_translation.fuzzy !=
+                                translation.fuzzy):
+                            to_write.append(old_translation)
+                    with Transaction().set_user(0), \
+                            Transaction().set_context(module=module):
+                        if to_write and not noupdate:
+                            cls.write(to_write, {
+                                    'value': translation.value,
+                                    'fuzzy': translation.fuzzy,
+                                    })
                         translations |= set(cls.browse(ids))
 
         if to_create:
-            translations |= set(cls.create(to_create))
+            with Transaction().set_user(0), \
+                    Transaction().set_context(module=module):
+                translations |= set(cls.create(to_create))
 
         if translations:
             all_translations = set(cls.search([
@@ -1353,7 +1357,6 @@ class TranslationClean(Wizard):
                 'delete_xml_record',
                 'xml_record_desc',
                 'write_xml_record',
-                'not_found_in_selection',
                 'relation_not_found',
                 'too_many_relations_found',
                 'xml_id_syntax_error',
@@ -1377,10 +1380,11 @@ class TranslationClean(Wizard):
             return False
         if model_name in pool.object_name_list():
             Model = pool.get(model_name)
-            errors = (Model._error_messages.values()
-                + Model._sql_error_messages.values())
-            for _, _, error in Model._sql_constraints:
-                errors.append(error)
+            errors = Model._error_messages.values()
+            if issubclass(Model, ModelSQL):
+                errors += Model._sql_error_messages.values()
+                for _, _, error in Model._sql_constraints:
+                    errors.append(error)
             if translation.src not in errors:
                 return True
         elif model_name in pool.object_name_list(type='wizard'):
@@ -1448,6 +1452,10 @@ class TranslationUpdate(Wizard):
     "Update translation"
     __name__ = "ir.translation.update"
 
+    _source_types = ['odt', 'view', 'wizard_button', 'selection', 'error']
+    _ressource_types = ['field', 'model', 'help']
+    _updatable_types = ['field', 'model', 'selection', 'help']
+
     start = StateView('ir.translation.update.start',
         'ir.translation_update_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1465,16 +1473,15 @@ class TranslationUpdate(Wizard):
         cursor = Transaction().cursor
         translation = Translation.__table__()
         lang = self.start.language.code
-        types = ['odt', 'view', 'wizard_button', 'selection', 'error']
         columns = [translation.name.as_('name'),
             translation.res_id.as_('res_id'), translation.type.as_('type'),
             translation.src.as_('src'), translation.module.as_('module')]
         cursor.execute(*(translation.select(*columns,
                     where=(translation.lang == 'en_US')
-                    & translation.type.in_(types))
+                    & translation.type.in_(self._source_types))
                 - translation.select(*columns,
                     where=(translation.lang == lang)
-                    & translation.type.in_(types))))
+                    & translation.type.in_(self._source_types))))
         to_create = []
         for row in cursor.dictfetchall():
             to_create.append({
@@ -1488,16 +1495,15 @@ class TranslationUpdate(Wizard):
         if to_create:
             with Transaction().set_user(0):
                 Translation.create(to_create)
-        types = ['field', 'model', 'help']
         columns = [translation.name.as_('name'),
             translation.res_id.as_('res_id'), translation.type.as_('type'),
             translation.module.as_('module')]
         cursor.execute(*(translation.select(*columns,
                     where=(translation.lang == 'en_US')
-                    & translation.type.in_(types))
+                    & translation.type.in_(self._ressource_types))
                 - translation.select(*columns,
                     where=(translation.lang == lang)
-                    & translation.type.in_(types))))
+                    & translation.type.in_(self._ressource_types))))
         to_create = []
         for row in cursor.dictfetchall():
             to_create.append({
@@ -1510,16 +1516,15 @@ class TranslationUpdate(Wizard):
         if to_create:
             with Transaction().set_user(0):
                 Translation.create(to_create)
-        types = ['field', 'model', 'selection', 'help']
         columns = [translation.name.as_('name'),
             translation.res_id.as_('res_id'), translation.type.as_('type'),
-            translation.src.as_('src')]
+            translation.src.as_('src'), translation.module.as_('module')]
         cursor.execute(*(translation.select(*columns,
                     where=(translation.lang == 'en_US')
-                    & translation.type.in_(types))
+                    & translation.type.in_(self._updatable_types))
                 - translation.select(*columns,
                     where=(translation.lang == lang)
-                    & translation.type.in_(types))))
+                    & translation.type.in_(self._updatable_types))))
         for row in cursor.dictfetchall():
             cursor.execute(*translation.update(
                     [translation.fuzzy, translation.src],
@@ -1527,7 +1532,8 @@ class TranslationUpdate(Wizard):
                     where=(translation.name == row['name'])
                     & (translation.type == row['type'])
                     & (translation.lang == lang)
-                    & (translation.res_id == (row['res_id'] or -1))))
+                    & (translation.res_id == (row['res_id'] or -1))
+                    & (translation.module == row['module'])))
 
         cursor.execute(*translation.select(
                 translation.src.as_('src'),
diff --git a/trytond/ir/translation.xml b/trytond/ir/translation.xml
index 5c57717..d6de503 100644
--- a/trytond/ir/translation.xml
+++ b/trytond/ir/translation.xml
@@ -60,15 +60,6 @@ this repository contains the full copyright notices and license terms. -->
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
-        <record model="ir.translation" id="translation_not_found_in_selection">
-            <field name="name">not_found_in_selection</field>
-            <field name="lang">en_US</field>
-            <field name="type">error</field>
-            <field name="src">Key %r not found in selection field %r</field>
-            <field name="value">Key %r not found in selection field %r</field>
-            <field name="module">ir</field>
-            <field name="fuzzy" eval="False"/>
-        </record>
         <record model="ir.translation" id="translation_relation_not_found">
             <field name="name">relation_not_found</field>
             <field name="lang">en_US</field>
@@ -109,8 +100,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">domain_validation_record</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The value of the field "%s" on "%s" is not valid according to its domain.</field>
-            <field name="value">The value of the field "%s" on "%s" is not valid according to its domain.</field>
+            <field name="src">The value of the field "%(field)s" on "%(model)s" is not valid according to its domain.</field>
+            <field name="value">The value of the field "%(field)s" on "%(model)s" is not valid according to its domain.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -118,8 +109,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">required_validation_record</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The field "%s" on "%s" is required.</field>
-            <field name="value">The field "%s" on "%s" is required.</field>
+            <field name="src">The field "%(field)s" on "%(model)s" is required.</field>
+            <field name="value">The field "%(field)s" on "%(model)s" is required.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -127,8 +118,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">size_validation_record</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The field "%s" on "%s" is too long.</field>
-            <field name="value">The field "%s" on "%s" is too long.</field>
+            <field name="src">The size "%(size)s" of the field "%(field)s" on "%(model)s" is too long.</field>
+            <field name="value">The size "%(size)s" of the field "%(field)s" on "(model)%s" is too long.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -136,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 field "%s" on "%s" has too many decimal digits.</field>
-            <field name="value">The field "%s" on "%s" has too many decimal digits.</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="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -146,8 +137,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">selection_validation_record</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The field "%s" on "%s" is not in the selection.</field>
-            <field name="value">The field "%s" on "%s" is not in the selection.</field>
+            <field name="src">The value "%(value)s" of field "%(field)s" on "%(model)s" is not in the selection.</field>
+            <field name="value">The value "%(value)s" of field "%(field)s" on "%(model)s" is not in the selection.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -155,8 +146,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">time_format_validation_record</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The time value of field "%s" on "%s" is not valid.</field>
-            <field name="value">The time value of field "%s" on "%s" is not valid.</field>
+            <field name="src">The time value "%(value)s" of field "%(field)s" on "%(model)s" is not valid.</field>
+            <field name="value">The time value "%(value)s" of field "%(field)s" on "%(model)s" is not valid.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -197,8 +188,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">required_field</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The field "%s" on "%s" is required.</field>
-            <field name="value">The field "%s" on "%s" is required.</field>
+            <field name="src">The field "%(field)s" on "%(model)s" is required.</field>
+            <field name="value">The field "%(field)s" on "%(model)s" is required.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -206,8 +197,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">foreign_model_missing</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">The value of field "%s" on "%s" doesn't exist.</field>
-            <field name="value">The value of field "%s" on "%s" doesn't exist.</field>
+            <field name="src">The value "%(value)s" of field "%(field)s" on "%(model)s" doesn't exist.</field>
+            <field name="value">The value "%(value)s" of field "%(field)s" on "%(model)s" doesn't exist.</field>
             <field name="module">ir</field>
             <field name="fuzzy" eval="False"/>
         </record>
@@ -215,8 +206,8 @@ this repository contains the full copyright notices and license terms. -->
             <field name="name">foreign_model_exist</field>
             <field name="lang">en_US</field>
             <field name="type">error</field>
-            <field name="src">Could not delete "%s" records because they are used on field "%s" of "%s".</field>
-            <field name="value">Could not delete "%s" records because they are used on field "%s" of "%s".</field>
+            <field name="src">Could not delete the records because they are used on field "%(field)s" of "%(model)s".</field>
+            <field name="value">Could not delete the records because they are used on field "%(field)s" of "%(model)s".</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 c04842d..56dbddd 100644
--- a/trytond/ir/trigger.py
+++ b/trytond/ir/trigger.py
@@ -29,20 +29,16 @@ class Trigger(ModelSQL, ModelView):
             'invisible': (Eval('on_create', False)
                 | Eval('on_write', False)
                 | Eval('on_delete', False)),
-        }, depends=['on_create', 'on_write', 'on_delete'],
-        on_change=['on_time'])
+        }, depends=['on_create', 'on_write', 'on_delete'])
     on_create = fields.Boolean('On Create', select=True, states={
         'invisible': Eval('on_time', False),
-        }, depends=['on_time'],
-        on_change=['on_create'])
+        }, depends=['on_time'])
     on_write = fields.Boolean('On Write', select=True, states={
         'invisible': Eval('on_time', False),
-        }, depends=['on_time'],
-        on_change=['on_write'])
+        }, depends=['on_time'])
     on_delete = fields.Boolean('On Delete', select=True, states={
         'invisible': Eval('on_time', False),
-        }, depends=['on_time'],
-        on_change=['on_delete'])
+        }, depends=['on_time'])
     condition = fields.Char('Condition', required=True,
         help='A Python statement evaluated with record represented by '
         '"self"\nIt triggers the action if true.')
@@ -97,6 +93,7 @@ class Trigger(ModelSQL, ModelView):
     def default_limit_number():
         return 0
 
+    @fields.depends('on_time')
     def on_change_on_time(self):
         if self.on_time:
             return {
@@ -106,6 +103,7 @@ class Trigger(ModelSQL, ModelView):
                     }
         return {}
 
+    @fields.depends('on_create')
     def on_change_on_create(self):
         if self.on_create:
             return {
@@ -113,6 +111,7 @@ class Trigger(ModelSQL, ModelView):
                     }
         return {}
 
+    @fields.depends('on_write')
     def on_change_on_write(self):
         if self.on_write:
             return {
@@ -120,6 +119,7 @@ class Trigger(ModelSQL, ModelView):
                     }
         return {}
 
+    @fields.depends('on_delete')
     def on_change_on_delete(self):
         if self.on_delete:
             return {
@@ -270,11 +270,10 @@ class Trigger(ModelSQL, ModelView):
         return res
 
     @classmethod
-    def write(cls, ids, values):
-        res = super(Trigger, cls).write(ids, values)
+    def write(cls, triggers, values, *args):
+        super(Trigger, cls).write(triggers, values, *args)
         # Restart the cache on the get_triggers method of ir.trigger
         cls._get_triggers_cache.clear()
-        return res
 
     @classmethod
     def delete(cls, records):
diff --git a/trytond/ir/ui/form.rnc b/trytond/ir/ui/form.rnc
index f696e9e..5943d85 100644
--- a/trytond/ir/ui/form.rnc
+++ b/trytond/ir/ui/form.rnc
@@ -41,29 +41,32 @@ attlist.field &= [ a:defaultValue = "1" ] attribute colspan { text }?
 attlist.field &=
   attribute widget {
     "date"
-    | "char"
-    | "many2one"
-    | "date"
-    | "one2many"
-    | "selection"
+    | "datetime"
+    | "time"
     | "float"
     | "numeric"
-    | "float_time"
     | "integer"
-    | "datetime"
+    | "selection"
+    | "char"
+    | "password"
+    | "float_time"
     | "boolean"
+    | "reference"
+    | "binary"
     | "text"
-    | "url"
+    | "one2many"
+    | "many2many"
+    | "many2one"
     | "email"
+    | "url"
     | "callto"
     | "sip"
-    | "sha"
-    | "reference"
-    | "binary"
-    | "many2many"
     | "image"
     | "progressbar"
+    | "one2one"
     | "richtext"
+    | "dict"
+    | "multiselection"
   }?
 attlist.field &= attribute fill { "0" | "1" }?
 attlist.field &= attribute yexpand { "0" | "1" }?
@@ -105,6 +108,7 @@ attlist.field &= attribute icon { text }?
 attlist.field &= [a:defaultValue = "1"] attribute completion { "0" | "1" }?
 attlist.field &= attribute string { text }?
 attlist.field &= [a:defaultValue = "1"] attribute factor { text }?
+attlist.field &= attribute filename { text }?
 image = element image { attlist.image, empty }
 attlist.image &= attribute name { text }
 attlist.image &= [ a:defaultValue = "1" ] attribute colspan { text }?
diff --git a/trytond/ir/ui/form.rng b/trytond/ir/ui/form.rng
index 5a68938..87b446c 100644
--- a/trytond/ir/ui/form.rng
+++ b/trytond/ir/ui/form.rng
@@ -140,29 +140,32 @@
       <attribute name="widget">
         <choice>
           <value>date</value>
-          <value>char</value>
-          <value>many2one</value>
-          <value>date</value>
-          <value>one2many</value>
-          <value>selection</value>
+          <value>datetime</value>
+          <value>time</value>
           <value>float</value>
           <value>numeric</value>
-          <value>float_time</value>
           <value>integer</value>
-          <value>datetime</value>
+          <value>selection</value>
+          <value>char</value>
+          <value>password</value>
+          <value>float_time</value>
           <value>boolean</value>
+          <value>reference</value>
+          <value>binary</value>
           <value>text</value>
-          <value>url</value>
+          <value>one2many</value>
+          <value>many2many</value>
+          <value>many2one</value>
           <value>email</value>
+          <value>url</value>
           <value>callto</value>
           <value>sip</value>
-          <value>sha</value>
-          <value>reference</value>
-          <value>binary</value>
-          <value>many2many</value>
           <value>image</value>
           <value>progressbar</value>
+          <value>one2one</value>
           <value>richtext</value>
+          <value>dict</value>
+          <value>multiselection</value>
         </choice>
       </attribute>
     </optional>
@@ -384,6 +387,11 @@
       <attribute name="factor" a:defaultValue="1"/>
     </optional>
   </define>
+  <define name="attlist.field" combine="interleave">
+    <optional>
+      <attribute name="filename"/>
+    </optional>
+  </define>
   <define name="image">
     <element name="image">
       <ref name="attlist.image"/>
diff --git a/trytond/ir/ui/menu.py b/trytond/ir/ui/menu.py
index edb7039..2d3b45e 100644
--- a/trytond/ir/ui/menu.py
+++ b/trytond/ir/ui/menu.py
@@ -136,10 +136,12 @@ class UIMenu(ModelSQL, ModelView):
             self.raise_user_error('wrong_name', (self.name,))
 
     def get_rec_name(self, name):
-        if self.parent:
-            return self.parent.get_rec_name(name) + SEPARATOR + self.name
-        else:
-            return self.name
+        parent = self.parent
+        name = self.name
+        while parent:
+            name = parent.name + SEPARATOR + name
+            parent = parent.parent
+        return name
 
     @classmethod
     def search_rec_name(cls, name, clause):
diff --git a/trytond/ir/ui/tree.rnc b/trytond/ir/ui/tree.rnc
index e422d93..3500bc8 100644
--- a/trytond/ir/ui/tree.rnc
+++ b/trytond/ir/ui/tree.rnc
@@ -21,12 +21,14 @@ attlist.field &=
     | "many2one"
     | "date"
     | "one2many"
+    | "many2many"
     | "selection"
     | "float"
     | "numeric"
     | "float_time"
     | "integer"
     | "datetime"
+    | "time"
     | "boolean"
     | "text"
     | "url"
@@ -34,6 +36,9 @@ attlist.field &=
     | "callto"
     | "sip"
     | "progressbar"
+    | "reference"
+    | "one2one"
+    | "binary"
   }?
 attlist.field &=
   [ a:defaultValue = "0" ] attribute tree_invisible { "0" | "1" }?
@@ -53,6 +58,7 @@ attlist.field &= attribute float_time { text }?
 attlist.field &= [a:defaultValue = "0"] attribute pre_validate { "0" | "1" }?
 attlist.field &= [a:defaultValue = "1"] attribute completion { "0" | "1" }?
 attlist.field &= [a:defaultValue = "1"] attribute factor { text }?
+attlist.field &= attribute filename { text }?
 prefix = element prefix { attlist.affix, empty }
 suffix = element suffix { attlist.affix, empty }
 attlist.affix &= attribute string { text }?
@@ -64,6 +70,8 @@ attlist.button &=
   [ a:defaultValue = "Unknown" ] attribute string { text }?
 attlist.button &= attribute confirm { text }?
 attlist.button &= attribute name { text }
+attlist.button &=
+  [ a:defaultValue = "0" ] attribute tree_invisible { "0" | "1" }?
 data = element data { attlist.data, xpath+ }
 attlist.data &= empty
 xpath = element xpath { attlist.xpath,
diff --git a/trytond/ir/ui/tree.rng b/trytond/ir/ui/tree.rng
index 12e9ef3..85ecc64 100644
--- a/trytond/ir/ui/tree.rng
+++ b/trytond/ir/ui/tree.rng
@@ -83,12 +83,14 @@
           <value>many2one</value>
           <value>date</value>
           <value>one2many</value>
+          <value>many2many</value>
           <value>selection</value>
           <value>float</value>
           <value>numeric</value>
           <value>float_time</value>
           <value>integer</value>
           <value>datetime</value>
+          <value>time</value>
           <value>boolean</value>
           <value>text</value>
           <value>url</value>
@@ -96,6 +98,9 @@
           <value>callto</value>
           <value>sip</value>
           <value>progressbar</value>
+          <value>reference</value>
+          <value>one2one</value>
+          <value>binary</value>
         </choice>
       </attribute>
     </optional>
@@ -177,6 +182,11 @@
       <attribute name="factor" a:defaultValue="1"/>
     </optional>
   </define>
+  <define name="attlist.field" combine="interleave">
+    <optional>
+      <attribute name="filename"/>
+    </optional>
+  </define>
   <define name="prefix">
     <element name="prefix">
       <ref name="attlist.affix"/>
@@ -228,6 +238,16 @@
   <define name="attlist.button" combine="interleave">
     <attribute name="name"/>
   </define>
+  <define name="attlist.button" combine="interleave">
+    <optional>
+      <attribute name="tree_invisible" a:defaultValue="0">
+        <choice>
+          <value>0</value>
+          <value>1</value>
+        </choice>
+      </attribute>
+    </optional>
+  </define>
   <define name="data">
     <element name="data">
       <ref name="attlist.data"/>
diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py
index 614d48f..5e16398 100644
--- a/trytond/ir/ui/view.py
+++ b/trytond/ir/ui/view.py
@@ -196,8 +196,8 @@ class View(ModelSQL, ModelView):
         return views
 
     @classmethod
-    def write(cls, views, vals):
-        super(View, cls).write(views, vals)
+    def write(cls, views, values, *args):
+        super(View, cls).write(views, values, *args)
         # Restart the cache
         ModelView._fields_view_get_cache.clear()
 
@@ -260,8 +260,8 @@ class ViewTreeWidth(ModelSQL, ModelView):
         return res
 
     @classmethod
-    def write(cls, records, vals):
-        super(ViewTreeWidth, cls).write(records, vals)
+    def write(cls, records, values, *args):
+        super(ViewTreeWidth, cls).write(records, values, *args)
         ModelView._fields_view_get_cache.clear()
 
     @classmethod
diff --git a/trytond/ir/view/action_act_window_form.xml b/trytond/ir/view/action_act_window_form.xml
index 1b14858..2cef84f 100644
--- a/trytond/ir/view/action_act_window_form.xml
+++ b/trytond/ir/view/action_act_window_form.xml
@@ -28,8 +28,6 @@ this repository contains the full copyright notices and license terms. -->
             <field name="search_value" colspan="5"/>
             <label name="limit"/>
             <field name="limit"/>
-            <label name="auto_refresh"/>
-            <field name="auto_refresh"/>
             <label name="window_name"/>
             <field name="window_name"/>
         </page>
diff --git a/trytond/ir/view/module_config_wizard_done_form.xml b/trytond/ir/view/module_config_wizard_done_form.xml
new file mode 100644
index 0000000..9163601
--- /dev/null
+++ b/trytond/ir/view/module_config_wizard_done_form.xml
@@ -0,0 +1,9 @@
+<?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="Module configuration" col="2">
+    <image name="tryton-dialog-information" xexpand="0"
+        xfill="0"/>
+    <label string="The configuration is done." id="done"
+        yalign="0.0" xalign="0.0" xexpand="1"/>
+</form>
diff --git a/trytond/model/fields/char.py b/trytond/model/fields/char.py
index 64f1c1f..1168cac 100644
--- a/trytond/model/fields/char.py
+++ b/trytond/model/fields/char.py
@@ -1,16 +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 warnings
+
 from sql import Query, Expression
 
 from ...config import CONFIG
 from .field import Field, FieldTranslate, size_validate, SQLType
 
 
-def autocomplete_validate(value):
-    if value:
-        assert isinstance(value, list), 'autocomplete must be a list'
-
-
 class Char(FieldTranslate):
     '''
     Define a char field (``unicode``).
@@ -31,22 +28,16 @@ class Char(FieldTranslate):
             readonly=readonly, domain=domain, states=states, select=select,
             on_change=on_change, on_change_with=on_change_with,
             depends=depends, context=context, loading=loading)
-        self.__autocomplete = None
-        self.autocomplete = autocomplete if autocomplete else None
+        self.autocomplete = set()
+        if autocomplete:
+            warnings.warn('autocomplete argument is deprecated, use the '
+                'depends decorator', DeprecationWarning, stacklevel=2)
+            self.autocomplete |= set(autocomplete)
         self.translate = translate
         self.__size = None
         self.size = size
     __init__.__doc__ += Field.__init__.__doc__
 
-    def _get_autocomplete(self):
-        return self.__autocomplete
-
-    def _set_autocomplete(self, value):
-        autocomplete_validate(value)
-        self.__autocomplete = value
-
-    autocomplete = property(_get_autocomplete, _set_autocomplete)
-
     def _get_size(self):
         return self.__size
 
diff --git a/trytond/model/fields/field.py b/trytond/model/fields/field.py
index 4a47616..b20a204 100644
--- a/trytond/model/fields/field.py
+++ b/trytond/model/fields/field.py
@@ -1,6 +1,9 @@
 #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 collections import namedtuple
+import warnings
+from functools import wraps
+
 from sql import operators, Column, Literal, Select, CombiningQuery
 from sql.conditionals import Coalesce, NullIf
 from sql.operators import Concat
@@ -45,16 +48,6 @@ def states_validate(value):
                 'values of states must return boolean'
 
 
-def on_change_validate(value):
-    if value:
-        assert isinstance(value, list), 'on_change must be a list'
-
-
-def on_change_with_validate(value):
-    if value:
-        assert isinstance(value, list), 'on_change_with must be a list'
-
-
 def depends_validate(value):
     assert isinstance(value, list), 'depends must be a list'
 
@@ -70,6 +63,31 @@ def size_validate(value):
             assert value.types() == set([int]), \
                 'size must return integer'
 
+
+def depends(*fields, **kwargs):
+    methods = kwargs.pop('methods', None)
+    assert not kwargs
+
+    def decorator(func):
+        depends = getattr(func, 'depends', set())
+        depends |= set(fields)
+        setattr(func, 'depends', depends)
+
+        if methods:
+            depend_methods = getattr(func, 'depend_methods', set())
+            depend_methods |= set(methods)
+            setattr(func, 'depend_methods', depend_methods)
+
+        @wraps(func)
+        def wrapper(self, *args, **kwargs):
+            for field in fields:
+                if not hasattr(self, field):
+                    setattr(self, field, None)
+            return func(self, *args, **kwargs)
+        return wrapper
+    return decorator
+
+
 SQL_OPERATORS = {
     '=': operators.Equal,
     '!=': operators.NotEqual,
@@ -128,10 +146,18 @@ class Field(object):
         self.__states = None
         self.states = states or {}
         self.select = bool(select)
-        self.__on_change = None
-        self.on_change = on_change
-        self.__on_change_with = None
-        self.on_change_with = on_change_with
+        self.on_change = set()
+        if on_change:
+            warnings.warn('on_change argument is deprecated, '
+                'use the depends decorator',
+                DeprecationWarning, stacklevel=3)
+            self.on_change |= set(on_change)
+        self.on_change_with = set()
+        if on_change_with:
+            warnings.warn('on_change_with argument is deprecated, '
+                'use the depends decorator',
+                DeprecationWarning, stacklevel=3)
+            self.on_change_with |= set(on_change_with)
         self.__depends = None
         self.depends = depends or []
         self.__context = None
@@ -159,23 +185,6 @@ class Field(object):
 
     states = property(_get_states, _set_states)
 
-    def _get_on_change(self):
-        return self.__on_change
-
-    def _set_on_change(self, value):
-        on_change_validate(value)
-        self.__on_change = value
-    on_change = property(_get_on_change, _set_on_change)
-
-    def _get_on_change_with(self):
-        return self.__on_change_with
-
-    def _set_on_change_with(self, value):
-        on_change_with_validate(value)
-        self.__on_change_with = value
-
-    on_change_with = property(_get_on_change_with, _set_on_change_with)
-
     def _get_depends(self):
         return self.__depends
 
@@ -213,6 +222,9 @@ class Field(object):
     def sql_type(self):
         raise NotImplementedError
 
+    def sql_column(self, table):
+        return Column(table, self.name)
+
     def _domain_value(self, operator, value):
         if isinstance(value, (Select, CombiningQuery)):
             return value
@@ -235,8 +247,9 @@ class Field(object):
         "Return a SQL expression for the domain using tables"
         table, _ = tables[None]
         name, operator, value = domain
+        assert name == self.name
         Operator = SQL_OPERATORS[operator]
-        column = Column(table, name)
+        column = self.sql_column(table)
         expression = Operator(column, self._domain_value(operator, value))
         if isinstance(expression, operators.In) and not expression.right:
             expression = Literal(False)
@@ -247,17 +260,18 @@ class Field(object):
 
     def convert_order(self, name, tables, Model):
         "Return a SQL expression to order"
+        assert name == self.name
         table, _ = tables[None]
         method = getattr(Model, 'order_%s' % name, None)
         if method:
             return method(tables)
         else:
-            return [Column(table, name)]
+            return [self.sql_column(table)]
 
 
 class FieldTranslate(Field):
 
-    def __get_translation_join(self, Model, name,
+    def _get_translation_join(self, Model, name,
             translation, model, table):
         language = Transaction().language
         if Model.__name__ == 'ir.model':
@@ -302,11 +316,12 @@ class FieldTranslate(Field):
         translation = Translation.__table__()
         model = IrModel.__table__()
         name, operator, value = domain
-        join = self.__get_translation_join(Model, name,
+        join = self._get_translation_join(Model, name,
             translation, model, table)
         Operator = SQL_OPERATORS[operator]
+        assert name == self.name
         column = Coalesce(NullIf(translation.value, ''),
-            Column(table, name))
+            self.sql_column(table))
         where = Operator(column, self._domain_value(operator, value))
         if isinstance(where, operators.In) and not where.right:
             where = Literal(False)
@@ -322,13 +337,14 @@ class FieldTranslate(Field):
         if not self.translate:
             return super(FieldTranslate, self).convert_order(name, tables,
                 Model)
+        assert name == self.name
 
         table, _ = tables[None]
         key = name + '.translation'
         if key not in tables:
             translation = Translation.__table__()
             model = IrModel.__table__()
-            join = self.__get_translation_join(Model, name,
+            join = self._get_translation_join(Model, name,
                 translation, model, table)
             tables[key] = {
                 None: (join.right, join.condition),
@@ -336,6 +352,7 @@ class FieldTranslate(Field):
         else:
             translation, _ = tables[key][None]
 
-        return [Coalesce(NullIf(translation.value, ''), Column(table, name))]
+        return [Coalesce(NullIf(translation.value, ''),
+                self.sql_column(table))]
 
 SQLType = namedtuple('SQLType', 'base type')
diff --git a/trytond/model/fields/function.py b/trytond/model/fields/function.py
index 2ebc511..d5489b7 100644
--- a/trytond/model/fields/function.py
+++ b/trytond/model/fields/function.py
@@ -91,12 +91,16 @@ class Function(Field):
                 name = [name]
             return call(name)
 
-    def set(self, ids, Model, name, value):
+    def set(self, Model, name, ids, value, *args):
         '''
         Call the setter.
         '''
         if self.setter:
-            getattr(Model, self.setter)(Model.browse(ids), name, value)
+            # 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/many2many.py b/trytond/model/fields/many2many.py
index 8a94044..03e6b09 100644
--- a/trytond/model/fields/many2many.py
+++ b/trytond/model/fields/many2many.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 itertools import chain
-from sql import Cast, Literal, Column
+from sql import Cast, Literal
 from sql.functions import Substring, Position
 
 from .field import Field, size_validate
@@ -95,128 +95,129 @@ class Many2Many(Field):
             res[origin_id].append(getattr(relation, self.target).id)
         return dict((key, tuple(value)) for key, value in res.iteritems())
 
-    def set(self, ids, model, name, values):
+    def set(self, Model, name, ids, values, *args):
         '''
         Set the values.
 
         values: A list of tuples:
             (``create``, ``[{<field name>: value}, ...]``),
-            (``write``, ``<ids>``, ``{<field name>: value}``),
+            (``write``, [``<ids>``, ``{<field name>: value}``, ...]),
             (``delete``, ``<ids>``),
-            (``delete_all``),
-            (``unlink``, ``<ids>``),
+            (``remove``, ``<ids>``),
             (``add``, ``<ids>``),
-            (``unlink_all``),
-            (``set``, ``<ids>``)
+            (``copy``, ``<ids>``, ``[{<field name>: value}, ...]``)
         '''
         pool = Pool()
-        if not values:
-            return
         Relation = pool.get(self.relation_name)
         Target = self.get_target()
         origin_field = Relation._fields[self.origin]
+        relation_to_create = []
+        relation_to_delete = []
+        target_to_write = []
+        target_to_delete = []
 
         def search_clause(ids):
             if origin_field._type == 'reference':
-                references = ['%s,%s' % (model.__name__, x) for x in ids]
+                references = ['%s,%s' % (Model.__name__, x) for x in ids]
                 return (self.origin, 'in', references)
             else:
                 return (self.origin, 'in', ids)
 
         def field_value(record_id):
             if origin_field._type == 'reference':
-                return '%s,%s' % (model.__name__, record_id)
+                return '%s,%s' % (Model.__name__, record_id)
             else:
                 return record_id
 
-        for act in values:
-            if act[0] == 'create':
-                to_create = []
-                for record_id in ids:
-                    for new in Target.create(act[1]):
-                        to_create.append({
-                                self.origin: field_value(record_id),
-                                self.target: new.id,
-                                })
-                if to_create:
-                    Relation.create(to_create)
-            elif act[0] == 'write':
-                Target.write(Target.browse(act[1]), act[2])
-            elif act[0] == 'delete':
-                Target.delete(Target.browse(act[1]))
-            elif act[0] == 'delete_all':
+        def create(ids, vlist):
+            for record_id in ids:
+                for new in Target.create(vlist):
+                    relation_to_create.append({
+                            self.origin: field_value(record_id),
+                            self.target: new.id,
+                            })
+
+        def write(_, *args):
+            actions = iter(args)
+            target_to_write.extend(sum(((Target.browse(ids), values)
+                        for ids, values in zip(actions, actions)), ()))
+
+        def delete(_, target_ids):
+            target_to_delete.extend(Target.browse(target_ids))
+
+        def add(ids, target_ids):
+            target_ids = map(int, target_ids)
+            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]
                 relations = Relation.search([
                         search_clause(ids),
+                        (self.target, 'in', sub_ids),
                         ])
-                Target.delete([getattr(r, self.target) for r in relations
-                        if getattr(r, self.target)])
-            elif act[0] == 'unlink':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
-                if not target_ids:
+                for relation in relations:
+                    existing_ids.add(getattr(relation, self.target).id)
+            for new_id in target_ids:
+                if new_id in existing_ids:
                     continue
-                relations = []
-                for i in range(0, len(target_ids),
-                        Transaction().cursor.IN_MAX):
-                    sub_ids = target_ids[i:i + Transaction().cursor.IN_MAX]
-                    relations += Relation.search([
-                            search_clause(ids),
-                            (self.target, 'in', sub_ids),
-                            ])
-                Relation.delete(relations)
-            elif act[0] == 'add':
-                target_ids = list(act[1])
-                if not target_ids:
-                    continue
-                existing_ids = []
-                for i in range(0, len(target_ids),
-                        Transaction().cursor.IN_MAX):
-                    sub_ids = target_ids[i:i + Transaction().cursor.IN_MAX]
-                    relations = Relation.search([
+                for record_id in ids:
+                    relation_to_create.append({
+                            self.origin: field_value(record_id),
+                            self.target: new_id,
+                            })
+
+        def remove(ids, target_ids):
+            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]
+                relation_to_delete.extend(Relation.search([
                             search_clause(ids),
                             (self.target, 'in', sub_ids),
-                            ])
-                    for relation in relations:
-                        existing_ids.append(getattr(relation, self.target).id)
-                to_create = []
-                for new_id in (x for x in target_ids if x not in existing_ids):
-                    for record_id in ids:
-                        to_create.append({
-                                self.origin: field_value(record_id),
-                                self.target: new_id,
-                                })
-                if to_create:
-                    Relation.create(to_create)
-            elif act[0] == 'unlink_all':
-                targets = Relation.search([
-                        search_clause(ids),
-                        (self.target + '.id', '!=', None),
-                        ])
-                Relation.delete(targets)
-            elif act[0] == 'set':
-                if not act[1]:
-                    target_ids = []
-                else:
-                    target_ids = list(act[1])
-                targets2 = Relation.search([
-                        search_clause(ids),
-                        (self.target + '.id', '!=', None),
-                        ])
-                Relation.delete(targets2)
-
-                to_create = []
-                for new_id in target_ids:
-                    for record_id in ids:
-                        to_create.append({
-                                self.origin: field_value(record_id),
-                                self.target: new_id,
-                                })
-                if to_create:
-                    Relation.create(to_create)
-            else:
-                raise Exception('Bad arguments')
+                            ]))
+
+        def copy(ids, copy_ids, default=None):
+            copy_ids = map(int, copy_ids)
+
+            if default is None:
+                default = {}
+            default = default.copy()
+            copies = Target.browse(copy_ids)
+            for new in Target.copy(copies, default=default):
+                for record_id in ids:
+                    relation_to_create.append({
+                            self.origin: field_value(record_id),
+                            self.target: new.id,
+                            })
+
+        actions = {
+            'create': create,
+            'write': write,
+            'delete': delete,
+            'add': add,
+            'remove': remove,
+            'copy': copy,
+            }
+        args = iter((ids, values) + args)
+        for ids, values in zip(args, args):
+            if not values:
+                continue
+            for value in values:
+                action = value[0]
+                args = value[1:]
+                actions[action](ids, *args)
+        if relation_to_create:
+            Relation.create(relation_to_create)
+        if relation_to_delete:
+            Relation.delete(relation_to_delete)
+        if target_to_write:
+            Target.write(*target_to_write)
+        if target_to_delete:
+            Target.delete(target_to_delete)
 
     def get_target(self):
         'Return the target model'
@@ -266,7 +267,7 @@ class Many2Many(Field):
         name, operator, value = domain[:3]
 
         origin_field = Relation._fields[self.origin]
-        origin = Column(relation, self.origin)
+        origin = getattr(Relation, self.origin).sql_column(relation)
         origin_where = None
         if origin_field._type == 'reference':
             origin_where = origin.like(Model.__name__ + ',%')
@@ -274,13 +275,13 @@ class Many2Many(Field):
                     Position(',', origin) + Literal(1)),
                 Relation.id.sql_type().base)
 
+        target = getattr(Relation, self.target).sql_column(relation)
         if '.' not in name:
             if operator in ('child_of', 'not child_of'):
                 if Target != Model:
                     query = Target.search([(domain[3], 'child_of', value)],
                         order=[], query=True)
-                    where = (Column(relation, self.target).in_(query)
-                        & (Column(relation, self.origin) != None))
+                    where = (target.in_(query) & (origin != None))
                     if origin_where:
                         where &= origin_where
                     query = relation.select(origin, where=where)
@@ -323,7 +324,7 @@ class Many2Many(Field):
             _, target_name = name.split('.', 1)
         target_domain = [(target_name,) + tuple(domain[1:])]
         query = Target.search(target_domain, order=[], query=True)
-        where = Column(relation, self.target).in_(query)
+        where = target.in_(query)
         if origin_where:
             where &= origin_where
         query = relation.select(origin, where=where)
diff --git a/trytond/model/fields/many2one.py b/trytond/model/fields/many2one.py
index 84f5621..e180475 100644
--- a/trytond/model/fields/many2one.py
+++ b/trytond/model/fields/many2one.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 types import NoneType
-from sql import Column, Query, Expression
+from sql import Query, Expression
 from sql.operators import Or
 
 from .field import Field, SQLType
@@ -99,8 +99,9 @@ class Many2One(Field):
         table, _ = tables[None]
         name, operator, ids = domain
         red_sql = reduce_ids(table.id, ids)
-        left = Column(table, self.left)
-        right = Column(table, self.right)
+        Target = self.get_target()
+        left = getattr(Target, self.left).sql_column(table)
+        right = getattr(Target, self.right).sql_column(table)
         cursor.execute(*table.select(left, right, where=red_sql))
         where = Or()
         for l, r in cursor.fetchall():
@@ -133,7 +134,7 @@ class Many2One(Field):
         Target = self.get_target()
         table, _ = tables[None]
         name, operator, value = domain[:3]
-        column = Column(table, name.split('.', 1)[0])
+        column = self.sql_column(table)
         if '.' not in name:
             if operator in ('child_of', 'not child_of'):
                 if Target != Model:
@@ -180,6 +181,7 @@ class Many2One(Field):
     def convert_order(self, name, tables, Model):
         if getattr(Model, 'order_%s' % name, None):
             return super(Many2One, self).convert_order(name, tables, Model)
+        assert name == self.name
 
         Target = self.get_target()
 
@@ -195,7 +197,7 @@ class Many2One(Field):
         if target_tables is None:
             target = Target.__table__()
             target_tables = {
-                None: (target, target.id == Column(table, name)),
+                None: (target, target.id == self.sql_column(table)),
                 }
             tables[name] = target_tables
         return ofield.convert_order(oname, target_tables, Target)
diff --git a/trytond/model/fields/numeric.py b/trytond/model/fields/numeric.py
index 38b87d7..359a6a0 100644
--- a/trytond/model/fields/numeric.py
+++ b/trytond/model/fields/numeric.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 decimal import Decimal
-from sql import Query, Expression
+from sql import Query, Expression, Cast, Literal, Select, CombiningQuery
 
 from ...config import CONFIG
 from .field import SQLType
@@ -30,3 +30,25 @@ class Numeric(Float):
         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']
+        if db_type == 'sqlite':
+            # Must be casted as Decimal is stored as bytes
+            column = Cast(column, self.sql_type().base)
+        return column
+
+    def _domain_value(self, operator, value):
+        value = super(Numeric, self)._domain_value(operator, value)
+        db_type = CONFIG['db_type']
+        if db_type == 'sqlite':
+            if isinstance(value, (Select, CombiningQuery)):
+                return value
+            # Must be casted as Decimal is adapted to bytes
+            type_ = self.sql_type().base
+            if operator in ('in', 'not in'):
+                return [Cast(Literal(v), type_) for v in value]
+            else:
+                return Cast(Literal(value), type_)
+        return value
diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py
index bb9f451..f9c4f2a 100644
--- a/trytond/model/fields/one2many.py
+++ b/trytond/model/fields/one2many.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 itertools import chain
-from sql import Cast, Literal, Column
+from sql import Cast, Literal
 from sql.functions import Substring, Position
 
 from .field import Field, size_validate
@@ -102,102 +102,110 @@ class One2Many(Field):
             res[origin_id].append(target.id)
         return dict((key, tuple(value)) for key, value in res.iteritems())
 
-    def set(self, ids, model, name, values):
+    def set(self, Model, name, ids, values, *args):
         '''
         Set the values.
         values: A list of tuples:
             (``create``, ``[{<field name>: value}, ...]``),
-            (``write``, ``<ids>``, ``{<field name>: value}``),
+            (``write``, [``<ids>``, ``{<field name>: value}``, ...]),
             (``delete``, ``<ids>``),
-            (``delete_all``),
-            (``unlink``, ``<ids>``),
             (``add``, ``<ids>``),
-            (``unlink_all``),
-            (``set``, ``<ids>``)
+            (``remove``, ``<ids>``),
+            (``copy``, ``<ids>``, ``[{<field name>: value}, ...]``)
         '''
-        if not values:
-            return
         Target = self.get_target()
         field = Target._fields[self.field]
+        to_create = []
+        to_write = []
+        to_delete = []
 
         def search_clause(ids):
             if field._type == 'reference':
-                references = ['%s,%s' % (model.__name__, x) for x in ids]
+                references = ['%s,%s' % (Model.__name__, x) for x in ids]
                 return (self.field, 'in', references)
             else:
                 return (self.field, 'in', ids)
 
         def field_value(record_id):
             if field._type == 'reference':
-                return '%s,%s' % (model.__name__, record_id)
+                return '%s,%s' % (Model.__name__, record_id)
             else:
                 return record_id
 
-        for act in values:
-            if act[0] == 'create':
-                to_create = []
-                for record_id in ids:
-                    value = field_value(record_id)
-                    for vals in act[1]:
-                        vals = vals.copy()
-                        vals[self.field] = value
-                        to_create.append(vals)
-                if to_create:
-                    Target.create(to_create)
-            elif act[0] == 'write':
-                Target.write(Target.browse(act[1]), act[2])
-            elif act[0] == 'delete':
-                Target.delete(Target.browse(act[1]))
-            elif act[0] == 'delete_all':
-                targets = Target.search([
-                        search_clause(ids),
-                        ])
-                Target.delete(targets)
-            elif act[0] == 'unlink':
-                target_ids = map(int, act[1])
-                if not target_ids:
-                    continue
-                targets = Target.search([
-                        search_clause(ids),
-                        ('id', 'in', target_ids),
-                        ])
-                Target.write(targets, {
-                        self.field: None,
-                        })
-            elif act[0] == 'add':
-                target_ids = map(int, act[1])
-                if not target_ids:
-                    continue
-                for record_id in ids:
-                    Target.write(Target.browse(target_ids), {
+        def create(ids, vlist):
+            for record_id in ids:
+                value = field_value(record_id)
+                for values in vlist:
+                    values = values.copy()
+                    values[self.field] = value
+                    to_create.append(values)
+
+        def write(_, *args):
+            actions = iter(args)
+            to_write.extend(sum(((Target.browse(ids), values)
+                        for ids, values in zip(actions, actions)), ()))
+
+        def delete(_, target_ids):
+            to_delete.extend(Target.browse(target_ids))
+
+        def add(ids, target_ids):
+            target_ids = map(int, target_ids)
+            if not target_ids:
+                return
+            targets = Target.browse(target_ids)
+            for record_id in ids:
+                to_write.extend((targets, {
                             self.field: field_value(record_id),
-                            })
-            elif act[0] == 'unlink_all':
+                            }))
+
+        def remove(ids, target_ids):
+            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]
                 targets = Target.search([
                         search_clause(ids),
+                        ('id', 'in', sub_ids),
                         ])
-                Target.write(targets, {
-                        self.field: None,
-                        })
-            elif act[0] == 'set':
-                if not act[1]:
-                    target_ids = [-1]
-                else:
-                    target_ids = map(int, act[1])
-                for record_id in ids:
-                    targets = Target.search([
-                            search_clause([record_id]),
-                            ('id', 'not in', target_ids),
-                            ])
-                    Target.write(targets, {
+                to_write.extend((targets, {
                             self.field: None,
-                            })
-                    if act[1]:
-                        Target.write(Target.browse(target_ids), {
-                                self.field: field_value(record_id),
-                                })
-            else:
-                raise Exception('Bad arguments')
+                            }))
+
+        def copy(ids, copy_ids, default=None):
+            copy_ids = map(int, copy_ids)
+
+            if default is None:
+                default = {}
+            default = default.copy()
+            copies = Target.browse(copy_ids)
+            for record_id in ids:
+                default[self.field] = field_value(record_id)
+                Target.copy(copies, default=default)
+
+        actions = {
+            'create': create,
+            'write': write,
+            'delete': delete,
+            'add': add,
+            'remove': remove,
+            'copy': copy,
+            }
+        args = iter((ids, values) + args)
+        for ids, values in zip(args, args):
+            if not values:
+                continue
+            for value in values:
+                action = value[0]
+                args = value[1:]
+                actions[action](ids, *args)
+        if to_create:
+            Target.create(to_create)
+        if to_write:
+            Target.write(*to_write)
+        if to_delete:
+            Target.delete(to_delete)
 
     def get_target(self):
         'Return the target Model'
@@ -223,7 +231,7 @@ class One2Many(Field):
         name, operator, value = domain[:3]
 
         origin_field = Target._fields[self.field]
-        origin = Column(target, self.field)
+        origin = getattr(Target, self.field).sql_column(target)
         origin_where = None
         if origin_field._type == 'reference':
             origin_where = origin.like(Model.__name__ + ',%')
diff --git a/trytond/model/fields/one2one.py b/trytond/model/fields/one2one.py
index eea5ac8..7e29afd 100644
--- a/trytond/model/fields/one2one.py
+++ b/trytond/model/fields/one2one.py
@@ -28,30 +28,31 @@ class One2One(Many2Many):
             res[i] = vals[0] if vals else False
         return res
 
-    def set(self, ids, model, name, value):
+    def set(self, Model, name, ids, value, *args):
         '''
         Set the values.
-
-        :param ids: A list of ids
-        :param model: A string with the name of the model
-        :param name: A string with the name of the field
-        :param value: The id to link
         '''
         pool = Pool()
         Relation = pool.get(self.relation_name)
-        relations = Relation.search([
-                (self.origin, 'in', ids),
-                ])
-        Relation.delete(relations)
-        if value:
-            to_create = []
-            for record_id in ids:
-                to_create.append({
-                        self.origin: record_id,
-                        self.target: value,
-                        })
-            if to_create:
-                Relation.create(to_create)
+        to_delete = []
+        to_create = []
+        args = iter((ids, value) + args)
+        for ids, value in zip(args, args):
+            relations = Relation.search([
+                    (self.origin, 'in', ids),
+                    ])
+            to_delete.extend(relations)
+            if value:
+                to_create = []
+                for record_id in ids:
+                    to_create.append({
+                            self.origin: record_id,
+                            self.target: value,
+                            })
+        if to_delete:
+            Relation.delete(to_delete)
+        if to_create:
+            Relation.create(to_create)
 
     def __set__(self, inst, value):
         Target = self.get_target()
diff --git a/trytond/model/fields/property.py b/trytond/model/fields/property.py
index 568b845..13737dd 100644
--- a/trytond/model/fields/property.py
+++ b/trytond/model/fields/property.py
@@ -46,23 +46,21 @@ class Property(Function):
         Property = pool.get('ir.property')
         return Property.get(name, model.__name__, ids)
 
-    def set(self, ids, model, name, value):
+    def set(self, Model, name, ids, value, *args):
         '''
         Set the property.
-
-        :param ids: A list of ids.
-        :param model: The model.
-        :param name: The name of the field.
-        :param value: The value to set.
         '''
         pool = Pool()
         Property = pool.get('ir.property')
-        if value is not None:
-            prop_value = '%s,%s' % (getattr(self, 'model_name', ''),
-                str(value))
-        else:
-            prop_value = None
-        Property.set(name, model.__name__, ids, prop_value)
+        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 62c4bad..1e3b378 100644
--- a/trytond/model/fields/reference.py
+++ b/trytond/model/fields/reference.py
@@ -1,8 +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.
-import contextlib
 from types import NoneType
-from sql import Cast, Literal, Column, Query, Expression
+from sql import Cast, Literal, Query, Expression
 from sql.functions import Substring, Position
 
 from .field import Field, SQLType
@@ -68,8 +67,8 @@ class Reference(Field):
             ref_to_check[ref_model][1].append(i)
 
         # Check if reference ids still exist
-        with contextlib.nested(Transaction().set_context(active_test=False),
-                Transaction().set_user(0)):
+        with Transaction().set_context(active_test=False), \
+                Transaction().set_user(0):
             for ref_model, (ref_ids, ids) in ref_to_check.iteritems():
                 try:
                     pool.get(ref_model)
@@ -120,7 +119,8 @@ class Reference(Field):
         Target = pool.get(target)
         table, _ = tables[None]
         name, target_name = name.split('.', 1)
-        column = Column(table, name)
+        assert name == self.name
+        column = self.sql_column(table)
         target_domain = [(target_name,) + tuple(domain[1:3])
             + tuple(domain[4:])]
         if 'active' in Target._fields:
diff --git a/trytond/model/fields/selection.py b/trytond/model/fields/selection.py
index ba44c00..230e153 100644
--- a/trytond/model/fields/selection.py
+++ b/trytond/model/fields/selection.py
@@ -1,6 +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 sql import Column
+import warnings
+
 from sql.conditionals import Case
 
 from ...config import CONFIG
@@ -32,7 +33,12 @@ class Selection(Field):
             self.selection = selection.copy()
         else:
             self.selection = selection
-        self.selection_change_with = selection_change_with
+        self.selection_change_with = set()
+        if selection_change_with:
+            warnings.warn('selection_change_with argument is deprecated, '
+                'use the depends decorator',
+                DeprecationWarning, stacklevel=2)
+            self.selection_change_with |= set(selection_change_with)
         self.sort = sort
         self.translate_selection = translate
     __init__.__doc__ += Field.__init__.__doc__
@@ -47,11 +53,12 @@ class Selection(Field):
         if getattr(Model, 'order_%s' % name, None):
             return super(Selection, self).convert_order(name, tables, Model)
 
+        assert name == self.name
         table, _ = tables[None]
         selections = Model.fields_get([name])[name]['selection']
         if not isinstance(selections, (tuple, list)):
             selections = getattr(Model, selections)()
-        column = Column(table, name)
+        column = self.sql_column(table)
         whens = []
         for key, value in selections:
             whens.append((column == key, value))
diff --git a/trytond/model/model.py b/trytond/model/model.py
index d105038..a6426bf 100644
--- a/trytond/model/model.py
+++ b/trytond/model/model.py
@@ -35,12 +35,39 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
             }
         cls._error_messages = {}
 
-        # Copy fields
+        if hasattr(cls, '__depend_methods'):
+            cls.__depend_methods = cls.__depend_methods.copy()
+        else:
+            cls.__depend_methods = collections.defaultdict(set)
+
+        # Copy fields and update depends
         for attr in dir(cls):
             if attr.startswith('_'):
                 continue
-            if isinstance(getattr(cls, attr), fields.Field):
-                setattr(cls, attr, copy.deepcopy(getattr(cls, attr)))
+            if not isinstance(getattr(cls, attr), fields.Field):
+                continue
+            field_name = attr
+            field = copy.deepcopy(getattr(cls, field_name))
+            setattr(cls, field_name, field)
+
+            for attribute in ('on_change', 'on_change_with', 'autocomplete',
+                    'selection_change_with'):
+                if attribute == 'selection_change_with':
+                    if isinstance(
+                            getattr(field, 'selection', None), basestring):
+                        function_name = field.selection
+                    else:
+                        continue
+                else:
+                    function_name = '%s_%s' % (attribute, field_name)
+                function = getattr(cls, function_name, None)
+                if function:
+                    if getattr(function, 'depends', None):
+                        setattr(field, attribute,
+                            getattr(field, attribute) | function.depends)
+                    if getattr(function, 'depend_methods', None):
+                        cls.__depend_methods[(field_name, attribute)] |= \
+                            function.depend_methods
 
     @classmethod
     def __post_setup__(cls):
@@ -78,9 +105,19 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
 
             for attribute in ('on_change', 'on_change_with', 'autocomplete'):
                 function_name = '%s_%s' % (attribute, field_name)
-                if getattr(field, attribute, False):
+                if getattr(cls, function_name, None):
                     cls.__rpc__.setdefault(function_name, RPC(instantiate=0))
 
+        # Update depend on methods
+        for (field_name, attribute), others in (
+                cls.__depend_methods.iteritems()):
+            field = getattr(cls, field_name)
+            for other in others:
+                other_field = getattr(cls, other)
+                setattr(field, attribute,
+                    getattr(field, attribute)
+                    | getattr(other_field, attribute))
+
         # Set name to fields
         for name, field in cls._fields.iteritems():
             if field.name is None:
@@ -120,10 +157,12 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
         Translation.register_error_messages(cls, module_name)
 
     @classmethod
-    def default_get(cls, fields_names, with_rec_name=True):
+    def default_get(cls, fields_names, with_rec_name=True,
+            with_on_change=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')
@@ -147,7 +186,8 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                     value[field_name + '.rec_name'] = Target(
                         value[field_name]).rec_name
 
-        value = cls._default_on_change(value)
+        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'):
@@ -186,6 +226,7 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
         pool = Pool()
         Translation = pool.get('ir.translation')
         FieldAccess = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
 
         #Add translation to cache
         language = Transaction().language
@@ -236,10 +277,15 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                     'loading',
                     'filename',
                     'selection_change_with',
+                    'domain',
                     ):
                 if getattr(cls._fields[field], arg, None) is not None:
-                    res[field][arg] = copy.copy(getattr(cls._fields[field],
-                        arg))
+                    value = getattr(cls._fields[field], arg)
+                    if isinstance(value, set):
+                        value = list(value)
+                    else:
+                        value = copy.copy(value)
+                    res[field][arg] = value
             if not accesses.get(field, {}).get('write', True):
                 res[field]['readonly'] = True
                 if res[field].get('states') and \
@@ -308,7 +354,6 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                     relation = copy.copy(
                         cls._fields[field].get_target().__name__)
                 res[field]['relation'] = relation
-                res[field]['domain'] = copy.copy(cls._fields[field].domain)
                 res[field]['context'] = copy.copy(cls._fields[field].context)
                 res[field]['create'] = accesses.get(field, {}).get('create',
                     True)
@@ -318,6 +363,14 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                     and hasattr(cls._fields[field], 'field'):
                 res[field]['relation_field'] = copy.copy(
                         cls._fields[field].field)
+            if res[field]['type'] == 'many2one':
+                target = cls._fields[field].get_target()
+                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
             if res[field]['type'] in ('datetime', 'time'):
                 res[field]['format'] = copy.copy(cls._fields[field].format)
             if res[field]['type'] == 'selection':
@@ -337,11 +390,13 @@ class Model(WarningErrorMixin, URLMixin, PoolBase):
                 if attr in res[field]:
                     res[field][attr] = encoder.encode(res[field][attr])
 
-        if fields_names:
+        for i in res.keys():
             # filter out fields which aren't in the fields_names list
-            for i in res.keys():
+            if fields_names:
                 if i not in fields_names:
                     del res[i]
+            elif not ModelAccess.check_relation(cls.__name__, i, mode='read'):
+                del res[i]
         return res
 
     def on_change_with(self, fieldnames):
diff --git a/trytond/model/modelsingleton.py b/trytond/model/modelsingleton.py
index 266e187..f9ff034 100644
--- a/trytond/model/modelsingleton.py
+++ b/trytond/model/modelsingleton.py
@@ -49,11 +49,15 @@ class ModelSingleton(ModelStorage):
         return res
 
     @classmethod
-    def write(cls, records, values):
+    def write(cls, records, values, *args):
         singleton = cls.get_singleton()
         if not singleton:
-            return cls.create([values])
-        return super(ModelSingleton, cls).write([singleton], values)
+            singleton, = cls.create([values])
+        actions = (records, values) + args
+        args = []
+        for values in actions[1:None:2]:
+            args.extend(([singleton], values))
+        return super(ModelSingleton, cls).write(*args)
 
     @classmethod
     def delete(cls, records):
@@ -79,12 +83,13 @@ class ModelSingleton(ModelStorage):
         return res
 
     @classmethod
-    def default_get(cls, fields_names, with_rec_name=True):
+    def default_get(cls, fields_names, with_rec_name=True,
+            with_on_change=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_rec_name=with_rec_name, with_on_change=with_on_change)
         singleton = cls.get_singleton()
         if singleton:
             if with_rec_name:
diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py
index 798a893..58b7dc7 100644
--- a/trytond/model/modelsql.py
+++ b/trytond/model/modelsql.py
@@ -1,9 +1,9 @@
 #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 contextlib
 import re
+import datetime
 from functools import reduce
-from itertools import islice, izip
+from itertools import islice, izip, chain
 
 from sql import Table, Column, Literal, Desc, Asc, Expression, Flavor
 from sql.functions import Now, Extract
@@ -11,7 +11,7 @@ from sql.conditionals import Coalesce
 from sql.operators import Or, And, Operator
 from sql.aggregate import Count, Max
 
-from trytond.model import ModelStorage
+from trytond.model import ModelStorage, ModelView
 from trytond.model import fields
 from trytond import backend
 from trytond.tools import reduce_ids
@@ -20,6 +20,7 @@ from trytond.transaction import Transaction
 from trytond.pool import Pool
 from trytond.cache import LRUDict
 from trytond.exceptions import ConcurrencyException
+from trytond.rpc import RPC
 _RE_UNIQUE = re.compile('UNIQUE\s*\((.*)\)', re.I)
 _RE_CHECK = re.compile('CHECK\s*\((.*)\)', re.I)
 
@@ -39,6 +40,10 @@ class ModelSQL(ModelStorage):
         cls._sql_constraints = []
         cls._order = [('id', 'ASC')]
         cls._sql_error_messages = {}
+        if issubclass(cls, ModelView):
+            cls.__rpc__.update({
+                    'history_revisions': RPC(),
+                    })
 
         if not cls._table:
             cls._table = cls.__name__.replace('.', '_')
@@ -218,8 +223,10 @@ class ModelSQL(ModelStorage):
                 if not ((target_records
                             or (values[field_name] in create_records))
                         and (values[field_name] not in delete_records)):
+                    error_args = cls._get_error_args(field_name)
+                    error_args['value'] = values[field_name]
                     cls.raise_user_error('foreign_model_missing',
-                        error_args=cls._get_error_args(field_name))
+                        error_args=error_args)
         for name, _, error in cls._sql_constraints:
             if name in exception[0]:
                 cls.raise_user_error(error)
@@ -228,6 +235,40 @@ class ModelSQL(ModelStorage):
                 cls.raise_user_error(error)
 
     @classmethod
+    def history_revisions(cls, ids):
+        pool = Pool()
+        ModelAccess = pool.get('ir.model.access')
+        User = pool.get('res.user')
+        cursor = Transaction().cursor
+
+        ModelAccess.check(cls.__name__, 'read')
+
+        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]
+            where = reduce_ids(table.id, sub_ids)
+            cursor.execute(*table.join(user, 'LEFT',
+                    Coalesce(table.write_uid, table.create_uid) == user.id)
+                .select(
+                    Coalesce(table.write_date, table.create_date),
+                    table.id,
+                    user.name,
+                    where=where))
+            revisions.append(cursor.fetchall())
+        revisions = list(chain(*revisions))
+        revisions.sort(reverse=True)
+        # SQLite uses char for COALESCE
+        if revisions and isinstance(revisions[0][0], basestring):
+            strptime = datetime.datetime.strptime
+            format_ = '%Y-%m-%d %H:%M:%S.%f'
+            revisions = [(strptime(timestamp, format_), id_, name)
+                for timestamp, id_, name in revisions]
+        return revisions
+
+    @classmethod
     def __insert_history(cls, ids, deleted=False):
         transaction = Transaction()
         cursor = transaction.cursor
@@ -263,6 +304,62 @@ class ModelSQL(ModelStorage):
                         [[id_, Now(), user] for id_ in sub_ids]))
 
     @classmethod
+    def restore_history(cls, ids, datetime):
+        'Restore record ids from history at the date time'
+        if not cls._history:
+            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
+            columns.append(Column(table, fname))
+            if fname == 'write_uid':
+                hcolumns.append(Literal(transaction.user))
+            elif fname == 'write_date':
+                hcolumns.append(Now())
+            else:
+                hcolumns.append(Column(history, fname))
+
+        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
+            cursor.execute(*history.select(*hcolumns,
+                    where=hwhere, order_by=horder, limit=1))
+            values = cursor.fetchone()
+            if not values:
+                to_delete.append(id_)
+            else:
+                to_update.append(id_)
+                values = list(values)
+                cursor.execute(*table.update(columns, values,
+                        where=table.id == id_))
+                rowcount = cursor.rowcount
+                if rowcount == -1 or rowcount is None:
+                    cursor.execute(*table.select(table.id,
+                            where=table.id == id_))
+                    rowcount = len(cursor.fetchall())
+                if rowcount < 1:
+                    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]
+                where = reduce_ids(table.id, sub_ids)
+                cursor.execute(*table.delete(where=where))
+            cls.__insert_history(to_delete, True)
+        if to_update:
+            cls.__insert_history(to_update)
+
+    @classmethod
     def __check_timestamp(cls, ids):
         transaction = Transaction()
         cursor = transaction.cursor
@@ -325,7 +422,8 @@ class ModelSQL(ModelStorage):
                     default.append(f)
 
             if default:
-                defaults = cls.default_get(default, with_rec_name=False)
+                defaults = cls.default_get(default, with_rec_name=False,
+                    with_on_change=False)
                 values.update(cls._clean_defaults(defaults))
 
             insert_columns = [table.create_uid, table.create_date]
@@ -356,8 +454,8 @@ class ModelSQL(ModelStorage):
                         id_new = cursor.lastid()
                 new_ids.append(id_new)
             except DatabaseIntegrityError, exception:
-                with contextlib.nested(Transaction().new_cursor(),
-                        Transaction().set_user(0)):
+                with Transaction().new_cursor(), \
+                        Transaction().set_user(0):
                     cls.__raise_integrity_error(exception, values)
                 raise
 
@@ -377,6 +475,7 @@ class ModelSQL(ModelStorage):
             set()).update(new_ids)
 
         translation_values = {}
+        fields_to_set = {}
         for values, new_id in izip(vlist, new_ids):
             for fname, value in values.iteritems():
                 field = cls._fields[fname]
@@ -385,17 +484,23 @@ class ModelSQL(ModelStorage):
                     translation_values.setdefault(
                         '%s,%s' % (cls.__name__, fname), {})[new_id] = value
                 if hasattr(field, 'set'):
-                    field.set([new_id], cls, fname, value)
+                    fields_to_set.setdefault(fname, []).extend(
+                        ([new_id], value))
 
         if translation_values:
             for name, translations in translation_values.iteritems():
                 Translation.set_ids(name, 'model', Transaction().language,
                     translations.keys(), translations.values())
 
+        for fname, fargs in fields_to_set.iteritems():
+            field = cls._fields[fname]
+            field.set(cls, fname, *fargs)
+
         cls.__insert_history(new_ids)
 
         records = cls.browse(new_ids)
-        cls._validate(records)
+        for i in range(0, len(records), RECORD_CACHE_SIZE):
+            cls._validate(records[i:i + RECORD_CACHE_SIZE])
 
         field_names = cls._fields.keys()
         cls._update_mptt(field_names, [new_ids] * len(field_names))
@@ -408,12 +513,16 @@ class ModelSQL(ModelStorage):
         pool = Pool()
         Rule = pool.get('ir.rule')
         Translation = pool.get('ir.translation')
+        ModelAccess = pool.get('ir.model.access')
+        if not fields_names:
+            fields_names = []
+            for field_name in cls._fields.keys():
+                if ModelAccess.check_relation(cls.__name__, field_name,
+                        mode='read'):
+                    fields_names.append(field_name)
         super(ModelSQL, cls).read(ids, fields_names=fields_names)
         cursor = Transaction().cursor
 
-        if not fields_names:
-            fields_names = cls._fields.keys()
-
         if not ids:
             return []
 
@@ -449,7 +558,7 @@ class ModelSQL(ModelStorage):
             table = cls.__table_history__()
             column = Coalesce(table.write_date, table.create_date)
             history_clause = (column <= Transaction().context['_datetime'])
-            history_order = column.desc
+            history_order = (column.desc, Column(table, '__id').desc)
             history_limit = 1
 
         columns = []
@@ -629,94 +738,106 @@ class ModelSQL(ModelStorage):
         return result
 
     @classmethod
-    def write(cls, records, values):
+    def write(cls, records, values, *args):
         DatabaseIntegrityError = backend.get('DatabaseIntegrityError')
         transaction = Transaction()
         cursor = transaction.cursor
         pool = Pool()
         Translation = pool.get('ir.translation')
         Config = pool.get('ir.configuration')
-        ids = map(int, records)
         in_max = cursor.IN_MAX
 
+        assert not len(args) % 2
+        all_records = sum(((records, values) + args)[0:None:2], [])
+        all_ids = [r.id for r in all_records]
+        all_field_names = set()
+
         # Call before cursor cache cleaning
         trigger_eligibles = cls.trigger_write_get_eligibles(records)
 
-        super(ModelSQL, cls).write(records, values)
-
-        if not records:
-            return
+        super(ModelSQL, cls).write(records, values, *args)
 
         if cls.table_query():
             return
         table = cls.__table__()
 
-        values = values.copy()
+        cls.__check_timestamp(all_ids)
 
-        cls.__check_timestamp(ids)
+        fields_to_set = {}
+        actions = iter((records, values) + args)
+        for records, values in zip(actions, actions):
+            ids = [r.id for r in records]
+            values = values.copy()
 
-        # Clean values
-        for key in ('create_uid', 'create_date',
-                'write_uid', 'write_date', 'id'):
-            if key in values:
-                del values[key]
+            # Clean values
+            for key in ('create_uid', 'create_date',
+                    'write_uid', 'write_date', 'id'):
+                if key in values:
+                    del values[key]
 
-        columns = [table.write_uid, table.write_date]
-        update_values = [transaction.user, Now()]
-        store_translation = Transaction().language == Config.get_language()
-        for fname, value in values.iteritems():
-            field = cls._fields[fname]
-            if not hasattr(field, 'set'):
-                if not getattr(field, 'translate', False) or store_translation:
-                    columns.append(Column(table, fname))
-                    update_values.append(field.sql_format(value))
+            columns = [table.write_uid, table.write_date]
+            update_values = [transaction.user, Now()]
+            store_translation = Transaction().language == Config.get_language()
+            for fname, value in values.iteritems():
+                field = cls._fields[fname]
+                if not hasattr(field, 'set'):
+                    if (not getattr(field, 'translate', False)
+                            or store_translation):
+                        columns.append(Column(table, fname))
+                        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]
-            red_sql = reduce_ids(table.id, sub_ids)
-            where = red_sql
-            if domain:
-                where &= table.id.in_(domain)
-            cursor.execute(*table.select(table.id, where=where))
-            rowcount = cursor.rowcount
-            if rowcount == -1 or rowcount is None:
-                rowcount = len(cursor.fetchall())
-            if not rowcount == len({}.fromkeys(sub_ids)):
+            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]
+                red_sql = reduce_ids(table.id, sub_ids)
+                where = red_sql
                 if domain:
-                    cursor.execute(*table.select(table.id, where=red_sql))
-                    rowcount = cursor.rowcount
-                    if rowcount == -1 or rowcount is None:
-                        rowcount = len(cursor.fetchall())
-                    if rowcount == len({}.fromkeys(sub_ids)):
-                        cls.raise_user_error('access_error', cls.__name__)
-                cls.raise_user_error('write_error', cls.__name__)
-            try:
-                cursor.execute(*table.update(columns, update_values,
-                        where=red_sql))
-            except DatabaseIntegrityError, exception:
-                with contextlib.nested(Transaction().new_cursor(),
-                        Transaction().set_user(0)):
-                    cls.__raise_integrity_error(exception, values,
-                        values.keys())
-                raise
+                    where &= table.id.in_(domain)
+                cursor.execute(*table.select(table.id, where=where))
+                rowcount = cursor.rowcount
+                if rowcount == -1 or rowcount is None:
+                    rowcount = len(cursor.fetchall())
+                if not rowcount == len({}.fromkeys(sub_ids)):
+                    if domain:
+                        cursor.execute(*table.select(table.id, where=red_sql))
+                        rowcount = cursor.rowcount
+                        if rowcount == -1 or rowcount is None:
+                            rowcount = len(cursor.fetchall())
+                        if rowcount == len({}.fromkeys(sub_ids)):
+                            cls.raise_user_error('access_error', cls.__name__)
+                    cls.raise_user_error('write_error', cls.__name__)
+                try:
+                    cursor.execute(*table.update(columns, update_values,
+                            where=red_sql))
+                except DatabaseIntegrityError, exception:
+                    with Transaction().new_cursor(), \
+                            Transaction().set_user(0):
+                        cls.__raise_integrity_error(exception, values,
+                            values.keys())
+                    raise
 
-        for fname, value in values.iteritems():
-            field = cls._fields[fname]
-            if (getattr(field, 'translate', False)
-                    and not hasattr(field, 'set')):
-                Translation.set_ids(
-                    '%s,%s' % (cls.__name__, fname), 'model',
-                    transaction.language, ids, [value] * len(ids))
-            if hasattr(field, 'set'):
-                field.set(ids, cls, fname, value)
+            for fname, value in values.iteritems():
+                field = cls._fields[fname]
+                if (getattr(field, 'translate', False)
+                        and not hasattr(field, 'set')):
+                    Translation.set_ids(
+                        '%s,%s' % (cls.__name__, fname), 'model',
+                        transaction.language, ids, [value] * len(ids))
+                if hasattr(field, 'set'):
+                    fields_to_set.setdefault(fname, []).extend((ids, value))
 
-        cls.__insert_history(ids)
-        cls._validate(records)
+            field_names = cls._fields.keys()
+            cls._update_mptt(field_names, [ids] * len(field_names), values)
+            all_field_names |= set(values.keys())
 
-        field_names = cls._fields.keys()
-        cls._update_mptt(field_names, [ids] * len(field_names), values)
+        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)
         cls.trigger_write(trigger_eligibles)
 
     @classmethod
@@ -836,12 +957,9 @@ class ModelSQL(ModelStorage):
                     if Model.search([
                                 (field_name, 'in', sub_ids),
                                 ], order=[]):
-                        error_args = []
-                        error_args.append(cls._get_error_args('id')[1])
-                        error_args.extend(list(
-                                Model._get_error_args(field_name)))
+                        error_args = Model._get_error_args(field_name)
                         cls.raise_user_error('foreign_model_exist',
-                            error_args=tuple(error_args))
+                            error_args=error_args)
 
             super(ModelSQL, cls).delete(sub_records)
 
@@ -884,11 +1002,6 @@ class ModelSQL(ModelStorage):
             forder = field.convert_order(fname, tables, cls)
             order_by.extend((Order(o) for o in forder))
 
-        if type(limit) not in (float, int, long, type(None)):
-            raise Exception('Error', 'Wrong limit type (%s)!' % type(limit))
-        if type(offset) not in (float, int, long, type(None)):
-            raise Exception('Error', 'Wrong offset type (%s)!' % type(offset))
-
         main_table, _ = tables[None]
 
         def convert_from(table, tables):
diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py
index 806db49..6bb5753 100644
--- a/trytond/model/modelstorage.py
+++ b/trytond/model/modelstorage.py
@@ -3,10 +3,6 @@
 
 import datetime
 import time
-import logging
-import contextlib
-import traceback
-import sys
 import csv
 import warnings
 try:
@@ -17,10 +13,11 @@ except ImportError:
 from decimal import Decimal
 from itertools import islice, ifilter, chain, izip
 from functools import reduce
+from operator import itemgetter
 
 from trytond.model import Model
 from trytond.model import fields
-from trytond.tools import safe_eval, reduce_domain
+from trytond.tools import safe_eval, 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
@@ -53,7 +50,8 @@ class ModelStorage(Model):
                     'create': RPC(readonly=False,
                         result=lambda r: map(int, r)),
                     'read': RPC(),
-                    'write': RPC(readonly=False, instantiate=0),
+                    'write': RPC(readonly=False,
+                        instantiate=slice(0, None, 2)),
                     'delete': RPC(readonly=False, instantiate=0),
                     'copy': RPC(readonly=False, instantiate=0,
                         result=lambda r: map(int, r)),
@@ -120,13 +118,19 @@ class ModelStorage(Model):
         ModelAccess = pool.get('ir.model.access')
         ModelFieldAccess = pool.get('ir.model.field.access')
 
+        if not fields_names:
+            fields_names = []
+            for field_name in cls._fields.keys():
+                if ModelAccess.check_relation(cls.__name__, field_name,
+                        mode='read'):
+                    fields_names.append(field_name)
+
         ModelAccess.check(cls.__name__, 'read')
-        ModelFieldAccess.check(cls.__name__,
-                fields_names or cls._fields.keys(), 'read')
+        ModelFieldAccess.check(cls.__name__, fields_names, 'read')
         return []
 
     @classmethod
-    def write(cls, records, values):
+    def write(cls, records, values, *args):
         '''
         Write values on records.
         '''
@@ -137,15 +141,21 @@ class ModelStorage(Model):
         ModelAccess.check(cls.__name__, 'write')
         ModelFieldAccess.check(cls.__name__,
             [x for x in values if x in cls._fields], 'write')
-        if not cls.check_xml_record(records, values):
-            cls.raise_user_error('write_xml_record',
-                    error_description='xml_record_desc')
+
+        assert not len(args) % 2
+        actions = iter((records, values) + args)
+        all_records = []
+        for records, values in zip(actions, actions):
+            if not cls.check_xml_record(records, values):
+                cls.raise_user_error('write_xml_record',
+                        error_description='xml_record_desc')
+            all_records += records
 
         # Increase transaction counter
         Transaction().counter += 1
 
         # Clean local cache
-        for record in records:
+        for record in all_records:
             local_cache = record._local_cache.get(record.id)
             if local_cache:
                 local_cache.clear()
@@ -153,7 +163,7 @@ class ModelStorage(Model):
         # Clean cursor cache
         for cache in Transaction().cursor.cache.itervalues():
             if cls.__name__ in cache:
-                for record in records:
+                for record in all_records:
                     if record.id in cache[cls.__name__]:
                         cache[cls.__name__][record.id].clear()
 
@@ -245,7 +255,6 @@ class ModelStorage(Model):
 
         def convert_data(field_defs, data):
             data = data.copy()
-            data_o2m = {}
             for field_name in field_defs:
                 ftype = field_defs[field_name]['type']
 
@@ -271,46 +280,26 @@ class ModelStorage(Model):
                         pass
                 elif ftype in ('one2many',):
                     if data[field_name]:
-                        data_o2m[field_name] = data[field_name]
-                    data[field_name] = None
+                        data[field_name] = [('copy', data[field_name])]
                 elif ftype == 'many2many':
                     if data[field_name]:
-                        data[field_name] = [('set', data[field_name])]
+                        data[field_name] = [('add', data[field_name])]
             if 'id' in data:
                 del data['id']
-            return data, data_o2m
+            return data
 
-        new_ids = {}
         fields_names = [n for n, f in cls._fields.iteritems()
             if (not isinstance(f, fields.Function)
                 or isinstance(f, fields.Property))]
         ids = map(int, records)
         datas = cls.read(ids, fields_names=fields_names)
         field_defs = cls.fields_get(fields_names=fields_names)
-        data_ids = []
         to_create = []
-        o2m_to_create = []
         for data in datas:
-            data_ids.append(data['id'])
-            data, data_o2m = convert_data(field_defs, data)
+            data = convert_data(field_defs, data)
             to_create.append(data)
-            o2m_to_create.append(data_o2m)
         new_records = cls.create(to_create)
-        for data_id, new_record, data_o2m in izip(data_ids, new_records,
-                o2m_to_create):
-            new_ids[data_id] = new_record.id
-            for field_name in data_o2m:
-                Relation = pool.get(
-                        field_defs[field_name]['relation'])
-                relation_field = field_defs[field_name]['relation_field']
-                if relation_field:
-                    field = Relation._fields[relation_field]
-                    if field._type == 'reference':
-                        value = str(new_record)
-                    else:
-                        value = new_record.id
-                    Relation.copy(Relation.browse(data_o2m[field_name]),
-                        default={relation_field: value})
+        new_ids = dict(izip(ids, map(int, new_records)))
 
         fields_translate = {}
         for field_name, field in field_defs.iteritems():
@@ -332,7 +321,7 @@ class ModelStorage(Model):
                                 fields_names=fields_translate.keys() + ['id'])
                         for data in datas:
                             data_id = data['id']
-                            data, _ = convert_data(fields_translate, data)
+                            data = convert_data(fields_translate, data)
                             cls.write([cls(new_ids[data_id])], data)
         return cls.browse(new_ids.values())
 
@@ -525,127 +514,108 @@ class ModelStorage(Model):
         '''
         Create records for all values in data.
         The field names of values must be defined in fields_names.
-        It returns a tuple with
-            - the number of records imported
-            - the last values if failed
-            - the exception if failed
-            - the warning if failed
         '''
         pool = Pool()
 
-        def process_lines(data, prefix, fields_def, position=0):
-
-            def warn(msgname, *args):
-                msg = cls.raise_user_error(msgname, args,
-                        raise_exception=False)
-                logger.warn(msg)
-
-            def get_selection(selection, value):
-                res = None
-                if not isinstance(selection, (tuple, list)):
-                    selection = getattr(cls, selection)()
-                for key, _ in selection:
-                    if str(key) == value:
-                        res = key
-                        break
-                if value and not res:
-                    warn('not_found_in_selection', value, '/'.join(field))
-                return res
-
-            def get_many2one(relation, value):
-                if not value:
-                    return None
-                Relation = pool.get(relation)
-                res = Relation.search([
-                    ('rec_name', '=', value),
+        @memoize(1000)
+        def get_many2one(relation, value):
+            if not value:
+                return None
+            Relation = pool.get(relation)
+            res = Relation.search([
+                ('rec_name', '=', value),
+                ], limit=2)
+            if len(res) < 1:
+                cls.raise_user_error('relation_not_found', (value, relation))
+            elif len(res) > 1:
+                cls.raise_user_error('too_many_relations_found',
+                    (value, relation))
+            else:
+                res = res[0].id
+            return res
+
+        @memoize(1000)
+        def get_many2many(relation, value):
+            if not value:
+                return None
+            res = []
+            Relation = pool.get(relation)
+            for word in csv.reader(StringIO.StringIO(value), delimiter=',',
+                    quoting=csv.QUOTE_NONE, escapechar='\\').next():
+                res2 = Relation.search([
+                    ('rec_name', '=', word),
                     ], limit=2)
-                if len(res) < 1:
-                    warn('relation_not_found', value, relation)
-                    res = None
-                elif len(res) > 1:
-                    warn('too_many_relations_found', value, relation)
-                    res = None
+                if len(res2) < 1:
+                    cls.raise_user_error('relation_not_found',
+                        (word, relation))
+                elif len(res2) > 1:
+                    cls.raise_user_error('too_many_relations_found',
+                        (word, relation))
                 else:
-                    res = res[0].id
-                return res
-
-            def get_many2many(relation, value):
-                if not value:
-                    return None
-                res = []
-                Relation = pool.get(relation)
-                for word in csv.reader(StringIO.StringIO(value), delimiter=',',
-                        quoting=csv.QUOTE_NONE, escapechar='\\').next():
-                    res2 = Relation.search([
-                        ('rec_name', '=', word),
-                        ], limit=2)
-                    if len(res2) < 1:
-                        warn('relation_not_found', word, relation)
-                    elif len(res2) > 1:
-                        warn('too_many_relations_found', word, relation)
-                    else:
-                        res.extend(res2)
-                if len(res):
-                    res = [('set', [x.id for x in res])]
-                return res
-
-            def get_one2one(relation, value):
-                return ('set', get_many2one(relation, value))
-
-            def get_reference(value):
-                if not value:
-                    return None
+                    res.extend(res2)
+            if len(res):
+                res = [('add', [x.id for x in res])]
+            return res
+
+        def get_one2one(relation, value):
+            return ('add', get_many2one(relation, value))
+
+        @memoize(1000)
+        def get_reference(value, field):
+            if not value:
+                return None
+            try:
+                relation, value = value.split(',', 1)
+            except Exception:
+                cls.raise_user_error('reference_syntax_error',
+                    (value, '/'.join(field)))
+            Relation = pool.get(relation)
+            res = Relation.search([
+                ('rec_name', '=', value),
+                ], limit=2)
+            if len(res) < 1:
+                cls.raise_user_error('relation_not_found', (value, relation))
+            elif len(res) > 1:
+                cls.raise_user_error('too_many_relations_found',
+                    (value, relation))
+            else:
+                res = '%s,%s' % (relation, res[0].id)
+            return res
+
+        @memoize(1000)
+        def get_by_id(value, field):
+            if not value:
+                return None
+            relation = None
+            ftype = fields_def[field[-1][:-3]]['type']
+            if ftype == 'many2many':
+                value = csv.reader(StringIO.StringIO(value), delimiter=',',
+                        quoting=csv.QUOTE_NONE, escapechar='\\').next()
+            elif ftype == 'reference':
                 try:
                     relation, value = value.split(',', 1)
                 except Exception:
-                    warn('reference_syntax_error', value, '/'.join(field))
-                    return None
-                Relation = pool.get(relation)
-                res = Relation.search([
-                    ('rec_name', '=', value),
-                    ], limit=2)
-                if len(res) < 1:
-                    warn('relation_not_found', value, relation)
-                    res = None
-                elif len(res) > 1:
-                    warn('too_many_relations_found', value, relation)
-                    res = None
-                else:
-                    res = '%s,%s' % (relation, res[0].id)
-                return res
-
-            def get_by_id(value):
-                if not value:
-                    return None
-                relation = None
-                ftype = fields_def[field[-1][:-3]]['type']
-                if ftype == 'many2many':
-                    value = csv.reader(StringIO.StringIO(value), delimiter=',',
-                            quoting=csv.QUOTE_NONE, escapechar='\\').next()
-                elif ftype == 'reference':
-                    try:
-                        relation, value = value.split(',', 1)
-                    except Exception:
-                        warn('reference_syntax_error', value, '/'.join(field))
-                        return None
-                    value = [value]
-                else:
-                    value = [value]
-                res_ids = []
-                for word in value:
-                    try:
-                        module, xml_id = word.rsplit('.', 1)
-                    except Exception:
-                        warn('xml_id_syntax_error', word, '/'.join(field))
-                        continue
-                    db_id = ModelData.get_id(module, xml_id)
-                    res_ids.append(db_id)
-                if ftype == 'many2many' and res_ids:
-                    return [('set', res_ids)]
-                elif ftype == 'reference' and res_ids:
-                    return '%s,%s' % (relation, str(res_ids[0]))
-                return res_ids and res_ids[0] or False
+                    cls.raise_user_error('reference_syntax_error',
+                        (value, '/'.join(field)))
+                value = [value]
+            else:
+                value = [value]
+            res_ids = []
+            for word in value:
+                try:
+                    module, xml_id = word.rsplit('.', 1)
+                except Exception:
+                    cls.raise_user_error('xml_id_syntax_error',
+                        (word, '/'.join(field)))
+                db_id = ModelData.get_id(module, xml_id)
+                res_ids.append(db_id)
+            if ftype == 'many2many' and res_ids:
+                return [('add', res_ids)]
+            elif ftype == 'reference' and res_ids:
+                return '%s,%s' % (relation, str(res_ids[0]))
+            return res_ids and res_ids[0] or False
 
+        def process_lines(data, prefix, fields_def, position=0):
             line = data[position]
             row = {}
             translate = {}
@@ -660,7 +630,7 @@ class ModelStorage(Model):
                 is_prefix_len = (len(field) == (prefix_len + 1))
                 value = line[i]
                 if is_prefix_len and field[-1].endswith(':id'):
-                    row[field[0][:-3]] = get_by_id(value)
+                    row[field[0][:-3]] = get_by_id(value, field)
                 elif is_prefix_len and ':lang=' in field[-1]:
                     field_name, lang = field[-1].split(':lang=')
                     translate.setdefault(lang, {})[field_name] = value or False
@@ -684,15 +654,13 @@ class ModelStorage(Model):
                     elif field_type == 'numeric':
                         res = Decimal(value) if value else None
                     elif field_type == 'date':
-                        res = (datetime.date(
-                                *time.strptime(value, '%Y-%m-%d')[:3])
+                        res = (datetime.datetime.strptime(value,
+                                '%Y-%m-%d').date()
                             if value else None)
                     elif field_type == 'datetime':
-                        res = (datetime.datetime(
-                                *time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6])
+                        res = (datetime.datetime.strptime(value,
+                                '%Y-%m-%d %H:%M:%S')
                             if value else None)
-                    elif field_type == 'selection':
-                        res = get_selection(this_field_def['selection'], value)
                     elif field_type == 'many2one':
                         res = get_many2one(this_field_def['relation'], value)
                     elif field_type == 'many2many':
@@ -700,7 +668,7 @@ class ModelStorage(Model):
                     elif field_type == 'one2one':
                         res = get_one2one(this_field_def['relation'], value)
                     elif field_type == 'reference':
-                        res = get_reference(value)
+                        res = get_reference(value, field)
                     else:
                         res = value or None
                     row[field[-1]] = res
@@ -742,42 +710,25 @@ class ModelStorage(Model):
 
         ModelData = pool.get('ir.model.data')
 
-        # logger for collecting warnings for the client
-        logger = logging.Logger("import")
-        warning_stream = StringIO.StringIO()
-        logger.addHandler(logging.StreamHandler(warning_stream))
-
         len_fields_names = len(fields_names)
         assert all(len(x) == len_fields_names for x in data)
         fields_names = [x.split('/') for x in fields_names]
         fields_def = cls.fields_get()
-        done = 0
 
-        warning = ''
+        to_create, translations, languages = [], [], set()
         while len(data):
-            res = {}
-            try:
-                (res, _, translate) = \
-                    process_lines(data, [], fields_def)
-                warning = warning_stream.getvalue()
-                if warning:
-                    # XXX should raise Exception
-                    Transaction().cursor.rollback()
-                    return (-1, res, warning, '')
-                new_id, = cls.create([res])
-                for lang in translate:
-                    with Transaction().set_context(language=lang):
-                        cls.write(new_id, translate[lang])
-            except Exception, exp:
-                logger = logging.getLogger('import')
-                logger.error(exp)
-                # XXX should raise Exception
-                Transaction().cursor.rollback()
-                tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
-                warning = '%s\n%s' % (tb_s, warning)
-                return (-1, res, exp, warning)
-            done += 1
-        return (done, 0, 0, 0)
+            (res, _, translate) = \
+                process_lines(data, [], fields_def)
+            to_create.append(res)
+            translations.append(translate)
+            languages.update(translate)
+        new_records = cls.create(to_create)
+        for language in languages:
+            translated = [t.get(language, {}) for t in translations]
+            with Transaction().set_context(language=language):
+                cls.write(*chain(*ifilter(itemgetter(1),
+                            izip(([r] for r in new_records), translated))))
+        return len(new_records)
 
     @classmethod
     def check_xml_record(cls, records, values):
@@ -863,7 +814,10 @@ class ModelStorage(Model):
     def _get_error_args(cls, field_name):
         pool = Pool()
         ModelField = pool.get('ir.model.field')
-        error_args = (field_name, cls.__name__)
+        error_args = {
+            'field': field_name,
+            'model': cls.__name__
+            }
         if ModelField:
             model_fields = ModelField.search([
                         ('name', '=', field_name),
@@ -871,8 +825,10 @@ class ModelStorage(Model):
                         ], limit=1)
             if model_fields:
                 model_field, = model_fields
-                error_args = (model_field.field_description,
-                        model_field.model.name)
+                error_args.update({
+                        'field': model_field.field_description,
+                        'model': model_field.model.name,
+                        })
         return error_args
 
     @classmethod
@@ -880,7 +836,7 @@ class ModelStorage(Model):
         pass
 
     @classmethod
-    def _validate(cls, records):
+    def _validate(cls, records, field_names=None):
         pool = Pool()
         # Ensure that records are readable
         with Transaction().set_user(0, set_context=True):
@@ -926,71 +882,70 @@ class ModelStorage(Model):
                             return True
             return False
 
+        def validate_domain(field):
+            if not field.domain:
+                return
+            if field._type == 'dict':
+                return
+            if field._type in ('many2one', 'one2many'):
+                Relation = pool.get(field.model_name)
+            elif field._type in ('many2many', 'one2one'):
+                Relation = field.get_target()
+            else:
+                Relation = cls
+            if is_pyson(field.domain):
+                pyson_domain = PYSONEncoder().encode(field.domain)
+                for record in records:
+                    env = EvalEnvironment(record, cls)
+                    env.update(Transaction().context)
+                    env['current_date'] = datetime.datetime.today()
+                    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)
+            else:
+                validate_relation_domain(
+                    field, records, Relation, field.domain)
+
+        def validate_relation_domain(field, records, Relation, domain):
+            if field._type in ('many2one', 'one2many', 'many2many', 'one2one'):
+                relations = []
+                for record in records:
+                    if getattr(record, field.name):
+                        if field._type in ('many2one', 'one2one'):
+                            relations.append(getattr(record, field.name))
+                        else:
+                            relations.extend(getattr(record, field.name))
+            else:
+                relations = records
+            if relations:
+                finds = Relation.search(['AND',
+                        [('id', 'in', [r.id for r in relations])],
+                        domain,
+                        ])
+                if set(relations) != set(finds):
+                    cls.raise_user_error('domain_validation_record',
+                        error_args=cls._get_error_args(field.name))
+
+        field_names = set(field_names or [])
+        function_fields = {name for name, field in cls._fields.iteritems()
+            if isinstance(field, fields.Function)}
         ctx_pref['active_test'] = False
         with Transaction().set_context(ctx_pref):
             for field_name, field in cls._fields.iteritems():
+                depends = set(field.depends)
+                if (field_names
+                        and field_name not in field_names
+                        and not (depends & field_names)
+                        and not (depends & function_fields)):
+                    continue
                 if isinstance(field, fields.Function) and \
                         not field.setter:
                     continue
-                # validate domain
-                if (field._type in
-                        ('many2one', 'many2many', 'one2many', 'one2one')
-                        and field.domain):
-                    if field._type in ('many2one', 'one2many'):
-                        Relation = pool.get(field.model_name)
-                    else:
-                        Relation = field.get_target()
-                    if is_pyson(field.domain):
-                        pyson_domain = PYSONEncoder().encode(field.domain)
-                        for record in records:
-                            env = EvalEnvironment(record, cls)
-                            env.update(Transaction().context)
-                            env['current_date'] = datetime.datetime.today()
-                            env['time'] = time
-                            env['context'] = Transaction().context
-                            env['active_id'] = record.id
-                            domain = PYSONDecoder(env).decode(pyson_domain)
-                            relation_ids = []
-                            if getattr(record, field_name):
-                                if field._type in ('many2one', 'one2one'):
-                                    relation_ids.append(
-                                        getattr(record, field_name).id)
-                                else:
-                                    relation_ids.extend(
-                                        [x.id for x in getattr(record,
-                                                field_name)])
-                            if relation_ids and not Relation.search([
-                                        'AND',
-                                        [('id', 'in', relation_ids)],
-                                        domain,
-                                        ]):
-                                cls.raise_user_error(
-                                        'domain_validation_record',
-                                        error_args=cls._get_error_args(
-                                            field_name))
-                    else:
-                        relation_ids = []
-                        for record in records:
-                            if getattr(record, field_name):
-                                if field._type in ('many2one', 'one2one'):
-                                    relation_ids.append(
-                                        getattr(record, field_name).id)
-                                else:
-                                    relation_ids.extend(
-                                        [x.id for x in getattr(record,
-                                                field_name)])
-                        if relation_ids:
-                            finds = Relation.search([
-                                'AND',
-                                [('id', 'in', relation_ids)],
-                                field.domain,
-                                ])
-                            find_ids = map(int, finds)
-                            if not set(relation_ids) == set(find_ids):
-                                cls.raise_user_error(
-                                        'domain_validation_record',
-                                        error_args=cls._get_error_args(
-                                            field_name))
+
+                validate_domain(field)
 
                 def required_test(value, field_name):
                     if (isinstance(value, (type(None), type(False), list,
@@ -1037,16 +992,19 @@ class ModelStorage(Model):
                             field_size = PYSONDecoder(env).decode(pyson_size)
                         else:
                             field_size = field.size
-                        if (len(getattr(record, field_name) or '')
-                                > field_size >= 0):
-                            cls.raise_user_error(
-                                'size_validation_record',
-                                error_args=cls._get_error_args(field_name))
+                        size = len(getattr(record, field_name) or '')
+                        if (size > field_size >= 0):
+                            error_args = cls._get_error_args(field_name)
+                            error_args['size'] = size
+                            cls.raise_user_error('size_validation_record',
+                                error_args=error_args)
 
                 def digits_test(value, digits, field_name):
                     def raise_user_error():
+                        error_args = cls._get_error_args(field_name)
+                        error_args['digits'] = digits[1]
                         cls.raise_user_error('digits_validation_record',
-                            error_args=cls._get_error_args(field_name))
+                            error_args=error_args)
                     if value is None:
                         return
                     if isinstance(value, Decimal):
@@ -1098,8 +1056,10 @@ class ModelStorage(Model):
                             test.add('')
                             test.add(None)
                         if value not in test:
+                            error_args = cls._get_error_args(field_name)
+                            error_args['value'] = value
                             cls.raise_user_error('selection_validation_record',
-                                error_args=cls._get_error_args(field_name))
+                                error_args=error_args)
 
                 def format_test(value, format, field_name):
                     if not value:
@@ -1108,8 +1068,10 @@ class ModelStorage(Model):
                         value = value.time()
                     if value != datetime.datetime.strptime(
                             value.strftime(format), format).time():
+                        error_args = cls._get_error_args(field_name)
+                        error_args['value'] = value
                         cls.raise_user_error('time_format_validation_record',
-                            error_args=cls._get_error_args(field_name))
+                            error_args=error_args)
 
                 # validate time format
                 if (field._type in ('datetime', 'time')
@@ -1154,7 +1116,7 @@ class ModelStorage(Model):
                     vals2 = obj._clean_defaults(defaults2)
                     vals[field].append(('create', [vals2]))
             elif fld_def._type in ('many2many',):
-                vals[field] = [('set', defaults[field])]
+                vals[field] = [('add', defaults[field])]
             elif fld_def._type in ('boolean',):
                 vals[field] = bool(defaults[field])
             else:
@@ -1164,15 +1126,16 @@ class ModelStorage(Model):
     def __init__(self, id=None, **kwargs):
         _ids = kwargs.pop('_ids', None)
         _local_cache = kwargs.pop('_local_cache', None)
-        super(ModelStorage, self).__init__(id, **kwargs)
         self._cursor = Transaction().cursor
         self._user = Transaction().user
         self._context = Transaction().context
+        if id is not None:
+            id = int(id)
         if _ids is not None:
             self._ids = _ids
             assert id in _ids
         else:
-            self._ids = [self.id]
+            self._ids = [id]
 
         self._cursor_cache = self._cursor.get_cache(self._context)
 
@@ -1182,6 +1145,8 @@ class ModelStorage(Model):
             self._local_cache = LRUDict(RECORD_CACHE_SIZE)
         self._local_cache.counter = Transaction().counter
 
+        super(ModelStorage, self).__init__(id, **kwargs)
+
     @property
     def _cache(self):
         cache = self._cursor_cache
@@ -1260,10 +1225,17 @@ class ModelStorage(Model):
         def filter_(id_):
             return (name not in self._cache.get(id_, {})
                 and name not in self._local_cache.get(id_, {}))
+
+        def unique(ids):
+            s = set()
+            for id_ in ids:
+                if id_ not in s:
+                    s.add(id_)
+                    yield id_
         index = self._ids.index(self.id)
         ids = chain(islice(self._ids, index, None),
             islice(self._ids, 0, max(index - 1, 0)))
-        ids = islice(ifilter(filter_, ids), self._cursor.IN_MAX)
+        ids = islice(unique(ifilter(filter_, ids)), self._cursor.IN_MAX)
 
         def instantiate(field, value, data):
             if field._type in ('many2one', 'one2one', 'reference'):
@@ -1304,9 +1276,9 @@ class ModelStorage(Model):
         model2ids = {}
         model2cache = {}
         # Read the data
-        with contextlib.nested(Transaction().set_cursor(self._cursor),
-                Transaction().set_user(self._user),
-                Transaction().set_context(self._context)):
+        with Transaction().set_cursor(self._cursor), \
+                Transaction().set_user(self._user), \
+                Transaction().set_context(self._context):
             if self.id in self._cache and name in self._cache[self.id]:
                 # Use values from cache
                 ids = islice(chain(islice(self._ids, index, None),
@@ -1360,17 +1332,41 @@ class ModelStorage(Model):
                         value = value.id
             if field._type in ('one2many', 'many2many'):
                 targets = value
-                value = [
-                    ('set', []),
-                    ]
+                if self.id >= 0:
+                    _values, self._values = self._values, None
+                    try:
+                        to_remove = [t.id for t in getattr(self, fname)]
+                    finally:
+                        self._values = _values
+                else:
+                    to_remove = []
+                to_add = []
+                to_create = []
+                to_write = []
                 for target in targets:
                     if target.id < 0:
-                        value.append(('create', [target._save_values]))
+                        if field._type == 'one2many':
+                            # Don't store old target link
+                            setattr(target, field.field, None)
+                        to_create.append(target._save_values)
                     else:
-                        value[0][1].append(target.id)
-                        if target._save_values:
-                            value.append(
-                                ('write', [target.id], target._save_values))
+                        if target.id in to_remove:
+                            to_remove.remove(target.id)
+                        else:
+                            to_add.append(target.id)
+                        target_values = target._save_values
+                        if target_values:
+                            to_write.append(
+                                ('write', [target.id], target_values))
+                value = []
+                if to_remove:
+                    value.append(('remove', to_remove))
+                if to_add:
+                    value.append(('add', to_add))
+                if to_create:
+                    value.append(('create', to_create))
+                if to_write:
+                    value.extend(to_write)
             values[fname] = value
         return values
 
@@ -1380,9 +1376,9 @@ class ModelStorage(Model):
         self._values = None
         if save_values or self.id < 0:
             try:
-                with contextlib.nested(Transaction().set_cursor(self._cursor),
-                        Transaction().set_user(self._user),
-                        Transaction().set_context(self._context)):
+                with Transaction().set_cursor(self._cursor), \
+                        Transaction().set_user(self._user), \
+                        Transaction().set_context(self._context):
                     if self.id < 0:
                         self._ids.remove(self.id)
                         try:
diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py
index eec4db9..ae853e2 100644
--- a/trytond/model/modelview.py
+++ b/trytond/model/modelview.py
@@ -295,33 +295,9 @@ class ModelView(Model):
         fields_to_remove = list(x for x, y in fread_accesses.iteritems()
                 if not y)
 
-        def check_relation(model, field):
-            if field._type in ('one2many', 'many2one'):
-                if not ModelAccess.check(field.model_name, mode='read',
-                        raise_exception=False):
-                    return False
-            if field._type in ('many2many', 'one2one'):
-                if (field.target
-                        and not ModelAccess.check(field.target, mode='read',
-                            raise_exception=False)):
-                    return False
-                elif (field.relation_name
-                        and not ModelAccess.check(field.relation_name,
-                            mode='read', raise_exception=False)):
-                    return False
-            if field._type == 'reference':
-                selection = field.selection
-                if isinstance(selection, basestring):
-                    selection = getattr(model, field.selection)()
-                for model_name, _ in selection:
-                    if not ModelAccess.check(model_name, mode='read',
-                            raise_exception=False):
-                        return False
-            return True
-
         # Find relation field without read access
         for name, field in cls._fields.iteritems():
-            if not check_relation(cls, field):
+            if not ModelAccess.check_relation(cls.__name__, name, mode='read'):
                 fields_to_remove.append(name)
 
         for name, field in cls._fields.iteritems():
diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py
index 6dbebf4..f954fb5 100644
--- a/trytond/modules/__init__.py
+++ b/trytond/modules/__init__.py
@@ -4,7 +4,6 @@ import os
 import sys
 import itertools
 import logging
-import contextlib
 from functools import reduce
 import imp
 import operator
@@ -226,7 +225,10 @@ def load_module_graph(graph, pool, lang=None):
         if (is_module_to_install(module)
                 or package_state in ('to install', 'to upgrade')):
             if package_state not in ('to install', 'to upgrade'):
-                package_state = 'to install'
+                if package_state == 'installed':
+                    package_state = 'to upgrade'
+                else:
+                    package_state = 'to install'
             for child in package.childs:
                 module2state[child.name] = package_state
             for type in classes.keys():
@@ -362,13 +364,8 @@ def register_classes():
 
 def load_modules(database_name, pool, update=False, lang=None):
     res = True
-    if not Transaction().cursor:
-        contextmanager = Transaction().start(database_name, 0)
-    else:
-        contextmanager = contextlib.nested(Transaction().new_cursor(),
-                Transaction().set_user(0),
-                Transaction().reset_context())
-    with contextmanager:
+
+    def _load_modules():
         cursor = Transaction().cursor
         if update:
             # Migration from 2.2: workflow module removed
@@ -425,5 +422,15 @@ def load_modules(database_name, pool, update=False, lang=None):
             Module = pool.get('ir.module.module')
             Module.update_list()
         cursor.commit()
+
+    if not Transaction().cursor:
+        with Transaction().start(database_name, 0):
+            _load_modules()
+    else:
+        with Transaction().new_cursor(), \
+                Transaction().set_user(0), \
+                Transaction().reset_context():
+            _load_modules()
+
     Cache.resets(database_name)
     return res
diff --git a/trytond/protocols/common.py b/trytond/protocols/common.py
index 7c1dca2..959b83a 100644
--- a/trytond/protocols/common.py
+++ b/trytond/protocols/common.py
@@ -4,12 +4,6 @@ import errno
 import os
 import socket
 import threading
-import sys
-import gzip
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
 from SocketServer import StreamRequestHandler
 
 
@@ -57,91 +51,3 @@ class RegisterHandlerMixin:
             self.server.handlers.remove(self)
         except KeyError:
             pass
-
-
-class GZipRequestHandlerMixin:
-
-    if sys.version_info[:2] <= (2, 6):
-        # Copy from SimpleXMLRPCServer.py with gzip encoding added
-        def do_POST(self):
-            """Handles the HTTP POST request.
-
-            Attempts to interpret all HTTP POST requests as XML-RPC calls,
-            which are forwarded to the server's _dispatch method for handling.
-            """
-
-            # Check that the path is legal
-            if not self.is_rpc_path_valid():
-                self.report_404()
-                return
-
-            try:
-                # Get arguments by reading body of request.
-                # We read this in chunks to avoid straining
-                # socket.read(); around the 10 or 15Mb mark, some platforms
-                # begin to have problems (bug #792570).
-                max_chunk_size = 10 * 1024 * 1024
-                size_remaining = int(self.headers["content-length"])
-                L = []
-                while size_remaining:
-                    chunk_size = min(size_remaining, max_chunk_size)
-                    L.append(self.rfile.read(chunk_size))
-                    size_remaining -= len(L[-1])
-                data = ''.join(L)
-
-                data = self.decode_request_content(data)
-                if data is None:
-                    return  # response has been sent
-
-                # In previous versions of SimpleXMLRPCServer, _dispatch
-                # could be overridden in this class, instead of in
-                # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
-                # check to see if a subclass implements _dispatch and dispatch
-                # using that method if present.
-                response = self.server._marshaled_dispatch(
-                        data, getattr(self, '_dispatch', None)
-                    )
-            except Exception:  # This should only happen if the module is buggy
-                # internal error, report as HTTP server error
-                self.send_response(500)
-                self.end_headers()
-            else:
-                # got a valid XML RPC response
-                self.send_response(200)
-                self.send_header("Content-type", "text/xml")
-
-                # Handle gzip encoding
-                if ('gzip' in self.headers.get('Accept-Encoding',
-                            '').split(',')
-                        and len(response) > self.encode_threshold):
-                    buffer = StringIO.StringIO()
-                    output = gzip.GzipFile(mode='wb', fileobj=buffer)
-                    output.write(response)
-                    output.close()
-                    buffer.seek(0)
-                    response = buffer.getvalue()
-                    self.send_header('Content-Encoding', 'gzip')
-
-                self.send_header("Content-length", str(len(response)))
-                self.end_headers()
-                self.wfile.write(response)
-
-        def decode_request_content(self, data):
-            #support gzip encoding of request
-            encoding = self.headers.get("content-encoding", "identity").lower()
-            if encoding == "identity":
-                return data
-            if encoding == "gzip":
-                f = StringIO.StringIO(data)
-                gzf = gzip.GzipFile(mode="rb", fileobj=f)
-                try:
-                    decoded = gzf.read()
-                except IOError:
-                    self.send_response(400, "error decoding gzip content")
-                f.close()
-                gzf.close()
-                return decoded
-            else:
-                self.send_response(501, "encoding %r not supported" % encoding)
-            self.send_header("Content-length", "0")
-            self.end_headers()
diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py
index 81e8dfc..2d1915d 100644
--- a/trytond/protocols/dispatcher.py
+++ b/trytond/protocols/dispatcher.py
@@ -56,8 +56,6 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
             return True
         elif method == 'version':
             return VERSION
-        elif method == 'timezone_get':
-            return CONFIG['timezone']
         elif method == 'list_lang':
             return [
                 ('bg_BG', 'Български'),
@@ -85,17 +83,8 @@ def dispatch(host, port, protocol, database_name, user, session, object_type,
         elif method == 'list':
             if CONFIG['prevent_dblist']:
                 raise Exception('AccessDenied')
-            database = Database().connect()
-            Flavor.set(Database.flavor)
-            try:
-                cursor = database.cursor()
-                try:
-                    res = database.list(cursor)
-                finally:
-                    cursor.close(close=True)
-            except Exception:
-                res = []
-            return res
+            with Transaction().start(None, 0, close=True) as transaction:
+                return transaction.database.list(transaction.cursor)
         elif method == 'create':
             return create(*args, **kwargs)
         elif method == 'restore':
@@ -224,18 +213,13 @@ def create(database_name, password, lang, admin_password):
     logger = logging.getLogger('database')
 
     try:
-        database = Database().connect()
-        cursor = database.cursor(autocommit=True)
-        try:
-            database.create(cursor, database_name)
-            cursor.commit()
-            cursor.close(close=True)
-        except Exception:
-            cursor.close()
-            raise
+        with Transaction().start(None, 0, close=True, autocommit=True) \
+            as transaction:
+            transaction.database.create(transaction.cursor, database_name)
+            transaction.cursor.commit()
 
         with Transaction().start(database_name, 0) as transaction:
-            database.init(transaction.cursor)
+            Database.init(transaction.cursor)
             transaction.cursor.execute(*ir_configuration.insert(
                     [ir_configuration.language], [[lang]]))
             transaction.cursor.commit()
@@ -279,11 +263,11 @@ def drop(database_name, password):
     time.sleep(1)
     logger = logging.getLogger('database')
 
-    database = Database().connect()
-    cursor = database.cursor(autocommit=True)
-    try:
+    with Transaction().start(None, 0, close=True, autocommit=True) \
+        as transaction:
+        cursor = transaction.cursor
         try:
-            database.drop(cursor, database_name)
+            Database.drop(cursor, database_name)
             cursor.commit()
         except Exception:
             logger.error('DROP DB: %s failed' % (database_name,))
@@ -293,8 +277,6 @@ def drop(database_name, password):
         else:
             logger.info('DROP DB: %s' % (database_name))
             Pool.stop(database_name)
-    finally:
-        cursor.close(close=True)
     return True
 
 
diff --git a/trytond/protocols/jsonrpc.py b/trytond/protocols/jsonrpc.py
index 201ba41..27ce605 100644
--- a/trytond/protocols/jsonrpc.py
+++ b/trytond/protocols/jsonrpc.py
@@ -3,8 +3,7 @@
 from trytond.protocols.sslsocket import SSLSocket
 from trytond.protocols.dispatcher import dispatch
 from trytond.config import CONFIG
-from trytond.protocols.common import daemon, GZipRequestHandlerMixin, \
-    RegisterHandlerMixin
+from trytond.protocols.common import daemon, RegisterHandlerMixin
 from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
 import SimpleXMLRPCServer
@@ -38,11 +37,12 @@ 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['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'])
+            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':
@@ -67,6 +67,7 @@ class JSONEncoder(json.JSONEncoder):
                         'hour': obj.hour,
                         'minute': obj.minute,
                         'second': obj.second,
+                        'microsecond': obj.microsecond,
                         }
             return {'__class__': 'date',
                     'year': obj.year,
@@ -78,6 +79,7 @@ class JSONEncoder(json.JSONEncoder):
                 'hour': obj.hour,
                 'minute': obj.minute,
                 'second': obj.second,
+                'microsecond': obj.microsecond,
                 }
         elif isinstance(obj, buffer):
             return {'__class__': 'buffer',
@@ -157,8 +159,7 @@ class GenericJSONRPCRequestHandler:
         return res
 
 
-class SimpleJSONRPCRequestHandler(GZipRequestHandlerMixin,
-        RegisterHandlerMixin,
+class SimpleJSONRPCRequestHandler(RegisterHandlerMixin,
         GenericJSONRPCRequestHandler,
         SimpleXMLRPCServer.SimpleXMLRPCRequestHandler,
         SimpleHTTPServer.SimpleHTTPRequestHandler):
@@ -305,34 +306,21 @@ class SimpleJSONRPCServer(SocketServer.TCPServer,
 
     def server_close(self):
         SocketServer.TCPServer.server_close(self)
-        for handler in self.handlers:
+        for handler in self.handlers.copy():
             self.shutdown_request(handler.request)
 
-    if sys.version_info[:2] <= (2, 6):
-
-        def shutdown_request(self, request):
-            """Called to shutdown and close an individual request."""
-            try:
-                #explicitly shutdown.  socket.close() merely releases
-                #the socket and waits for GC to perform the actual close.
-                request.shutdown(socket.SHUT_WR)
-            except socket.error:
-                pass  # some platforms may raise ENOTCONN here
-            self.close_request(request)
-
 
 class SimpleThreadedJSONRPCServer(SocketServer.ThreadingMixIn,
         SimpleJSONRPCServer):
     timeout = 1
     daemon_threads = True
+    disable_nagle_algorithm = True
 
     def server_bind(self):
         self.socket.setsockopt(socket.SOL_SOCKET,
                 socket.SO_REUSEADDR, 1)
         self.socket.setsockopt(socket.SOL_SOCKET,
             socket.SO_KEEPALIVE, 1)
-        self.socket.setsockopt(socket.IPPROTO_TCP,
-            socket.TCP_NODELAY, 1)
         SimpleJSONRPCServer.server_bind(self)
 
 
diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py
index 5e13d7d..0625f1e 100644
--- a/trytond/protocols/xmlrpc.py
+++ b/trytond/protocols/xmlrpc.py
@@ -3,8 +3,7 @@
 from trytond.protocols.sslsocket import SSLSocket
 from trytond.protocols.dispatcher import dispatch
 from trytond.config import CONFIG
-from trytond.protocols.common import daemon, GZipRequestHandlerMixin, \
-    RegisterHandlerMixin
+from trytond.protocols.common import daemon, RegisterHandlerMixin
 from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
 from trytond import security
@@ -50,6 +49,7 @@ def dump_time(self, value, write):
         'hour': value.hour,
         'minute': value.minute,
         'second': value.second,
+        'microsecond': value.microsecond,
         }
     self.dump_struct(value, write)
 
@@ -85,7 +85,8 @@ def end_struct(self, data):
         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 = datetime.time(dct['hour'], dct['minute'], dct['second'],
+                dct['microsecond'])
         elif dct['__class__'] == 'Decimal':
             dct = Decimal(dct['decimal'])
     self._stack[mark:] = [dct]
@@ -145,8 +146,7 @@ class GenericXMLRPCRequestHandler:
             security.logout(database_name, user, session)
 
 
-class SimpleXMLRPCRequestHandler(GZipRequestHandlerMixin,
-        RegisterHandlerMixin,
+class SimpleXMLRPCRequestHandler(RegisterHandlerMixin,
         GenericXMLRPCRequestHandler,
         SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
     protocol_version = "HTTP/1.1"
@@ -202,21 +202,9 @@ class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
 
     def server_close(self):
         SimpleXMLRPCServer.SimpleXMLRPCServer.server_close(self)
-        for handler in self.handlers:
+        for handler in self.handlers.copy():
             self.shutdown_request(handler.request)
 
-    if sys.version_info[:2] <= (2, 6):
-
-        def shutdown_request(self, request):
-            """Called to shutdown and close an individual request."""
-            try:
-                #explicitly shutdown.  socket.close() merely releases
-                #the socket and waits for GC to perform the actual close.
-                request.shutdown(socket.SHUT_WR)
-            except socket.error:
-                pass  # some platforms may raise ENOTCONN here
-            self.close_request(request)
-
 
 class SimpleThreadedXMLRPCServer6(SimpleThreadedXMLRPCServer):
     address_family = socket.AF_INET6
diff --git a/trytond/pyson.py b/trytond/pyson.py
index 1fc1e64..6771f20 100644
--- a/trytond/pyson.py
+++ b/trytond/pyson.py
@@ -537,6 +537,32 @@ class DateTime(Date):
             )
 
 
+class Len(PYSON):
+
+    def __init__(self, value):
+        super(Len, self).__init__()
+        if isinstance(value, PYSON):
+            assert value.types().issubset(set([dict, list, str])), \
+                'value must be a dict or a list or a string'
+        else:
+            assert type(value) in [dict, list, str], \
+                'value must be a dict or list or a string'
+        self._value = value
+
+    def pyson(self):
+        return {
+            '__class__': 'Len',
+            'v': self._value,
+            }
+
+    def types(self):
+        return set([int, long])
+
+    @staticmethod
+    def eval(dct, context):
+        return len(dct['v'])
+
+
 class Id(PYSON):
     """The database id for filesystem id"""
 
@@ -567,4 +593,5 @@ CONTEXT = {
     'In': In,
     'Date': Date,
     'DateTime': DateTime,
+    'Len': Len,
 }
diff --git a/trytond/report/report.py b/trytond/report/report.py
index 2536b1e..42a6d5d 100644
--- a/trytond/report/report.py
+++ b/trytond/report/report.py
@@ -122,8 +122,9 @@ class Report(URLMixin, PoolBase):
             raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
         action_report = action_reports[0]
         records = None
-        if action_report.model:
-            records = cls._get_records(ids, action_report.model, data)
+        model = action_report.model or data.get('model')
+        if model:
+            records = cls._get_records(ids, model, data)
         type, data = cls.parse(action_report, records, data, {})
         return (type, buffer(data), action_report.direct_print,
             action_report.name)
diff --git a/trytond/res/group.py b/trytond/res/group.py
index 2d4ce47..800b13d 100644
--- a/trytond/res/group.py
+++ b/trytond/res/group.py
@@ -82,11 +82,15 @@ class Group(ModelSQL, ModelView):
         pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
         pool.get('res.user')._get_preferences_cache.clear()
+        # Restart the cache for model access and view
+        pool.get('ir.model.access')._get_access_cache.clear()
+        pool.get('ir.model.field.access')._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         return res
 
     @classmethod
-    def write(cls, groups, vals):
-        super(Group, cls).write(groups, vals)
+    def write(cls, groups, values, *args):
+        super(Group, cls).write(groups, values, *args)
         pool = Pool()
         # Restart the cache on the domain_get method
         pool.get('ir.rule')._domain_get_cache.clear()
@@ -94,6 +98,10 @@ class Group(ModelSQL, ModelView):
         pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
         pool.get('res.user')._get_preferences_cache.clear()
+        # Restart the cache for model access and view
+        pool.get('ir.model.access')._get_access_cache.clear()
+        pool.get('ir.model.field.access')._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
 
     @classmethod
     def delete(cls, groups):
@@ -105,6 +113,10 @@ class Group(ModelSQL, ModelView):
         pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
         pool.get('res.user')._get_preferences_cache.clear()
+        # Restart the cache for model access and view
+        pool.get('ir.model.access')._get_access_cache.clear()
+        pool.get('ir.model.field.access')._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
 
 
 class Group2:
diff --git a/trytond/res/ir.py b/trytond/res/ir.py
index 784ca53..a867611 100644
--- a/trytond/res/ir.py
+++ b/trytond/res/ir.py
@@ -44,8 +44,8 @@ class UIMenuGroup(ModelSQL):
         return res
 
     @classmethod
-    def write(cls, records, vals):
-        super(UIMenuGroup, cls).write(records, vals)
+    def write(cls, records, values, *args):
+        super(UIMenuGroup, cls).write(records, values, *args)
         # Restart the cache on the domain_get method
         Pool().get('ir.rule')._domain_get_cache.clear()
 
@@ -91,12 +91,16 @@ class ActionGroup(ModelSQL):
         return res
 
     @classmethod
-    def write(cls, records, vals):
+    def write(cls, records, values, *args):
         Action = Pool().get('ir.action')
-        if vals.get('action'):
-            vals = vals.copy()
-            vals['action'] = Action.get_action_id(vals['action'])
-        super(ActionGroup, cls).write(records, vals)
+        actions = iter((records, values) + args)
+        args = []
+        for records, values in zip(actions, actions):
+            if values.get('action'):
+                values = values.copy()
+                values['action'] = Action.get_action_id(values['action'])
+            args.extend((records, values))
+        super(ActionGroup, cls).write(*args)
         # Restart the cache on the domain_get method
         Pool().get('ir.rule')._domain_get_cache.clear()
 
@@ -153,9 +157,9 @@ class ModelButtonGroup(ModelSQL):
         return result
 
     @classmethod
-    def write(cls, records, values):
+    def write(cls, records, values, *args):
         pool = Pool()
-        super(ModelButtonGroup, cls).write(records, values)
+        super(ModelButtonGroup, cls).write(records, values, *args)
         # Restart the cache for get_groups
         pool.get('ir.model.button')._groups_cache.clear()
 
@@ -219,8 +223,8 @@ class Lang:
     __name__ = 'ir.lang'
 
     @classmethod
-    def write(cls, langs, vals):
-        super(Lang, cls).write(langs, vals)
+    def write(cls, langs, values, *args):
+        super(Lang, cls).write(langs, values, *args)
         # Restart the cache for get_preferences
         Pool().get('res.user')._get_preferences_cache.clear()
 
@@ -256,9 +260,9 @@ class SequenceTypeGroup(ModelSQL):
         return res
 
     @classmethod
-    def write(cls, records, vals):
+    def write(cls, records, values, *args):
         Rule = Pool().get('ir.rule')
-        super(SequenceTypeGroup, cls).write(records, vals)
+        super(SequenceTypeGroup, cls).write(records, values, *args)
         # Restart the cache on the domain_get method
         Rule._domain_get_cache.clear()
 
@@ -314,10 +318,10 @@ class ModuleConfigWizardItem:
         return result
 
     @classmethod
-    def write(cls, items, values):
+    def write(cls, items, values, *args):
         pool = Pool()
         User = pool.get('res.user')
-        super(ModuleConfigWizardItem, cls).write(items, values)
+        super(ModuleConfigWizardItem, cls).write(items, values, *args)
         # Restart the cache for get_preferences
         User._get_preferences_cache.clear()
 
diff --git a/trytond/res/ir.xml b/trytond/res/ir.xml
index 45c6557..73c8d96 100644
--- a/trytond/res/ir.xml
+++ b/trytond/res/ir.xml
@@ -787,9 +787,9 @@ this repository contains the full copyright notices and license terms. -->
             <field name="request_user" ref="user_admin"/>
             <field name="user" ref="user_trigger"/>
             <field name="active" eval="True"/>
-            <field name="interval_number">5</field>
+            <field name="interval_number" eval="5"/>
             <field name="interval_type">minutes</field>
-            <field name="number_calls">-1</field>
+            <field name="number_calls" eval="-1"/>
             <field name="repeat_missed" eval="False"/>
             <field name="model">ir.trigger</field>
             <field name="function">trigger_time</field>
diff --git a/trytond/res/locale/bg_BG.po b/trytond/res/locale/bg_BG.po
index a68b490..83777ba 100644
--- a/trytond/res/locale/bg_BG.po
+++ b/trytond/res/locale/bg_BG.po
@@ -365,6 +365,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Парола"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr ""
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr ""
@@ -377,10 +381,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Правила"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Ключ"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr ""
@@ -393,10 +393,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Лента със статус"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Времева зона"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Предупреждения"
diff --git a/trytond/res/locale/ca_ES.po b/trytond/res/locale/ca_ES.po
index fd2c2f7..71decc4 100644
--- a/trytond/res/locale/ca_ES.po
+++ b/trytond/res/locale/ca_ES.po
@@ -12,7 +12,7 @@ msgstr "Contrasenya incorrecta."
 
 msgctxt "error:res.user:"
 msgid "You can not have two users with the same login!"
-msgstr "No podeu tenir dos usuaris amb el mateix identificador d'entrada!"
+msgstr "No podeu tenir dos usuaris amb el mateix nom d'usuari."
 
 msgctxt "error:res.user:"
 msgid ""
@@ -20,9 +20,9 @@ msgid ""
 "as it is used internally for resources\n"
 "created by the system (updates, module installation, ...)"
 msgstr ""
-"No podeu eliminar l'usuari del sistema\n"
-"que es utilitzat per recursos del sistema\n"
-"(actualitzacions, instal·lacions,...)"
+"No podeu eliminar l'usuari principal que\n"
+"s'utilitza internament pels recursos creats\n"
+"pel sistema (actualitzacions, instal·lacions,...)."
 
 msgctxt "field:ir.action-res.group,action:"
 msgid "Action"
@@ -62,7 +62,7 @@ msgstr "Actiu"
 
 msgctxt "field:ir.model.button-res.group,button:"
 msgid "Button"
-msgstr "Butons"
+msgstr "Botó"
 
 msgctxt "field:ir.model.button-res.group,create_date:"
 msgid "Create Date"
@@ -274,7 +274,7 @@ msgstr "Usuari creació"
 
 msgctxt "field:res.group,field_access:"
 msgid "Access Field"
-msgstr "Camp accés"
+msgstr "Accés a camps"
 
 msgctxt "field:res.group,id:"
 msgid "ID"
@@ -282,11 +282,11 @@ msgstr "ID"
 
 msgctxt "field:res.group,menu_access:"
 msgid "Access Menu"
-msgstr "Permís menú"
+msgstr "Accés a menús"
 
 msgctxt "field:res.group,model_access:"
 msgid "Access Model"
-msgstr "Permís model"
+msgstr "Accés a models"
 
 msgctxt "field:res.group,name:"
 msgid "Name"
@@ -330,7 +330,7 @@ msgstr "Usuari creació"
 
 msgctxt "field:res.user,email:"
 msgid "Email"
-msgstr "Email"
+msgstr "Correu electrònic"
 
 msgctxt "field:res.user,groups:"
 msgid "Groups"
@@ -364,9 +364,13 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Contrasenya"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Hash de la contrasenya"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
-msgstr "PySON Menú"
+msgstr "Menú PySON"
 
 msgctxt "field:res.user,rec_name:"
 msgid "Name"
@@ -376,10 +380,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Regles"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Salt"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Sessions"
@@ -392,10 +392,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Progrés"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Zona horaria"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Alertes"
@@ -542,11 +538,11 @@ msgstr "Usuari modificació"
 
 msgctxt "help:ir.sequence.type,groups:"
 msgid "Groups allowed to edit the sequences of this type"
-msgstr "Grups permesos per editar les seqüències d'aquest tipus"
+msgstr "Els grups permesos per editar les seqüències d'aquest tipus."
 
 msgctxt "help:res.user,actions:"
 msgid "Actions that will be run at login"
-msgstr "Accions que seràn executades al identificar-se"
+msgstr "Les accions que s'executaran quan s'identifiqui."
 
 msgctxt "model:ir.action,name:act_group_form"
 msgid "Groups"
@@ -566,11 +562,11 @@ msgstr "Acció - grup"
 
 msgctxt "model:ir.cron,name:cron_trigger_time"
 msgid "Run On Time Triggers"
-msgstr "Executa al moment del disparador"
+msgstr "Executa disparadors per temps"
 
 msgctxt "model:ir.model.button-res.group,name:"
 msgid "Model Button - Group"
-msgstr "Model butó - grup"
+msgstr "Model Botó - Grup"
 
 msgctxt "model:ir.model.field-res.group,name:"
 msgid "Model Field Group Rel"
@@ -602,7 +598,7 @@ msgstr "Usuaris"
 
 msgctxt "model:ir.ui.menu-res.group,name:"
 msgid "UI Menu - Group"
-msgstr "UI menú - Grup"
+msgstr "Menú UI - Grup"
 
 msgctxt "model:res.group,name:"
 msgid "Group"
@@ -618,11 +614,11 @@ msgstr "Usuari"
 
 msgctxt "model:res.user,name:user_admin"
 msgid "Administrator"
-msgstr "Administració"
+msgstr "Administrador"
 
 msgctxt "model:res.user,name:user_trigger"
 msgid "Cron Trigger"
-msgstr "Cron disparador"
+msgstr "Planificador de disparadors"
 
 msgctxt "model:res.user-ir.action,name:"
 msgid "User - Action"
@@ -634,7 +630,7 @@ msgstr "Usuari - Grup"
 
 msgctxt "model:res.user.config.start,name:"
 msgid "User Config Init"
-msgstr "Configuració usuari"
+msgstr "Configuració inicial usuari"
 
 msgctxt "model:res.user.login.attempt,name:"
 msgid "Login Attempt"
@@ -646,7 +642,7 @@ msgstr "Alerta usuari"
 
 msgctxt "view:res.group:"
 msgid "Access Permissions"
-msgstr "Accés permisos"
+msgstr "Permisos d'accés"
 
 msgctxt "view:res.group:"
 msgid "Group"
@@ -666,7 +662,7 @@ msgstr "Afegir usuaris"
 
 msgctxt "view:res.user.config.start:"
 msgid "Be careful that the login must be unique!"
-msgstr "El identificador ha de ser únic."
+msgstr "L'identificador ha de ser únic."
 
 msgctxt "view:res.user.config.start:"
 msgid "You can now add some users into the system."
@@ -674,7 +670,7 @@ msgstr "Ara podeu afegir usuaris al sistema."
 
 msgctxt "view:res.user.warning:"
 msgid "Warning"
-msgstr "Esperant"
+msgstr "Alerta"
 
 msgctxt "view:res.user.warning:"
 msgid "Warnings"
@@ -682,7 +678,7 @@ msgstr "Alertes"
 
 msgctxt "view:res.user:"
 msgid "Access Permissions"
-msgstr "Accés permisos"
+msgstr "Permisos d'accés"
 
 msgctxt "view:res.user:"
 msgid "Actions"
@@ -710,12 +706,12 @@ msgstr "Cancel·la"
 
 msgctxt "wizard_button:res.user.config,start,user:"
 msgid "Ok"
-msgstr "Dacord"
+msgstr "D'acord"
 
 msgctxt "wizard_button:res.user.config,user,add:"
 msgid "Add"
-msgstr "Afegir"
+msgstr "Afegeix"
 
 msgctxt "wizard_button:res.user.config,user,end:"
 msgid "End"
-msgstr "Finalitzat"
+msgstr "Finalitza"
diff --git a/trytond/res/locale/cs_CZ.po b/trytond/res/locale/cs_CZ.po
index 3531d01..57228c3 100644
--- a/trytond/res/locale/cs_CZ.po
+++ b/trytond/res/locale/cs_CZ.po
@@ -361,6 +361,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr ""
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr ""
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr ""
@@ -373,10 +377,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr ""
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr ""
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr ""
@@ -389,10 +389,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr ""
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr ""
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr ""
diff --git a/trytond/res/locale/de_DE.po b/trytond/res/locale/de_DE.po
index 6ab5064..7b1e739 100644
--- a/trytond/res/locale/de_DE.po
+++ b/trytond/res/locale/de_DE.po
@@ -363,6 +363,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Passwort"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Passwort Hash"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "PySON Menü"
@@ -375,10 +379,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Datensatzregeln"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Salt"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Sitzungen"
@@ -391,10 +391,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Status Anzeige"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Zeitzone"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Warnungen"
@@ -546,7 +542,7 @@ msgstr ""
 
 msgctxt "help:res.user,actions:"
 msgid "Actions that will be run at login"
-msgstr "Aktionen, die nach dem Login ausgeführt werden"
+msgstr "Diese Aktionen werden nach dem Login ausgeführt"
 
 msgctxt "model:ir.action,name:act_group_form"
 msgid "Groups"
diff --git a/trytond/res/locale/es_AR.po b/trytond/res/locale/es_AR.po
index 55a354e..f889836 100644
--- a/trytond/res/locale/es_AR.po
+++ b/trytond/res/locale/es_AR.po
@@ -82,7 +82,7 @@ msgstr "ID"
 
 msgctxt "field:ir.model.button-res.group,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.button-res.group,write_date:"
 msgid "Write Date"
@@ -218,7 +218,7 @@ msgstr "ID"
 
 msgctxt "field:ir.sequence.type-res.group,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.sequence.type-res.group,sequence_type:"
 msgid "Sequence Type"
@@ -364,6 +364,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Contraseña"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Hash de contraseña"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "Menú PySON"
@@ -376,10 +380,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Reglas"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Sal"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Sesiones"
@@ -392,10 +392,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Barra de estado"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Zona horaria"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Avisos"
@@ -426,7 +422,7 @@ msgstr "ID"
 
 msgctxt "field:res.user-ir.action,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:res.user-ir.action,user:"
 msgid "User"
@@ -494,7 +490,7 @@ msgstr "Nombre de usuario"
 
 msgctxt "field:res.user.login.attempt,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:res.user.login.attempt,write_date:"
 msgid "Write Date"
@@ -670,7 +666,7 @@ msgstr "Marcar para actualizar"
 
 msgctxt "view:res.group:"
 msgid "Access Permissions"
-msgstr "Permisos de Acceso"
+msgstr "Permisos de acceso"
 
 msgctxt "view:res.group:"
 msgid "Group"
@@ -698,15 +694,15 @@ msgstr "Ahora puede agregar algunos usuarios al sistema."
 
 msgctxt "view:res.user.warning:"
 msgid "Warning"
-msgstr "Advertencia"
+msgstr "Aviso"
 
 msgctxt "view:res.user.warning:"
 msgid "Warnings"
-msgstr "Advertencias"
+msgstr "Avisos"
 
 msgctxt "view:res.user:"
 msgid "Access Permissions"
-msgstr "Permisos de Acceso"
+msgstr "Permisos de acceso"
 
 msgctxt "view:res.user:"
 msgid "Actions"
diff --git a/trytond/res/locale/es_CO.po b/trytond/res/locale/es_CO.po
index 3eb1929..b351bbe 100644
--- a/trytond/res/locale/es_CO.po
+++ b/trytond/res/locale/es_CO.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 ""
@@ -82,7 +82,7 @@ msgstr "ID"
 
 msgctxt "field:ir.model.button-res.group,rec_name:"
 msgid "Name"
-msgstr "Nombre del campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.model.button-res.group,write_date:"
 msgid "Write Date"
@@ -190,15 +190,15 @@ msgstr "Modificado por Usuario"
 
 msgctxt "field:ir.sequence,groups:"
 msgid "User Groups"
-msgstr "Grupos de usuarios"
+msgstr "Grupos de Usuarios"
 
 msgctxt "field:ir.sequence.strict,groups:"
 msgid "User Groups"
-msgstr "Grupos de usuarios"
+msgstr "Grupos de Usuarios"
 
 msgctxt "field:ir.sequence.type,groups:"
 msgid "User Groups"
-msgstr "Grupos de usuarios"
+msgstr "Grupos de Usuarios"
 
 msgctxt "field:ir.sequence.type-res.group,create_date:"
 msgid "Create Date"
@@ -210,7 +210,7 @@ msgstr "Creado por Usuario"
 
 msgctxt "field:ir.sequence.type-res.group,group:"
 msgid "User Groups"
-msgstr "Grupos de usuarios"
+msgstr "Grupos de Usuarios"
 
 msgctxt "field:ir.sequence.type-res.group,id:"
 msgid "ID"
@@ -218,11 +218,11 @@ msgstr "ID"
 
 msgctxt "field:ir.sequence.type-res.group,rec_name:"
 msgid "Name"
-msgstr "Nombre del campo"
+msgstr "Nombre"
 
 msgctxt "field:ir.sequence.type-res.group,sequence_type:"
 msgid "Sequence Type"
-msgstr "Tipo de secuencia"
+msgstr "Tipo de Secuencia"
 
 msgctxt "field:ir.sequence.type-res.group,write_date:"
 msgid "Write Date"
@@ -354,7 +354,7 @@ msgstr "Nombre de usuario"
 
 msgctxt "field:res.user,menu:"
 msgid "Menu Action"
-msgstr "Menú de acciones"
+msgstr "Menú de Acciones"
 
 msgctxt "field:res.user,name:"
 msgid "Name"
@@ -364,6 +364,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Contraseña"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Contraseña Hash"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "Menú PySON"
@@ -376,10 +380,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Reglas"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Sal"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Sesiones"
@@ -390,11 +390,7 @@ msgstr "Firma"
 
 msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
-msgstr "Barra de estado"
-
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Zona horaria"
+msgstr "Barra de Estado"
 
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
@@ -426,7 +422,7 @@ msgstr "ID"
 
 msgctxt "field:res.user-ir.action,rec_name:"
 msgid "Name"
-msgstr "Nombre del campo"
+msgstr "Nombre"
 
 msgctxt "field:res.user-ir.action,user:"
 msgid "User"
@@ -494,7 +490,7 @@ msgstr "Nombre de usuario"
 
 msgctxt "field:res.user.login.attempt,rec_name:"
 msgid "Name"
-msgstr "Nombre del campo"
+msgstr "Nombre"
 
 msgctxt "field:res.user.login.attempt,write_date:"
 msgid "Write Date"
@@ -542,7 +538,7 @@ msgstr "Modificado por Usuario"
 
 msgctxt "help:ir.sequence.type,groups:"
 msgid "Groups allowed to edit the sequences of this type"
-msgstr "Los grupos son autorizados para editar secuencias en este tipo"
+msgstr "Grupos autorizados para editar secuencias en este tipo"
 
 msgctxt "help:res.user,actions:"
 msgid "Actions that will be run at login"
@@ -566,7 +562,7 @@ 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 a Tiempo"
 
 msgctxt "model:ir.model.button-res.group,name:"
 msgid "Model Button - Group"
@@ -578,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"
@@ -642,31 +638,31 @@ msgstr "Intento de Login"
 
 msgctxt "model:res.user.warning,name:"
 msgid "User Warning"
-msgstr "Aviso al usuario"
+msgstr "Aviso a Usuario"
 
 msgctxt "view:ir.module.module:"
 msgid "Cancel Installation"
-msgstr "Cancelar instalación"
+msgstr "Cancelar Instalación"
 
 msgctxt "view:ir.module.module:"
 msgid "Cancel Uninstallation"
-msgstr "Cancelar la desinstalación"
+msgstr "Cancelar la Desinstalación"
 
 msgctxt "view:ir.module.module:"
 msgid "Cancel Upgrade"
-msgstr "Cancelar actualización"
+msgstr "Cancelar Actualización"
 
 msgctxt "view:ir.module.module:"
 msgid "Mark for Installation"
-msgstr "Marcar para Instalación"
+msgstr "Instalar"
 
 msgctxt "view:ir.module.module:"
 msgid "Mark for Uninstallation (beta)"
-msgstr "Marcar para desinstalar (beta)"
+msgstr "Desinstalar (beta)"
 
 msgctxt "view:ir.module.module:"
 msgid "Mark for Upgrade"
-msgstr "Marcar para actualizar"
+msgstr "Actualizar"
 
 msgctxt "view:res.group:"
 msgid "Access Permissions"
@@ -690,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 hacer los nombres de usuario únicos!"
+msgstr "Asegúrese de que los nombres de usuario sean únicos!"
 
 msgctxt "view:res.user.config.start:"
 msgid "You can now add some users into the system."
@@ -714,7 +710,7 @@ msgstr "Acciones"
 
 msgctxt "view:res.user:"
 msgid "Group Membership"
-msgstr "Membresía del grupo"
+msgstr "Miembro del Grupo"
 
 msgctxt "view:res.user:"
 msgid "Preferences"
diff --git a/trytond/res/locale/es_ES.po b/trytond/res/locale/es_ES.po
index 3abb1f4..7ed0da6 100644
--- a/trytond/res/locale/es_ES.po
+++ b/trytond/res/locale/es_ES.po
@@ -20,9 +20,9 @@ msgid ""
 "as it is used internally for resources\n"
 "created by the system (updates, module installation, ...)"
 msgstr ""
-"No puede eliminar el usuario del sistema\n"
-"que se usa para recursos del sistema\n"
-"(actualizaciones, instalaciones, ...)."
+"No puede eliminar el usuario principal que\n"
+"se usa internamente para los recursos creados\n"
+"por el sistema (actualizaciones, instalaciones, ...)."
 
 msgctxt "field:ir.action-res.group,action:"
 msgid "Action"
@@ -62,7 +62,7 @@ msgstr "Activo"
 
 msgctxt "field:ir.model.button-res.group,button:"
 msgid "Button"
-msgstr "Inferior"
+msgstr "Botón"
 
 msgctxt "field:ir.model.button-res.group,create_date:"
 msgid "Create Date"
@@ -354,7 +354,7 @@ msgstr "Nombre de usuario"
 
 msgctxt "field:res.user,menu:"
 msgid "Menu Action"
-msgstr "Menú de acciones"
+msgstr "Acción menú"
 
 msgctxt "field:res.user,name:"
 msgid "Name"
@@ -364,6 +364,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Contraseña"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Hash de la contraseña"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "Menú PySON"
@@ -376,10 +380,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Reglas"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Sal"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Sesiones"
@@ -392,10 +392,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Barra de estado"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Zona horaria"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Avisos"
@@ -570,7 +566,7 @@ msgstr "Ejecutar disparadores por tiempo"
 
 msgctxt "model:ir.model.button-res.group,name:"
 msgid "Model Button - Group"
-msgstr "Botón modelo - Grupo"
+msgstr "Modelo Botón - Grupo"
 
 msgctxt "model:ir.model.field-res.group,name:"
 msgid "Model Field Group Rel"
@@ -622,7 +618,7 @@ msgstr "Administrador"
 
 msgctxt "model:res.user,name:user_trigger"
 msgid "Cron Trigger"
-msgstr "Disparador planificador"
+msgstr "Planificador de disparadores"
 
 msgctxt "model:res.user-ir.action,name:"
 msgid "User - Action"
@@ -634,7 +630,7 @@ msgstr "Usuario - Grupo"
 
 msgctxt "model:res.user.config.start,name:"
 msgid "User Config Init"
-msgstr "Configuración usuario"
+msgstr "Configuración inicial usuario"
 
 msgctxt "model:res.user.login.attempt,name:"
 msgid "Login Attempt"
@@ -690,7 +686,7 @@ msgstr "Añadir usuarios"
 
 msgctxt "view:res.user.config.start:"
 msgid "Be careful that the login must be unique!"
-msgstr "Debe tener en cuenta que el identificador debe ser único."
+msgstr "El identificador debe ser único."
 
 msgctxt "view:res.user.config.start:"
 msgid "You can now add some users into the system."
diff --git a/trytond/res/locale/fr_FR.po b/trytond/res/locale/fr_FR.po
index 31bc64b..758ba94 100644
--- a/trytond/res/locale/fr_FR.po
+++ b/trytond/res/locale/fr_FR.po
@@ -364,6 +364,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Mot de passe"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Hachage de mot de passe"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "Menu PySON"
@@ -376,10 +380,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Règles"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Sel"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Sessions"
@@ -392,10 +392,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Barre d'état"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Fuseau horaire"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Avertissements"
diff --git a/trytond/res/locale/nl_NL.po b/trytond/res/locale/nl_NL.po
index 9d70347..3bf2b91 100644
--- a/trytond/res/locale/nl_NL.po
+++ b/trytond/res/locale/nl_NL.po
@@ -387,6 +387,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr ""
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr ""
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr ""
@@ -400,10 +404,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr ""
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr ""
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr ""
@@ -416,10 +416,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr ""
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr ""
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr ""
diff --git a/trytond/res/locale/ru_RU.po b/trytond/res/locale/ru_RU.po
index b1b4488..22ebae5 100644
--- a/trytond/res/locale/ru_RU.po
+++ b/trytond/res/locale/ru_RU.po
@@ -364,6 +364,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Пароль"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr ""
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "Меню PySON"
@@ -376,10 +380,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Правила"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Соль"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Сессии"
@@ -392,10 +392,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Строка состояния"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Часовой пояс"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Предупреждения"
diff --git a/trytond/res/locale/sl_SI.po b/trytond/res/locale/sl_SI.po
index 8e8eb0e..e481897 100644
--- a/trytond/res/locale/sl_SI.po
+++ b/trytond/res/locale/sl_SI.po
@@ -363,6 +363,10 @@ msgctxt "field:res.user,password:"
 msgid "Password"
 msgstr "Geslo"
 
+msgctxt "field:res.user,password_hash:"
+msgid "Password Hash"
+msgstr "Zgoščena vrednost gesla"
+
 msgctxt "field:res.user,pyson_menu:"
 msgid "PySON Menu"
 msgstr "PySON meni"
@@ -375,10 +379,6 @@ msgctxt "field:res.user,rule_groups:"
 msgid "Rules"
 msgstr "Pravila"
 
-msgctxt "field:res.user,salt:"
-msgid "Salt"
-msgstr "Sol"
-
 msgctxt "field:res.user,sessions:"
 msgid "Sessions"
 msgstr "Seje"
@@ -391,10 +391,6 @@ msgctxt "field:res.user,status_bar:"
 msgid "Status Bar"
 msgstr "Statusna vrstica"
 
-msgctxt "field:res.user,timezone:"
-msgid "Timezone"
-msgstr "Časovni pas"
-
 msgctxt "field:res.user,warnings:"
 msgid "Warnings"
 msgstr "Opozorila"
diff --git a/trytond/res/user.py b/trytond/res/user.py
index e50c8fd..4416c53 100644
--- a/trytond/res/user.py
+++ b/trytond/res/user.py
@@ -10,7 +10,14 @@ import datetime
 from itertools import groupby, ifilter
 from operator import attrgetter
 from sql import Literal
+from sql.conditionals import Coalesce
 from sql.aggregate import Count
+from sql.operators import Concat
+
+try:
+    import bcrypt
+except ImportError:
+    bcrypt = None
 
 from ..model import ModelView, ModelSQL, fields
 from ..wizard import Wizard, StateView, Button, StateTransition
@@ -23,13 +30,6 @@ from ..config import CONFIG
 from ..pyson import PYSONEncoder
 from ..rpc import RPC
 
-try:
-    import pytz
-    TIMEZONES = [(x, x) for x in pytz.common_timezones]
-except ImportError:
-    TIMEZONES = []
-TIMEZONES += [(None, '')]
-
 __all__ = [
     'User', 'LoginAttempt', 'UserAction', 'UserGroup', 'Warning_',
     'UserConfigStart', 'UserConfig',
@@ -41,8 +41,9 @@ class User(ModelSQL, ModelView):
     __name__ = "res.user"
     name = fields.Char('Name', required=True, select=True, translate=True)
     login = fields.Char('Login', required=True)
-    password = fields.Sha('Password')
-    salt = fields.Char('Salt', size=8)
+    password_hash = fields.Char('Password Hash')
+    password = fields.Function(fields.Char('Password'), getter='get_password',
+        setter='set_password')
     signature = fields.Text('Signature')
     active = fields.Boolean('Active')
     menu = fields.Many2One('ir.action', 'Menu Action',
@@ -61,7 +62,6 @@ class User(ModelSQL, ModelView):
             ])
     language_direction = fields.Function(fields.Char('Language Direction'),
             'get_language_direction')
-    timezone = fields.Selection(TIMEZONES, 'Timezone', translate=False)
     email = fields.Char('Email')
     status_bar = fields.Function(fields.Char('Status Bar'), 'get_status_bar')
     warnings = fields.One2Many('res.user.warning', 'user', 'Warnings')
@@ -97,7 +97,6 @@ class User(ModelSQL, ModelView):
         cls._context_fields = [
             'language',
             'language_direction',
-            'timezone',
             'groups',
         ]
         cls._error_messages.update({
@@ -111,8 +110,9 @@ class User(ModelSQL, ModelView):
     @classmethod
     def __register__(cls, module_name):
         TableHandler = backend.get('TableHandler')
+        cursor = Transaction().cursor
         super(User, cls).__register__(module_name)
-        table = TableHandler(Transaction().cursor, cls, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.6
 
@@ -135,9 +135,16 @@ class User(ModelSQL, ModelView):
         # Migration from 2.6
         table.drop_column('login_try', exception=True)
 
-    @staticmethod
-    def default_password():
-        return None
+        # Migration from 3.0
+        if table.column_exist('password') and table.column_exist('salt'):
+            sqltable = cls.__table__()
+            password_hash_new = Concat('sha1$', Concat(sqltable.password,
+                Concat('$', Coalesce(sqltable.salt, ''))))
+            cursor.execute(*sqltable.update(
+                columns=[sqltable.password_hash],
+                values=[password_hash_new]))
+            table.drop_column('password', exception=True)
+            table.drop_column('salt', exception=True)
 
     @staticmethod
     def default_active():
@@ -172,6 +179,20 @@ class User(ModelSQL, ModelView):
     def get_status_bar(self, name):
         return self.name
 
+    def get_password(self, name):
+        return 'x' * 10
+
+    @classmethod
+    def set_password(cls, users, name, value):
+        if value == 'x' * 10:
+            return
+        to_write = []
+        for user in users:
+            to_write.extend([[user], {
+                        'password_hash': cls.hash_password(value),
+                        }])
+        cls.write(*to_write)
+
     @staticmethod
     def get_sessions(users, name):
         Session = Pool().get('ir.session')
@@ -202,13 +223,6 @@ class User(ModelSQL, ModelView):
         Action = pool.get('ir.action')
         if 'menu' in vals:
             vals['menu'] = Action.get_action_id(vals['menu'])
-        if 'password' in vals:
-            if vals['password'] == 'x' * 10:
-                del vals['password']
-            elif vals['password']:
-                vals['salt'] = ''.join(random.sample(
-                    string.ascii_letters + string.digits, 8))
-                vals['password'] += vals['salt']
         return vals
 
     @classmethod
@@ -220,13 +234,18 @@ class User(ModelSQL, ModelView):
         return res
 
     @classmethod
-    def write(cls, users, vals):
-        vals = cls._convert_vals(vals)
-        super(User, cls).write(users, vals)
+    def write(cls, users, values, *args):
+        actions = iter((users, values) + args)
+        all_users = []
+        args = []
+        for users, values in zip(actions, actions):
+            all_users += users
+            args.extend((users, cls._convert_vals(values)))
+        super(User, cls).write(*args)
         # Clean cursor cache as it could be filled by domain_get
         for cache in Transaction().cursor.cache.itervalues():
             if cls.__name__ in cache:
-                for user in users:
+                for user in all_users:
                     if user.id in cache[cls.__name__]:
                         cache[cls.__name__][user.id].clear()
         # Restart the cache for domain_get method
@@ -240,6 +259,8 @@ class User(ModelSQL, ModelView):
         cls._get_preferences_cache.clear()
         # Restart the cache of check
         pool.get('ir.model.access')._get_access_cache.clear()
+        # Restart the cache of check
+        pool.get('ir.model.field.access')._get_access_cache.clear()
         # Restart the cache
         ModelView._fields_view_get_cache.clear()
 
@@ -252,14 +273,6 @@ class User(ModelSQL, ModelView):
         cls._get_login_cache.clear()
 
     @classmethod
-    def read(cls, ids, fields_names=None):
-        res = super(User, cls).read(ids, fields_names=fields_names)
-        for val in res:
-            if 'password' in val:
-                val['password'] = 'x' * 10
-        return res
-
-    @classmethod
     def search_rec_name(cls, name, clause):
         users = cls.search([
             ('login', '=', clause[2]),
@@ -344,7 +357,7 @@ class User(ModelSQL, ModelView):
         if preferences is not None:
             return preferences.copy()
         user = Transaction().user
-        with Transaction().set_user(0):
+        with Transaction().set_user(0, set_context=True):
             user = cls(user)
         preferences = cls._get_preferences(user, context_only=context_only)
         cls._get_preferences_cache.set(key, preferences)
@@ -397,6 +410,7 @@ class User(ModelSQL, ModelView):
 
         def convert2selection(definition, name):
             del definition[name]['relation']
+            del definition[name]['domain']
             definition[name]['type'] = 'selection'
             selection = []
             definition[name]['selection'] = selection
@@ -444,9 +458,9 @@ class User(ModelSQL, ModelView):
             return result
         cursor = Transaction().cursor
         table = cls.__table__()
-        cursor.execute(*table.select(table.id, table.password, table.salt,
+        cursor.execute(*table.select(table.id, table.password_hash,
                 where=(table.login == login) & table.active))
-        result = cursor.fetchone() or (None, None, None)
+        result = cursor.fetchone() or (None, None)
         cls._get_login_cache.set(login, result)
         return result
 
@@ -457,18 +471,69 @@ class User(ModelSQL, ModelView):
         '''
         LoginAttempt = Pool().get('res.user.login.attempt')
         time.sleep(2 ** LoginAttempt.count(login) - 1)
-        user_id, user_password, salt = cls._get_login(login)
+        user_id, password_hash = cls._get_login(login)
         if user_id:
-            password += salt or ''
-            if isinstance(password, unicode):
-                password = password.encode('utf-8')
-            password_sha = hashlib.sha1(password).hexdigest()
-            if password_sha == user_password:
+            if cls.check_password(password, password_hash):
                 LoginAttempt.remove(login)
                 return user_id
         LoginAttempt.add(login)
         return 0
 
+    @staticmethod
+    def hash_method():
+        return 'bcrypt' if bcrypt else 'sha1'
+
+    @classmethod
+    def hash_password(cls, password):
+        '''Hash given password in the form
+        <hash_method>$<password>$<salt>...'''
+        if not password:
+            return ''
+        return getattr(cls, 'hash_' + cls.hash_method())(password)
+
+    @classmethod
+    def check_password(cls, password, hash_):
+        if not hash_:
+            return False
+        hash_method = hash_.split('$', 1)[0]
+        return getattr(cls, 'check_' + hash_method)(password, hash_)
+
+    @classmethod
+    def hash_sha1(cls, password):
+        if isinstance(password, unicode):
+            password = password.encode('utf-8')
+        salt = ''.join(random.sample(string.ascii_letters + string.digits, 8))
+        hash_ = hashlib.sha1(password + salt).hexdigest()
+        return '$'.join(['sha1', hash_, salt])
+
+    @classmethod
+    def check_sha1(cls, password, hash_):
+        if isinstance(password, unicode):
+            password = password.encode('utf-8')
+        if isinstance(hash_, unicode):
+            hash_ = hash_.encode('utf-8')
+        hash_method, hash_, salt = hash_.split('$', 2)
+        salt = salt or ''
+        assert hash_method == 'sha1'
+        return hash_ == hashlib.sha1(password + salt).hexdigest()
+
+    @classmethod
+    def hash_bcrypt(cls, password):
+        if isinstance(password, unicode):
+            password = password.encode('utf-8')
+        hash_ = bcrypt.hashpw(password, bcrypt.gensalt())
+        return '$'.join(['bcrypt', hash_])
+
+    @classmethod
+    def check_bcrypt(cls, password, hash_):
+        if isinstance(password, unicode):
+            password = password.encode('utf-8')
+        if isinstance(hash_, unicode):
+            hash_ = hash_.encode('utf-8')
+        hash_method, hash_ = hash_.split('$', 1)
+        assert hash_method == 'bcrypt'
+        return hash_ == bcrypt.hashpw(password, hash_)
+
 
 class LoginAttempt(ModelSQL):
     """Login Attempt
@@ -539,9 +604,12 @@ class UserAction(ModelSQL):
         return super(UserAction, cls).create(vlist)
 
     @classmethod
-    def write(cls, records, values):
-        values = cls._convert_values(values)
-        super(UserAction, cls).write(records, values)
+    def write(cls, records, values, *args):
+        actions = iter((records, values) + args)
+        args = []
+        for records, values in zip(actions, actions):
+            args.extend((records, cls._convert_values(values)))
+        super(UserAction, cls).write(*args)
 
 
 class UserGroup(ModelSQL):
diff --git a/trytond/res/view/user_form.xml b/trytond/res/view/user_form.xml
index 3cdcab7..629ff82 100644
--- a/trytond/res/view/user_form.xml
+++ b/trytond/res/view/user_form.xml
@@ -11,7 +11,7 @@ this repository contains the full copyright notices and license terms. -->
             <label name="login"/>
             <field name="login"/>
             <label name="password"/>
-            <field name="password"/>
+            <field name="password" widget="password"/>
             <label name="email"/>
             <field name="email" widget="email"/>
             <separator name="signature" colspan="4"/>
@@ -29,8 +29,6 @@ this repository contains the full copyright notices and license terms. -->
         <page string="Preferences" col="2" id="preferences">
             <label name="language"/>
             <field name="language" widget="selection"/>
-            <label name="timezone"/>
-            <field name="timezone"/>
         </page>
     </notebook>
 </form>
diff --git a/trytond/res/view/user_form_preferences.xml b/trytond/res/view/user_form_preferences.xml
index 8e34286..42bbd25 100644
--- a/trytond/res/view/user_form_preferences.xml
+++ b/trytond/res/view/user_form_preferences.xml
@@ -9,7 +9,7 @@ this repository contains the full copyright notices and license terms. -->
             <label name="email"/>
             <field name="email" widget="email"/>
             <label name="password"/>
-            <field name="password"/>
+            <field name="password" widget="password"/>
             <separator name="signature" colspan="4"/>
             <field name="signature" colspan="4"/>
         </page>
@@ -24,8 +24,6 @@ this repository contains the full copyright notices and license terms. -->
         <page string="Preferences" col="2" id="preferences">
             <label name="language"/>
             <field name="language" widget="selection"/>
-            <label name="timezone"/>
-            <field name="timezone"/>
         </page>
     </notebook>
 </form>
diff --git a/trytond/rpc.py b/trytond/rpc.py
index afa0827..1e3e2a9 100644
--- a/trytond/rpc.py
+++ b/trytond/rpc.py
@@ -8,7 +8,7 @@ class RPC(object):
     '''Define RPC behavior
 
     readonly: The transaction mode
-    instantiate: The position of the argument to be instanciated
+    instantiate: The position or the slice of the arguments to be instanciated
     result: The function to transform the result
     '''
 
@@ -41,6 +41,12 @@ class RPC(object):
                     return obj(**data)
                 else:
                     return obj.browse(data)
-            data = args[self.instantiate]
-            args[self.instantiate] = instance(data)
+            if isinstance(self.instantiate, slice):
+                for i, data in enumerate(args[self.instantiate]):
+                    start, _, step = self.instantiate.indices(len(args))
+                    i = i * step + start
+                    args[i] = instance(data)
+            else:
+                data = args[self.instantiate]
+                args[self.instantiate] = instance(data)
         return args, kwargs, context, timestamp
diff --git a/trytond/server.py b/trytond/server.py
index 23a1989..720b224 100644
--- a/trytond/server.py
+++ b/trytond/server.py
@@ -29,7 +29,6 @@ class TrytonServer(object):
 
         CONFIG.update_etc(options['configfile'])
         CONFIG.update_cmdline(options)
-        CONFIG.set_timezone()
 
         if CONFIG['logfile']:
             logf = CONFIG['logfile']
diff --git a/trytond/tests/__init__.py b/trytond/tests/__init__.py
index 3ce4543..8da0e0a 100644
--- a/trytond/tests/__init__.py
+++ b/trytond/tests/__init__.py
@@ -12,6 +12,7 @@ from .access import *
 from .wizard import *
 from .workflow import *
 from .copy_ import *
+from history import *
 
 
 def register():
@@ -21,6 +22,7 @@ def register():
         Integer,
         IntegerDefault,
         IntegerRequired,
+        IntegerDomain,
         Float,
         FloatDefault,
         FloatRequired,
@@ -39,9 +41,6 @@ def register():
         TextRequired,
         TextSize,
         TextTranslate,
-        Sha,
-        ShaDefault,
-        ShaRequired,
         Date,
         DateDefault,
         DateRequired,
@@ -108,7 +107,6 @@ def register():
         ImportDataNumericRequired,
         ImportDataChar,
         ImportDataText,
-        ImportDataSha,
         ImportDataDate,
         ImportDataDateTime,
         ImportDataSelection,
@@ -134,8 +132,15 @@ def register():
         CopyOne2ManyTarget,
         CopyOne2ManyReference,
         CopyOne2ManyReferenceTarget,
+        CopyMany2Many,
+        CopyMany2ManyTarget,
+        CopyMany2ManyRelation,
+        CopyMany2ManyReference,
+        CopyMany2ManyReferenceTarget,
+        CopyMany2ManyReferenceRelation,
         Many2OneTarget,
         Many2OneDomainValidation,
+        TestHistory,
         module='tests', type_='model')
     Pool.register(
         TestWizard,
diff --git a/trytond/tests/copy_.py b/trytond/tests/copy_.py
index c2eb916..d0d92cc 100644
--- a/trytond/tests/copy_.py
+++ b/trytond/tests/copy_.py
@@ -6,6 +6,9 @@ from trytond.model import ModelSQL, fields
 __all__ = [
     'CopyOne2Many', 'CopyOne2ManyTarget',
     'CopyOne2ManyReference', 'CopyOne2ManyReferenceTarget',
+    'CopyMany2Many', 'CopyMany2ManyTarget', 'CopyMany2ManyRelation',
+    'CopyMany2ManyReference', 'CopyMany2ManyReferenceTarget',
+    'CopyMany2ManyReferenceRelation',
     ]
 
 
@@ -40,3 +43,52 @@ class CopyOne2ManyReferenceTarget(ModelSQL):
             (None, ''),
             ('test.copy.one2many_reference', 'One2Many'),
             ])
+
+
+class CopyMany2Many(ModelSQL):
+    "Copy Many2Many"
+    __name__ = 'test.copy.many2many'
+    name = fields.Char('Name')
+    many2many = fields.Many2Many('test.copy.many2many.rel', 'many2many',
+        'many2many_target', 'Many2Many')
+
+
+class CopyMany2ManyTarget(ModelSQL):
+    "Copy Many2Many Target"
+    __name__ = 'test.copy.many2many.target'
+    name = fields.Char('Name')
+
+
+class CopyMany2ManyRelation(ModelSQL):
+    "Copy Many2Many Relation"
+    __name__ = 'test.copy.many2many.rel'
+    name = fields.Char('Name')
+    many2many = fields.Many2One('test.copy.many2many', 'Many2Many')
+    many2many_target = fields.Many2One('test.copy.many2many.target',
+        'Many2Many Target')
+
+
+class CopyMany2ManyReference(ModelSQL):
+    "Copy Many2ManyReference"
+    __name__ = 'test.copy.many2many_reference'
+    name = fields.Char('Name')
+    many2many = fields.Many2Many('test.copy.many2many_reference.rel',
+        'many2many', 'many2many_target', 'Many2Many')
+
+
+class CopyMany2ManyReferenceTarget(ModelSQL):
+    "Copy Many2ManyReference Target"
+    __name__ = 'test.copy.many2many_reference.target'
+    name = fields.Char('Name')
+
+
+class CopyMany2ManyReferenceRelation(ModelSQL):
+    "Copy Many2ManyReference Relation"
+    __name__ = 'test.copy.many2many_reference.rel'
+    name = fields.Char('Name')
+    many2many = fields.Reference('Many2Many', [
+            (None, ''),
+            ('test.copy.many2many_reference', 'Many2Many'),
+            ])
+    many2many_target = fields.Many2One('test.copy.many2many_reference.target',
+        'Many2ManyReference Target')
diff --git a/trytond/tests/export_data.py b/trytond/tests/export_data.py
index 8c9f3c6..5f61332 100644
--- a/trytond/tests/export_data.py
+++ b/trytond/tests/export_data.py
@@ -25,7 +25,6 @@ class ExportData(ModelSQL):
     numeric = fields.Numeric('Numeric')
     char = fields.Char('Char')
     text = fields.Text('Text')
-    sha = fields.Sha('Sha')
     date = fields.Date('Date')
     datetime = fields.DateTime('DateTime')
     selection = fields.Selection([
diff --git a/trytond/tests/history.py b/trytond/tests/history.py
new file mode 100644
index 0000000..b23d8ec
--- /dev/null
+++ b/trytond/tests/history.py
@@ -0,0 +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 ModelSQL, fields
+
+__all__ = ['TestHistory']
+
+
+class TestHistory(ModelSQL):
+    'Test History'
+    __name__ = 'test.history'
+    _history = True
+    value = fields.Integer('Value')
diff --git a/trytond/tests/import_data.py b/trytond/tests/import_data.py
index 870bc21..ecc7a94 100644
--- a/trytond/tests/import_data.py
+++ b/trytond/tests/import_data.py
@@ -7,7 +7,7 @@ __all__ = [
     'ImportDataBoolean', 'ImportDataInteger', 'ImportDataIntegerRequired',
     'ImportDataFloat', 'ImportDataFloatRequired',
     'ImportDataNumeric', 'ImportDataNumericRequired',
-    'ImportDataChar', 'ImportDataText', 'ImportDataSha',
+    'ImportDataChar', 'ImportDataText',
     'ImportDataDate', 'ImportDataDateTime', 'ImportDataSelection',
     'ImportDataMany2OneTarget', 'ImportDataMany2One',
     'ImportDataMany2ManyTarget', 'ImportDataMany2Many',
@@ -71,12 +71,6 @@ class ImportDataText(ModelSQL):
     text = fields.Text('Text')
 
 
-class ImportDataSha(ModelSQL):
-    "Import Data Sha"
-    __name__ = 'test.import_data.sha'
-    sha = fields.Sha('Sha')
-
-
 class ImportDataDate(ModelSQL):
     "Import Data Date"
     __name__ = 'test.import_data.date'
diff --git a/trytond/tests/run-tests.py b/trytond/tests/run-tests.py
new file mode 100755
index 0000000..13bbd88
--- /dev/null
+++ b/trytond/tests/run-tests.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+#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 logging
+import argparse
+import os
+import time
+import unittest
+
+from trytond.config import CONFIG
+
+if __name__ != '__main__':
+    raise ImportError('%s can not be imported' % __name__)
+
+logging.basicConfig(level=logging.ERROR)
+parser = argparse.ArgumentParser()
+parser.add_argument("-c", "--config", dest="config",
+    help="specify config file")
+parser.add_argument("-m", "--modules", action="store_true", dest="modules",
+    default=False, help="Run also modules tests")
+parser.add_argument("-v", action="count", default=0, dest="verbosity",
+    help="Increase verbosity")
+parser.add_argument('tests', metavar='test', nargs='*')
+opt = parser.parse_args()
+
+CONFIG['db_type'] = 'sqlite'
+CONFIG.update_etc(opt.config)
+if not CONFIG['admin_passwd']:
+    CONFIG['admin_passwd'] = 'admin'
+
+if CONFIG['db_type'] == 'sqlite':
+    database_name = ':memory:'
+else:
+    database_name = 'test_' + str(int(time.time()))
+os.environ['DB_NAME'] = database_name
+
+from trytond.tests.test_tryton import all_suite, modules_suite
+if not opt.modules:
+    suite = all_suite(opt.tests)
+else:
+    suite = modules_suite(opt.tests)
+unittest.TextTestRunner(verbosity=opt.verbosity).run(suite)
diff --git a/trytond/tests/test.py b/trytond/tests/test.py
index 6efeba4..f6cbaf7 100644
--- a/trytond/tests/test.py
+++ b/trytond/tests/test.py
@@ -8,12 +8,11 @@ from trytond.pyson import Eval
 
 __all__ = [
     'Boolean', 'BooleanDefault',
-    'Integer', 'IntegerDefault', 'IntegerRequired',
+    'Integer', 'IntegerDefault', 'IntegerRequired', 'IntegerDomain',
     'Float', 'FloatDefault', 'FloatRequired', 'FloatDigits',
     'Numeric', 'NumericDefault', 'NumericRequired', 'NumericDigits',
     'Char', 'CharDefault', 'CharRequired', 'CharSize', 'CharTranslate',
     'Text', 'TextDefault', 'TextRequired', 'TextSize', 'TextTranslate',
-    'Sha', 'ShaDefault', 'ShaRequired',
     'Date', 'DateDefault', 'DateRequired',
     'DateTime', 'DateTimeDefault', 'DateTimeRequired', 'DateTimeFormat',
     'Time', 'TimeDefault', 'TimeRequired', 'TimeFormat',
@@ -80,6 +79,12 @@ class IntegerRequired(ModelSQL):
             required=True)
 
 
+class IntegerDomain(ModelSQL):
+    'Integer Domain'
+    __name__ = 'test.integer_domain'
+    integer = fields.Integer('Integer', domain=[('integer', '>', 42)])
+
+
 class Float(ModelSQL):
     'Float'
     __name__ = 'test.float'
@@ -226,31 +231,6 @@ class TextTranslate(ModelSQL):
             required=False, translate=True)
 
 
-class Sha(ModelSQL):
-    'Sha'
-    __name__ = 'test.sha'
-    sha = fields.Sha(string='Sha', help='Test sha',
-            required=False)
-
-
-class ShaDefault(ModelSQL):
-    'Sha Default'
-    __name__ = 'test.sha_default'
-    sha = fields.Sha(string='Sha', help='Test sha',
-            required=False)
-
-    @staticmethod
-    def default_sha():
-        return 'Sha'
-
-
-class ShaRequired(ModelSQL):
-    'Sha Required'
-    __name__ = 'test.sha_required'
-    sha = fields.Sha(string='Sha', help='Test sha',
-            required=True)
-
-
 class Date(ModelSQL):
     'Date'
     __name__ = 'test.date'
@@ -625,10 +605,11 @@ class Selection(ModelSQL):
             ('', ''), ('arabic', 'Arabic'), ('hexa', 'Hexadecimal')],
         'Selection')
     dyn_select = fields.Selection('get_selection',
-        'Instance Dynamic Selection', selection_change_with=['select'])
+        'Instance Dynamic Selection')
     dyn_select_static = fields.Selection('static_selection',
         'Static Selection')
 
+    @fields.depends('select')
     def get_selection(self):
         if self.select == 'arabic':
             return [('', '')] + [(str(i), str(i)) for i in range(1, 11)]
diff --git a/trytond/tests/test_access.py b/trytond/tests/test_access.py
index 5efe63f..6322ac4 100644
--- a/trytond/tests/test_access.py
+++ b/trytond/tests/test_access.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -9,9 +8,7 @@ from trytond.transaction import Transaction
 
 
 class ModelAccessTestCase(unittest.TestCase):
-    '''
-    Test Model Access
-    '''
+    'Test Model Access'
 
     def setUp(self):
         install_module('tests')
@@ -21,9 +18,7 @@ class ModelAccessTestCase(unittest.TestCase):
         self.group = POOL.get('res.group')
 
     def test0010perm_read(self):
-        '''
-        Test Read Access
-        '''
+        'Test Read Access'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([('model', '=', 'test.access')])
@@ -104,9 +99,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access._get_access_cache.clear()
 
     def test0020perm_write(self):
-        '''
-        Test Write Access
-        '''
+        'Test Write Access'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([('model', '=', 'test.access')])
@@ -186,9 +179,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access._get_access_cache.clear()
 
     def test0030perm_create(self):
-        '''
-        Test Create Access
-        '''
+        'Test Create Access'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([('model', '=', 'test.access')])
@@ -267,9 +258,7 @@ class ModelAccessTestCase(unittest.TestCase):
             self.model_access._get_access_cache.clear()
 
     def test0040perm_delete(self):
-        '''
-        Test Delete Access
-        '''
+        'Test Delete Access'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([('model', '=', 'test.access')])
@@ -354,9 +343,7 @@ class ModelAccessTestCase(unittest.TestCase):
 
 
 class ModelFieldAccessTestCase(unittest.TestCase):
-    '''
-    Test Model Field Access
-    '''
+    'Test Model Field Access'
 
     def setUp(self):
         install_module('tests')
@@ -366,9 +353,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
         self.group = POOL.get('res.group')
 
     def test0010perm_read(self):
-        '''
-        Test Read Access
-        '''
+        'Test Read Access'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             field1, = self.field.search([
@@ -578,9 +563,7 @@ class ModelFieldAccessTestCase(unittest.TestCase):
             self.field_access._get_access_cache.clear()
 
     def test0010perm_write(self):
-        '''
-        Test Write Access
-        '''
+        'Test Write Access'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             field1, = self.field.search([
@@ -793,7 +776,3 @@ def suite():
     suite_.addTests(unittest.TestLoader(
         ).loadTestsFromTestCase(ModelFieldAccessTestCase))
     return suite_
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_cache.py b/trytond/tests/test_cache.py
index f204826..9aa859c 100644
--- a/trytond/tests/test_cache.py
+++ b/trytond/tests/test_cache.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
@@ -39,7 +38,3 @@ def suite():
     for testcase in (CacheTestCase,):
         suite.addTests(func(testcase))
     return suite
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_copy.py b/trytond/tests/test_copy.py
index 164d5c6..d6c3156 100644
--- a/trytond/tests/test_copy.py
+++ b/trytond/tests/test_copy.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -9,9 +8,7 @@ from trytond.transaction import Transaction
 
 
 class CopyTestCase(unittest.TestCase):
-    '''
-    Test copy.
-    '''
+    'Test copy'
 
     def setUp(self):
         install_module('tests')
@@ -20,13 +17,16 @@ class CopyTestCase(unittest.TestCase):
         self.one2many_reference = POOL.get('test.copy.one2many_reference')
         self.one2many_reference_target = \
             POOL.get('test.copy.one2many_reference.target')
+        self.many2many = POOL.get('test.copy.many2many')
+        self.many2many_target = POOL.get('test.copy.many2many.target')
+        self.many2many_reference = POOL.get('test.copy.many2many_reference')
+        self.many2many_reference_target = \
+            POOL.get('test.copy.many2many_reference.target')
 
     def test0130one2many(self):
-        '''
-        Test one2many.
-        '''
+        'Test one2many'
         with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+                context=CONTEXT):
             for One2many, Target in (
                     (self.one2many, self.one2many_target),
                     (self.one2many_reference, self.one2many_reference_target),
@@ -47,12 +47,31 @@ class CopyTestCase(unittest.TestCase):
                 self.assertEqual([x.name for x in one2many.one2many],
                     [x.name for x in one2many_copy.one2many])
 
-            transaction.cursor.rollback()
+    def test0140many2many(self):
+        'Test many2many'
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT):
+            for Many2many, Target in (
+                    (self.many2many, self.many2many_target),
+                    (self.many2many_reference,
+                        self.many2many_reference_target),
+                    ):
+                many2many = Many2many(name='Test')
+                many2many.many2many = [
+                    Target(name='Target 1'),
+                    Target(name='Target 2'),
+                    ]
+                many2many.save()
+
+                many2many_copy, = Many2many.copy([many2many])
+
+                self.assertNotEqual(many2many, many2many_copy)
+                self.assertEqual(len(many2many.many2many),
+                    len(many2many_copy.many2many))
+                self.assertEqual(many2many.many2many, many2many_copy.many2many)
+                self.assertEqual([x.name for x in many2many.many2many],
+                    [x.name for x in many2many_copy.many2many])
 
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(CopyTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_exportdata.py b/trytond/tests/test_exportdata.py
index 6b9efbf..989d2d2 100644
--- a/trytond/tests/test_exportdata.py
+++ b/trytond/tests/test_exportdata.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -19,9 +18,7 @@ from trytond.transaction import Transaction
 
 
 class ExportDataTestCase(unittest.TestCase):
-    '''
-    Test export_data.
-    '''
+    'Test export_data'
 
     def setUp(self):
         install_module('tests')
@@ -30,9 +27,7 @@ class ExportDataTestCase(unittest.TestCase):
         self.export_data_relation = POOL.get('test.export_data.relation')
 
     def test0010boolean(self):
-        '''
-        Test boolean.
-        '''
+        'Test boolean'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -56,9 +51,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0020integer(self):
-        '''
-        Test integer.
-        '''
+        'Test integer'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -80,9 +73,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0030float(self):
-        '''
-        Test float.
-        '''
+        'Test float'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -104,9 +95,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0040numeric(self):
-        '''
-        Test numeric.
-        '''
+        'Test numeric'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -130,9 +119,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0050char(self):
-        '''
-        Test char.
-        '''
+        'Test char'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -154,9 +141,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0060text(self):
-        '''
-        Test text.
-        '''
+        'Test text'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -177,25 +162,8 @@ class ExportDataTestCase(unittest.TestCase):
 
             transaction.cursor.rollback()
 
-    def test0070sha(self):
-        '''
-        Test sha.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
-            export1, = self.export_data.create([{
-                        'sha': 'Test',
-                        }])
-            self.assertEqual(
-                self.export_data.export_data([export1], ['sha']),
-                [['640ab2bae07bedc4c163f679a746f7ab7fb5d1fa']])
-
-            transaction.cursor.rollback()
-
     def test0080date(self):
-        '''
-        Test date.
-        '''
+        'Test date'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -217,9 +185,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0090datetime(self):
-        '''
-        Test datetime.
-        '''
+        'Test datetime'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -243,9 +209,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0100selection(self):
-        '''
-        Test selection.
-        '''
+        'Test selection'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{
@@ -269,9 +233,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0110many2one(self):
-        '''
-        Test many2one.
-        '''
+        'Test many2one'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             target, = self.export_data_target.create([{
@@ -299,16 +261,14 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0120many2many(self):
-        '''
-        Test many2many.
-        '''
+        'Test many2many'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             target1, = self.export_data_target.create([{
                         'name': 'Target 1',
                         }])
             export1, = self.export_data.create([{
-                        'many2many': [('set', [target1])],
+                        'many2many': [('add', [target1])],
                         }])
             self.assertEqual(
                 self.export_data.export_data([export1], ['many2many/name']),
@@ -318,7 +278,7 @@ class ExportDataTestCase(unittest.TestCase):
                         'name': 'Target 2',
                         }])
             self.export_data.write([export1], {
-                    'many2many': [('set', [target1.id, target2.id])],
+                    'many2many': [('add', [target1.id, target2.id])],
                     })
             self.assertEqual(
                 self.export_data.export_data([export1], ['id',
@@ -340,9 +300,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0130one2many(self):
-        '''
-        Test one2many.
-        '''
+        'Test one2many'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             export1, = self.export_data.create([{}])
@@ -376,9 +334,7 @@ class ExportDataTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0140reference(self):
-        '''
-        Test reference.
-        '''
+        'Test reference'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             target1, = self.export_data_target.create([{}])
@@ -405,7 +361,3 @@ class ExportDataTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(ExportDataTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_fields.py b/trytond/tests/test_fields.py
index 0d7ae44..8d260cc 100644
--- a/trytond/tests/test_fields.py
+++ b/trytond/tests/test_fields.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -20,9 +19,7 @@ from trytond.exceptions import UserError
 
 
 class FieldsTestCase(unittest.TestCase):
-    '''
-    Test Fields.
-    '''
+    'Test Fields'
 
     def setUp(self):
         install_module('tests')
@@ -32,6 +29,7 @@ class FieldsTestCase(unittest.TestCase):
         self.integer = POOL.get('test.integer')
         self.integer_default = POOL.get('test.integer_default')
         self.integer_required = POOL.get('test.integer_required')
+        self.integer_domain = POOL.get('test.integer_domain')
 
         self.float = POOL.get('test.float')
         self.float_default = POOL.get('test.float_default')
@@ -55,10 +53,6 @@ class FieldsTestCase(unittest.TestCase):
         self.text_size = POOL.get('test.text_size')
         self.text_translate = POOL.get('test.text_translate')
 
-        self.sha = POOL.get('test.sha')
-        self.sha_default = POOL.get('test.sha_default')
-        self.sha_required = POOL.get('test.sha_required')
-
         self.date = POOL.get('test.date')
         self.date_default = POOL.get('test.date_default')
         self.date_required = POOL.get('test.date_required')
@@ -119,9 +113,7 @@ class FieldsTestCase(unittest.TestCase):
         self.m2o_target = POOL.get('test.many2one_target')
 
     def test0010boolean(self):
-        '''
-        Test Boolean.
-        '''
+        'Test Boolean'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             boolean1, = self.boolean.create([{
@@ -219,9 +211,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0020integer(self):
-        '''
-        Test Integer.
-        '''
+        'Test Integer'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             integer1, = self.integer.create([{
@@ -400,10 +390,18 @@ class FieldsTestCase(unittest.TestCase):
 
             transaction.cursor.rollback()
 
+    def test0021integer(self):
+        'Test Integer with domain'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            self.integer_domain.create([{
+                        'integer': 100,
+                        }])
+            self.assertRaises(UserError, self.integer_domain.create, [{
+                        'integer': 10,
+                        }])
+
     def test0030float(self):
-        '''
-        Test Float.
-        '''
+        'Test Float'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             float1, = self.float.create([{
@@ -606,9 +604,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0040numeric(self):
-        '''
-        Test Numeric.
-        '''
+        'Test Numeric'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             numeric1, = self.numeric.create([{
@@ -816,10 +812,21 @@ class FieldsTestCase(unittest.TestCase):
 
             transaction.cursor.rollback()
 
+    def test0041numeric(self):
+        'Test numeric search cast'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            numeric1, numeric2 = self.numeric.create([{
+                        'numeric': Decimal('1.1'),
+                        }, {
+                        'numeric': Decimal('100.0'),
+                        }])
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
     def test0050char(self):
-        '''
-        Test Char.
-        '''
+        'Test Char'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             for char in (self.char_translate, self.char):
@@ -1069,9 +1076,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0060text(self):
-        '''
-        Test Text.
-        '''
+        'Test Text'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             for text in (self.text_translate, self.text):
@@ -1319,202 +1324,8 @@ class FieldsTestCase(unittest.TestCase):
 
             transaction.cursor.rollback()
 
-    def test0070sha(self):
-        '''
-        Test Sha.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
-            sha1, = self.sha.create([{
-                        'sha': 'Test',
-                        }])
-            self.assert_(sha1)
-            self.assertEqual(sha1.sha,
-                '640ab2bae07bedc4c163f679a746f7ab7fb5d1fa')
-
-            sha = self.sha.search([
-                    ('sha', '=', 'Test'),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', '=', 'Foo'),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', '=', None),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', '!=', 'Test'),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', '!=', 'Foo'),
-                    ])
-            self.assert_(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', '!=', None),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'in', ['Test']),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'in', ['Foo']),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'in', [None]),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'in', []),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'not in', ['Test']),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'not in', ['Foo']),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'not in', [None]),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'not in', []),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'like', 'Test'),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'like', 'Foo'),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'ilike', 'Test'),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'ilike', 'foo'),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'not like', 'Test'),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha = self.sha.search([
-                    ('sha', 'not like', 'Foo'),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha = self.sha.search([
-                    ('sha', 'not ilike', 'foo'),
-                    ])
-            self.assertEqual(sha, [sha1])
-
-            sha2, = self.sha.create([{
-                        'sha': None,
-                        }])
-            self.assert_(sha2)
-            self.assertEqual(sha2.sha, None)
-
-            sha = self.sha.search([
-                    ('sha', '=', None),
-                    ])
-            self.assertEqual(sha, [sha2])
-
-            sha = self.sha.search([
-                    ('sha', 'in', [None, 'Test']),
-                    ])
-            self.assertEqual(sha, [sha1, sha2])
-
-            sha = self.sha.search([
-                    ('sha', 'not in', [None, 'Test']),
-                    ])
-            self.assertEqual(sha, [])
-
-            sha3, = self.sha.create([{}])
-            self.assert_(sha3)
-            self.assertEqual(sha3.sha, None)
-
-            sha4, = self.sha_default.create([{}])
-            self.assert_(sha4)
-            self.assertEqual(sha4.sha,
-                'ba79baeb9f10896a46ae74715271b7f586e74640')
-
-            self.sha.write([sha1], {
-                    'sha': None,
-                    })
-            self.assertEqual(sha1.sha, None)
-
-            self.sha.write([sha2], {
-                    'sha': 'Test',
-                    })
-            self.assertEqual(sha2.sha,
-                '640ab2bae07bedc4c163f679a746f7ab7fb5d1fa')
-
-            self.assertRaises(Exception, self.sha_required.create, [{}])
-            transaction.cursor.rollback()
-
-            sha5, = self.sha_required.create([{
-                        'sha': 'Test',
-                        }])
-            self.assert_(sha5)
-
-            sha6, = self.sha.create([{
-                        'sha': u'é',
-                        }])
-            self.assert_(sha6)
-            self.assertEqual(sha6.sha,
-                u'bf15be717ac1b080b4f1c456692825891ff5073d')
-
-            sha = self.sha.search([
-                    ('sha', '=', u'é'),
-                    ])
-            self.assertEqual(sha, [sha6])
-
-            self.sha.write([sha6], {
-                    'sha': 'é',
-                    })
-            self.assertEqual(sha6.sha,
-                u'bf15be717ac1b080b4f1c456692825891ff5073d')
-
-            sha = self.sha.search([
-                    ('sha', '=', 'é'),
-                    ])
-            self.assertEqual(sha, [sha6])
-
-            transaction.cursor.rollback()
-
     def test0080date(self):
-        '''
-        Test Date.
-        '''
+        'Test Date'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             today = datetime.date(2009, 1, 1)
@@ -1756,9 +1567,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0090datetime(self):
-        '''
-        Test DateTime.
-        '''
+        'Test DateTime'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             today = datetime.datetime(2009, 1, 1, 12, 0, 0)
@@ -2024,9 +1833,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0100time(self):
-        '''
-        Test Time.
-        '''
+        'Test Time'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             pre_evening = datetime.time(16, 30)
@@ -2268,9 +2075,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0110one2one(self):
-        '''
-        Test One2One.
-        '''
+        'Test One2One'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             target1, = self.one2one_target.create([{
@@ -2396,9 +2201,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0120one2many(self):
-        '''
-        Test One2Many.
-        '''
+        'Test One2Many'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             for one2many, one2many_target in (
@@ -2498,7 +2301,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 one2many.write([one2many1], {
                         'targets': [
-                            ('unlink', [target2.id]),
+                            ('remove', [target2.id]),
                             ],
                         })
                 self.assertEqual(one2many1.targets, (target1,))
@@ -2509,7 +2312,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 one2many.write([one2many1], {
                         'targets': [
-                            ('unlink_all',),
+                            ('remove', [target1.id]),
                             ],
                         })
                 self.assertEqual(one2many1.targets, ())
@@ -2520,7 +2323,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 one2many.write([one2many1], {
                         'targets': [
-                            ('set', [target1.id, target2.id]),
+                            ('add', [target1.id, target2.id]),
                             ],
                         })
                 self.assertEqual(one2many1.targets,
@@ -2528,23 +2331,37 @@ class FieldsTestCase(unittest.TestCase):
 
                 one2many.write([one2many1], {
                         'targets': [
-                            ('delete', [target2.id]),
+                            ('copy', [target1.id], {'name': 'copy1'}),
                             ],
                         })
-                self.assertEqual(one2many1.targets, (target1,))
                 targets = one2many_target.search([
-                        ('id', '=', target2.id),
+                        ('id', 'not in', [target1.id, target2.id]),
                         ])
-                self.assertEqual(targets, [])
+                self.assertEqual(len(targets), 1)
+                self.assertEqual(targets[0].name, 'copy1')
 
                 one2many.write([one2many1], {
                         'targets': [
-                            ('delete_all',),
+                            ('copy', [target2.id]),
                             ],
                         })
-                self.assertEqual(one2many1.targets, ())
+                self.assertEqual(len(one2many1.targets), 4)
+                targets = one2many_target.search([
+                        ('id', 'not in', [target1.id, target2.id]),
+                        ])
+                self.assertEqual(len(targets), 2)
+                names = set([target.name for target in targets])
+                self.assertEqual(names, set(('copy1', 'target2')))
+
+                copy_ids = [target.id for target in targets]
+                one2many.write([one2many1], {
+                        'targets': [
+                            ('delete', [target2.id] + copy_ids),
+                            ],
+                        })
+                self.assertEqual(one2many1.targets, (target1,))
                 targets = one2many_target.search([
-                        ('id', '=', target1.id),
+                        ('id', '=', target2.id),
                         ])
                 self.assertEqual(targets, [])
 
@@ -2583,9 +2400,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0130many2many(self):
-        '''
-        Test Many2Many.
-        '''
+        'Test Many2Many'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             for many2many, many2many_target in (
@@ -2680,7 +2495,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 many2many.write([many2many1], {
                         'targets': [
-                            ('unlink', [target2.id]),
+                            ('remove', [target2.id]),
                             ],
                         })
                 self.assertEqual(many2many1.targets, (target1,))
@@ -2691,7 +2506,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 many2many.write([many2many1], {
                         'targets': [
-                            ('unlink_all',),
+                            ('remove', [target1.id]),
                             ],
                         })
                 self.assertEqual(many2many1.targets, ())
@@ -2702,7 +2517,7 @@ class FieldsTestCase(unittest.TestCase):
 
                 many2many.write([many2many1], {
                         'targets': [
-                            ('set', [target1.id, target2.id]),
+                            ('add', [target1.id, target2.id]),
                             ],
                         })
                 self.assertEqual(many2many1.targets,
@@ -2710,23 +2525,37 @@ class FieldsTestCase(unittest.TestCase):
 
                 many2many.write([many2many1], {
                         'targets': [
-                            ('delete', [target2.id]),
+                            ('copy', [target1.id], {'name': 'copy1'}),
                             ],
                         })
-                self.assertEqual(many2many1.targets, (target1,))
                 targets = many2many_target.search([
-                        ('id', '=', target2.id),
+                        ('id', 'not in', [target1.id, target2.id]),
                         ])
-                self.assertEqual(targets, [])
+                self.assertEqual(len(targets), 1)
+                self.assertEqual(targets[0].name, 'copy1')
 
                 many2many.write([many2many1], {
                         'targets': [
-                            ('delete_all',),
+                            ('copy', [target2.id]),
                             ],
                         })
-                self.assertEqual(many2many1.targets, ())
+                self.assertEqual(len(many2many1.targets), 4)
+                targets = many2many_target.search([
+                        ('id', 'not in', [target1.id, target2.id]),
+                        ])
+                self.assertEqual(len(targets), 2)
+                names = set([target.name for target in targets])
+                self.assertEqual(names, set(('copy1', 'target2')))
+
+                copy_ids = [target.id for target in targets]
+                many2many.write([many2many1], {
+                        'targets': [
+                            ('delete', [target2.id] + copy_ids),
+                            ],
+                        })
+                self.assertEqual(many2many1.targets, (target1,))
                 targets = many2many_target.search([
-                        ('id', '=', target1.id),
+                        ('id', '=', target2.id),
                         ])
                 self.assertEqual(targets, [])
 
@@ -2751,19 +2580,10 @@ class FieldsTestCase(unittest.TestCase):
                         'name': str(i),
                         } for i in range(6)])
 
-            self.many2many_size.create([{
-                        'targets': [('set', size_targets[:5])],
-                        }])
-            self.assertRaises(Exception, self.many2many_size.create, [{
-                        'targets': [('set', size_targets)],
-                        }])
-
             transaction.cursor.rollback()
 
     def test0140reference(self):
-        '''
-        Test Reference.
-        '''
+        'Test Reference'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             target1, = self.reference_target.create([{
@@ -2895,9 +2715,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0150property(self):
-        '''
-        Test Property with supported field types.
-        '''
+        'Test Property with supported field types'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
 
@@ -3164,6 +2982,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0160selection(self):
+        'Test Selection'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             selection1, = self.selection.create([{'select': 'arabic'}])
@@ -3212,6 +3031,7 @@ class FieldsTestCase(unittest.TestCase):
             self.assertEqual(selection6.select, 'latin')
 
     def test0170dict(self):
+        'Test Dict'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             dict1, = self.dict_.create([{
@@ -3239,6 +3059,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0180binary(self):
+        'Test Binary'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             bin1, = self.binary.create([{
@@ -3272,6 +3093,7 @@ class FieldsTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0190many2one(self):
+        'Test Many2One'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
 
@@ -3297,7 +3119,3 @@ class FieldsTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(FieldsTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_history.py b/trytond/tests/test_history.py
new file mode 100644
index 0000000..5b4c064
--- /dev/null
+++ b/trytond/tests/test_history.py
@@ -0,0 +1,178 @@
+#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 datetime
+
+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
+
+
+class HistoryTestCase(unittest.TestCase):
+    'Test History'
+
+    def setUp(self):
+        install_module('tests')
+
+    def test0010read(self):
+        'Test read history'
+        History = POOL.get('test.history')
+
+        # Create some history entry
+        # It is needed to commit to have different timestamps
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(value=1)
+            history.save()
+            history_id = history.id
+            first = history.create_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 2
+            history.save()
+            second = history.write_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 3
+            history.save()
+            third = history.write_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            for timestamp, value in [
+                    (first, 1),
+                    (second, 2),
+                    (third, 3),
+                    (datetime.datetime.now(), 3),
+                    (datetime.datetime.max, 3),
+                    ]:
+                with Transaction().set_context(_datetime=timestamp):
+                    history = History(history_id)
+                    self.assertEqual(history.value, value)
+
+            with Transaction().set_context(_datetime=datetime.datetime.min):
+                self.assertRaises(UserError, History.read, [history_id])
+
+    @unittest.skipIf(CONFIG['db_type'] in ('sqlite', 'mysql'),
+        'now() is not the start of the transaction')
+    def test0020read_same_timestamp(self):
+        'Test read 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.write_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()
+            third = history.write_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            for timestamp, value in [
+                    (first, 2),
+                    (third, 3),
+                    ]:
+                with Transaction().set_context(_datetime=timestamp):
+                    history = History(history_id)
+                    self.assertEqual(history.value, value)
+
+    def test0030history_revisions(self):
+        'Test history revisions'
+        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
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 2
+            history.save()
+            second = history.write_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 3
+            history.save()
+            third = history.write_date
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            revisions = History.history_revisions([history_id])
+            self.assertEqual(revisions, [
+                    (third, history_id, u'Administrator'),
+                    (second, history_id, u'Administrator'),
+                    (first, history_id, u'Administrator'),
+                    ])
+
+    def test0040restore_history(self):
+        'Test restore history'
+        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
+
+            transaction.cursor.commit()
+
+        with Transaction().start(DB_NAME, USER,
+                context=CONTEXT) as transaction:
+            history = History(history_id)
+            history.value = 2
+            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, 1)
+
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
+            History.restore_history([history_id], datetime.datetime.min)
+            self.assertRaises(UserError, History.read, [history_id])
+
+
+def suite():
+    return unittest.TestLoader().loadTestsFromTestCase(HistoryTestCase)
diff --git a/trytond/tests/test_importdata.py b/trytond/tests/test_importdata.py
index a3dc761..8718032 100644
--- a/trytond/tests/test_importdata.py
+++ b/trytond/tests/test_importdata.py
@@ -1,17 +1,17 @@
-#!/usr/bin/env python
 # -*- 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 unittest
+from decimal import InvalidOperation
+
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.transaction import Transaction
+from trytond.exceptions import UserError
 
 
 class ImportDataTestCase(unittest.TestCase):
-    '''
-    Test import_data.
-    '''
+    'Test import_data'
 
     def setUp(self):
         install_module('tests')
@@ -24,7 +24,6 @@ class ImportDataTestCase(unittest.TestCase):
         self.numeric_required = POOL.get('test.import_data.numeric_required')
         self.char = POOL.get('test.import_data.char')
         self.text = POOL.get('test.import_data.text')
-        self.sha = POOL.get('test.import_data.sha')
         self.date = POOL.get('test.import_data.date')
         self.datetime = POOL.get('test.import_data.datetime')
         self.selection = POOL.get('test.import_data.selection')
@@ -34,450 +33,368 @@ class ImportDataTestCase(unittest.TestCase):
         self.reference = POOL.get('test.import_data.reference')
 
     def test0010boolean(self):
-        '''
-        Test boolean.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test boolean'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.boolean.import_data(['boolean'],
-                [['True']]), (1, 0, 0, 0))
+                [['True']]), 1)
 
             self.assertEqual(self.boolean.import_data(['boolean'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
             self.assertEqual(self.boolean.import_data(['boolean'],
-                [['False']]), (1, 0, 0, 0))
+                [['False']]), 1)
 
             self.assertEqual(self.boolean.import_data(['boolean'],
-                [['0']]), (1, 0, 0, 0))
+                [['0']]), 1)
 
             self.assertEqual(self.boolean.import_data(['boolean'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.boolean.import_data(['boolean'],
-                [['True'], ['False']]), (2, 0, 0, 0))
+                [['True'], ['False']]), 2)
 
-            self.assertEqual(self.boolean.import_data(['boolean'],
-                [['foo']])[0], -1)
-
-            transaction.cursor.rollback()
+            self.assertRaises(ValueError, self.boolean.import_data,
+                ['boolean'], [['foo']])
 
     def test0020integer(self):
-        '''
-        Test integer.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test integer'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.integer.import_data(['integer'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
             self.assertEqual(self.integer.import_data(['integer'],
-                [['-1']]), (1, 0, 0, 0))
+                [['-1']]), 1)
 
             self.assertEqual(self.integer.import_data(['integer'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.integer.import_data(['integer'],
-                [['1'], ['2']]), (2, 0, 0, 0))
+                [['1'], ['2']]), 2)
 
-            self.assertEqual(self.integer.import_data(['integer'],
-                [['1.1']])[0], -1)
+            self.assertRaises(ValueError, self.integer.import_data,
+                ['integer'], [['1.1']])
 
-            self.assertEqual(self.integer.import_data(['integer'],
-                [['-1.1']])[0], -1)
+            self.assertRaises(ValueError, self.integer.import_data,
+                ['integer'], [['-1.1']])
 
-            self.assertEqual(self.integer.import_data(['integer'],
-                [['foo']])[0], -1)
+            self.assertRaises(ValueError, self.integer.import_data,
+                ['integer'], [['foo']])
 
             self.assertEqual(self.integer.import_data(['integer'],
-                [['0']]), (1, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['0']]), 1)
 
     def test0021integer_required(self):
-        '''
-        Test required integer.
-        '''
+        'Test required integer'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.integer_required.import_data(['integer'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
             self.assertEqual(self.integer_required.import_data(['integer'],
-                [['-1']]), (1, 0, 0, 0))
+                [['-1']]), 1)
 
-            self.assertEqual(self.integer_required.import_data(['integer'],
-                [['']])[0], -1)
+            self.assertRaises(UserError, self.integer_required.import_data,
+                ['integer'], [['']])
+            transaction.cursor.rollback()
 
             self.assertEqual(self.integer_required.import_data(['integer'],
-                [['1'], ['2']]), (2, 0, 0, 0))
+                [['1'], ['2']]), 2)
 
-            self.assertEqual(self.integer_required.import_data(['integer'],
-                [['1.1']])[0], -1)
+            self.assertRaises(ValueError, self.integer_required.import_data,
+                ['integer'], [['1.1']])
 
-            self.assertEqual(self.integer_required.import_data(['integer'],
-                [['-1.1']])[0], -1)
+            self.assertRaises(ValueError, self.integer_required.import_data,
+                ['integer'], [['-1.1']])
 
-            self.assertEqual(self.integer_required.import_data(['integer'],
-                [['foo']])[0], -1)
+            self.assertRaises(ValueError, self.integer_required.import_data,
+                ['integer'], [['foo']])
 
             self.assertEqual(self.integer_required.import_data(['integer'],
-                [['0']]), (1, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['0']]), 1)
 
     def test0030float(self):
-        '''
-        Test float.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test float'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.float.import_data(['float'],
-                [['1.1']]), (1, 0, 0, 0))
+                [['1.1']]), 1)
 
             self.assertEqual(self.float.import_data(['float'],
-                [['-1.1']]), (1, 0, 0, 0))
+                [['-1.1']]), 1)
 
             self.assertEqual(self.float.import_data(['float'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
             self.assertEqual(self.float.import_data(['float'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.float.import_data(['float'],
-                [['1.1'], ['2.2']]), (2, 0, 0, 0))
+                [['1.1'], ['2.2']]), 2)
 
-            self.assertEqual(self.float.import_data(['float'],
-                [['foo']])[0], -1)
+            self.assertRaises(ValueError, self.float.import_data,
+                ['float'], [['foo']])
 
             self.assertEqual(self.float.import_data(['float'],
-                [['0']]), (1, 0, 0, 0))
+                [['0']]), 1)
 
             self.assertEqual(self.float.import_data(['float'],
-                [['0.0']]), (1, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['0.0']]), 1)
 
     def test0031float_required(self):
-        '''
-        Test required float.
-        '''
+        'Test required float'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.float_required.import_data(['float'],
-                [['1.1']]), (1, 0, 0, 0))
+                [['1.1']]), 1)
 
             self.assertEqual(self.float_required.import_data(['float'],
-                [['-1.1']]), (1, 0, 0, 0))
+                [['-1.1']]), 1)
 
             self.assertEqual(self.float_required.import_data(['float'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
-            self.assertEqual(self.float_required.import_data(['float'],
-                [['']])[0], -1)
+            self.assertRaises(UserError, self.float_required.import_data,
+                ['float'], [['']])
+            transaction.cursor.rollback()
 
             self.assertEqual(self.float_required.import_data(['float'],
-                [['1.1'], ['2.2']]), (2, 0, 0, 0))
+                [['1.1'], ['2.2']]), 2)
 
-            self.assertEqual(self.float_required.import_data(['float'],
-                [['foo']])[0], -1)
+            self.assertRaises(ValueError, self.float_required.import_data,
+                ['float'], [['foo']])
 
             self.assertEqual(self.float_required.import_data(['float'],
-                [['0']]), (1, 0, 0, 0))
+                [['0']]), 1)
 
             self.assertEqual(self.float_required.import_data(['float'],
-                [['0.0']]), (1, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['0.0']]), 1)
 
     def test0040numeric(self):
-        '''
-        Test numeric.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test numeric'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['1.1']]), (1, 0, 0, 0))
+                [['1.1']]), 1)
 
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['-1.1']]), (1, 0, 0, 0))
+                [['-1.1']]), 1)
 
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['1.1'], ['2.2']]), (2, 0, 0, 0))
+                [['1.1'], ['2.2']]), 2)
 
-            self.assertEqual(self.numeric.import_data(['numeric'],
-                [['foo']])[0], -1)
+            self.assertRaises(InvalidOperation, self.numeric.import_data,
+                ['numeric'], [['foo']])
 
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['0']]), (1, 0, 0, 0))
+                [['0']]), 1)
 
             self.assertEqual(self.numeric.import_data(['numeric'],
-                [['0.0']]), (1, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['0.0']]), 1)
 
     def test0041numeric_required(self):
-        '''
-        Test required numeric.
-        '''
+        'Test required numeric'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['1.1']]), (1, 0, 0, 0))
+                [['1.1']]), 1)
 
             self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['-1.1']]), (1, 0, 0, 0))
+                [['-1.1']]), 1)
 
             self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['1']]), (1, 0, 0, 0))
+                [['1']]), 1)
 
-            self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['']])[0], -1)
+            self.assertRaises(UserError, self.numeric_required.import_data,
+                ['numeric'], [['']])
+            transaction.cursor.rollback()
 
             self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['1.1'], ['2.2']]), (2, 0, 0, 0))
+                [['1.1'], ['2.2']]), 2)
 
-            self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['foo']])[0], -1)
+            self.assertRaises(InvalidOperation,
+                self.numeric_required.import_data, ['numeric'], [['foo']])
 
             self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['0']]), (1, 0, 0, 0))
+                [['0']]), 1)
 
             self.assertEqual(self.numeric_required.import_data(['numeric'],
-                [['0.0']]), (1, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['0.0']]), 1)
 
     def test0050char(self):
-        '''
-        Test char.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test char'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.char.import_data(['char'],
-                [['test']]), (1, 0, 0, 0))
+                [['test']]), 1)
 
             self.assertEqual(self.char.import_data(['char'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.char.import_data(['char'],
-                [['test'], ['foo'], ['bar']]), (3, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['test'], ['foo'], ['bar']]), 3)
 
     def test0060text(self):
-        '''
-        Test text.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test text'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.text.import_data(['text'],
-                [['test']]), (1, 0, 0, 0))
+                [['test']]), 1)
 
             self.assertEqual(self.text.import_data(['text'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.text.import_data(['text'],
-                [['test'], ['foo'], ['bar']]), (3, 0, 0, 0))
-
-            transaction.cursor.rollback()
-
-    def test0070sha(self):
-        '''
-        Test sha.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
-            self.assertEqual(self.sha.import_data(['sha'],
-                [['test']]), (1, 0, 0, 0))
-
-            self.assertEqual(self.sha.import_data(['sha'],
-                [['']]), (1, 0, 0, 0))
-
-            self.assertEqual(self.sha.import_data(['sha'],
-                [['test'], ['foo']]), (2, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                [['test'], ['foo'], ['bar']]), 3)
 
     def test0080date(self):
-        '''
-        Test date.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
-            self.assertEqual(self.date.import_data(['date'],
-                [['2010-01-01']]), (1, 0, 0, 0))
-
+        'Test date'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.date.import_data(['date'],
-                [['']]), (1, 0, 0, 0))
+                [['2010-01-01']]), 1)
 
             self.assertEqual(self.date.import_data(['date'],
-                [['2010-01-01'], ['2010-02-01']]), (2, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.date.import_data(['date'],
-                [['foo']])[0], -1)
+                [['2010-01-01'], ['2010-02-01']]), 2)
 
-            transaction.cursor.rollback()
+            self.assertRaises(ValueError, self.date.import_data,
+                ['date'], [['foo']])
 
     def test0090datetime(self):
-        '''
-        Test datetime.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
-            self.assertEqual(self.datetime.import_data(['datetime'],
-                [['2010-01-01 12:00:00']]), (1, 0, 0, 0))
-
+        'Test datetime'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.datetime.import_data(['datetime'],
-                [['']]), (1, 0, 0, 0))
+                [['2010-01-01 12:00:00']]), 1)
 
             self.assertEqual(self.datetime.import_data(['datetime'],
-                [['2010-01-01 12:00:00'], ['2010-01-01 13:30:00']]),
-                (2, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.datetime.import_data(['datetime'],
-                [['foo']])[0], -1)
+                [['2010-01-01 12:00:00'], ['2010-01-01 13:30:00']]), 2)
 
-            transaction.cursor.rollback()
+            self.assertRaises(ValueError, self.datetime.import_data,
+                ['datetime'], [['foo']])
 
     def test0100selection(self):
-        '''
-        Test selection.
-        '''
+        'Test selection'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.selection.import_data(['selection'],
-                [['select1']]), (1, 0, 0, 0))
-
-            self.assertEqual(self.selection.import_data(['selection'],
-                [['']]), (1, 0, 0, 0))
+                [['select1']]), 1)
 
             self.assertEqual(self.selection.import_data(['selection'],
-                [['select1'], ['select2']]), (2, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.selection.import_data(['selection'],
-                [['foo']])[0], -1)
+                [['select1'], ['select2']]), 2)
 
+            self.assertRaises(UserError, self.selection.import_data,
+                ['selection'], [['foo']])
             transaction.cursor.rollback()
 
     def test0110many2one(self):
-        '''
-        Test many2one.
-        '''
+        'Test many2one'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.many2one.import_data(['many2one'],
-                [['Test']]), (1, 0, 0, 0))
+                [['Test']]), 1)
 
             self.assertEqual(self.many2one.import_data(['many2one:id'],
-                [['tests.import_data_many2one_target_test']]), (1, 0, 0, 0))
+                [['tests.import_data_many2one_target_test']]), 1)
 
             self.assertEqual(self.many2one.import_data(['many2one'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.many2one.import_data(['many2one'],
-                [['Test'], ['Test']]), (2, 0, 0, 0))
+                [['Test'], ['Test']]), 2)
 
-            self.assertEqual(self.many2one.import_data(['many2one'],
-                [['foo']])[0], -1)
-
-            self.assertEqual(self.many2one.import_data(['many2one'],
-                [['Duplicate']])[0], -1)
+            self.assertRaises(UserError, self.many2one.import_data,
+                ['many2one'], [['foo']])
+            transaction.cursor.rollback()
 
-            self.assertEqual(self.many2one.import_data(['many2one:id'],
-                [['foo']])[0], -1)
+            self.assertRaises(UserError, self.many2one.import_data,
+                ['many2one'], [['Duplicate']])
+            transaction.cursor.rollback()
 
-            self.assertEqual(self.many2one.import_data(['many2one:id'],
-                [['tests.foo']])[0], -1)
+            self.assertRaises(UserError, self.many2one.import_data,
+                ['many2one:id'], [['foo']])
+            transaction.cursor.rollback()
 
+            self.assertRaises(Exception, self.many2one.import_data,
+                ['many2one:id'], [['tests.foo']])
             transaction.cursor.rollback()
 
     def test0120many2many(self):
-        '''
-        Test many2many.
-        '''
+        'Test many2many'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test 1']]), (1, 0, 0, 0))
+                [['Test 1']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many:id'],
-                [['tests.import_data_many2many_target_test1']]), (1, 0, 0, 0))
+                [['tests.import_data_many2many_target_test1']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test 1,Test 2']]), (1, 0, 0, 0))
+                [['Test 1,Test 2']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many:id'],
                 [['tests.import_data_many2many_target_test1,'
-                    'tests.import_data_many2many_target_test2']]),
-                (1, 0, 0, 0))
-
-            self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test\, comma']]), (1, 0, 0, 0))
+                    'tests.import_data_many2many_target_test2']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test\, comma,Test 1']]), (1, 0, 0, 0))
+                [['Test\, comma']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many'],
-                [['']]), (1, 0, 0, 0))
+                [['Test\, comma,Test 1']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test 1'], ['Test 2']]), (2, 0, 0, 0))
+                [['']]), 1)
 
             self.assertEqual(self.many2many.import_data(['many2many'],
-                [['foo']])[0], -1)
+                [['Test 1'], ['Test 2']]), 2)
 
-            self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test 1,foo']])[0], -1)
+            self.assertRaises(UserError, self.many2many.import_data,
+                ['many2many'], [['foo']])
+            transaction.cursor.rollback()
 
-            self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Duplicate']])[0], -1)
+            self.assertRaises(UserError, self.many2many.import_data,
+                ['many2many'], [['Test 1,foo']])
+            transaction.cursor.rollback()
 
-            self.assertEqual(self.many2many.import_data(['many2many'],
-                [['Test 1,Duplicate']])[0], -1)
+            self.assertRaises(UserError, self.many2many.import_data,
+                ['many2many'], [['Duplicate']])
+            transaction.cursor.rollback()
 
+            self.assertRaises(UserError, self.many2many.import_data,
+                ['many2many'], [['Test 1,Duplicate']])
             transaction.cursor.rollback()
 
     def test0130one2many(self):
-        '''
-        Test one2many.
-        '''
-        with Transaction().start(DB_NAME, USER,
-                context=CONTEXT) as transaction:
+        'Test one2many'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT):
             self.assertEqual(self.one2many.import_data(
-                    ['name', 'one2many/name'], [['Test', 'Test 1']]),
-                (1, 0, 0, 0))
+                    ['name', 'one2many/name'], [['Test', 'Test 1']]), 1)
 
             self.assertEqual(self.one2many.import_data(
-                    ['name', 'one2many/name'], [
-                        ['Test', 'Test 1'], ['', 'Test 2']]),
-                (1, 0, 0, 0))
+                    ['name', 'one2many/name'],
+                    [['Test', 'Test 1'], ['', 'Test 2']]), 1)
 
             self.assertEqual(self.one2many.import_data(
                     ['name', 'one2many/name'],
                     [
                         ['Test 1', 'Test 1'],
                         ['', 'Test 2'],
-                        ['Test 2', 'Test 1']]),
-                (2, 0, 0, 0))
-
-            transaction.cursor.rollback()
+                        ['Test 2', 'Test 1']]), 2)
 
     def test0140reference(self):
-        '''
-        Test reference.
-        '''
+        'Test reference'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.assertEqual(self.reference.import_data(['reference'],
-                [['test.import_data.reference.selection,Test']]),
-                (1, 0, 0, 0))
+                [['test.import_data.reference.selection,Test']]), 1)
             reference, = self.reference.search([])
             self.assertEqual(reference.reference.__name__,
                 'test.import_data.reference.selection')
@@ -485,46 +402,45 @@ class ImportDataTestCase(unittest.TestCase):
 
             self.assertEqual(self.reference.import_data(['reference:id'],
                 [['test.import_data.reference.selection,'
-                    'tests.import_data_reference_selection_test']]),
-                (1, 0, 0, 0))
+                    'tests.import_data_reference_selection_test']]), 1)
             reference, = self.reference.search([])
             self.assertEqual(reference.reference.__name__,
                 'test.import_data.reference.selection')
             transaction.cursor.rollback()
 
             self.assertEqual(self.reference.import_data(['reference'],
-                [['']]), (1, 0, 0, 0))
+                [['']]), 1)
             reference, = self.reference.search([])
             self.assertEqual(reference.reference, None)
             transaction.cursor.rollback()
 
             self.assertEqual(self.reference.import_data(['reference'],
                 [['test.import_data.reference.selection,Test'],
-                    ['test.import_data.reference.selection,Test']]),
-                (2, 0, 0, 0))
+                    ['test.import_data.reference.selection,Test']]), 2)
             for reference in self.reference.search([]):
                 self.assertEqual(reference.reference.__name__,
                     'test.import_data.reference.selection')
             transaction.cursor.rollback()
 
-            self.assertEqual(self.reference.import_data(['reference'],
-                [['test.import_data.reference.selection,foo']])[0], -1)
-
-            self.assertEqual(self.reference.import_data(['reference'],
-                [['test.import_data.reference.selection,Duplicate']])[0], -1)
+            self.assertRaises(UserError, self.reference.import_data,
+                ['reference'], [['test.import_data.reference.selection,foo']])
+            transaction.cursor.rollback()
 
-            self.assertEqual(self.reference.import_data(['reference:id'],
-                [['test.import_data.reference.selection,foo']])[0], -1)
+            self.assertRaises(UserError, self.reference.import_data,
+                ['reference'],
+                [['test.import_data.reference.selection,Duplicate']])
+            transaction.cursor.rollback()
 
-            self.assertEqual(self.reference.import_data(['reference:id'],
-                [['test.import_data.reference.selection,test.foo']])[0], -1)
+            self.assertRaises(UserError, self.reference.import_data,
+                ['reference:id'],
+                [['test.import_data.reference.selection,foo']])
+            transaction.cursor.rollback()
 
+            self.assertRaises(Exception, self.reference.import_data,
+                ['reference:id'],
+                [['test.import_data.reference.selection,test.foo']])
             transaction.cursor.rollback()
 
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(ImportDataTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_mixins.py b/trytond/tests/test_mixins.py
index be6ecf2..a699f7b 100644
--- a/trytond/tests/test_mixins.py
+++ b/trytond/tests/test_mixins.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -46,7 +45,3 @@ def suite():
     for testcase in (UrlTestCase,):
         suite.addTests(func(testcase))
     return suite
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_modelsingleton.py b/trytond/tests/test_modelsingleton.py
index a066d38..673e668 100644
--- a/trytond/tests/test_modelsingleton.py
+++ b/trytond/tests/test_modelsingleton.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -10,18 +9,14 @@ from trytond.transaction import Transaction
 
 
 class ModelSingletonTestCase(unittest.TestCase):
-    '''
-    Test ModelSingleton
-    '''
+    'Test ModelSingleton'
 
     def setUp(self):
         install_module('tests')
         self.singleton = POOL.get('test.singleton')
 
     def test0010read(self):
-        '''
-        Test read method.
-        '''
+        'Test read method'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             singleton, = self.singleton.read([1], ['name'])
@@ -48,9 +43,7 @@ class ModelSingletonTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0020create(self):
-        '''
-        Test create method.
-        '''
+        'Test create method'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             singleton, = self.singleton.create([{'name': 'bar'}])
@@ -68,9 +61,7 @@ class ModelSingletonTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0030copy(self):
-        '''
-        Test copy method.
-        '''
+        'Test copy method'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             singleton, = self.singleton.search([])
@@ -90,9 +81,7 @@ class ModelSingletonTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0040default_get(self):
-        '''
-        Test default_get method.
-        '''
+        'Test default_get method'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             default = self.singleton.default_get(['name'])
@@ -120,9 +109,7 @@ class ModelSingletonTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0050search(self):
-        '''
-        Test search method.
-        '''
+        'Test search method'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             singletons = self.singleton.search([])
@@ -139,7 +126,3 @@ class ModelSingletonTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(ModelSingletonTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_modelsql.py b/trytond/tests/test_modelsql.py
index 2bedafc..233ed9a 100644
--- a/trytond/tests/test_modelsql.py
+++ b/trytond/tests/test_modelsql.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -14,9 +13,7 @@ from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
 
 
 class ModelSQLTestCase(unittest.TestCase):
-    '''
-    Test ModelSQL
-    '''
+    'Test ModelSQL'
 
     def setUp(self):
         install_module('tests')
@@ -24,9 +21,7 @@ class ModelSQLTestCase(unittest.TestCase):
         self.modelsql_timestamp = POOL.get('test.modelsql.timestamp')
 
     def test0010required_field_missing(self):
-        '''
-        Test error message when a required field is missing.
-        '''
+        '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
@@ -49,9 +44,7 @@ class ModelSQLTestCase(unittest.TestCase):
                 self.fail('UserError should be caught')
 
     def test0020check_timestamp(self):
-        '''
-        Test check timestamp.
-        '''
+        'Test check timestamp'
         # cursor must be committed between each changes otherwise NOW() returns
         # always the same timestamp.
         with Transaction().start(DB_NAME, USER,
@@ -63,7 +56,7 @@ class ModelSQLTestCase(unittest.TestCase):
             timestamp = self.modelsql_timestamp.read([record.id],
                 ['_timestamp'])[0]['_timestamp']
 
-            if CONFIG['db_type'] == 'sqlite':
+            if CONFIG['db_type'] in ('sqlite', 'mysql'):
                 # timestamp precision of sqlite is the second
                 time.sleep(1)
 
@@ -87,6 +80,3 @@ class ModelSQLTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(ModelSQLTestCase)
-
-if __name__ == '__main__':
-    unittest.TextTestRunner(verbosity=2).run(suite())
diff --git a/trytond/tests/test_mptt.py b/trytond/tests/test_mptt.py
index 5b5fd49..7631456 100644
--- a/trytond/tests/test_mptt.py
+++ b/trytond/tests/test_mptt.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -10,9 +9,7 @@ from trytond.transaction import Transaction
 
 
 class MPTTTestCase(unittest.TestCase):
-    '''
-    Test Modified Preorder Tree Traversal.
-    '''
+    'Test Modified Preorder Tree Traversal'
 
     def setUp(self):
         install_module('tests')
@@ -66,9 +63,7 @@ class MPTTTestCase(unittest.TestCase):
             self.ReParent(record)
 
     def test0010create(self):
-        '''
-        Create tree.
-        '''
+        'Test create tree'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             new_records = [None, None, None]
@@ -88,15 +83,14 @@ class MPTTTestCase(unittest.TestCase):
             transaction.cursor.commit()
 
     def test0030reparent(self):
-        '''
-        Re-parent.
-        '''
+        'Test re-parent'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             self.ReParent()
             transaction.cursor.rollback()
 
     def test0040active(self):
+        'Test active'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             records = self.mptt.search([])
@@ -142,9 +136,7 @@ class MPTTTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0050delete(self):
-        '''
-        Delete.
-        '''
+        'Test delete'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             records = self.mptt.search([])
@@ -174,7 +166,3 @@ class MPTTTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(MPTTTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_pyson.py b/trytond/tests/test_pyson.py
index 8687c32..d295b98 100644
--- a/trytond/tests/test_pyson.py
+++ b/trytond/tests/test_pyson.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -9,14 +8,10 @@ from trytond import pyson
 
 
 class PYSONTestCase(unittest.TestCase):
-    '''
-    Test PySON.
-    '''
+    'Test PySON'
 
     def test0010Eval(self):
-        '''
-        Test pyson.Eval
-        '''
+        'Test pyson.Eval'
         self.assert_(pyson.Eval('test').pyson() == {
             '__class__': 'Eval',
             'v': 'test',
@@ -36,9 +31,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assert_(pyson.PYSONDecoder().decode(eval) == 0)
 
     def test0020Not(self):
-        '''
-        Test pyson.Not
-        '''
+        'Test pyson.Not'
         self.assert_(pyson.Not(True).pyson() == {
             '__class__': 'Not',
             'v': True,
@@ -55,9 +48,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertTrue(pyson.PYSONDecoder().decode(eval))
 
     def test0030Bool(self):
-        '''
-        Test pyson.Bool
-        '''
+        'Test pyson.Bool'
         self.assert_(pyson.Bool('test').pyson() == {
             '__class__': 'Bool',
             'v': 'test',
@@ -96,9 +87,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertFalse(pyson.PYSONDecoder().decode(eval))
 
     def test0040And(self):
-        '''
-        Test pyson.And
-        '''
+        'Test pyson.And'
         self.assert_(pyson.And(True, False).pyson() == {
             '__class__': 'And',
             's': [True, False],
@@ -140,9 +129,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertFalse(pyson.PYSONDecoder().decode(eval))
 
     def test0050Or(self):
-        '''
-        Test pyson.Or
-        '''
+        'Test pyson.Or'
         self.assert_(pyson.Or(True, False).pyson() == {
             '__class__': 'Or',
             's': [True, False],
@@ -184,9 +171,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertTrue(pyson.PYSONDecoder().decode(eval))
 
     def test0060Equal(self):
-        '''
-        Test pyson.Equal
-        '''
+        'Test pyson.Equal'
         self.assert_(pyson.Equal('test', 'test').pyson() == {
             '__class__': 'Equal',
             's1': 'test',
@@ -204,9 +189,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertFalse(pyson.PYSONDecoder().decode(eval))
 
     def test0070Greater(self):
-        '''
-        Test pyson.Greater
-        '''
+        'Test pyson.Greater'
         self.assert_(pyson.Greater(1, 0).pyson() == {
             '__class__': 'Greater',
             's1': 1,
@@ -239,9 +222,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertTrue(pyson.PYSONDecoder().decode(eval))
 
     def test0080Less(self):
-        '''
-        Test pyson.Less
-        '''
+        'Test pyson.Less'
         self.assert_(pyson.Less(0, 1).pyson() == {
             '__class__': 'Less',
             's1': 0,
@@ -274,9 +255,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertTrue(pyson.PYSONDecoder().decode(eval))
 
     def test0090If(self):
-        '''
-        Test pyson.If
-        '''
+        'Test pyson.If'
         self.assert_(pyson.If(True, 'foo', 'bar').pyson() == {
             '__class__': 'If',
             'c': True,
@@ -298,9 +277,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assert_(pyson.PYSONDecoder().decode(eval) == 'bar')
 
     def test0100Get(self):
-        '''
-        Test pyson.Get
-        '''
+        'Test pyson.Get'
         self.assert_(pyson.Get({'foo': 'bar'}, 'foo', 'default').pyson() == {
             '__class__': 'Get',
             'v': {'foo': 'bar'},
@@ -327,9 +304,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assert_(pyson.PYSONDecoder().decode(eval) == 'default')
 
     def test0110In(self):
-        '''
-        Test pyson.In
-        '''
+        'Test pyson.In'
         self.assert_(pyson.In('foo', {'foo': 'bar'}).pyson() == {
             '__class__': 'In',
             'k': 'foo',
@@ -372,9 +347,7 @@ class PYSONTestCase(unittest.TestCase):
         self.assertFalse(pyson.PYSONDecoder().decode(eval))
 
     def test0120Date(self):
-        '''
-        Test pyson.Date
-        '''
+        'Test pyson.Date'
         self.assert_(pyson.Date(2010, 1, 12, -1, 12, -7).pyson() == {
             '__class__': 'Date',
             'y': 2010,
@@ -426,9 +399,7 @@ class PYSONTestCase(unittest.TestCase):
                 == datetime.date(2010, 2, 22))
 
     def test0130DateTime(self):
-        '''
-        Test pyson.DateTime
-        '''
+        'Test pyson.DateTime'
         self.assert_(pyson.DateTime(2010, 1, 12, 10, 30, 20, 0,
             -1, 12, -7, 2, 15, 30, 1).pyson() == {
                 '__class__': 'DateTime',
@@ -525,11 +496,28 @@ class PYSONTestCase(unittest.TestCase):
         self.assert_(pyson.PYSONDecoder().decode(eval)
                 == datetime.datetime(2010, 2, 22, 10, 30, 20, 200))
 
-    def test0900Composite(self):
-        '''
-        Test Composite
-        '''
+    def test0140Len(self):
+        'Test pyson.Len'
+        self.assert_(pyson.Len([1, 2, 3]).pyson() == {
+                '__class__': 'Len',
+                'v': [1, 2, 3],
+                })
+
+        self.assertRaises(AssertionError, pyson.Len, object())
+
+        self.assert_(pyson.Len([1, 2, 3]).types() == set([int, long]))
+
+        eval = pyson.PYSONEncoder().encode(pyson.Len([1, 2, 3]))
+        self.assertEqual(pyson.PYSONDecoder().decode(eval), 3)
 
+        eval = pyson.PYSONEncoder().encode(pyson.Len({1: 2, 3: 4}))
+        self.assertEqual(pyson.PYSONDecoder().decode(eval), 2)
+
+        eval = pyson.PYSONEncoder().encode(pyson.Len('foo bar'))
+        self.assertEqual(pyson.PYSONDecoder().decode(eval), 7)
+
+    def test0900Composite(self):
+        'Test Composite'
         eval = pyson.PYSONEncoder().encode(['id', pyson.If(pyson.Not(
             pyson.In('company', pyson.Eval('context', {}))), '=', '!='),
             pyson.Get(pyson.Eval('context', {}), 'company', -1)])
@@ -541,7 +529,3 @@ class PYSONTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(PYSONTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_sequence.py b/trytond/tests/test_sequence.py
index 0e3b870..727bb1c 100644
--- a/trytond/tests/test_sequence.py
+++ b/trytond/tests/test_sequence.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -10,18 +9,14 @@ from trytond.transaction import Transaction
 
 
 class SequenceTestCase(unittest.TestCase):
-    '''
-    Test Sequence
-    '''
+    'Test Sequence'
 
     def setUp(self):
         install_module('tests')
         self.sequence = POOL.get('ir.sequence')
 
     def test0010incremental(self):
-        '''
-        Test incremental
-        '''
+        'Test incremental'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             sequence, = self.sequence.create([{
@@ -47,9 +42,7 @@ class SequenceTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0020decimal_timestamp(self):
-        '''
-        Test Decimal Timestamp
-        '''
+        'Test Decimal Timestamp'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             sequence, = self.sequence.create([{
@@ -72,9 +65,7 @@ class SequenceTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0030hexadecimal_timestamp(self):
-        '''
-        Test Hexadecimal Timestamp
-        '''
+        'Test Hexadecimal Timestamp'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             sequence, = self.sequence.create([{
@@ -98,9 +89,7 @@ class SequenceTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0040prefix_suffix(self):
-        '''
-        Test prefix/suffix
-        '''
+        'Test prefix/suffix'
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
             sequence, = self.sequence.create([{
                         'name': 'Test incremental',
@@ -123,7 +112,3 @@ class SequenceTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(SequenceTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_tools.py b/trytond/tests/test_tools.py
index 86881af..a760611 100644
--- a/trytond/tests/test_tools.py
+++ b/trytond/tests/test_tools.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # -*- 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.
@@ -13,61 +12,45 @@ from trytond.tools import reduce_ids, safe_eval, datetime_strftime, \
 
 
 class ToolsTestCase(unittest.TestCase):
-    '''
-    Test tools.
-    '''
+    'Test tools'
     table = sql.Table('test')
 
     def test0000reduce_ids_empty(self):
-        '''
-        Test reduce_ids empty list.
-        '''
+        'Test reduce_ids empty list'
         self.assertEqual(reduce_ids(self.table.id, []), sql.Literal(False))
 
     def test0010reduce_ids_continue(self):
-        '''
-        Test reduce_ids continue list.
-        '''
+        'Test reduce_ids continue list'
         self.assertEqual(reduce_ids(self.table.id, range(10)),
             sql.operators.Or(((self.table.id >= 0) & (self.table.id <= 9),)))
 
     def test0020reduce_ids_one_hole(self):
-        '''
-        Test reduce_ids continue list with one hole.
-        '''
+        'Test reduce_ids continue list with one hole'
         self.assertEqual(reduce_ids(self.table.id, range(10) + range(20, 30)),
             ((self.table.id >= 0) & (self.table.id <= 9))
             | ((self.table.id >= 20) & (self.table.id <= 29)))
 
     def test0030reduce_ids_short_continue(self):
-        '''
-        Test reduce_ids short continue list.
-        '''
+        'Test reduce_ids short continue list'
         self.assertEqual(reduce_ids(self.table.id, range(4)),
             sql.operators.Or((self.table.id.in_(range(4)),)))
 
     def test0040reduce_ids_complex(self):
-        '''
-        Test reduce_ids complex list.
-        '''
+        'Test reduce_ids complex list'
         self.assertEqual(reduce_ids(self.table.id,
                 range(10) + range(25, 30) + range(15, 20)),
             (((self.table.id >= 0) & (self.table.id <= 14))
                 | (self.table.id.in_(range(25, 30)))))
 
     def test0050reduce_ids_complex_small_continue(self):
-        '''
-        Test reduce_ids complex list with small continue.
-        '''
+        'Test reduce_ids complex list with small continue'
         self.assertEqual(reduce_ids(self.table.id,
                 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 18, 19, 21]),
             (((self.table.id >= 1) & (self.table.id <= 12))
                 | (self.table.id.in_([15, 18, 19, 21]))))
 
     def test0055reduce_ids_float(self):
-        '''
-        Test reduce_ids with integer as float.
-        '''
+        'Test reduce_ids with integer as float'
         self.assertEqual(reduce_ids(self.table.id,
                 [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
                     15.0, 18.0, 19.0, 21.0]),
@@ -76,44 +59,32 @@ class ToolsTestCase(unittest.TestCase):
         self.assertRaises(AssertionError, reduce_ids, self.table.id, [1.1])
 
     def test0060safe_eval_builtin(self):
-        '''
-        Attempt to access a unsafe builtin.
-        '''
+        'Attempt to access a unsafe builtin'
         self.assertRaises(Exception, safe_eval, "open('test.txt', 'w')")
 
     def test0061safe_eval_getattr(self):
-        '''
-        Attempt to get arround direct attr access.
-        '''
+        'Attempt to get arround direct attr access'
         self.assertRaises(Exception, safe_eval, "getattr(int, '__abs__')")
 
     def test0062safe_eval_func_globals(self):
-        '''
-        Attempt to access global enviroment where fun was defined.
-        '''
+        'Attempt to access global enviroment where fun was defined'
         self.assertRaises(Exception, safe_eval,
                 "def x(): pass; print x.func_globals")
 
     def test0063safe_eval_lowlevel(self):
-        '''
-        Lowlevel tricks to access 'object'.
-        '''
+        "Lowlevel tricks to access 'object'"
         self.assertRaises(Exception, safe_eval,
                 "().__class__.mro()[1].__subclasses__()")
 
     def test0070datetime_strftime(self):
-        '''
-        Test datetime_strftime
-        '''
+        'Test datetime_strftime'
         self.assert_(datetime_strftime(datetime.date(2005, 3, 2),
             '%Y-%m-%d'), '2005-03-02')
         self.assert_(datetime_strftime(datetime.date(1805, 3, 2),
             '%Y-%m-%d'), '1805-03-02')
 
     def test_reduce_domain(self):
-        '''
-        Test reduce_domain
-        '''
+        'Test reduce_domain'
         clause = ('x', '=', 'x')
         tests = (
             ([clause], ['AND', clause]),
@@ -147,7 +118,3 @@ def suite():
     for testcase in (ToolsTestCase,):
         suite.addTests(func(testcase))
     return suite
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_transaction.py b/trytond/tests/test_transaction.py
index 6928a98..311d806 100644
--- a/trytond/tests/test_transaction.py
+++ b/trytond/tests/test_transaction.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 #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
@@ -29,37 +28,61 @@ def manipulate_cursor(*args, **kwargs):
 
 
 class TransactionTestCase(unittest.TestCase):
-    '''
-    Test the Transaction Context manager
-    '''
+    'Test the Transaction Context manager'
 
     def setUp(self):
         install_module('tests')
 
     def test0010nonexistdb(self):
-        '''
-        Attempt opening a transaction with a non existant DB and ensure that
-        it stops cleanly and allows starting of next transaction
-        '''
+        '''Attempt opening a transaction with a non existant DB
+        and ensure that it stops cleanly and allows starting of next
+        transaction'''
         self.assertRaises(
             Exception, empty_transaction, "Non existant DB", USER,
             context=CONTEXT)
         self.assertTrue(empty_transaction(DB_NAME, USER, context=CONTEXT))
 
     def test0020cursorclose(self):
-        '''
-        Manipulate the cursor during the transaction so that the close in
-        transaction stop fails. Ensure that this does not affect opening of
-        another transaction
-        '''
+        '''Manipulate the cursor during the transaction so that
+        the close in transaction stop fails.
+        Ensure that this does not affect opening of another transaction'''
         self.assertRaises(
             Exception, manipulate_cursor, DB_NAME, USER, context=CONTEXT)
         self.assertTrue(empty_transaction(DB_NAME, USER, context=CONTEXT))
 
+    def test0030set_user(self):
+        'Test set_user'
+        with Transaction().start(DB_NAME, USER, context=CONTEXT) \
+                as transaction:
+            self.assertEqual(transaction.user, USER)
+            self.assertEqual(transaction.context.get('user'), None)
+
+            with Transaction().set_user(0):
+                self.assertEqual(transaction.user, 0)
+                self.assertEqual(transaction.context.get('user'), None)
+
+            with Transaction().set_user(0, set_context=True):
+                self.assertEqual(transaction.user, 0)
+                self.assertEqual(transaction.context.get('user'), USER)
+
+                # Nested same set_user should keep original context user
+                with Transaction().set_user(0, set_context=True):
+                    self.assertEqual(transaction.user, 0)
+                    self.assertEqual(transaction.context.get('user'), USER)
+
+                # Unset user context
+                with Transaction().set_user(0, set_context=False):
+                    self.assertEqual(transaction.user, 0)
+                    self.assertEqual(transaction.context.get('user'), None)
+
+            # set context for non root
+            self.assertRaises(ValueError,
+                Transaction().set_user, 2, set_context=True)
+
+            # not set context for non root
+            with Transaction().set_user(2):
+                self.assertEqual(transaction.user, 2)
+
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(TransactionTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_trigger.py b/trytond/tests/test_trigger.py
index c54aa4c..23bd815 100644
--- a/trytond/tests/test_trigger.py
+++ b/trytond/tests/test_trigger.py
@@ -1,30 +1,11 @@
-#!/usr/bin/env python
 # -*- 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 unittest
 import time
 from xmlrpclib import MAXINT
-try:
-    from itertools import combinations
-except ImportError:
-    def combinations(iterable, r):
-        pool = tuple(iterable)
-        n = len(pool)
-        if r > n:
-            return
-        indices = range(r)
-        yield tuple(pool[i] for i in indices)
-        while True:
-            for i in reversed(range(r)):
-                if indices[i] != i + n - r:
-                    break
-            else:
-                return
-            indices[i] += 1
-            for j in range(i + 1, r):
-                indices[j] = indices[j - 1] + 1
-            yield tuple(pool[i] for i in indices)
+from itertools import combinations
+
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
         install_module
 from trytond.tests.trigger import TRIGGER_LOGS
@@ -32,9 +13,7 @@ from trytond.transaction import Transaction
 
 
 class TriggerTestCase(unittest.TestCase):
-    '''
-    Test Trigger
-    '''
+    'Test Trigger'
 
     def setUp(self):
         install_module('tests')
@@ -44,9 +23,7 @@ class TriggerTestCase(unittest.TestCase):
         self.model = POOL.get('ir.model')
 
     def test0010constraints(self):
-        '''
-        Test constraints
-        '''
+        'Test constraints'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([
@@ -87,9 +64,7 @@ class TriggerTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0020on_create(self):
-        '''
-        Test on_create
-        '''
+        'Test on_create'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([
@@ -160,9 +135,7 @@ class TriggerTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0030on_write(self):
-        '''
-        Test on_write
-        '''
+        'Test on_write'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([
@@ -273,9 +246,7 @@ class TriggerTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0040on_delete(self):
-        '''
-        Test on_delete
-        '''
+        'Test on_delete'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([
@@ -364,9 +335,7 @@ class TriggerTestCase(unittest.TestCase):
             transaction.cursor.rollback()
 
     def test0050on_time(self):
-        '''
-        Test on_time
-        '''
+        'Test on_time'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             model, = self.model.search([
@@ -462,7 +431,3 @@ class TriggerTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(TriggerTestCase)
-
-if __name__ == '__main__':
-    suite = suite()
-    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py
index fb35ab2..dd0cf7d 100644
--- a/trytond/tests/test_tryton.py
+++ b/trytond/tests/test_tryton.py
@@ -1,71 +1,35 @@
-#!/usr/bin/env python
 # -*- 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 logging
-logging.basicConfig(level=logging.ERROR)
-import sys
 import os
-DIR = os.path.abspath(os.path.normpath(os.path.join(__file__,
-    '..', '..', '..', 'trytond')))
-if os.path.isdir(DIR):
-    sys.path.insert(0, os.path.dirname(DIR))
-
+import sys
 import unittest
 import doctest
 from lxml import etree
-import time
-import optparse
-
-_MODULES = False
-_CONFIGFILE = None
-if __name__ == '__main__':
-    parser = optparse.OptionParser()
-
-    parser.add_option("-c", "--config", dest="config",
-        help="specify config file")
-    parser.add_option("-m", "--modules", action="store_true", dest="modules",
-        default=False, help="Run also modules tests")
-    opt, args = parser.parse_args()
-
-    if args:
-        parser.error("Invalid argument")
-    if opt.modules:
-        _MODULES = True
-    if opt.config:
-        _CONFIGFILE = opt.config
 
 from trytond.config import CONFIG
-CONFIG['db_type'] = 'sqlite'
-CONFIG.update_etc(_CONFIGFILE)
-CONFIG.set_timezone()
-if not CONFIG['admin_passwd']:
-    CONFIG['admin_passwd'] = 'admin'
-
 from trytond.pool import Pool
 from trytond import backend
 from trytond.protocols.dispatcher import create
 from trytond.transaction import Transaction
 from trytond.pyson import PYSONEncoder, Eval
 
-Pool.start()
+__all__ = ['POOL', 'DB_NAME', 'USER', 'USER_PASSWORD', 'CONTEXT',
+    'install_module', 'test_view', 'test_depends', 'doctest_dropdb',
+    'suite', 'all_suite', 'modules_suite']
 
-if CONFIG['db_type'] == 'sqlite':
-    DB_NAME = ':memory:'
-else:
-    DB_NAME = 'test_' + str(int(time.time()))
+Pool.start()
 USER = 1
 USER_PASSWORD = 'admin'
 CONTEXT = {}
+DB_NAME = os.environ['DB_NAME']
 DB = backend.get('Database')(DB_NAME)
 Pool.test = True
 POOL = Pool(DB_NAME)
 
 
 class ModelViewTestCase(unittest.TestCase):
-    '''
-    Test ModelView
-    '''
+    'Test ModelView'
 
     def setUp(self):
         install_module('ir')
@@ -73,32 +37,25 @@ class ModelViewTestCase(unittest.TestCase):
         install_module('webdav')
 
     def test0000test(self):
+        'Test test'
         self.assertRaises(Exception, install_module, 'nosuchmodule')
         self.assertRaises(Exception, test_view, 'nosuchmodule')
 
     def test0010ir(self):
-        '''
-        Test ir.
-        '''
+        'Test ir'
         test_view('ir')
 
     def test0020res(self):
-        '''
-        Test res.
-        '''
+        'Test res'
         test_view('res')
 
     def test0040webdav(self):
-        '''
-        Test webdav.
-        '''
+        'Test webdav'
         test_view('webdav')
 
 
 class FieldDependsTestCase(unittest.TestCase):
-    '''
-    Test Field depends
-    '''
+    'Test Field depends'
 
     def setUp(self):
         install_module('ir')
@@ -106,6 +63,7 @@ class FieldDependsTestCase(unittest.TestCase):
         install_module('webdav')
 
     def test0010depends(self):
+        'Test depends'
         test_depends()
 
 
@@ -218,6 +176,18 @@ 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 suite():
     '''
     Return test suite for other modules
@@ -225,60 +195,37 @@ def suite():
     return unittest.TestSuite()
 
 
-def all_suite():
+def all_suite(modules=None):
     '''
     Return all tests suite of current module
     '''
     suite_ = suite()
-    import trytond.tests.test_tools as test_tools
-    suite_.addTests(test_tools.suite())
-    import trytond.tests.test_pyson as test_pyson
-    suite_.addTests(test_pyson.suite())
-    import trytond.tests.test_transaction as test_transaction
-    suite_.addTests(test_transaction.suite())
-    import trytond.tests.test_fields as test_fields
-    suite_.addTests(test_fields.suite())
-    import trytond.tests.test_modelsingleton as test_modelsingleton
-    suite_.addTests(test_modelsingleton.suite())
-    suite_.addTests(unittest.TestLoader(
-        ).loadTestsFromTestCase(ModelViewTestCase))
-    suite_.addTests(unittest.TestLoader(
-        ).loadTestsFromTestCase(FieldDependsTestCase))
-    import trytond.tests.test_mptt as test_mptt
-    suite_.addTests(test_mptt.suite())
-    import trytond.tests.test_importdata as test_importdata
-    suite_.addTests(test_importdata.suite())
-    import trytond.tests.test_exportdata as test_exportdata
-    suite_.addTests(test_exportdata.suite())
-    import trytond.tests.test_trigger as test_trigger
-    suite_.addTests(test_trigger.suite())
-    import trytond.tests.test_sequence as test_sequence
-    suite_.addTests(test_sequence.suite())
-    import trytond.tests.test_access as test_access
-    suite_.addTests(test_access.suite())
-    import trytond.tests.test_mixins as test_mixins
-    suite_.addTests(test_mixins.suite())
-    import trytond.tests.test_wizard as test_wizard
-    suite_.addTests(test_wizard.suite())
-    import trytond.tests.test_modelsql as test_modelsql
-    suite_.addTests(test_modelsql.suite())
-    import trytond.tests.test_cache as test_cache
-    suite_.addTests(test_cache.suite())
-    import trytond.tests.test_copy as test_copy
-    suite_.addTests(test_copy.suite())
+    for fn in os.listdir(os.path.dirname(__file__)):
+        if fn.startswith('test_') and fn.endswith('.py'):
+            if modules and fn[:-3] not in modules:
+                continue
+            modname = 'trytond.tests.' + fn[:-3]
+            __import__(modname)
+            module = module = sys.modules[modname]
+            suite_.addTest(module.suite())
     return suite_
 
 
-def modules_suite():
+def modules_suite(modules=None):
     '''
     Return all tests suite of all modules
     '''
-    suite_ = all_suite()
+    if modules:
+        suite_ = suite()
+    else:
+        suite_ = all_suite()
     from trytond.modules import create_graph, get_module_list, \
         MODULES_PATH, EGG_MODULES
     graph = create_graph(get_module_list())[0]
     for package in graph:
         module = package.name
+        if modules and module not in modules:
+            continue
         test_module = 'trytond.modules.%s.tests' % module
         if os.path.isdir(os.path.join(MODULES_PATH, module)) or \
                 module in EGG_MODULES:
@@ -310,10 +257,3 @@ def modules_suite():
             tests.append(test)
     tests.extend(doc_tests)
     return unittest.TestSuite(tests)
-
-if __name__ == '__main__':
-    if not _MODULES:
-        _SUITE = all_suite()
-    else:
-        _SUITE = modules_suite()
-    unittest.TextTestRunner(verbosity=2).run(_SUITE)
diff --git a/trytond/tests/test_user.py b/trytond/tests/test_user.py
new file mode 100644
index 0000000..8014845
--- /dev/null
+++ b/trytond/tests/test_user.py
@@ -0,0 +1,62 @@
+# 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.transaction import Transaction
+from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
+    install_module
+from trytond.res.user import bcrypt
+
+
+class UserTestCase(unittest.TestCase):
+    'Test User'
+
+    def setUp(self):
+        install_module('res')
+        self.user = POOL.get('res.user')
+
+    def create_user(self, login, password, hash_method=None):
+        user, = self.user.create([{
+                    'name': login,
+                    'login': login,
+                    }])
+        if hash_method:
+            hash = getattr(self.user, 'hash_' + hash_method)
+            self.user.write([user], {
+                    'password_hash': hash(password),
+                    })
+        else:
+            self.user.write([user], {
+                    'password': password,
+                    })
+
+    def check_user(self, login, password):
+        user, = self.user.search([('login', '=', login)])
+        user_id = self.user.get_login(login, password)
+        self.assertEqual(user_id, user.id)
+
+        bad_user_id = self.user.get_login(login, password + 'wrong')
+        self.assertEqual(bad_user_id, 0)
+
+    def test0010test_hash(self):
+        'Test default hash password'
+        with Transaction().start(DB_NAME, USER, CONTEXT):
+            self.create_user('user', '12345')
+            self.check_user('user', '12345')
+
+    def test0011test_sha1(self):
+        'Test sha1 password'
+        with Transaction().start(DB_NAME, USER, CONTEXT):
+            self.create_user('user', '12345', 'sha1')
+            self.check_user('user', '12345')
+
+    @unittest.skipIf(bcrypt is None, 'requires bcrypt')
+    def test0012test_bcrypt(self):
+        'Test bcrypt password'
+        with Transaction().start(DB_NAME, USER, CONTEXT):
+            self.create_user('user', '12345', 'bcrypt')
+            self.check_user('user', '12345')
+
+
+def suite():
+    return unittest.TestLoader().loadTestsFromTestCase(UserTestCase)
diff --git a/trytond/tests/test_wizard.py b/trytond/tests/test_wizard.py
index 43207c2..4e99bed 100644
--- a/trytond/tests/test_wizard.py
+++ b/trytond/tests/test_wizard.py
@@ -94,6 +94,3 @@ class WizardTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(WizardTestCase)
-
-if __name__ == '__main__':
-    unittest.TextTestRunner(verbosity=2).run(suite())
diff --git a/trytond/tests/test_workflow.py b/trytond/tests/test_workflow.py
index cf564cb..74adbdb 100644
--- a/trytond/tests/test_workflow.py
+++ b/trytond/tests/test_workflow.py
@@ -31,7 +31,3 @@ class WorkflowTestCase(unittest.TestCase):
 
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(WorkflowTestCase)
-
-
-if __name__ == '__main__':
-    unittest.TextTestRunner(verbosity=2).run(suite())
diff --git a/trytond/tools/__init__.py b/trytond/tools/__init__.py
index 90e391d..e209cff 100644
--- a/trytond/tools/__init__.py
+++ b/trytond/tools/__init__.py
@@ -2,10 +2,6 @@
 #this repository contains the full copyright notices and license terms.
 from .misc import *
 from .datetime_strftime import *
-try:
-    from collections import OrderedDict
-except ImportError:
-    from .ordereddict import OrderedDict
 
 
 class ClassProperty(property):
diff --git a/trytond/tools/ordereddict.py b/trytond/tools/ordereddict.py
deleted file mode 100644
index 856bd15..0000000
--- a/trytond/tools/ordereddict.py
+++ /dev/null
@@ -1,287 +0,0 @@
-# Copyright (C) 2009 by Raymond Hettinger
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and
-# pypy.
-# Passes Python2.7's test suite and incorporates all the latest updates.
-
-try:
-    from thread import get_ident as _get_ident
-except ImportError:
-    from dummy_thread import get_ident as _get_ident
-
-try:
-    from _abcoll import KeysView, ValuesView, ItemsView
-except ImportError:
-    pass
-
-
-class OrderedDict(dict):
-    'Dictionary that remembers insertion order'
-    # An inherited dict maps keys to values.
-    # The inherited dict provides __getitem__, __len__, __contains__, and get.
-    # The remaining methods are order-aware.
-    # Big-O running times for all methods are the same as for regular
-    # dictionaries.
-
-    # The internal self.__map dictionary maps keys to links in a doubly linked
-    # list.
-    # The circular doubly linked list starts and ends with a sentinel element.
-    # The sentinel element never gets deleted (this simplifies the algorithm).
-    # Each link is stored as a list of length three:  [PREV, NEXT, KEY].
-
-    def __init__(self, *args, **kwds):
-        '''Initialize an ordered dictionary.  Signature is the same as for
-        regular dictionaries, but keyword arguments are not recommended
-        because their insertion order is arbitrary.
-
-        '''
-        if len(args) > 1:
-            raise TypeError('expected at most 1 arguments, got %d' % len(args))
-        try:
-            self.__root
-        except AttributeError:
-            self.__root = root = []                     # sentinel node
-            root[:] = [root, root, None]
-            self.__map = {}
-        self.__update(*args, **kwds)
-
-    def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
-        'od.__setitem__(i, y) <==> od[i]=y'
-        # Setting a new item creates a new link which goes at the end of the
-        # linked list, and the inherited dictionary is updated with the new
-        # key/value pair.
-        if key not in self:
-            root = self.__root
-            last = root[0]
-            last[1] = root[0] = self.__map[key] = [last, root, key]
-        dict_setitem(self, key, value)
-
-    def __delitem__(self, key, dict_delitem=dict.__delitem__):
-        'od.__delitem__(y) <==> del od[y]'
-        # Deleting an existing item uses self.__map to find the link which is
-        # then removed by updating the links in the predecessor and successor
-        # nodes.
-        dict_delitem(self, key)
-        link_prev, link_next, key = self.__map.pop(key)
-        link_prev[1] = link_next
-        link_next[0] = link_prev
-
-    def __iter__(self):
-        'od.__iter__() <==> iter(od)'
-        root = self.__root
-        curr = root[1]
-        while curr is not root:
-            yield curr[2]
-            curr = curr[1]
-
-    def __reversed__(self):
-        'od.__reversed__() <==> reversed(od)'
-        root = self.__root
-        curr = root[0]
-        while curr is not root:
-            yield curr[2]
-            curr = curr[0]
-
-    def clear(self):
-        'od.clear() -> None.  Remove all items from od.'
-        try:
-            for node in self.__map.itervalues():
-                del node[:]
-            root = self.__root
-            root[:] = [root, root, None]
-            self.__map.clear()
-        except AttributeError:
-            pass
-        dict.clear(self)
-
-    def popitem(self, last=True):
-        '''od.popitem() -> (k, v), return and remove a (key, value) pair.
-        Pairs are returned in LIFO order if last is true or FIFO order if
-        false.
-
-        '''
-        if not self:
-            raise KeyError('dictionary is empty')
-        root = self.__root
-        if last:
-            link = root[0]
-            link_prev = link[0]
-            link_prev[1] = root
-            root[0] = link_prev
-        else:
-            link = root[1]
-            link_next = link[1]
-            root[1] = link_next
-            link_next[0] = root
-        key = link[2]
-        del self.__map[key]
-        value = dict.pop(self, key)
-        return key, value
-
-    # -- the following methods do not depend on the internal structure --
-
-    def keys(self):
-        'od.keys() -> list of keys in od'
-        return list(self)
-
-    def values(self):
-        'od.values() -> list of values in od'
-        return [self[key] for key in self]
-
-    def items(self):
-        'od.items() -> list of (key, value) pairs in od'
-        return [(key, self[key]) for key in self]
-
-    def iterkeys(self):
-        'od.iterkeys() -> an iterator over the keys in od'
-        return iter(self)
-
-    def itervalues(self):
-        'od.itervalues -> an iterator over the values in od'
-        for k in self:
-            yield self[k]
-
-    def iteritems(self):
-        'od.iteritems -> an iterator over the (key, value) items in od'
-        for k in self:
-            yield (k, self[k])
-
-    def update(*args, **kwds):
-        '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.
-
-        If E is a dict instance, does:         for k in E: od[k] = E[k]
-        If E has a .keys() method, does:       for k in E.keys(): od[k] = E[k]
-        Or if E is an iterable of items, does: for k, v in E: od[k] = v
-        In either case, this is followed by:   for k, v in F.items(): od[k] = v
-
-        '''
-        if len(args) > 2:
-            raise TypeError('update() takes at most 2 positional '
-                            'arguments (%d given)' % (len(args),))
-        elif not args:
-            raise TypeError('update() takes at least 1 argument (0 given)')
-        self = args[0]
-        # Make progressively weaker assumptions about "other"
-        other = ()
-        if len(args) == 2:
-            other = args[1]
-        if isinstance(other, dict):
-            for key in other:
-                self[key] = other[key]
-        elif hasattr(other, 'keys'):
-            for key in other.keys():
-                self[key] = other[key]
-        else:
-            for key, value in other:
-                self[key] = value
-        for key, value in kwds.items():
-            self[key] = value
-
-    # let subclasses override update without breaking __init__
-    __update = update
-
-    __marker = object()
-
-    def pop(self, key, default=__marker):
-        '''od.pop(k[,d]) -> v, remove specified key and return the
-        corresponding value.  If key is not found, d is returned if given,
-        otherwise KeyError is raised.
-
-        '''
-        if key in self:
-            result = self[key]
-            del self[key]
-            return result
-        if default is self.__marker:
-            raise KeyError(key)
-        return default
-
-    def setdefault(self, key, default=None):
-        'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
-        if key in self:
-            return self[key]
-        self[key] = default
-        return default
-
-    def __repr__(self, _repr_running={}):
-        'od.__repr__() <==> repr(od)'
-        call_key = id(self), _get_ident()
-        if call_key in _repr_running:
-            return '...'
-        _repr_running[call_key] = 1
-        try:
-            if not self:
-                return '%s()' % (self.__class__.__name__,)
-            return '%s(%r)' % (self.__class__.__name__, self.items())
-        finally:
-            del _repr_running[call_key]
-
-    def __reduce__(self):
-        'Return state information for pickling'
-        items = [[k, self[k]] for k in self]
-        inst_dict = vars(self).copy()
-        for k in vars(OrderedDict()):
-            inst_dict.pop(k, None)
-        if inst_dict:
-            return (self.__class__, (items,), inst_dict)
-        return self.__class__, (items,)
-
-    def copy(self):
-        'od.copy() -> a shallow copy of od'
-        return self.__class__(self)
-
-    @classmethod
-    def fromkeys(cls, iterable, value=None):
-        '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
-        and values equal to v (which defaults to None).
-
-        '''
-        d = cls()
-        for key in iterable:
-            d[key] = value
-        return d
-
-    def __eq__(self, other):
-        '''od.__eq__(y) <==> od==y.  Comparison to another OD is
-        order-sensitive while comparison to a regular mapping is
-        order-insensitive.
-
-        '''
-        if isinstance(other, OrderedDict):
-            return len(self) == len(other) and self.items() == other.items()
-        return dict.__eq__(self, other)
-
-    def __ne__(self, other):
-        return not self == other
-
-    # -- the following methods are only used in Python 2.7 --
-
-    def viewkeys(self):
-        "od.viewkeys() -> a set-like object providing a view on od's keys"
-        return KeysView(self)
-
-    def viewvalues(self):
-        "od.viewvalues() -> an object providing a view on od's values"
-        return ValuesView(self)
-
-    def viewitems(self):
-        "od.viewitems() -> a set-like object providing a view on od's items"
-        return ItemsView(self)
diff --git a/trytond/transaction.py b/trytond/transaction.py
index be6ebea..39b5aab 100644
--- a/trytond/transaction.py
+++ b/trytond/transaction.py
@@ -58,6 +58,8 @@ class Transaction(local):
     __metaclass__ = Singleton
 
     cursor = None
+    database = None
+    close = None
     user = None
     context = None
     create_records = None
@@ -65,21 +67,28 @@ class Transaction(local):
     delete = None  # TODO check to merge with delete_records
     timestamp = None
 
-    def start(self, database_name, user, readonly=False, context=None):
+    def start(self, database_name, user, readonly=False, context=None,
+            close=False, autocommit=False):
         '''
         Start transaction
         '''
         Database = backend.get('Database')
         assert self.user is None
+        assert self.database is None
         assert self.cursor is None
+        assert self.close is None
         assert self.context is None
         if not database_name:
             database = Database().connect()
         else:
             database = Database(database_name).connect()
         Flavor.set(Database.flavor)
-        self.cursor = database.cursor(readonly=readonly)
+        cursor = database.cursor(readonly=readonly,
+            autocommit=autocommit)
         self.user = user
+        self.database = database
+        self.cursor = cursor
+        self.close = close
         self.context = context or {}
         self.create_records = {}
         self.delete_records = {}
@@ -93,9 +102,11 @@ class Transaction(local):
         Stop transaction
         '''
         try:
-            self.cursor.close()
+            self.cursor.close(close=self.close)
         finally:
             self.cursor = None
+            self.database = None
+            self.close = None
             self.user = None
             self.context = None
             self.create_records = None
@@ -119,10 +130,16 @@ class Transaction(local):
         return manager
 
     def set_user(self, user, set_context=False):
+        if user != 0 and set_context:
+            raise ValueError('set_context only allowed for root')
         manager = _AttributeManager(user=self.user,
-                context=self.context.copy())
+                context=self.context)
+        self.context = self.context.copy()
         if set_context:
-            self.context.update({'user': self.user})
+            if user != self.user:
+                self.context['user'] = self.user
+        else:
+            self.context.pop('user', None)
         self.user = user
         return manager
 
@@ -131,11 +148,11 @@ class Transaction(local):
         self.cursor = cursor
         return manager
 
-    def new_cursor(self):
+    def new_cursor(self, autocommit=False, readonly=False):
         Database = backend.get('Database')
         manager = _CursorManager(self.cursor)
         database = Database(self.cursor.database_name).connect()
-        self.cursor = database.cursor()
+        self.cursor = database.cursor(autocommit=autocommit, readonly=readonly)
         return manager
 
     @property
diff --git a/trytond/version.py b/trytond/version.py
index d6a2ba0..788322a 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.0.4"
+VERSION = "3.2.0"
 LICENSE = "GPL-3"
 WEBSITE = "http://www.tryton.org/"
diff --git a/trytond/webdav/locale/ca_ES.po b/trytond/webdav/locale/ca_ES.po
index a44eb9a..cd15ec2 100644
--- a/trytond/webdav/locale/ca_ES.po
+++ b/trytond/webdav/locale/ca_ES.po
@@ -28,7 +28,7 @@ msgstr "Ruta"
 
 msgctxt "field:ir.attachment,shares:"
 msgid "Shares"
-msgstr "Directori compartit"
+msgstr "Directoris compartits"
 
 msgctxt "field:ir.attachment,url:"
 msgid "URL"
@@ -124,7 +124,7 @@ msgstr "Usuari"
 
 msgctxt "field:webdav.share,write_date:"
 msgid "Write Date"
-msgstr "Data modicación"
+msgstr "Data modificació"
 
 msgctxt "field:webdav.share,write_uid:"
 msgid "Write User"
diff --git a/trytond/webdav/locale/es_AR.po b/trytond/webdav/locale/es_AR.po
index 341d49e..593b79d 100644
--- a/trytond/webdav/locale/es_AR.po
+++ b/trytond/webdav/locale/es_AR.po
@@ -112,7 +112,7 @@ msgstr "Ruta"
 
 msgctxt "field:webdav.share,rec_name:"
 msgid "Name"
-msgstr "Nombre campo"
+msgstr "Nombre"
 
 msgctxt "field:webdav.share,url:"
 msgid "URL"
diff --git a/trytond/webdav/locale/es_CO.po b/trytond/webdav/locale/es_CO.po
index 37ad2d4..c2d65ff 100644
--- a/trytond/webdav/locale/es_CO.po
+++ b/trytond/webdav/locale/es_CO.po
@@ -7,12 +7,12 @@ msgid ""
 "You can not create an attachment named \"%(attachment)s in collection "
 "\"%(collection)s\" because there is already a collection with that name."
 msgstr ""
-"No puede crear un adjunto llamado \"%(attachment)s\" porque existe una "
-"colección con el mismo nombre."
+"No puede crear un adjunto llamado \"%(attachment)s\" en la colección "
+"\"%(collections)s\" con el porque hay una colección con el ese nombre."
 
 msgctxt "error:webdav.collection:"
 msgid "The collection name must be unique inside a collection!"
-msgstr "¡El nombre de la colección debe ser único dentro de dicha colección!"
+msgstr "El nombre de la colección debe ser único dentro de dicha colección!"
 
 msgctxt "error:webdav.collection:"
 msgid ""
@@ -40,7 +40,7 @@ msgstr "Hijos"
 
 msgctxt "field:webdav.collection,complete_name:"
 msgid "Complete Name"
-msgstr "Nombre completo"
+msgstr "Nombre Completo"
 
 msgctxt "field:webdav.collection,create_date:"
 msgid "Create Date"
@@ -112,7 +112,7 @@ msgstr "Ruta"
 
 msgctxt "field:webdav.share,rec_name:"
 msgid "Name"
-msgstr "Nombre del campo"
+msgstr "Nombre"
 
 msgctxt "field:webdav.share,url:"
 msgid "URL"
@@ -164,7 +164,7 @@ msgstr "Colección"
 
 msgctxt "model:webdav.share,name:"
 msgid "Share"
-msgstr "Recurso compartido"
+msgstr "Recurso Compartido"
 
 msgctxt "view:ir.attachment:"
 msgid "WebDAV"
@@ -180,7 +180,7 @@ msgstr "Colecciones"
 
 msgctxt "view:webdav.share:"
 msgid "Share"
-msgstr "Recurso compartido"
+msgstr "Recurso Compartido"
 
 msgctxt "view:webdav.share:"
 msgid "Shares"
diff --git a/trytond/webdav/locale/es_ES.po b/trytond/webdav/locale/es_ES.po
index 70f0f69..3b7312a 100644
--- a/trytond/webdav/locale/es_ES.po
+++ b/trytond/webdav/locale/es_ES.po
@@ -7,7 +7,7 @@ msgid ""
 "You can not create an attachment named \"%(attachment)s in collection "
 "\"%(collection)s\" because there is already a collection with that name."
 msgstr ""
-"No puede crear el adjunto con el nombre \"%(attachment)s al directorio "
+"No puede crear el adjunto con el nombre \"%(attachment)s en el directorio "
 "\"%(collection)s\" porque ya existe otro con este nombre."
 
 msgctxt "error:webdav.collection:"
@@ -124,7 +124,7 @@ msgstr "Usuario"
 
 msgctxt "field:webdav.share,write_date:"
 msgid "Write Date"
-msgstr "Fecha modicación"
+msgstr "Fecha modificación"
 
 msgctxt "field:webdav.share,write_uid:"
 msgid "Write User"
diff --git a/trytond/webdav/webdav.py b/trytond/webdav/webdav.py
index d62179d..d35b1dc 100644
--- a/trytond/webdav/webdav.py
+++ b/trytond/webdav/webdav.py
@@ -797,39 +797,34 @@ class Attachment(ModelSQL, ModelView):
         return result
 
     @classmethod
-    def set_shares(cls, attachments, name, value):
+    def set_shares(cls, attachments, name, values):
         Share = Pool().get('webdav.share')
 
-        if not value:
+        if not values:
             return
 
-        for action in value:
-            if action[0] == 'create':
-                to_create = []
-                for attachment in attachments:
-                    for values in action[1]:
-                        values = values.copy()
-                        values['path'] = attachment.path
-                        to_create.append(values)
-                if to_create:
-                    Share.create(to_create)
-            elif action[0] == 'write':
-                Share.write(action[1], action[2])
-            elif action[0] == 'delete':
-                Share.delete(Share.browse(action[1]))
-            elif action[0] == 'delete_all':
-                paths = [a.path for a in attachments]
-                shares = Share.search([
-                        ('path', 'in', paths),
-                        ])
-                Share.delete(shares)
-            elif action[0] == 'unlink':
-                pass
-            elif action[0] == 'add':
-                pass
-            elif action[0] == 'unlink_all':
-                pass
-            elif action[0] == 'set':
-                pass
-            else:
-                raise Exception('Bad arguments')
+        def create(vlist):
+            to_create = []
+            for attachment in attachments:
+                for values in vlist:
+                    values = values.copy()
+                    values['path'] = attachment.path
+                    to_create.append(values)
+            if to_create:
+                Share.create(to_create)
+
+        def write(ids, values):
+            Share.write(Share.browse(ids), values)
+
+        def delete(share_ids):
+            Share.delete(Share.browse(share_ids))
+
+        actions = {
+            'create': create,
+            'write': write,
+            'delete': delete,
+            }
+        for value in values:
+            action = value[0]
+            args = value[1:]
+            actions[action](*args)
diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py
index a4b2cc7..c3c725e 100644
--- a/trytond/wizard/wizard.py
+++ b/trytond/wizard/wizard.py
@@ -190,7 +190,14 @@ class Wizard(WarningErrorMixin, URLMixin, PoolBase):
     def delete(cls, session_id):
         "Delete the session"
         Session = Pool().get('ir.session.wizard')
+        end = getattr(cls, cls.end_state, None)
+        if end:
+            wizard = cls(session_id)
+            action = end(wizard)
+        else:
+            action = None
         Session.delete([Session(session_id)])
+        return action
 
     @classmethod
     def execute(cls, session_id, data, state_name):
-- 
tryton-server



More information about the tryton-debian-vcs mailing list